diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index 6995846..9c004b1 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -1,6 +1,6 @@ --- name: Deploy docs -on: # yamllint disable-line rule:truthy +on: # yamllint disable-line rule:truthy push: branches: [main] workflow_dispatch: @@ -13,16 +13,16 @@ jobs: # Grant GITHUB_TOKEN the permissions required to make a gh-pages deployment permissions: - contents: write # to let mkdocs write the new docs - pages: write # to deploy to Pages - id-token: write # allow to generate an OpenID Connect (OIDC) token + contents: write # to let mkdocs write the new docs + pages: write # to deploy to Pages + id-token: write # allow to generate an OpenID Connect (OIDC) token steps: # https://github.com/actions/checkout - name: Checkout uses: actions/checkout@v6.0.2 with: - fetch-depth: 0 # otherwise, you will fail to push refs to dest repo + fetch-depth: 0 # otherwise, you will fail to push refs to dest repo - name: Configure git for the bot # Gives the bot that commits to gh-pages a name & email address @@ -49,6 +49,9 @@ jobs: run: | uv tool install rust-just + - name: Install gen-sqla + run: uv tool install linkml + - name: Install dependencies run: uv sync --dev --no-progress diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index a5ce87d..35dc07f 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -3,7 +3,7 @@ --- name: Build and test -on: # yamllint disable-line rule:truthy +on: # yamllint disable-line rule:truthy push: branches: [main] pull_request: @@ -15,7 +15,6 @@ permissions: {} jobs: test: - runs-on: ubuntu-latest strategy: matrix: @@ -23,7 +22,6 @@ jobs: fail-fast: false steps: - # https://github.com/actions/checkout - name: Check out repository uses: actions/checkout@v6.0.2 @@ -48,6 +46,12 @@ jobs: run: | uv tool install rust-just + - name: Install gen-sqla + run: uv tool install linkml --python ${{ matrix.python-version }} + + - name: Install dependencies + run: uv sync --dev --no-progress --no-install-project + - name: Install project run: uv sync --dev diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..f9ee822 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,80 @@ +name: Model Release + +on: + push: + tags: + - "v*" + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Tag to release (e.g. v1.2.3)" + required: true + +env: + FORCE_COLOR: "1" + RELEASE_TAG: ${{ github.ref_name || github.event.inputs.tag }} + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write # Required to create GitHub Releases and upload assets + + steps: + - name: Checkout repository + uses: actions/checkout@v6.0.2 + with: + fetch-depth: 0 # Required for uv-dynamic-versioning to read git tags + persist-credentials: false + + - name: Set up uv + uses: astral-sh/setup-uv@v7.2.1 + with: + enable-cache: true + + - name: Set up Python + uses: actions/setup-python@v6.2.0 + with: + python-version: "3.10" + + - name: Install just + run: uv tool install rust-just + + # Install gen-sqla before uv sync so the hatch build hook can find it + # when building the editable install during `uv sync --no-install-project`. + - name: Install gen-sqla + run: uv tool install linkml + + - name: Install dependencies + run: uv sync --dev --no-progress --no-install-project + + - name: Install project + run: uv sync --dev --no-progress + + - name: Generate all model artifacts + run: just site + # Runs gen-project, gen-doc, _gen_ftddd, _gen_sqla, _gen_harmony + # Produces: project/sqlalchemy/, project/harmony/, project/owl/, etc. + # hatch_build.py will skip re-generating the SQLAlchemy file since + # just site already created it (dest_file.exists() guard). + + - name: Build wheel and sdist + run: uv build + + - name: Zip full project artifacts + run: zip -r project-artifacts.zip project/ + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.RELEASE_TAG }} + name: "Release ${{ env.RELEASE_TAG }}" + generate_release_notes: true + update_release: true + files: | + dist/*.whl + dist/*.tar.gz + project-artifacts.zip + fail_on_unmatched_files: true diff --git a/.github/workflows/test_pages_build.yaml b/.github/workflows/test_pages_build.yaml index 88098fc..b8cc9d8 100644 --- a/.github/workflows/test_pages_build.yaml +++ b/.github/workflows/test_pages_build.yaml @@ -1,6 +1,6 @@ name: Preview documentation build -on: # yamllint disable-line rule:truthy +on: # yamllint disable-line rule:truthy pull_request: types: - opened @@ -18,10 +18,10 @@ jobs: run: # Grant GITHUB_TOKEN the permissions required to make a gh-pages deployment permissions: - contents: write # to let mkdocs write the new docs - pages: write # to deploy to Pages - id-token: write # allow to generate an OpenID Connect (OIDC) token - pull-requests: write # add comment on the PR with the preview URL + contents: write # to let mkdocs write the new docs + pages: write # to deploy to Pages + id-token: write # allow to generate an OpenID Connect (OIDC) token + pull-requests: write # add comment on the PR with the preview URL runs-on: ubuntu-latest steps: # https://github.com/actions/checkout @@ -48,6 +48,9 @@ jobs: run: | uv tool install rust-just + - name: Install gen-sqla + run: uv tool install linkml + - name: Install dependencies run: uv sync --dev --no-progress diff --git a/.gitignore b/.gitignore index e67997c..df2b606 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# local stuff +.bash_history_local +.envrc + # generated part of documentation /docs/elements/*.md /docs/schema/*.yaml diff --git a/justfile b/justfile index 2374ac9..209e2f3 100644 --- a/justfile +++ b/justfile @@ -76,7 +76,11 @@ clean: _clean_project # (Re-)Generate project and documentation locally [group('model development')] -site: gen-project gen-doc +site: gen-project gen-doc gensqla + +# SQL Alchemy model +[group('model development')] +gensqla: _gen_sqla # Deploy documentation site to Github Pages [group('deployment')] diff --git a/project.justfile b/project.justfile index bf84fbc..a32825b 100644 --- a/project.justfile +++ b/project.justfile @@ -1 +1,9 @@ ## Add your own just recipes here. This is imported by the main justfile. + +# Overriding recipes from the root justfile by adding a recipe with the same +# name in this file is not possible until a known issue in just is fixed, +# https://github.com/casey/just/issues/2540 +[group('model development')] +_gen_sqla: + mkdir -p {{dest}}/sqlalchemy && \ + uv run gen-sqla {{source_schema_path}} --declarative > {{dest}}/sqlalchemy/{{schema_name}}.py diff --git a/pyproject.toml b/pyproject.toml index 418756c..bc950b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,8 @@ dev = [ "mkdocs-mermaid2-plugin>=1.1.1", "jupyter>=1.0.0", "mknotebooks>= 0.8.0", + "hatchling>=1.27.0", + "tomli>=2.0.0" ] # See https://hatch.pypa.io/latest/config/build/#file-selection for how to @@ -40,6 +42,22 @@ vcs = "git" style = "pep440" fallback-version = "0.0.0" +[tool.hatch.build.targets.sdist] +exclude = [ + ".direnv", + ".venv", + "scratch" +] +include = [ + "project/sqlalchemy/", + "scripts/", +] + +# Release the SQL Alchemy files +[tool.hatch.build.hooks.custom] +path = "scripts/hatch_build.py" + + # Ref.: https://docs.pytest.org/en/stable/reference/reference.html#configuration-options [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/scripts/hatch_build.py b/scripts/hatch_build.py new file mode 100755 index 0000000..f873099 --- /dev/null +++ b/scripts/hatch_build.py @@ -0,0 +1,69 @@ +import shutil +import subprocess +from pathlib import Path + +try: + import tomllib # Python 3.11+ +except ImportError: + import tomli as tomllib # Python 3.9 / 3.10 + +from hatchling.builders.hooks.plugin.interface import BuildHookInterface + + +class CustomBuildHook(BuildHookInterface): + def initialize(self, version, build_data): + root = Path(self.root) + + with open(root / "pyproject.toml", "rb") as f: + pyproject = tomllib.load(f) + schema_name = pyproject["project"]["name"] + + source_schema_path = ( + root / "src" / schema_name / "schema" / f"{schema_name}.yaml" + ) + + # Generate directly into the package dir - this gets included in the + # sdist naturally and is present when hatchling builds the wheel + pkg_dir = root / schema_name + pkg_dir.mkdir(parents=True, exist_ok=True) + dest_file = pkg_dir / f"{schema_name}.py" + + if not dest_file.exists(): + gen_sqla = root / ".venv" / "bin" / "gen-sqla" + if not gen_sqla.exists(): + found = shutil.which("gen-sqla") + if not found: + raise RuntimeError( + "gen-sqla not found. Run `uv sync --group dev` first, " + "or run `just _gen_sqla` before `uv build`." + ) + gen_sqla = Path(found) + + with open(dest_file, "w") as out: + subprocess.run( + [str(gen_sqla), str(source_schema_path), "--declarative"], + stdout=out, + check=True, + ) + + # Also copy to project/sqlalchemy/ for consistency with just site output + sqla_dir = root / "project" / "sqlalchemy" + sqla_dir.mkdir(parents=True, exist_ok=True) + shutil.copy(dest_file, sqla_dir / f"{schema_name}.py") + + build_data["shared_data"] = {} + build_data["packages"] = [schema_name] + build_data["include"] = [f"{schema_name}/*.csv", f"{schema_name}/*.py"] + build_data["artifacts"].append(str(dest_file)) + build_data["force_include"][str(dest_file)] = f"{schema_name}/{schema_name}.py" + + # Bundle harmony CSVs if present + harmony_src = root / "project" / "harmony" + if harmony_src.exists(): + for csv_file in harmony_src.glob("*.csv"): + dest_csv = pkg_dir / csv_file.name + shutil.copy(csv_file, dest_csv) + build_data["artifacts"].append(str(dest_csv)) + build_data["force_include"][str(dest_csv)] = ( + f"{schema_name}/{csv_file.name}" + )