Skip to content

refactor: make invalid contract states unrepresentable #137

refactor: make invalid contract states unrepresentable

refactor: make invalid contract states unrepresentable #137

Workflow file for this run

name: Release Please
on:
push:
branches: [main]
pull_request:
types: [closed]
branches: [main]
workflow_dispatch:
schedule:
- cron: "0 9 * * 1"
concurrency:
group: release-please-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false
permissions: {}
jobs:
release-pr:
name: Create or update release PR
runs-on: ubuntu-latest
permissions:
actions: write # Dispatch CI for the token-created release branch.
contents: write # Update the release branch.
issues: write # Create the autorelease label.
pull-requests: write # Create or update the release PR.
concurrency:
group: release-please-plan
cancel-in-progress: true
if: >-
github.event_name != 'pull_request' &&
(
github.event_name != 'push' ||
(
!contains(github.event.head_commit.message, '[skip ci]') &&
!contains(github.event.head_commit.message, '[no-release]') &&
!contains(github.event.head_commit.message, 'chore(main): release')
)
)
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: main
fetch-depth: 0
fetch-tags: true
persist-credentials: false
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
with:
python-version: "3.12"
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: "24.15.0"
package-manager-cache: false
- name: Install git-cliff
uses: taiki-e/install-action@41049aa56687c35e0afa74eed4f09cec4f9afabf # v2
with:
tool: git-cliff
- name: Determine release baseline
id: version
run: |
set -euo pipefail
current_version=$(uv version --short)
npm_version=$(node -p "require('./npm/package.json').version")
registry_version=$(jq -r '.version' server.json)
registry_package_version=$(jq -r '.packages[0].version' server.json)
if [[ "$current_version" != "$npm_version" ]] ||
[[ "$current_version" != "$registry_version" ]] ||
[[ "$current_version" != "$registry_package_version" ]]; then
echo "Python, npm, and MCP Registry versions must match $current_version" >&2
exit 1
fi
latest_tag=$(git describe --tags --abbrev=0 --match 'v[0-9]*.[0-9]*.[0-9]*' 2>/dev/null || true)
if [[ -n "$latest_tag" ]]; then
latest_version="${latest_tag#v}"
if [[ "$current_version" != "$latest_version" ]]; then
echo "::notice::Package version $current_version is awaiting tag v$current_version"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
baseline="$latest_tag"
else
latest_version="$current_version"
baseline=$(
git log -1 \
-G "^version = \\\"${current_version}\\\"$" \
--format=%H \
-- pyproject.toml
)
if [[ -z "$baseline" ]]; then
echo "Could not locate the commit that introduced version $current_version" >&2
exit 1
fi
fi
commits_since=$(git rev-list --count "${baseline}..HEAD")
if [[ "$commits_since" -eq 0 ]]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
{
echo "baseline=$baseline"
echo "latest_version=$latest_version"
echo "skip=false"
} >> "$GITHUB_OUTPUT"
- name: Check for existing release PR
id: existing-pr
if: steps.version.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
branch="release-flameox"
existing=$(
gh pr list \
--state open \
--base main \
--head "$branch" \
--json number,headRefName \
--jq '.[0] // empty | "\(.number) \(.headRefName)"' 2>/dev/null || true
)
if [[ -n "$existing" ]]; then
{
echo "number=${existing%% *}"
echo "branch=${existing#* }"
} >> "$GITHUB_OUTPUT"
else
{
echo "number="
echo "branch="
} >> "$GITHUB_OUTPUT"
fi
- name: Create release branch
id: branch
if: steps.version.outputs.skip != 'true'
env:
EXISTING_BRANCH: ${{ steps.existing-pr.outputs.branch }}
run: |
set -euo pipefail
branch="${EXISTING_BRANCH:-release-flameox}"
git checkout -B "$branch" origin/main
echo "branch=$branch" >> "$GITHUB_OUTPUT"
- name: Bump synchronized package versions
if: steps.version.outputs.skip != 'true'
run: |
set -euo pipefail
uv version --bump patch --no-sync
version=$(uv version --short)
npm version "$version" --prefix npm --no-git-tag-version
jq --arg version "$version" \
'.version = $version | .packages[0].version = $version' \
server.json > server.json.tmp
mv server.json.tmp server.json
uv lock
{
echo "RELEASE_VERSION=$version"
echo "RELEASE_TAG=v$version"
} >> "$GITHUB_ENV"
- name: Generate changelog
if: steps.version.outputs.skip != 'true'
env:
BASELINE: ${{ steps.version.outputs.baseline }}
GITHUB_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ github.token }}
run: |
git cliff \
--config cliff.toml \
--tag "$RELEASE_TAG" \
--prepend CHANGELOG.md \
"${BASELINE}..HEAD"
- name: Commit and push release branch
if: steps.version.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
RELEASE_BRANCH: ${{ steps.branch.outputs.branch }}
run: |
gh auth setup-git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add \
pyproject.toml \
uv.lock \
npm/package.json \
npm/package-lock.json \
server.json \
CHANGELOG.md
git commit -m "chore(main): release flameox $RELEASE_VERSION"
git fetch origin \
"refs/heads/$RELEASE_BRANCH:refs/remotes/origin/$RELEASE_BRANCH" \
2>/dev/null || true
git push --force-with-lease origin "$RELEASE_BRANCH"
- name: Create or update release PR
if: steps.version.outputs.skip != 'true'
env:
BASELINE: ${{ steps.version.outputs.baseline }}
EXISTING_NUMBER: ${{ steps.existing-pr.outputs.number }}
GH_TOKEN: ${{ github.token }}
RELEASE_BRANCH: ${{ steps.branch.outputs.branch }}
run: |
set -euo pipefail
body=$(
git cliff \
--config cliff.toml \
--tag "$RELEASE_TAG" \
"${BASELINE}..HEAD^"
)
gh label create autorelease \
--color "ededed" \
--description "Automated release pull request" \
--force
if [[ -n "$EXISTING_NUMBER" ]]; then
gh pr edit "$EXISTING_NUMBER" \
--title "chore(main): release flameox $RELEASE_VERSION" \
--body "$body" \
--add-label autorelease
else
gh pr create \
--base main \
--head "$RELEASE_BRANCH" \
--title "chore(main): release flameox $RELEASE_VERSION" \
--label autorelease \
--body "$body"
fi
- name: Dispatch release PR validation
if: steps.version.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
RELEASE_BRANCH: ${{ steps.branch.outputs.branch }}
run: gh workflow run ci.yml --ref "$RELEASE_BRANCH"
create-tag:
name: Create release tag
runs-on: ubuntu-latest
permissions:
actions: write # Dispatch the isolated publishing workflow.
contents: write # Push the verified release tag.
if: >-
github.event_name == 'pull_request' &&
github.event.action == 'closed' &&
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'autorelease')
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: main
fetch-depth: 0
persist-credentials: false
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5
with:
python-version: "3.12"
# This job only reads the project version; avoid creating an empty cache
# that setup-uv later fails to save during post-job cleanup.
enable-cache: false
- name: Resolve and verify release version
id: version
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
set -euo pipefail
version=$(sed -n 's/^chore(main): release flameox \([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)$/\1/p' <<< "$PR_TITLE")
if [[ -z "$version" ]]; then
echo "Could not extract a stable version from release PR title: $PR_TITLE" >&2
exit 1
fi
if [[ "$(uv version --short)" != "$version" ]] ||
[[ "$(node -p "require('./npm/package.json').version")" != "$version" ]] ||
[[ "$(jq -r '.version' server.json)" != "$version" ]] ||
[[ "$(jq -r '.packages[0].version' server.json)" != "$version" ]]; then
echo "Merged package and MCP Registry versions do not match $version" >&2
exit 1
fi
{
echo "version=$version"
echo "tag=v$version"
} >> "$GITHUB_OUTPUT"
- name: Create and push tag
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.version.outputs.tag }}
run: |
gh auth setup-git
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$RELEASE_TAG" -m "$RELEASE_TAG"
git push origin "$RELEASE_TAG"
- name: Dispatch release workflow
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.version.outputs.tag }}
run: |
gh workflow run release.yml \
--ref "$RELEASE_TAG" \
--field tag="$RELEASE_TAG"