Release Pipeline #3
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 Pipeline | |
| on: | |
| workflow_run: | |
| workflows: ["CI"] | |
| types: [completed] | |
| branches: [main] | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: read | |
| # Gemini API key for release automation (version detection, changelog, release notes). | |
| # ML provider keys (OpenAI, Anthropic, Bedrock) are not needed for this package. | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.CICD_GEMINI_API_KEY_OPEN_RUNTIME }} | |
| jobs: | |
| # ============================================================================ | |
| # Gate: Verify CI passed AND commit is not from release bot | |
| # ============================================================================ | |
| pre-check: | |
| runs-on: ubuntu-latest | |
| if: github.event.workflow_run.conclusion == 'success' | |
| outputs: | |
| should_release: ${{ steps.gate.outputs.should_release }} | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| # Layer 4: Infinite loop prevention (git-based, not event-property-based) | |
| - name: Check for release bot commit | |
| id: gate | |
| run: | | |
| AUTHOR=$(git log -1 --format='%an') | |
| MSG=$(git log -1 --format='%s') | |
| echo "Commit author: $AUTHOR" | |
| echo "Commit subject: $MSG" | |
| if [[ "$AUTHOR" == "github-actions[bot]" ]] || [[ "$MSG" == *"[skip ci]"* ]] || [[ "$MSG" == *"bot(release)"* ]]; then | |
| echo "should_release=false" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Release bot commit — skipping release pipeline." | |
| else | |
| echo "should_release=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| # ============================================================================ | |
| # Job 1: Determine Version | |
| # ============================================================================ | |
| determine-version: | |
| needs: pre-check | |
| if: needs.pre-check.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| new_version: ${{ steps.version.outputs.new_version }} | |
| prev_tag: ${{ steps.version.outputs.prev_tag }} | |
| should_release: ${{ steps.version.outputs.should_release }} | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Configure Git for HTTPS with Token | |
| shell: bash | |
| run: | | |
| TOKEN="${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "git@github.com:" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "ssh://git@github.com/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/open-runtime/".insteadOf "git@github.com:open-runtime/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/pieces-app/".insteadOf "git@github.com:pieces-app/" | |
| - uses: dart-lang/setup-dart@v1.7.1 | |
| with: | |
| sdk: "3.9.2" | |
| - name: Cache Dart pub dependencies | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/.pub-cache | |
| key: ${{ runner.os }}-dart-pub-${{ hashFiles('**/pubspec.yaml') }} | |
| restore-keys: ${{ runner.os }}-dart-pub- | |
| - run: dart pub get | |
| env: | |
| GIT_LFS_SKIP_SMUDGE: "1" | |
| - uses: actions/setup-node@v6.2.0 | |
| with: | |
| node-version: "22" | |
| - run: npm install -g @google/gemini-cli@latest | |
| - name: Cache Go modules (GitHub MCP server) | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-mcp-v1 | |
| restore-keys: ${{ runner.os }}-go-mcp- | |
| - name: Determine version | |
| id: version | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.CICD_GEMINI_API_KEY_OPEN_RUNTIME }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: dart run runtime_ci_tooling:manage_cicd determine-version --output-github-actions | |
| - name: Upload version bump rationale | |
| if: steps.version.outputs.should_release == 'true' | |
| uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: version-bump-rationale | |
| path: .runtime_ci/version_bumps/ | |
| retention-days: 90 | |
| - name: Upload CI/CD audit trail | |
| if: steps.version.outputs.should_release == 'true' | |
| uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: cicd-audit-determine-version | |
| path: .runtime_ci/runs/ | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| # ============================================================================ | |
| # Job 2: Pre-Release Triage | |
| # ============================================================================ | |
| pre-release-triage: | |
| needs: determine-version | |
| if: needs.determine-version.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Configure Git for HTTPS with Token | |
| shell: bash | |
| run: | | |
| TOKEN="${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "git@github.com:" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "ssh://git@github.com/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/open-runtime/".insteadOf "git@github.com:open-runtime/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/pieces-app/".insteadOf "git@github.com:pieces-app/" | |
| - uses: dart-lang/setup-dart@v1.7.1 | |
| with: | |
| sdk: "3.9.2" | |
| - name: Cache Dart pub dependencies | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/.pub-cache | |
| key: ${{ runner.os }}-dart-pub-${{ hashFiles('**/pubspec.yaml') }} | |
| restore-keys: ${{ runner.os }}-dart-pub- | |
| - run: dart pub get | |
| env: | |
| GIT_LFS_SKIP_SMUDGE: "1" | |
| - uses: actions/setup-node@v6.2.0 | |
| with: | |
| node-version: "22" | |
| - run: npm install -g @google/gemini-cli@latest | |
| - name: Cache Go modules (GitHub MCP server) | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-mcp-v1 | |
| restore-keys: ${{ runner.os }}-go-mcp- | |
| - name: Pre-release triage | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.CICD_GEMINI_API_KEY_OPEN_RUNTIME }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| dart run runtime_ci_tooling:manage_cicd triage pre-release \ | |
| --prev-tag "${{ needs.determine-version.outputs.prev_tag }}" \ | |
| --version "${{ needs.determine-version.outputs.new_version }}" | |
| # Find manifest from .runtime_ci/runs/ audit trail | |
| MANIFEST=$(find .runtime_ci/runs -name "issue_manifest.json" -type f 2>/dev/null | sort -r | head -1) | |
| if [ -n "$MANIFEST" ]; then | |
| cp "$MANIFEST" /tmp/issue_manifest.json | |
| else | |
| echo '{"version":"${{ needs.determine-version.outputs.new_version }}","github_issues":[],"sentry_issues":[],"cross_repo_issues":[]}' > /tmp/issue_manifest.json | |
| fi | |
| - uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: issue-manifest | |
| path: /tmp/issue_manifest.json | |
| if-no-files-found: error | |
| retention-days: 1 | |
| - name: Upload CI/CD audit trail | |
| uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: cicd-audit-pre-release-triage | |
| path: .runtime_ci/runs/ | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| # ============================================================================ | |
| # Job 3: Stage 1 -- Explorer Agent | |
| # ============================================================================ | |
| explore-changes: | |
| needs: [determine-version, pre-release-triage] | |
| if: needs.determine-version.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Configure Git for HTTPS with Token | |
| shell: bash | |
| run: | | |
| TOKEN="${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "git@github.com:" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "ssh://git@github.com/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/open-runtime/".insteadOf "git@github.com:open-runtime/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/pieces-app/".insteadOf "git@github.com:pieces-app/" | |
| - uses: dart-lang/setup-dart@v1.7.1 | |
| with: | |
| sdk: "3.9.2" | |
| - name: Cache Dart pub dependencies | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/.pub-cache | |
| key: ${{ runner.os }}-dart-pub-${{ hashFiles('**/pubspec.yaml') }} | |
| restore-keys: ${{ runner.os }}-dart-pub- | |
| - run: dart pub get | |
| env: | |
| GIT_LFS_SKIP_SMUDGE: "1" | |
| - uses: actions/setup-node@v6.2.0 | |
| with: | |
| node-version: "22" | |
| - run: npm install -g @google/gemini-cli@latest | |
| - name: Cache Go modules (GitHub MCP server) | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-mcp-v1 | |
| restore-keys: ${{ runner.os }}-go-mcp- | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: issue-manifest | |
| path: /tmp/ | |
| - name: Stage 1 Explorer | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.CICD_GEMINI_API_KEY_OPEN_RUNTIME }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| dart run runtime_ci_tooling:manage_cicd explore \ | |
| --prev-tag "${{ needs.determine-version.outputs.prev_tag }}" \ | |
| --version "${{ needs.determine-version.outputs.new_version }}" | |
| - name: Create fallback stage1 artifacts if missing | |
| run: | | |
| [ -f /tmp/commit_analysis.json ] || echo '{"commits":[],"note":"Gemini unavailable - no analysis generated"}' > /tmp/commit_analysis.json | |
| [ -f /tmp/pr_data.json ] || echo '{"pull_requests":[],"note":"Gemini unavailable"}' > /tmp/pr_data.json | |
| [ -f /tmp/breaking_changes.json ] || echo '{"breaking_changes":[],"note":"Gemini unavailable"}' > /tmp/breaking_changes.json | |
| - uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: stage1-artifacts | |
| path: | | |
| /tmp/commit_analysis.json | |
| /tmp/pr_data.json | |
| /tmp/breaking_changes.json | |
| if-no-files-found: error | |
| retention-days: 1 | |
| - name: Upload CI/CD audit trail | |
| uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: cicd-audit-explore | |
| path: .runtime_ci/runs/ | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| # ============================================================================ | |
| # Job 4: Stage 2 -- Composer Agent + Documentation | |
| # ============================================================================ | |
| compose-artifacts: | |
| needs: [determine-version, explore-changes] | |
| if: needs.determine-version.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Configure Git for HTTPS with Token | |
| shell: bash | |
| run: | | |
| TOKEN="${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "git@github.com:" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "ssh://git@github.com/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/open-runtime/".insteadOf "git@github.com:open-runtime/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/pieces-app/".insteadOf "git@github.com:pieces-app/" | |
| - uses: dart-lang/setup-dart@v1.7.1 | |
| with: | |
| sdk: "3.9.2" | |
| - name: Cache Dart pub dependencies | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/.pub-cache | |
| key: ${{ runner.os }}-dart-pub-${{ hashFiles('**/pubspec.yaml') }} | |
| restore-keys: ${{ runner.os }}-dart-pub- | |
| - run: dart pub get | |
| env: | |
| GIT_LFS_SKIP_SMUDGE: "1" | |
| - uses: actions/setup-node@v6.2.0 | |
| with: | |
| node-version: "22" | |
| - run: npm install -g @google/gemini-cli@latest | |
| - name: Cache Go modules (GitHub MCP server) | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-mcp-v1 | |
| restore-keys: ${{ runner.os }}-go-mcp- | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: stage1-artifacts | |
| path: /tmp/ | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: issue-manifest | |
| path: /tmp/ | |
| - name: Stage 2 Composer | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.CICD_GEMINI_API_KEY_OPEN_RUNTIME }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| dart run runtime_ci_tooling:manage_cicd compose \ | |
| --prev-tag "${{ needs.determine-version.outputs.prev_tag }}" \ | |
| --version "${{ needs.determine-version.outputs.new_version }}" | |
| - name: Documentation update | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.CICD_GEMINI_API_KEY_OPEN_RUNTIME }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| dart run runtime_ci_tooling:manage_cicd documentation \ | |
| --prev-tag "${{ needs.determine-version.outputs.prev_tag }}" \ | |
| --version "${{ needs.determine-version.outputs.new_version }}" | |
| - uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: composed-artifacts | |
| path: | | |
| CHANGELOG.md | |
| README.md | |
| if-no-files-found: error | |
| retention-days: 1 | |
| - name: Upload CI/CD audit trail | |
| uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: cicd-audit-compose | |
| path: .runtime_ci/runs/ | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| # ============================================================================ | |
| # Job 5: Autodoc (module documentation) — runs parallel to release-notes | |
| # Commits docs/ and autodoc.json to main early so work is preserved even if | |
| # create-release subsequently fails. | |
| # ============================================================================ | |
| autodoc: | |
| name: "Autodoc (module documentation)" | |
| needs: [determine-version] | |
| if: needs.determine-version.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN }} | |
| persist-credentials: true | |
| - name: Configure Git for HTTPS with Token | |
| shell: bash | |
| run: | | |
| TOKEN="${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN }}" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "git@github.com:" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "ssh://git@github.com/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/open-runtime/".insteadOf "git@github.com:open-runtime/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/pieces-app/".insteadOf "git@github.com:pieces-app/" | |
| - name: Set up Dart | |
| uses: dart-lang/setup-dart@v1.7.1 | |
| with: | |
| sdk: 3.9.2 | |
| - name: Cache Dart pub | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/.pub-cache | |
| key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }} | |
| restore-keys: ${{ runner.os }}-pub- | |
| - name: Install dependencies | |
| run: dart pub get | |
| env: | |
| GIT_LFS_SKIP_SMUDGE: "1" | |
| - uses: actions/setup-node@v6.2.0 | |
| with: | |
| node-version: "22" | |
| - run: npm install -g @google/gemini-cli@latest | |
| - name: Cache Go modules (GitHub MCP server) | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-mcp-v1 | |
| restore-keys: ${{ runner.os }}-go-mcp- | |
| - name: Generate module documentation | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.CICD_GEMINI_API_KEY_OPEN_RUNTIME }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: dart run runtime_ci_tooling:manage_cicd autodoc | |
| - name: Create fallback autodoc artifacts if missing | |
| shell: bash | |
| run: | | |
| mkdir -p docs .runtime_ci | |
| if [ ! -f .runtime_ci/autodoc.json ]; then | |
| echo '{"modules":[],"note":"Gemini unavailable - documentation not generated"}' > .runtime_ci/autodoc.json | |
| echo "Created fallback autodoc.json" | |
| fi | |
| - name: Commit generated documentation to main | |
| shell: bash | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add docs/ .runtime_ci/autodoc.json || true | |
| if ! git diff --cached --quiet; then | |
| git commit -m "bot(autodocs): update generated documentation [skip ci]" | |
| git push origin main | |
| echo "Documentation committed and pushed to main." | |
| else | |
| echo "No documentation changes to commit." | |
| fi | |
| - name: Upload autodoc artifacts | |
| uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: autodoc-artifacts | |
| path: | | |
| docs/ | |
| .runtime_ci/autodoc.json | |
| if-no-files-found: error | |
| retention-days: 1 | |
| - name: Upload CI/CD audit (autodoc) | |
| if: always() | |
| uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: cicd-audit-autodoc | |
| path: .runtime_ci/runs/ | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| # ============================================================================ | |
| # Job 6: Stage 3 -- Release Notes Author | |
| # ============================================================================ | |
| release-notes: | |
| needs: [determine-version, explore-changes, pre-release-triage, compose-artifacts, autodoc] | |
| if: needs.determine-version.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Configure Git for HTTPS with Token | |
| shell: bash | |
| run: | | |
| TOKEN="${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "git@github.com:" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "ssh://git@github.com/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/open-runtime/".insteadOf "git@github.com:open-runtime/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/pieces-app/".insteadOf "git@github.com:pieces-app/" | |
| - uses: dart-lang/setup-dart@v1.7.1 | |
| with: | |
| sdk: "3.9.2" | |
| - name: Cache Dart pub dependencies | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/.pub-cache | |
| key: ${{ runner.os }}-dart-pub-${{ hashFiles('**/pubspec.yaml') }} | |
| restore-keys: ${{ runner.os }}-dart-pub- | |
| - run: dart pub get | |
| env: | |
| GIT_LFS_SKIP_SMUDGE: "1" | |
| - uses: actions/setup-node@v6.2.0 | |
| with: | |
| node-version: "22" | |
| - run: npm install -g @google/gemini-cli@latest | |
| - name: Cache Go modules (GitHub MCP server) | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-mcp-v1 | |
| restore-keys: ${{ runner.os }}-go-mcp- | |
| # Download ALL previous stage artifacts for context | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: stage1-artifacts | |
| path: /tmp/ | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: issue-manifest | |
| path: /tmp/ | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: composed-artifacts | |
| path: ./artifacts/ | |
| - uses: actions/download-artifact@v7.0.0 | |
| continue-on-error: true | |
| with: | |
| name: version-bump-rationale | |
| path: ./.runtime_ci/version_bumps/ | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: autodoc-artifacts | |
| path: ./autodoc-artifacts/ | |
| # Copy CHANGELOG from compose stage so release notes can reference it | |
| - name: Apply compose artifacts | |
| run: | | |
| if [ -f ./artifacts/CHANGELOG.md ]; then | |
| cp ./artifacts/CHANGELOG.md ./CHANGELOG.md | |
| fi | |
| if [ -f ./artifacts/README.md ]; then | |
| cp ./artifacts/README.md ./README.md | |
| fi | |
| - name: Stage 3 Release Notes Author | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.CICD_GEMINI_API_KEY_OPEN_RUNTIME }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| dart run runtime_ci_tooling:manage_cicd release-notes \ | |
| --prev-tag "${{ needs.determine-version.outputs.prev_tag }}" \ | |
| --version "${{ needs.determine-version.outputs.new_version }}" | |
| # Consolidate all release notes files under .runtime_ci/release_notes/ before upload. | |
| # Mixing relative and absolute paths in upload-artifact causes path | |
| # resolution issues. Keep everything under one root. | |
| - name: Consolidate release notes | |
| run: | | |
| VERSION="${{ needs.determine-version.outputs.new_version }}" | |
| mkdir -p ".runtime_ci/release_notes/v${VERSION}" | |
| cp /tmp/release_notes_body.md ".runtime_ci/release_notes/v${VERSION}/" 2>/dev/null || true | |
| cp /tmp/migration_guide.md ".runtime_ci/release_notes/v${VERSION}/" 2>/dev/null || true | |
| echo "Contents of .runtime_ci/release_notes/v${VERSION}/:" | |
| ls -la ".runtime_ci/release_notes/v${VERSION}/" 2>/dev/null || echo "(empty)" | |
| - name: Ensure release notes artifact is non-empty | |
| shell: bash | |
| run: | | |
| VERSION="${{ needs.determine-version.outputs.new_version }}" | |
| mkdir -p ".runtime_ci/release_notes/v${VERSION}" | |
| if [ ! -f ".runtime_ci/release_notes/v${VERSION}/release_notes_body.md" ]; then | |
| echo "Release notes unavailable for v${VERSION}." > ".runtime_ci/release_notes/v${VERSION}/release_notes_body.md" | |
| echo "Created fallback release_notes_body.md" | |
| fi | |
| - uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: release-notes-artifacts | |
| path: .runtime_ci/release_notes/ | |
| if-no-files-found: error | |
| retention-days: 1 | |
| - name: Upload CI/CD audit trail | |
| uses: actions/upload-artifact@v6.0.0 | |
| with: | |
| name: cicd-audit-release-notes | |
| path: .runtime_ci/runs/ | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| # ============================================================================ | |
| # Job 6: Create Release | |
| # ============================================================================ | |
| create-release: | |
| needs: [determine-version, compose-artifacts, release-notes] | |
| if: needs.determine-version.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Use PERSONAL_ACCESS_TOKEN for checkout so git push inherits the | |
| # token owner's bypass permissions on enterprise branch rulesets. | |
| # persist-credentials: true keeps the token in git config for push. | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN }} | |
| persist-credentials: true | |
| - name: Configure Git for HTTPS with Token | |
| shell: bash | |
| run: | | |
| TOKEN="${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN }}" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "git@github.com:" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "ssh://git@github.com/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/open-runtime/".insteadOf "git@github.com:open-runtime/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/pieces-app/".insteadOf "git@github.com:pieces-app/" | |
| - uses: dart-lang/setup-dart@v1.7.1 | |
| with: | |
| sdk: "3.9.2" | |
| - name: Cache Dart pub dependencies | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/.pub-cache | |
| key: ${{ runner.os }}-dart-pub-${{ hashFiles('**/pubspec.yaml') }} | |
| restore-keys: ${{ runner.os }}-dart-pub- | |
| - run: dart pub get | |
| env: | |
| GIT_LFS_SKIP_SMUDGE: "1" | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: composed-artifacts | |
| path: ./artifacts/ | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: release-notes-artifacts | |
| path: ./release-notes-artifacts/ | |
| - uses: actions/download-artifact@v7.0.0 | |
| continue-on-error: true | |
| with: | |
| name: version-bump-rationale | |
| path: ./.runtime_ci/version_bumps/ | |
| - name: Download CI/CD audit trails | |
| continue-on-error: true | |
| uses: actions/download-artifact@v7.0.0 | |
| with: | |
| pattern: cicd-audit-* | |
| path: .runtime_ci/runs_incoming/ | |
| merge-multiple: false | |
| - name: Prepare artifacts | |
| run: | | |
| VERSION="${{ needs.determine-version.outputs.new_version }}" | |
| mkdir -p ./artifacts ./.runtime_ci/version_bumps "./.runtime_ci/release_notes/v${VERSION}" | |
| # Stage 3 release notes: downloaded artifact has release_notes/ root | |
| # so files land at ./release-notes-artifacts/vX.X.X/release_notes.md | |
| if [ -d "./release-notes-artifacts/v${VERSION}" ]; then | |
| cp -r "./release-notes-artifacts/v${VERSION}/"* "./.runtime_ci/release_notes/v${VERSION}/" 2>/dev/null || true | |
| echo "Copied Stage 3 artifacts from release-notes-artifacts/v${VERSION}/" | |
| elif [ -d "./release-notes-artifacts" ]; then | |
| # Fallback: search recursively for release_notes.md | |
| FOUND=$(find ./release-notes-artifacts -name "release_notes.md" -type f 2>/dev/null | head -1) | |
| if [ -n "$FOUND" ]; then | |
| cp "$(dirname "$FOUND")"/* "./.runtime_ci/release_notes/v${VERSION}/" 2>/dev/null || true | |
| echo "Found release notes via recursive search: $FOUND" | |
| fi | |
| fi | |
| # Copy release_notes_body.md to /tmp/ for Dart script | |
| if [ -f "./.runtime_ci/release_notes/v${VERSION}/release_notes_body.md" ]; then | |
| cp "./.runtime_ci/release_notes/v${VERSION}/release_notes_body.md" /tmp/release_notes_body.md | |
| elif [ -f "./.runtime_ci/release_notes/v${VERSION}/release_notes.md" ]; then | |
| cp "./.runtime_ci/release_notes/v${VERSION}/release_notes.md" /tmp/release_notes_body.md | |
| fi | |
| # List what we found | |
| echo "Release notes contents:" | |
| ls -la "./.runtime_ci/release_notes/v${VERSION}/" 2>/dev/null || echo "(empty)" | |
| # Merge all downloaded audit trail artifacts from different jobs into | |
| # a single .runtime_ci/runs/ directory so archive-run can find them. | |
| - name: Merge CI/CD audit trails | |
| continue-on-error: true | |
| run: | | |
| dart run runtime_ci_tooling:manage_cicd merge-audit-trails \ | |
| --incoming-dir .runtime_ci/runs_incoming \ | |
| --output-dir .runtime_ci/runs | |
| # Archive BEFORE create-release so .runtime_ci/audit/ is committed alongside | |
| # the release. This replaces the old post-release archive that could | |
| # never work because .runtime_ci/runs/ didn't exist on the fresh runner. | |
| - name: Archive audit trail | |
| run: | | |
| dart run runtime_ci_tooling:manage_cicd archive-run \ | |
| --version "${{ needs.determine-version.outputs.new_version }}" | |
| - name: Create release | |
| env: | |
| GH_TOKEN: ${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN }} | |
| run: | | |
| dart run runtime_ci_tooling:manage_cicd create-release \ | |
| --version "${{ needs.determine-version.outputs.new_version }}" \ | |
| --prev-tag "${{ needs.determine-version.outputs.prev_tag }}" \ | |
| --artifacts-dir ./artifacts \ | |
| --repo "${{ github.repository }}" | |
| # ============================================================================ | |
| # Job 7: Post-Release Triage | |
| # ============================================================================ | |
| post-release-triage: | |
| needs: [determine-version, create-release] | |
| if: needs.determine-version.outputs.should_release == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Configure Git for HTTPS with Token | |
| shell: bash | |
| run: | | |
| TOKEN="${{ secrets.TSAVO_AT_PIECES_PERSONAL_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "git@github.com:" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/".insteadOf "ssh://git@github.com/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/open-runtime/".insteadOf "git@github.com:open-runtime/" | |
| git config --global url."https://x-access-token:${TOKEN}@github.com/pieces-app/".insteadOf "git@github.com:pieces-app/" | |
| - uses: dart-lang/setup-dart@v1.7.1 | |
| with: | |
| sdk: "3.9.2" | |
| - name: Cache Dart pub dependencies | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/.pub-cache | |
| key: ${{ runner.os }}-dart-pub-${{ hashFiles('**/pubspec.yaml') }} | |
| restore-keys: ${{ runner.os }}-dart-pub- | |
| - run: dart pub get | |
| env: | |
| GIT_LFS_SKIP_SMUDGE: "1" | |
| - uses: actions/setup-node@v6.2.0 | |
| with: | |
| node-version: "22" | |
| - run: npm install -g @google/gemini-cli@latest | |
| - name: Cache Go modules (GitHub MCP server) | |
| uses: actions/cache@v5.0.3 | |
| with: | |
| path: ~/go/pkg/mod | |
| key: ${{ runner.os }}-go-mcp-v1 | |
| restore-keys: ${{ runner.os }}-go-mcp- | |
| - uses: actions/download-artifact@v7.0.0 | |
| with: | |
| name: issue-manifest | |
| path: /tmp/ | |
| - name: Post-release triage | |
| env: | |
| GEMINI_API_KEY: ${{ secrets.CICD_GEMINI_API_KEY_OPEN_RUNTIME }} | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| dart run runtime_ci_tooling:manage_cicd triage post-release \ | |
| --version "${{ needs.determine-version.outputs.new_version }}" \ | |
| --release-tag "v${{ needs.determine-version.outputs.new_version }}" \ | |
| --release-url "https://github.com/${{ github.repository }}/releases/tag/v${{ needs.determine-version.outputs.new_version }}" \ | |
| --manifest /tmp/issue_manifest.json |