Skip to content

Release

Release #265

Workflow file for this run

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