From f7bb7378b993c19949374aeb41c04c095b24e668 Mon Sep 17 00:00:00 2001 From: Thomas Hanke Date: Fri, 17 Apr 2026 13:07:16 +0200 Subject: [PATCH 1/3] fix: force rebuild of release artifacts by touching edit file --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 78473b4..7c91e68 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -140,6 +140,7 @@ jobs: ROBOT_ENV: ROBOT_JAVA_ARGS=-Xmx6G run: | cd src/ontology + touch log-edit.owl make PAT=false \ VERSION=${{ steps.version.outputs.version }} \ 'ANNOTATE_ONTOLOGY_VERSION=annotate -V $(ONTBASE)/$(VERSION) --annotation owl:versionInfo $(VERSION)' \ From 0c9f7feded5a06fa762b5dad724f015bef4f9907 Mon Sep 17 00:00:00 2001 From: Thomas Hanke Date: Fri, 17 Apr 2026 14:17:10 +0200 Subject: [PATCH 2/3] fix: force unconditional make rebuild with -B flag --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7c91e68..f78b77c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: run: | cd src/ontology touch log-edit.owl - make PAT=false \ + make -B PAT=false \ VERSION=${{ steps.version.outputs.version }} \ 'ANNOTATE_ONTOLOGY_VERSION=annotate -V $(ONTBASE)/$(VERSION) --annotation owl:versionInfo $(VERSION)' \ refresh-imports all_assets From e514ded11d98f0e462731ad73562d905884feca5 Mon Sep 17 00:00:00 2001 From: Thomas Hanke Date: Fri, 17 Apr 2026 14:29:43 +0200 Subject: [PATCH 3/3] refactor: stamp versionIRI via robot annotate, remove enforce-tags - release workflow now uses robot annotate to set semver versionIRI instead of full ODK rebuild (qc.yml owns artifact rebuilds) - adds early guard for existing release to prevent duplicate release errors - tag validation moved into release workflow, enforce-tags.yml removed --- .github/workflows/enforce-tags.yml | 51 --------- .github/workflows/release.yml | 177 +++++++++-------------------- 2 files changed, 56 insertions(+), 172 deletions(-) delete mode 100644 .github/workflows/enforce-tags.yml diff --git a/.github/workflows/enforce-tags.yml b/.github/workflows/enforce-tags.yml deleted file mode 100644 index 1a3b2de..0000000 --- a/.github/workflows/enforce-tags.yml +++ /dev/null @@ -1,51 +0,0 @@ -# ============================================================================== -# WORKFLOW: Enforce Tag Naming Convention -# ============================================================================== -# Purpose: Reject any git tag that does not follow the required semver format. -# -# Required format: v.. (e.g. v1.0.0, v2.3.11) -# -# Tags that do NOT match are immediately deleted and the push fails with a -# clear error message. This prevents accidental date-based tags (v2025-11-20), -# branch-style tags, or other formats from entering the repository. -# -# Note: This workflow fires on every tag push. For conforming tags it exits -# in ~5 seconds and costs almost nothing. -# -# Alternative (native, no workflow needed): -# GitHub Rulesets → New ruleset → Tags → Metadata restrictions → -# Branch/tag name pattern → regex: v\d+\.\d+\.\d+ -# (Requires GitHub Team/Pro or public repository) -# ============================================================================== - -name: Enforce Tag Convention - -on: - push: - tags: - - "*" - -permissions: - contents: write # Required to delete non-conforming tags - -jobs: - check-tag: - name: Validate tag format - runs-on: ubuntu-latest - - steps: - - name: Check tag matches v.. - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - TAG="${GITHUB_REF_NAME}" - echo "Tag pushed: $TAG" - - if echo "$TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then - echo "Tag '$TAG' conforms to semver convention — OK." - else - echo "::error::Tag '$TAG' rejected. Only semver tags are allowed (e.g. v1.0.0)." - echo "Deleting non-conforming tag..." - gh api repos/${{ github.repository }}/git/refs/tags/${TAG} -X DELETE - exit 1 - fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f78b77c..0478e7a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,34 +1,25 @@ # ============================================================================== # WORKFLOW: Release Ontology # ============================================================================== -# Purpose: Build a versioned ontology release, create a GitHub release with +# Purpose: Stamp release version into artifacts, create a GitHub release with # attached serialization artifacts, and trigger documentation rebuild. # # ┌─────────────────────────────────────────────────────────────────────┐ # │ What this workflow does │ # │ ───────────────────────────────────────────────────────────────── │ -# │ 1. Builds all release artifacts via ODK `make all_assets` │ -# │ 2. Overrides owl:versionIRI to follow the PMDco convention: │ -# │ / (semver, no file extension) │ -# │ e.g. https://w3id.org/pmd/myont/1.0.0 │ -# │ NOT the ODK default: …/releases/2025-11-20/myont.owl │ -# │ 2. Commits release artifacts back to main │ -# │ 3. Creates a git tag v pointing to that commit │ -# │ 4. Creates a GitHub release with OWL / TTL / JSON attached │ -# │ 5. Dispatches trigger-docs so docs.yml rebuilds the versioned │ -# │ documentation site │ +# │ 1. Validates semver tag format (replaces enforce-tags.yml) │ +# │ 2. Stamps owl:versionIRI and owl:versionInfo into all artifacts │ +# │ using ROBOT annotate — no ODK rebuild (qc.yml owns that) │ +# │ versionIRI: / (PMDco convention) │ +# │ e.g. https://w3id.org/pmd/log/1.0.0 │ +# │ 3. Commits stamped artifacts back to main │ +# │ 4. Creates a git tag v pointing to that commit │ +# │ 5. Creates a GitHub release with OWL / TTL attached │ +# │ 6. Dispatches trigger-docs so docs.yml rebuilds versioned docs │ # └─────────────────────────────────────────────────────────────────────┘ # # Triggers: -# A. Manual (workflow_dispatch) — you enter the version number in the UI -# B. Automatic — push a git tag matching v*.*.* (semver) -# -# How to release: -# Option A (recommended): -# Actions → Release Ontology → Run workflow → enter version e.g. 1.0.0 -# -# Option B: -# git tag v1.0.0 && git push origin v1.0.0 +# Manual only — Actions → Release Ontology → Run workflow → enter version # # Version format: semver without the "v" prefix (e.g. 1.0.0, 2.1.3) # @@ -39,7 +30,6 @@ name: Release Ontology on: - # ── Manual trigger ──────────────────────────────────────────────────────── workflow_dispatch: inputs: version: @@ -52,22 +42,9 @@ on: type: string default: "" - # ── Tag push trigger ────────────────────────────────────────────────────── - # Push a v*.*.* tag to trigger a release without going through the UI. - # The version is derived from the tag name (v prefix is stripped). - push: - tags: - - v[0-9]+.[0-9]+.[0-9]+ - -# ============================================================================== -# PERMISSIONS -# ============================================================================== permissions: - contents: write # Required for: git tag, git push, gh release create, artifact commit + contents: write -# ============================================================================== -# JOBS -# ============================================================================== jobs: release: name: Build & Release @@ -78,110 +55,76 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 0 # Full history needed for git tag operations + fetch-depth: 0 - # ── Guard: only run if repo has been scaffolded ─────────────────────── - # src/ontology/ only exists after setup-repo has run. - # A release against the uninitialized template is not meaningful. - # ────────────────────────────────────────────────────────────────────── - name: Check ontology scaffold exists - id: scaffold run: | - if [ -d "src/ontology" ] && [ -f "src/ontology/Makefile" ]; then - echo "present=true" >> $GITHUB_OUTPUT - else - echo "present=false" >> $GITHUB_OUTPUT + if [ ! -d "src/ontology" ] || [ ! -f "src/ontology/Makefile" ]; then echo "::error::src/ontology not found. Run the Setup New Ontology workflow first." exit 1 fi - # ── Resolve version string ──────────────────────────────────────────── - # For workflow_dispatch: use the version input directly. - # For tag push: strip the "v" prefix from the tag name. - # ────────────────────────────────────────────────────────────────────── + # ── Validate and resolve version ───────────────────────────────────── - name: Resolve version id: version run: | - if [ "${{ github.event_name }}" = "push" ]; then - VERSION="${GITHUB_REF_NAME#v}" - else - VERSION="${{ inputs.version }}" - fi - - # Validate semver format - echo "$VERSION" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' || { + VERSION="${{ inputs.version }}" + echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$' || { echo "::error::Version '$VERSION' is not valid semver (expected X.Y.Z)" exit 1 } - echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Releasing version: $VERSION" - # ── Build release artifacts with PMDco-convention versionIRI ───────── - # ODK default ANNOTATE_ONTOLOGY_VERSION sets: - # owl:versionIRI /releases//.owl ← wrong - # - # We override it to use the PMDco convention: - # owl:versionIRI / (semver, no file extension) - # - # Example for myont v1.0.0 with ONTBASE=https://w3id.org/pmd/myont: - # owl:versionIRI https://w3id.org/pmd/myont/1.0.0 - # owl:versionInfo 1.0.0 - # - # Single-quoting ANNOTATE_ONTOLOGY_VERSION prevents the shell from - # expanding $(ONTBASE) and $(VERSION) — make expands them itself using - # values already defined in the ODK-generated Makefile. - # - # refresh-imports is run before all_assets (same as qc.yml ontology-build) - # to ensure SLME modules are extracted from current mirrors. - # ────────────────────────────────────────────────────────────────────── - - name: Build release artifacts + # ── Guard: fail early if release already exists ─────────────────────── + - name: Check release does not already exist env: - ROBOT_ENV: ROBOT_JAVA_ARGS=-Xmx6G + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - cd src/ontology - touch log-edit.owl - make -B PAT=false \ - VERSION=${{ steps.version.outputs.version }} \ - 'ANNOTATE_ONTOLOGY_VERSION=annotate -V $(ONTBASE)/$(VERSION) --annotation owl:versionInfo $(VERSION)' \ - refresh-imports all_assets - - # ── Commit release artifacts to main ───────────────────────────────── - # Versioned artifacts must be in main so that docs.yml can find them - # when building the release documentation page. + VERSION="${{ steps.version.outputs.version }}" + if gh release view "v${VERSION}" > /dev/null 2>&1; then + echo "::error::Release v${VERSION} already exists. Delete it and the tag first." + exit 1 + fi + + # ── Stamp versionIRI into all release artifacts ─────────────────────── + # qc.yml owns the full ODK rebuild on push to main. + # The release workflow only stamps the correct semver versionIRI + # onto the already-built committed artifacts using ROBOT annotate. # ────────────────────────────────────────────────────────────────────── + - name: Stamp version into artifacts + run: | + VERSION="${{ steps.version.outputs.version }}" + ONTBASE="https://w3id.org/pmd/log" + for f in src/ontology/log-full.owl src/ontology/log-base.owl src/ontology/log-simple.owl src/ontology/log.owl; do + [ -f "$f" ] || continue + robot annotate \ + --input "$f" \ + --version-iri "${ONTBASE}/${VERSION}" \ + --annotation owl:versionInfo "${VERSION}" \ + --output "$f" + done + + # ── Commit stamped artifacts ────────────────────────────────────────── - name: Commit release artifacts uses: EndBug/add-and-commit@v9 with: message: Release v${{ steps.version.outputs.version }} cwd: . - add: src/ontology/*.owl src/ontology/*.json src/ontology/*.ttl src/ontology/imports/*.owl --force + add: src/ontology/*.owl src/ontology/*.ttl --force default_author: github_actions push: true # ── Create git tag ──────────────────────────────────────────────────── - # For workflow_dispatch: tag does not yet exist — create it on the - # freshly committed release artifact commit. - # For tag push: tag already exists — skip creation silently. - # ────────────────────────────────────────────────────────────────────── - name: Create git tag - if: github.event_name == 'workflow_dispatch' run: | VERSION="${{ steps.version.outputs.version }}" git config user.email "github-actions[bot]@users.noreply.github.com" git config user.name "github-actions[bot]" - # Tag may already exist if the user manually created it before running this workflow git tag "v${VERSION}" 2>/dev/null && git push origin "v${VERSION}" || \ echo "Tag v${VERSION} already exists — skipping." # ── Create GitHub release ───────────────────────────────────────────── - # Attaches all serialization files as release assets. - # Excludes the edit file (*-edit.owl) — that is the source, not a release - # artifact. - # - # --generate-notes: auto-generate release notes from commit history. - # If release_notes input was provided, it is prepended. - # ────────────────────────────────────────────────────────────────────── - name: Create GitHub release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -189,8 +132,7 @@ jobs: VERSION="${{ steps.version.outputs.version }}" NOTES="${{ inputs.release_notes }}" - # Collect release artifacts (exclude edit file and import sources) - ASSETS=$(find src/ontology -maxdepth 1 \( -name "*.owl" -o -name "*.ttl" -o -name "*.json" \) \ + ASSETS=$(find src/ontology -maxdepth 1 \( -name "*.owl" -o -name "*.ttl" \) \ ! -name "*-edit.owl" | tr '\n' ' ') if [ -z "$ASSETS" ]; then @@ -200,27 +142,20 @@ jobs: echo "Attaching assets: $ASSETS" - # Build gh flags - FLAGS="--title \"Release v${VERSION}\" --tag v${VERSION} --generate-notes" if [ -n "$NOTES" ]; then - FLAGS="$FLAGS --notes \"${NOTES}\"" + gh release create "v${VERSION}" \ + --title "Release v${VERSION}" \ + --notes "$NOTES" \ + --generate-notes \ + $ASSETS + else + gh release create "v${VERSION}" \ + --title "Release v${VERSION}" \ + --generate-notes \ + $ASSETS fi - gh release create "v${VERSION}" \ - --title "Release v${VERSION}" \ - --tag "v${VERSION}" \ - --generate-notes \ - ${NOTES:+--notes-start-tag "$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo '')"} \ - $ASSETS || \ - gh release create "v${VERSION}" \ - --title "Release v${VERSION}" \ - --generate-notes \ - $ASSETS - # ── Trigger versioned documentation build ──────────────────────────── - # Passes the version in the client_payload so docs.yml knows which - # version directory to create under the GitHub Pages site. - # ────────────────────────────────────────────────────────────────────── - name: Trigger versioned documentation build uses: peter-evans/repository-dispatch@v3 with: