diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 388ef11..cfb02a8 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,371 +1,210 @@ # ============================================================================== -# WORKFLOW: Create Widoco Documentation +# WORKFLOW: Generate & Deploy Versioned Documentation # ============================================================================== -# Purpose: Generates and deploys human-readable HTML documentation for the ontology +# Purpose: Build Widoco HTML documentation and deploy to GitHub Pages with +# full version history preserved. # -# This workflow: -# 1. Detects when ontology files are built/updated -# 2. Generates HTML documentation using Widoco from the *-full.owl file -# 3. Deploys documentation to GitHub Pages for public access +# ╔═════════════════════════════════════════════════════════════════════╗ +# ║ BREAKING CHANGE vs. previous docs.yml ║ +# ║ ───────────────────────────────────────────────────────────────── ║ +# ║ This workflow now deploys to the `gh-pages` BRANCH instead of ║ +# ║ using the GitHub Actions Pages deployment API. ║ +# ║ ║ +# ║ REQUIRED one-time change in your repository settings: ║ +# ║ Settings → Pages → Build and deployment → Source ║ +# ║ Change from: "GitHub Actions" ║ +# ║ Change to: "Deploy from a branch" ║ +# ║ Branch: gh-pages / (root) ║ +# ║ ║ +# ║ WHY this change was made: ║ +# ║ The old approach (actions/deploy-pages) replaces the entire ║ +# ║ Pages site on every deploy — making versioned docs impossible. ║ +# ║ The gh-pages branch approach (peaceiris/actions-gh-pages with ║ +# ║ keep_files: true) accumulates version directories without ║ +# ║ overwriting older releases. ║ +# ║ ║ +# ║ MIGRATION from old approach: ║ +# ║ Your existing Pages content will be replaced on the next run. ║ +# ║ If you want to preserve it, manually copy the content of your ║ +# ║ existing Pages deployment into the gh-pages branch before ║ +# ║ triggering this workflow. ║ +# ╚═════════════════════════════════════════════════════════════════════╝ # -# Widoco (WIzard for DOCumenting Ontologies) creates professional documentation -# that includes: -# - Class hierarchies and definitions -# - Property descriptions and domains/ranges -# - Annotation property documentation -# - Interactive WebVOWL diagrams -# - Multi-language support (English and German) +# ┌─────────────────────────────────────────────────────────────────────┐ +# │ Resulting GitHub Pages structure │ +# │ ───────────────────────────────────────────────────────────────── │ +# │ / root index — lists all versions │ +# │ /dev/ always the latest build from main (push) │ +# │ /dev/doc/ Widoco HTML — development version │ +# │ /dev/doc/ontology.ttl TTL (served by Widoco) │ +# │ /dev/doc/ontology.jsonld JSON-LD (served by Widoco) │ +# │ /dev/doc/ontology.rdf RDF/XML (served by Widoco) │ +# │ /2025-11-20/ date-tagged release (preserved forever) │ +# │ /2025-11-20/doc/ Widoco HTML for that release │ +# │ /1.0.0/ semver release (also supported) │ +# │ /1.0.0/doc/ Widoco HTML for v1.0.0 │ +# └─────────────────────────────────────────────────────────────────────┘ # -# Execution Chain Position: -# setup-repo → refresh-imports → qc → [DOCS] +# How the version is determined: +# - From release.yml dispatch: client_payload.version = "1.0.0" +# - From workflow_dispatch: version input (leave empty for "latest") +# - From regular qc.yml push: no version in payload → "latest" # -# This is the final step in the workflow chain. -# -# Read more about Widoco: https://github.com/dgarijo/Widoco -# Read more about GitHub Pages: https://docs.github.com/en/pages +# Customisation: +# - Widoco language: change -lang en-de (see step "Build Widoco docs") +# - Widoco version: change the JAR URL (see step "Build Widoco docs") +# - Memory: set JAVA_OPTS=-Xmx4G if Widoco runs out of memory # ============================================================================== -name: Create Widoco Documentation +name: Generate Versioned Documentation -# ============================================================================== -# WORKFLOW TRIGGERS -# ============================================================================== -# This workflow can be triggered in three ways: -# 1. Via repository_dispatch from qc workflow (after build completes) -# 2. Automatically on pushes to main branch that modify ontology files -# 3. Manually via workflow_dispatch for forced doc regeneration -# -# Note: Pull requests trigger documentation build for validation but do not -# deploy to GitHub Pages (see deploy job conditions). -# -# IMPORTANT: Push triggers skip commits made by github-actions[bot] to prevent -# duplicate runs when the workflow chain is triggered via repository_dispatch. -# ============================================================================== on: - # ============================================================================ - # TRIGGER 1: Repository Dispatch (from qc workflow) - # ============================================================================ - # Listens for 'trigger-docs' event dispatched by qc.yml - # This ensures documentation is generated immediately after ontology build - # ============================================================================ + # ── From qc.yml (regular build) or release.yml (versioned release) ──────── + # release.yml passes {"version": "1.0.0"} in client_payload. + # qc.yml passes no version → docs are published under /latest/. repository_dispatch: types: [trigger-docs] - # ============================================================================ - # TRIGGER 2: Push Events to Main Branch (for production deployments) - # ============================================================================ - # Triggers when ontology files are pushed to main branch - # - # Why main branch only? - # - GitHub Pages typically deploys from main/master - # - Prevents documentation churn from feature branch work - # - Ensures only stable, reviewed changes trigger public doc updates - # - # Why src/ontology/**? - # - Documentation is generated from ontology files - # - Only relevant when ontology content changes - # - Excludes unrelated changes (README, workflow files, etc.) - # - # To enable preview docs for other branches: - # Change branches: ["main"] to branches: ["*"] - # - # Note: This trigger is skipped for commits made by github-actions[bot] - # to prevent duplicate runs when triggered via repository_dispatch - # ============================================================================ - # push: - # branches: ["main"] # Production deployments from main only - # paths: - # - 'src/ontology/**' - - # ============================================================================ - # TRIGGER 3: Pull Request Events (for preview/validation) - # ============================================================================ - # Triggers documentation build during PR review - # Note: Deploy step is skipped for PRs (see deploy job condition) - # - # Purpose: - # - Validates that documentation can be generated successfully - # - Allows preview of documentation changes before merge - # - Catches Widoco errors early in development cycle - # # ============================================================================ - # pull_request: - # branches: ["*"] - # paths: - # - 'src/ontology/**' - - # ============================================================================ - # TRIGGER 4: Manual Trigger (for forced regeneration) - # ============================================================================ - # Allows manual execution via GitHub UI - # Useful for: - # - Forcing documentation refresh without code changes - # - Testing documentation generation - # - Recovering from failed deployments - # ============================================================================ + # ── Manual trigger ──────────────────────────────────────────────────────── workflow_dispatch: - -# ============================================================================== -# ENVIRONMENT VARIABLES -# ============================================================================== -# Global variables available to all jobs in this workflow -# ============================================================================== -env: - # Default branch for git operations - DEFAULT_BRANCH: main + inputs: + version: + description: "Version to document (e.g. 1.0.0). Leave empty to rebuild 'latest'." + required: false + type: string + default: '' # ============================================================================== # PERMISSIONS # ============================================================================== -# Granular permissions for GITHUB_TOKEN to enable GitHub Pages deployment -# -# Why these specific permissions? -# - contents: read : Allows reading repository files for building -# - pages: write : Enables uploading artifacts to GitHub Pages -# - id-token: write : Supports OIDC authentication for Pages deployment -# -# These are the minimum permissions needed for secure Pages deployment. -# Read more: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions -# ============================================================================== permissions: - contents: read # Read repo files - pages: write # Upload to Pages - id-token: write # OIDC authentication + contents: write # Required to push to gh-pages branch # ============================================================================== -# CONCURRENCY CONTROL -# ============================================================================== -# Ensures only one Pages deployment runs at a time -# -# Why is this important? -# - Prevents race conditions during deployment -# - Ensures sequential processing of queued deployments -# - Avoids corrupted or partial documentation deployments -# -# Configuration: -# - group: "pages" : All Pages deployments share this concurrency group -# - cancel-in-progress: false : Don't cancel running deployments; queue instead -# -# Effect: If multiple commits trigger this workflow rapidly, they queue -# and deploy sequentially rather than racing or canceling each other. +# CONCURRENCY # ============================================================================== +# Serialize deployments — never run two at the same time or the gh-pages branch +# could get corrupted by two concurrent pushes. concurrency: - group: "pages" - cancel-in-progress: false # Queue deployments instead of canceling + group: gh-pages-deploy + cancel-in-progress: false # ============================================================================== # JOBS # ============================================================================== jobs: + # ============================================================================ # JOB 1: check # ============================================================================ - # Pre-flight check to verify required ontology file exists - # - # Purpose: - # - Prevents build failures in repositories without built ontologies - # - Skips workflow gracefully if *-full.owl doesn't exist - # - Provides clear feedback about missing files - # - # Outputs: - # - ontology-exists: Boolean flag indicating if file was found - # - ontology-file: Path to the *-full.owl file (if exists) - # - # Why check for *-full.owl? - # - This is the standard ODK output file for complete ontology releases - # - Contains merged ontology with all imports - # - Required input for Widoco documentation generation - # - # CRITICAL: Skips workflow for commits made by github-actions[bot] on push - # to prevent duplicate runs when triggered via repository_dispatch + # Verify the ontology has been scaffolded and find the full OWL file. + # Resolves the version string for this build. # ============================================================================ check: + name: Pre-flight check runs-on: ubuntu-latest - - # CRITICAL: Skip this workflow if triggered by push from github-actions[bot] - # This prevents duplicate runs in the workflow chain - # The chain uses repository_dispatch, so push triggers from bot commits are redundant - # if: | - # github.event_name != 'push' || - # github.event.head_commit.author.name != 'github-actions[bot]' - - # Job outputs (available to dependent jobs via needs.check.outputs) + outputs: ontology-exists: ${{ steps.check.outputs.exists }} - ontology-file: ${{ steps.check.outputs.file }} - + ontology-file: ${{ steps.check.outputs.file }} + version: ${{ steps.ver.outputs.version }} + steps: - # ======================================================================== - # STEP 1.1: Checkout Repository - # ======================================================================== - # Fetch repository code to check for ontology file - # Full history included for consistency with other workflows - # ======================================================================== - name: Checkout repository uses: actions/checkout@v4 - with: - fetch-depth: 0 # Full history - # ======================================================================== - # STEP 1.2: Check for Ontology File - # ======================================================================== - # Searches for *-full.owl file in src/ontology/ - # - # Search logic: - # - find: Recursively searches directory - # - -name "*-full.owl": Matches any file ending in -full.owl - # - -type f: Only matches regular files (not directories) - # - head -1: Takes first match (handles multiple matches gracefully) - # - # Output handling: - # - If found: Sets exists=true and file= - # - If not found: Sets exists=false and file="" - # ======================================================================== - name: Check for ontology file id: check run: | - # Search for *-full.owl file - ONTOLOGY_FILE=$(find src/ontology/ -name "*-full.owl" -type f | head -1) - - # Check if file was found + ONTOLOGY_FILE=$(find src/ontology/ -name "*-full.owl" -type f 2>/dev/null | head -1) if [ -z "$ONTOLOGY_FILE" ]; then - echo "No *-full.owl file found in src/ontology/ directory." - echo "This usually means the ontology hasn't been built yet." + echo "No *-full.owl found — ontology not yet built. Skipping docs." echo "exists=false" >> $GITHUB_OUTPUT echo "file=" >> $GITHUB_OUTPUT else - echo "Found ontology file: $ONTOLOGY_FILE" + echo "Found: $ONTOLOGY_FILE" echo "exists=true" >> $GITHUB_OUTPUT echo "file=$ONTOLOGY_FILE" >> $GITHUB_OUTPUT fi + # ── Determine version ───────────────────────────────────────────────── + # Priority: repository_dispatch payload → workflow_dispatch input → "latest" + # ────────────────────────────────────────────────────────────────────── + - name: Resolve version + id: ver + run: | + PAYLOAD="${{ github.event.client_payload.version }}" + INPUT="${{ inputs.version }}" + VERSION="${PAYLOAD:-${INPUT:-latest}}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Building docs for version: $VERSION" + # ============================================================================ - # JOB 2: build + # JOB 2: build-and-deploy # ============================================================================ - # Generates HTML documentation using Widoco - # - # Dependencies: Requires check job to pass and find ontology file - # Condition: Only runs if ontology-exists == 'true' + # Runs Widoco, copies serializations, assembles the Pages site, and pushes + # to the gh-pages branch. # - # Process: - # 1. Setup GitHub Pages environment - # 2. Download Widoco JAR (pinned version for reproducibility) - # 3. Generate HTML documentation with Widoco - # 4. Create default entry point (index.html) - # 5. Upload documentation as Pages artifact + # Uses peaceiris/actions-gh-pages with keep_files: true so that each run + # ADDS a new version directory without deleting existing ones. # ============================================================================ - build: - runs-on: ubuntu-latest - - # Job dependencies + build-and-deploy: + name: Build & deploy docs needs: check - - # Conditional execution: only run if ontology file exists if: needs.check.outputs.ontology-exists == 'true' - + runs-on: ubuntu-latest + steps: - # ======================================================================== - # STEP 2.1: Checkout Repository - # ======================================================================== - # Fetch repository code for documentation generation - # ======================================================================== - name: Checkout repository uses: actions/checkout@v4 with: - fetch-depth: 0 # Full history + fetch-depth: 0 - # ======================================================================== - # STEP 2.2: Setup GitHub Pages Environment - # ======================================================================== - # Configures Node.js and other dependencies for Pages builds - # - # This action: - # - Detects and validates Pages configuration - # - Sets up build environment variables - # - Prepares for artifact upload - # - # Uses v5 for latest features and compatibility - # Read more: https://github.com/actions/configure-pages - # ======================================================================== - - name: Setup Pages - uses: actions/configure-pages@v5 + # ── Fetch existing gh-pages content ─────────────────────────────────── + # We need the list of already-published versions so we can regenerate + # the root index.html with all versions (old + new). + # ────────────────────────────────────────────────────────────────────── + - name: Fetch existing gh-pages content + run: | + git fetch origin gh-pages:gh-pages 2>/dev/null || true + if git show-ref --verify --quiet refs/heads/gh-pages; then + git worktree add ./gh-pages-existing gh-pages + else + mkdir -p ./gh-pages-existing + echo "gh-pages branch does not exist yet — first deployment." + fi - # ======================================================================== - # STEP 2.3: Build HTML Documentation with Widoco - # ======================================================================== - # Downloads and runs Widoco to generate HTML documentation - # - # Widoco Configuration: - # Version: v1.4.25 (pinned for reproducibility) - # Java Requirement: JDK 11+ (provided by runner) - # - # Command-line Options Explained: - # -ontFile - # Input: Path to the OWL ontology file - # Example: src/ontology/myonto-full.owl - # - # -outFolder - # Output: Directory for generated HTML files - # Set to ./_site for GitHub Pages compatibility - # - # -uniteSections - # Combines all documentation sections into unified pages - # Improves navigation and reduces page count - # - # -includeAnnotationProperties - # Documents annotation properties (labels, comments, etc.) - # Essential for complete ontology documentation - # - # -lang en-de - # Generates documentation in English and German - # Creates index-en.html and index-de.html - # To change languages: Use ISO codes (e.g., en-es for English/Spanish) - # - # -getOntologyMetadata - # Extracts and displays ontology metadata - # Includes: title, description, version, authors, etc. - # - # -noPlaceHolderText - # Removes placeholder text from documentation - # Creates cleaner, more professional output - # - # -rewriteAll - # Forces complete regeneration of all files - # Ensures documentation is fully up-to-date - # - # -webVowl - # Includes interactive WebVOWL visualization - # Provides graphical view of ontology structure - # Users can explore classes and relationships visually - # - # Memory Requirements: - # - Typical usage: 2-4GB RAM - # - Large ontologies may need more memory (adjust runner if needed) - # - Widoco uses Java, so heap size is configurable via JAVA_OPTS + # ── Download Widoco ─────────────────────────────────────────────────── + # Pinned to v1.4.25. To upgrade, update the URL below. + # ────────────────────────────────────────────────────────────────────── + - name: Download Widoco + run: | + wget -q -O widoco.jar \ + https://github.com/dgarijo/Widoco/releases/download/v1.4.25/widoco-1.4.25-jar-with-dependencies_JDK-11.jar + + # ── Build Widoco docs ───────────────────────────────────────────────── + # Output goes to ./_site//doc/ # - # Output Files: - # - index-en.html : English documentation - # - index-de.html : German documentation - # - sections/ : Individual documentation sections - # - webvowl/ : WebVOWL visualization files - # - resources/ : CSS, JavaScript, images + # Widoco flags: + # -lang en-de English + German docs (change as needed) + # -uniteSections All sections in one page per lang + # -includeAnnotationProperties Documents annotation properties + # -getOntologyMetadata Reads title/authors/version from OWL file + # -noPlaceHolderText Removes template placeholder text + # -rewriteAll Always regenerate (don't skip existing files) + # -webVowl Include interactive WebVOWL diagram # - # Read more: https://github.com/dgarijo/Widoco#usage - # ======================================================================== - - name: Build HTML documentation with Widoco + # To change language, edit -lang. Available codes: en, de, es, fr, it, ... + # ────────────────────────────────────────────────────────────────────── + - name: Build Widoco docs run: | - # Download pinned Widoco release for reproducibility - # Using specific version ensures consistent output across builds - echo "Downloading Widoco v1.4.25..." - wget -O widoco.jar https://github.com/dgarijo/Widoco/releases/download/v1.4.25/widoco-1.4.25-jar-with-dependencies_JDK-11.jar - - # Create output directory for GitHub Pages - mkdir ./_site - - # Generate documentation with Widoco - echo "Generating documentation with Widoco..." + VERSION="${{ needs.check.outputs.version }}" + ONTFILE="${{ needs.check.outputs.ontology-file }}" + mkdir -p "./_site/${VERSION}/doc" + java -jar widoco.jar \ - -ontFile ${{ needs.check.outputs.ontology-file }} \ - -outFolder ./_site \ + -ontFile "$ONTFILE" \ + -outFolder "./_site/${VERSION}/doc" \ -uniteSections \ -includeAnnotationProperties \ -lang en-de \ @@ -373,115 +212,123 @@ jobs: -noPlaceHolderText \ -rewriteAll \ -webVowl - - echo "Documentation generation complete." - # ======================================================================== - # STEP 2.4: Add Default Entry Point - # ======================================================================== - # Creates index.html as default landing page for GitHub Pages - # - # Why is this needed? - # - GitHub Pages serves index.html by default - # - Widoco generates index-en.html, index-de.html, etc. - # - Without index.html, Pages shows directory listing or 404 - # - # Solution: Copy English version to index.html - # - Provides default language for visitors - # - Users can switch to other languages via links in documentation - # - # To change default language: - # Replace index-en.html with index-de.html (or other language code) - # ======================================================================== - - name: Add default entry point + # Default entry point (English) + cp "./_site/${VERSION}/doc/index-en.html" "./_site/${VERSION}/doc/index.html" || true + + # ── Note: serializations are served by Widoco automatically ───────────── + # Widoco copies the source ontology file and generates download buttons + # for JSON-LD, RDF/XML, N-Triples, and TTL in the HTML documentation. + # The files are placed at the root of the doc/ output as ontology.jsonld, + # ontology.rdf, ontology.ntx, ontology.ttl — no manual copy needed. + + # ── Update /dev/ ────────────────────────────────────────────────────── + # /dev/ always mirrors the latest build from main (every push). + # Named releases (e.g. /2025-11-20/ or /1.0.0/) are preserved alongside. + # This gives a stable URL for development/preview access. + # ────────────────────────────────────────────────────────────────────── + - name: Update /dev/ run: | - echo "Creating default index.html from English version..." - cp ./_site/index-en.html ./_site/index.html - echo "Default entry point created." + VERSION="${{ needs.check.outputs.version }}" + rm -rf ./_site/dev + cp -r "./_site/${VERSION}" ./_site/dev - # ======================================================================== - # STEP 2.5: Upload Pages Artifact - # ======================================================================== - # Packages documentation for GitHub Pages deployment - # - # What this does: - # - Compresses _site/ directory into artifact - # - Uploads artifact to GitHub (accessible to deploy job) - # - Prepares documentation for deployment - # - # Artifact contents: - # - All HTML files (index.html, sections, etc.) - # - WebVOWL visualization files - # - CSS, JavaScript, images - # - Multi-language documentation - # - # Uses v3 for stability - # Read more: https://github.com/actions/upload-pages-artifact - # ======================================================================== - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 + # ── Build root index.html ───────────────────────────────────────────── + # Generates a simple version listing page as the site root. + # Reads existing version directories from the previously fetched + # gh-pages branch and adds the current version to the list. + # ────────────────────────────────────────────────────────────────────── + - name: Build root index.html + run: | + VERSION="${{ needs.check.outputs.version }}" - # ============================================================================ - # JOB 3: deploy - # ============================================================================ - # Deploys generated documentation to GitHub Pages - # - # Dependencies: Requires build job to complete successfully - # Condition: Only runs for non-PR events (prevents PR preview deployments) - # - # Environment: github-pages - # - Protected environment for production deployments - # - Can require approvals if configured in repo settings - # - Provides deployment URL for accessing published docs - # - # Process: - # 1. Downloads artifact from build job - # 2. Deploys to GitHub Pages - # 3. Returns public URL for documentation - # ============================================================================ - deploy: - # Environment configuration - # Uses 'github-pages' environment for protected deployments - # Output URL is accessible via steps.deployment.outputs.page_url - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - runs-on: ubuntu-latest - - # Job dependencies - needs: build - - # Conditional execution: skip for pull requests - # Why? - # - PRs shouldn't auto-deploy to production Pages - # - Prevents documentation churn during review - # - Deploy happens automatically after PR merge to main - # - # For branch-specific previews: - # Remove this condition and configure GitHub Pages to use branch deployments - if: github.event_name != 'pull_request' - - steps: - # ======================================================================== - # STEP 3.1: Deploy to GitHub Pages - # ======================================================================== - # Deploys documentation artifact to GitHub Pages - # - # What this does: - # 1. Downloads artifact uploaded by build job - # 2. Extracts files to Pages hosting environment - # 3. Publishes documentation at configured Pages URL - # 4. Returns deployment URL as output + python3 - <<'PYEOF' + import os, re + + current = os.environ.get("CURRENT_VERSION", "latest") + + # Collect versions from existing gh-pages + the one just built + existing = set() + for d in os.listdir("./gh-pages-existing"): + if os.path.isdir(f"./gh-pages-existing/{d}") and d not in ("latest", ".git"): + existing.add(d) + existing.add(current) + existing.discard("latest") + + # Sort: semver entries first (newest first), then non-semver entries + def sort_key(v): + m = re.match(r'^(\d+)\.(\d+)\.(\d+)$', v) + return (1, -int(m.group(1)), -int(m.group(2)), -int(m.group(3))) if m else (2, v) + + versions = sorted(existing, key=sort_key) + + rows = "" + for v in versions: + tag = " ← current" if v == current else "" + rows += f""" + + {v}{tag} + HTML Documentation + """ + + html = f""" + + + + Ontology Documentation + + + + +

