Release #265
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| # Triggers after CI succeeds on main. | |
| # 1. Deploys GitHub Pages (every push) | |
| # 2. Creates GitHub Release + publishes to PyPI (only when VERSION changes) | |
| # | |
| # NOTE: GitHub Pages must be configured to deploy from "GitHub Actions" source | |
| # (Settings → Pages → Source → GitHub Actions) | |
| on: | |
| workflow_run: | |
| workflows: [CI] | |
| types: [completed] | |
| jobs: | |
| deploy-pages: | |
| name: Deploy GitHub Pages | |
| runs-on: ubuntu-latest | |
| if: >- | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_branch == 'main' | |
| permissions: | |
| pages: write | |
| id-token: write | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deploy.outputs.page_url }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24 | |
| cache: npm | |
| cache-dependency-path: website/package-lock.json | |
| - name: Build Astro site | |
| run: | | |
| cd website | |
| npm ci | |
| npm run build | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@v6 | |
| - name: Generate PWA demo page | |
| run: | | |
| sudo apt-get install -y qrencode | |
| uv run meridian dev preview --output website/dist/demo | |
| - name: Add deploy artifacts | |
| run: | | |
| # Copy CLI files into Astro dist | |
| cp install.sh website/dist/install.sh | |
| cp setup.sh website/dist/setup.sh | |
| cp VERSION website/dist/version | |
| # Generate checksums | |
| cd website/dist | |
| sha256sum install.sh setup.sh > SHA256SUMS | |
| cd ../.. | |
| # Bundle AI docs | |
| mkdir -p website/dist/ai | |
| for f in website/src/content/docs/en/cli-reference.md website/src/content/docs/en/architecture.md website/src/content/docs/en/troubleshooting.md website/src/content/docs/en/deploy.md website/src/content/docs/en/relay.md website/src/content/docs/en/recovery.md; do | |
| awk 'BEGIN{skip=0} /^---$/{skip++;next} skip<2{next} {print}' "$f" | |
| echo "" | |
| done > website/dist/ai/reference.md | |
| - uses: actions/upload-pages-artifact@v4 | |
| with: | |
| path: website/dist | |
| - name: Deploy to GitHub Pages | |
| id: deploy | |
| uses: actions/deploy-pages@v4 | |
| release: | |
| name: Create GitHub Release | |
| runs-on: ubuntu-latest | |
| if: >- | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_branch == 'main' | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| fetch-depth: 0 | |
| - name: Read version | |
| id: version | |
| run: | | |
| VERSION=$(cat VERSION | tr -d '[:space:]') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Check if tag already exists | |
| id: check_tag | |
| run: | | |
| if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Determine release type from semver | |
| id: semver | |
| if: steps.check_tag.outputs.exists == 'false' | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| # Get previous tag | |
| PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | |
| if [ -z "$PREV_TAG" ]; then | |
| echo "type=major" >> "$GITHUB_OUTPUT" | |
| echo "notes=Initial release" >> "$GITHUB_OUTPUT" | |
| else | |
| PREV="${PREV_TAG#v}" | |
| IFS='.' read -r pm pi pp <<< "$PREV" | |
| IFS='.' read -r cm ci cp <<< "$VERSION" | |
| if [ "$pm" != "$cm" ]; then | |
| echo "type=major" >> "$GITHUB_OUTPUT" | |
| elif [ "$pi" != "$ci" ]; then | |
| echo "type=minor" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "type=patch" >> "$GITHUB_OUTPUT" | |
| fi | |
| fi | |
| - name: Generate release notes | |
| id: notes | |
| if: steps.check_tag.outputs.exists == 'false' | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| # Extract the changelog section for this version | |
| # Matches from "## [X.Y.Z]" to the next "## [" or end of file | |
| if grep -q "## \[$VERSION\]" CHANGELOG.md; then | |
| sed -n "/^## \[$VERSION\]/,/^## \[/p" CHANGELOG.md | sed '$d' > /tmp/release-notes.md | |
| else | |
| # Fallback to git log if version not in CHANGELOG | |
| PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | |
| if [ -n "$PREV_TAG" ]; then | |
| git log "${PREV_TAG}..HEAD" --pretty=format:"- %s" --no-merges > /tmp/release-notes.md | |
| else | |
| git log --pretty=format:"- %s" --no-merges -20 > /tmp/release-notes.md | |
| fi | |
| fi | |
| - name: Create tag and release | |
| id: create_release | |
| if: steps.check_tag.outputs.exists == 'false' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| TYPE="${{ steps.semver.outputs.type }}" | |
| VERSION="${{ steps.version.outputs.version }}" | |
| # Prefix based on type | |
| case "$TYPE" in | |
| major) TITLE="Meridian $VERSION" ;; | |
| minor) TITLE="Meridian $VERSION" ;; | |
| patch) TITLE="Meridian $VERSION (patch)" ;; | |
| esac | |
| git tag "$TAG" | |
| git push origin "$TAG" | |
| gh release create "$TAG" \ | |
| --title "$TITLE" \ | |
| --notes-file /tmp/release-notes.md \ | |
| --latest | |
| echo "created=true" >> "$GITHUB_OUTPUT" | |
| outputs: | |
| released: ${{ steps.create_release.outputs.created || 'false' }} | |
| publish-pypi: | |
| name: Publish to PyPI | |
| runs-on: ubuntu-latest | |
| needs: release | |
| if: needs.release.outputs.released == 'true' | |
| environment: pypi | |
| permissions: | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Build package | |
| run: | | |
| pip install hatchling build | |
| # Generate AI reference from human docs (not committed, built fresh) | |
| mkdir -p src/meridian/data | |
| for f in website/src/content/docs/en/cli-reference.md website/src/content/docs/en/architecture.md website/src/content/docs/en/troubleshooting.md website/src/content/docs/en/deploy.md website/src/content/docs/en/relay.md website/src/content/docs/en/recovery.md; do | |
| awk 'BEGIN{skip=0} /^---$/{skip++;next} skip<2{next} {print}' "$f" | |
| echo "" | |
| done > src/meridian/data/ai-reference.md | |
| python -m build | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 |