Skip to content

Release

Release #107

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
release_type:
description: 'Release type'
required: true
type: choice
options:
- 'pre-release (next)'
- 'stable release'
concurrency:
group: release
cancel-in-progress: true
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Clean
run: npm run clean
- name: Build
# Needed for test/typecheck/lint/integration. A second rebuild runs
# after `changeset version` so the published dist embeds the bumped
# __VERSION__ (see walkerOS/packages/config/tsup/index.mjs).
run: npx turbo run build --filter='!@walkeros/website'
- name: Test, typecheck, lint, integration
run: npx turbo run test typecheck lint test:integration
- name: Capture changeset info
id: changesets
run: |
CHANGES=""
PACKAGES=""
RELEASE_NEEDED="false"
for file in .changeset/*.md; do
if [ -f "$file" ] && [ "$(basename $file)" != "README.md" ]; then
# Extract package names from frontmatter
PKGS=$(sed -n '/^---$/,/^---$/p' "$file" | grep -oE "'@?walkeros[^']*'" | tr -d "'" | sort -u)
for pkg in $PKGS; do
# Add to packages list (dedupe later)
PACKAGES="$PACKAGES$pkg\n"
done
# Check if core or collector triggers release
if echo "$PKGS" | grep -qE "@walkeros/(core|collector)$"; then
RELEASE_NEEDED="true"
fi
# Capture description (text after frontmatter)
CONTENT=$(sed -n '/^---$/,/^---$/d; p' "$file" | sed '/^$/d')
if [ -n "$CONTENT" ]; then
CHANGES="$CHANGES$CONTENT\n\n"
fi
fi
done
# Dedupe raw bare names (one per line) and markdown-formatted variants
PACKAGES_RAW=$(echo -e "$PACKAGES" | sort -u | grep -v '^$')
PACKAGES_FORMATTED=$(echo "$PACKAGES_RAW" | while read pkg; do
echo "- [$pkg](https://www.npmjs.com/package/$pkg)"
done)
echo "changes<<EOF" >> $GITHUB_OUTPUT
echo -e "$CHANGES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "packages<<EOF" >> $GITHUB_OUTPUT
echo "$PACKAGES_FORMATTED" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "packages_raw<<EOF" >> $GITHUB_OUTPUT
echo "$PACKAGES_RAW" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "release_needed=$RELEASE_NEEDED" >> $GITHUB_OUTPUT
- name: Version packages (stable)
if: inputs.release_type == 'stable release'
run: |
# Exit pre-release mode if active; skip if not in pre-release
if [ -f ".changeset/pre.json" ]; then
npx changeset pre exit
else
echo "Not in pre-release mode, skipping 'pre exit'"
fi
npx changeset version
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Version packages (pre-release)
if: inputs.release_type == 'pre-release (next)'
run: npx changeset version --snapshot next
- name: Update lock file
run: npm install --package-lock-only
- name: Rebuild packages with bumped versions
run: npx turbo run build --filter='!@walkeros/website'
- name: Publish to npm
run: |
if [ "${{ inputs.release_type }}" = "pre-release (next)" ]; then
npx changeset publish --tag next
else
npx changeset publish
fi
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Deprecate prior next versions for this stable
if: inputs.release_type == 'stable release'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
PACKAGES_RAW: ${{ steps.changesets.outputs.packages_raw }}
run: |
while IFS= read -r pkg; do
[ -z "$pkg" ] && continue
stable_version=$(npm view "$pkg" version 2>/dev/null) || continue
[ -z "$stable_version" ] && continue
next_versions=$(npm view "$pkg" versions --json 2>/dev/null | \
STABLE="$stable_version" node -e "
const versions = JSON.parse(require('fs').readFileSync(0, 'utf8'));
const stable = process.env.STABLE;
const re = new RegExp('^' + stable.replace(/[.]/g, '\\\\.') + '-next[.\\\\-]');
versions
.filter(v => re.test(v))
.forEach(v => console.log(v));
" || true)
if [ -z "$next_versions" ]; then
echo "$pkg: no next versions matching ${stable_version}-next*"
continue
fi
while IFS= read -r v; do
echo "Deprecating $pkg@$v"
npm deprecate "$pkg@$v" "Superseded by $pkg@$stable_version" \
|| echo " (deprecate failed for $pkg@$v)"
done <<< "$next_versions"
done <<< "$PACKAGES_RAW"
- name: Commit version changes (stable only)
if: inputs.release_type == 'stable release'
run: |
git add -A
git commit -m "chore: version packages" || echo "No changes to commit"
git push
- name: Create GitHub Release
if: |
inputs.release_type == 'stable release' &&
steps.changesets.outputs.release_needed == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="v$(node -p "require('./packages/collector/package.json').version")"
cat > /tmp/release-notes.md << 'NOTES_EOF'
## Changes
${{ steps.changesets.outputs.changes }}
## Published Packages
${{ steps.changesets.outputs.packages }}
NOTES_EOF
gh release create "$VERSION" \
--title "walkerOS $VERSION" \
--notes-file /tmp/release-notes.md
# Docker image publishing
- name: Get CLI version
id: versions
run: |
CLI_VERSION=$(node -p "require('./packages/cli/package.json').version")
echo "cli=$CLI_VERSION" >> $GITHUB_OUTPUT
- name: Verify CLI published to npm
run: |
CLI_VERSION=${{ steps.versions.outputs.cli }}
for i in $(seq 1 12); do
if npm view "@walkeros/cli@$CLI_VERSION" version 2>/dev/null; then
echo "Verified: @walkeros/cli@$CLI_VERSION exists on npm"
exit 0
fi
echo "Attempt $i/12: waiting for npm propagation..."
sleep 15
done
echo "ERROR: @walkeros/cli@$CLI_VERSION not found on npm after publish"
exit 1
- name: Check if Flow image exists
id: check-flow
run: |
if docker manifest inspect walkeros/flow:${{ steps.versions.outputs.cli }} 2>/dev/null; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Check if CLI image exists
id: check-cli
run: |
if docker manifest inspect walkeros/cli:${{ steps.versions.outputs.cli }} 2>/dev/null; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Login to Docker Hub
if: steps.check-flow.outputs.exists == 'false' || steps.check-cli.outputs.exists == 'false'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Flow image
if: steps.check-flow.outputs.exists == 'false'
uses: docker/build-push-action@v6
with:
context: ./packages/cli
file: ./packages/cli/Dockerfile
push: true
build-args: |
CLI_VERSION=${{ steps.versions.outputs.cli }}
tags: |
walkeros/flow:${{ steps.versions.outputs.cli }}
walkeros/flow:${{ inputs.release_type == 'stable release' && 'latest' || 'next' }}
- name: Build and push CLI image
if: steps.check-cli.outputs.exists == 'false'
uses: docker/build-push-action@v6
with:
context: ./packages/cli
file: ./packages/cli/Dockerfile.cli
push: true
build-args: |
CLI_VERSION=${{ steps.versions.outputs.cli }}
tags: |
walkeros/cli:${{ steps.versions.outputs.cli }}
walkeros/cli:${{ inputs.release_type == 'stable release' && 'latest' || 'next' }}
- name: Comment on PR
if: steps.changesets.outputs.packages != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER=$(gh pr list --head "${{ github.ref_name }}" --state open --json number --jq '.[0].number' || true)
if [ -n "$PR_NUMBER" ] && [ "$PR_NUMBER" != "null" ]; then
if [ "${{ inputs.release_type }}" = "pre-release (next)" ]; then
EMOJI="📦"
TITLE="Pre-release published (next)"
INSTALL_TAG="@next"
DOCKER_TAG=":next"
else
EMOJI="🚀"
TITLE="Stable release published"
INSTALL_TAG="@latest"
DOCKER_TAG=":latest"
fi
# Build Docker section if any image was published
DOCKER_SECTION=""
if [ "${{ steps.check-flow.outputs.exists }}" = "false" ] || [ "${{ steps.check-cli.outputs.exists }}" = "false" ]; then
DOCKER_SECTION="
🐳 **Docker images published**"
if [ "${{ steps.check-flow.outputs.exists }}" = "false" ]; then
DOCKER_SECTION="$DOCKER_SECTION
- walkeros/flow:${{ steps.versions.outputs.cli }} ($DOCKER_TAG)"
fi
if [ "${{ steps.check-cli.outputs.exists }}" = "false" ]; then
DOCKER_SECTION="$DOCKER_SECTION
- walkeros/cli:${{ steps.versions.outputs.cli }} ($DOCKER_TAG)"
fi
DOCKER_SECTION="$DOCKER_SECTION
Docker: \`docker pull walkeros/flow$DOCKER_TAG\`"
fi
BODY="$EMOJI **$TITLE**
**Packages**
${{ steps.changesets.outputs.packages }}
Install: \`npm i @walkeros/core$INSTALL_TAG\`$DOCKER_SECTION"
gh pr comment "$PR_NUMBER" --body "$BODY"
echo "Commented on PR #$PR_NUMBER"
else
echo "No open PR found for branch ${{ github.ref_name }}"
fi