Ontology Documentation

+

+ Development build: dev/doc/ +  —  updated on every push to main +

+

Releases

+ + + + + {rows} + +
VersionDocumentation
+ + + """ + with open("./_site/index.html", "w") as f: + f.write(html) + print("root index.html written.") + PYEOF + env: + CURRENT_VERSION: ${{ needs.check.outputs.version }} + + # ── Deploy to gh-pages branch ───────────────────────────────────────── + # keep_files: true → existing version directories are NOT deleted. + # Only new/updated files are written. # - # Deployment URL format: - # - https://.github.io// (for user/org repos) - # - https://.github.io// (for org repos) - # - Custom domain if configured in repo Pages settings + # This is the key mechanism that preserves old release docs. # - # Uses v4 for latest deployment features - # Read more: https://github.com/actions/deploy-pages - # ======================================================================== - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 \ No newline at end of file + # NOTE: If you need to DELETE an old version from Pages, you must + # manually remove it from the gh-pages branch via: + # git clone --branch gh-pages + # rm -rf / + # git commit -am "Remove version " && git push + # ────────────────────────────────────────────────────────────────────── + - name: Deploy to gh-pages branch + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./_site + keep_files: true + user_name: github-actions[bot] + user_email: github-actions[bot]@users.noreply.github.com + commit_message: "docs: publish ${{ needs.check.outputs.version }}" diff --git a/.github/workflows/enforce-tags.yml b/.github/workflows/enforce-tags.yml new file mode 100644 index 0000000..1a3b2de --- /dev/null +++ b/.github/workflows/enforce-tags.yml @@ -0,0 +1,51 @@ +# ============================================================================== +# 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 new file mode 100644 index 0000000..897a860 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,229 @@ +# ============================================================================== +# WORKFLOW: Release Ontology +# ============================================================================== +# Purpose: Build a versioned ontology release, 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 │ +# └─────────────────────────────────────────────────────────────────────┘ +# +# 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 +# +# Version format: semver without the "v" prefix (e.g. 1.0.0, 2.1.3) +# +# Execution chain: +# [this] → docs (versioned Widoco build + GitHub Pages deploy) +# ============================================================================== + +name: Release Ontology + +on: + # ── Manual trigger ──────────────────────────────────────────────────────── + workflow_dispatch: + inputs: + version: + description: Release version — semver X.Y.Z without v prefix (e.g. 1.0.0). Becomes owl:versionIRI ontbase/1.0.0. + required: true + type: string + release_notes: + description: Release notes (markdown). Leave empty to auto-generate from commits. + required: false + 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 + +# ============================================================================== +# JOBS +# ============================================================================== +jobs: + release: + name: Build & Release + runs-on: ubuntu-latest + container: obolibrary/odkfull:v1.6 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Full history needed for git tag operations + + # ── 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 + 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. + # ────────────────────────────────────────────────────────────────────── + - 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]+$' || { + 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. + # + # IMP=false / PAT=false skips import refresh for speed — imports were + # already refreshed by the last regular build. Remove IMP=false to + # force a full import refresh as part of the release. + # ────────────────────────────────────────────────────────────────────── + - name: Build release artifacts + env: + ROBOT_ENV: ROBOT_JAVA_ARGS=-Xmx6G + run: | + cd src/ontology + make IMP=false PAT=false \ + VERSION=${{ steps.version.outputs.version }} \ + 'ANNOTATE_ONTOLOGY_VERSION=annotate -V $(ONTBASE)/$(VERSION) --annotation owl:versionInfo $(VERSION)' \ + 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. + # ────────────────────────────────────────────────────────────────────── + - 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 + 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 }} + run: | + 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" \) \ + ! -name "*-edit.owl" | tr '\n' ' ') + + if [ -z "$ASSETS" ]; then + echo "::error::No release artifacts found in src/ontology/" + exit 1 + fi + + 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}\"" + 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: + token: ${{ secrets.GITHUB_TOKEN }} + event-type: trigger-docs + client-payload: '{"version": "${{ steps.version.outputs.version }}"}' diff --git a/README.md b/README.md index 04b4bd8..89ed97c 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ - [Quick Start](#quick-start) - [Configuration Files](#configuration-files) - [CI/CD Workflows](#cicd-workflows) +- [Release Process](#release-process) - [Development Guide](#development-guide) - [Import Architecture](#import-architecture) - [ID Range Allocation](#id-range-allocation) @@ -96,6 +97,8 @@ application-ontology-template/ ├── .github/workflows/ # CI/CD pipeline (5 workflows) │ ├── setup-repo.yml # Initial ontology scaffolding (22 steps) │ ├── qc.yml # PR quality checks + full build +│ ├── release.yml # Versioned release + GitHub release creation +│ ├── enforce-tags.yml # Reject non-semver tags │ ├── refresh-imports.yml # Re-extract external imports via SLME │ ├── update-repo.yml # Sync repo structure from ODK config │ └── docs.yml # Generate Widoco HTML documentation @@ -380,14 +383,20 @@ env: | **Trigger** | Manual dispatch / `repository_dispatch: trigger-docs` | | **What it does** | Generates HTML documentation using [Widoco](https://github.com/dgarijo/Widoco) and deploys to GitHub Pages | +### `release.yml` — Release Ontology +| | | +|:---|:---| +| **Trigger** | Manual dispatch (enter version in UI) / push `v*.*.*` git tag | +| **Container** | `obolibrary/odkfull:v1.6` | +| **What it does** | Builds release artifacts → sets PMDco-convention `owl:versionIRI` → commits to `main` → creates GitHub release with OWL/TTL/JSON attached → triggers versioned docs | + ### Workflow Chain +<<<<<<< HEAD ``` -setup-repo ──► qc (ontology-build) ──► docs (Widoco) - ▲ - push to main ───────┘ - -any pull_request ──► qc (pr-checks) ──► PR comment +setup-repo ──► qc (build) ──► docs (Widoco) + ▲ + push to main ───┘ ``` ---