Release #107
Workflow file for this run
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 | |
| 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 |