feat: exercise no-env orchestration 20260703073815-28645931346 #31
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
| # AUTO-GENERATED by cascade - DO NOT EDIT MANUALLY | |
| # Regenerate with: cascade generate-workflow --config .github/manifest.yaml | |
| name: Orchestrate CI/CD | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - 'src/**' | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: 'Dry run mode' | |
| type: boolean | |
| default: false | |
| concurrency: | |
| group: orchestrate-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| actions: read | |
| jobs: | |
| setup: | |
| name: Setup | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| outputs: | |
| run_build_lib: ${{ steps.setup.outputs.run_build_lib }} | |
| head_sha: ${{ steps.setup.outputs.head_sha }} | |
| version: ${{ steps.setup.outputs.version }} | |
| previous_tag: ${{ steps.setup.outputs.previous_tag }} | |
| changelog_base_sha: ${{ steps.setup.outputs.changelog_base_sha }} | |
| base_build_lib: ${{ steps.setup.outputs.base_build_lib }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup CLI | |
| uses: stablekernel/cascade/.github/actions/setup-cli@v0.7.0-rc.0 | |
| with: | |
| token: ${{ secrets.CASCADE_STATE_TOKEN }} | |
| version: v0.7.0-rc.0 | |
| - name: Run Setup | |
| id: setup | |
| run: | | |
| cascade orchestrate setup \ | |
| --config .github/manifest.yaml \ | |
| --gha-output | |
| build-lib: | |
| name: Build (lib) | |
| needs: [setup] | |
| if: | | |
| needs.setup.outputs.run_build_lib == 'true' | |
| uses: ./.github/workflows/build-lib.yaml | |
| with: | |
| sha: ${{ needs.setup.outputs.head_sha }} | |
| finalize: | |
| name: Finalize | |
| needs: [setup, build-lib] | |
| if: always() && needs.setup.result == 'success' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: write | |
| outputs: | |
| lib_artifact: ${{ needs.build-lib.outputs.artifact }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Generate Summary | |
| run: | | |
| echo "## Orchestration Complete" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "### Callback Results" >> "$GITHUB_STEP_SUMMARY" | |
| echo "| Callback | Result | On Failure |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "|----------|--------|------------|" >> "$GITHUB_STEP_SUMMARY" | |
| echo "| Build (lib) | ${{ needs.build-lib.result }} | abort |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "### Outputs" >> "$GITHUB_STEP_SUMMARY" | |
| HAS_OUTPUTS=false | |
| if [[ -n "${{ needs.build-lib.outputs.artifact }}" ]]; then | |
| if [[ "$HAS_OUTPUTS" == "false" ]]; then | |
| echo "| Output | Value |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "|--------|-------|" >> "$GITHUB_STEP_SUMMARY" | |
| HAS_OUTPUTS=true | |
| fi | |
| echo "| lib_artifact | ${{ needs.build-lib.outputs.artifact }} |" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| if [[ "$HAS_OUTPUTS" == "false" ]]; then | |
| echo "_No outputs produced_" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Setup CLI | |
| uses: stablekernel/cascade/.github/actions/setup-cli@v0.7.0-rc.0 | |
| with: | |
| token: ${{ secrets.CASCADE_STATE_TOKEN }} | |
| version: v0.7.0-rc.0 | |
| - name: Generate Changelog | |
| id: changelog | |
| env: | |
| GH_TOKEN: ${{ secrets.CASCADE_STATE_TOKEN }} | |
| run: | | |
| # Use changelog_base_sha which compares this env to next env | |
| # This shows commits in this env NOT yet promoted to next env | |
| RESULT=$(cascade generate-changelog \ | |
| --base-sha "${{ needs.setup.outputs.changelog_base_sha }}" \ | |
| --head-sha "${{ needs.setup.outputs.head_sha }}" \ | |
| --contributors \ | |
| --repo "${{ github.repository }}") | |
| echo "changelog<<EOF" >> "$GITHUB_OUTPUT" | |
| echo "$RESULT" | jq -r '.changelog' >> "$GITHUB_OUTPUT" | |
| echo "EOF" >> "$GITHUB_OUTPUT" | |
| - name: Manage Release | |
| uses: ./.github/actions/manage-release | |
| with: | |
| repo: ${{ github.repository }} | |
| action: update | |
| tag: ${{ needs.setup.outputs.version }} | |
| create_tag: 'true' | |
| environment: prerelease | |
| sha: ${{ needs.setup.outputs.head_sha }} | |
| changelog: ${{ steps.changelog.outputs.changelog }} | |
| previous_tag: ${{ needs.setup.outputs.previous_tag }} | |
| token: ${{ secrets.CASCADE_STATE_TOKEN }} | |
| - name: Update Manifest | |
| env: | |
| GH_TOKEN: ${{ secrets.CASCADE_STATE_TOKEN }} | |
| HEAD_SHA: ${{ needs.setup.outputs.head_sha }} | |
| VERSION: ${{ needs.setup.outputs.version }} | |
| run: | | |
| MANIFEST_FILE=".github/manifest.yaml" | |
| MANIFEST_KEY="ci" | |
| if [[ ! -f "$MANIFEST_FILE" ]]; then | |
| echo "No $MANIFEST_FILE found - skipping state update" | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| BRANCH="${GITHUB_REF##refs/heads/}" | |
| apply_state_edits() { | |
| TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) | |
| # Update state under prerelease key (no environments) | |
| yq eval -i ".$MANIFEST_KEY.state.prerelease.sha = \"$HEAD_SHA\"" "$MANIFEST_FILE" | |
| yq eval -i ".$MANIFEST_KEY.state.prerelease.version = \"$VERSION\"" "$MANIFEST_FILE" | |
| yq eval -i ".$MANIFEST_KEY.state.prerelease.committed_at = \"$TIMESTAMP\"" "$MANIFEST_FILE" | |
| yq eval -i ".$MANIFEST_KEY.state.prerelease.committed_by = \"${{ github.actor }}\"" "$MANIFEST_FILE" | |
| } | |
| if [[ "$GITHUB_SERVER_URL" != "https://github.com" ]]; then | |
| # act/gitea e2e: no GitHub API, and the trunk is neither protected nor | |
| # signature-checked, so push the state commit directly with retries. | |
| for attempt in 1 2 3 4 5; do | |
| git fetch origin "$BRANCH" | |
| git reset --hard "origin/$BRANCH" | |
| apply_state_edits | |
| if git diff --quiet "$MANIFEST_FILE"; then | |
| echo "No state changes" | |
| exit 0 | |
| fi | |
| git add "$MANIFEST_FILE" | |
| git commit -m "chore: update state [skip ci]" | |
| if git push origin "HEAD:$BRANCH"; then | |
| echo "Pushed state on attempt $attempt" | |
| exit 0 | |
| fi | |
| echo "Push attempt $attempt rejected (likely concurrent run); retrying..." >&2 | |
| sleep $((RANDOM % 5 + 2)) | |
| done | |
| echo "::error::Failed to push state after 5 attempts" >&2 | |
| exit 1 | |
| fi | |
| # Real GitHub: write state through the Contents REST API. API commits are | |
| # signed by GitHub (Verified) and, with a bypass-capable token, update the | |
| # trunk even when a required status check protects it. | |
| for attempt in 1 2 3 4 5; do | |
| git fetch origin "$BRANCH" | |
| git reset --hard "origin/$BRANCH" | |
| apply_state_edits | |
| if git diff --quiet "$MANIFEST_FILE"; then | |
| echo "No state changes" | |
| exit 0 | |
| fi | |
| CONTENT_B64=$(base64 -w0 "$MANIFEST_FILE" 2>/dev/null || base64 "$MANIFEST_FILE" | tr -d '\n') | |
| CURRENT_SHA=$(gh api "repos/${{ github.repository }}/contents/$MANIFEST_FILE?ref=$BRANCH" --jq '.sha' 2>/dev/null || true) | |
| API_ARGS=("repos/${{ github.repository }}/contents/$MANIFEST_FILE" -X PUT | |
| -f "message=chore: update state [skip ci]" | |
| -f "content=$CONTENT_B64" | |
| -f "branch=$BRANCH" | |
| -f "author[name]=github-actions[bot]" | |
| -f "author[email]=github-actions[bot]@users.noreply.github.com" | |
| -f "committer[name]=github-actions[bot]" | |
| -f "committer[email]=github-actions[bot]@users.noreply.github.com") | |
| if [[ -n "$CURRENT_SHA" ]]; then | |
| API_ARGS+=(-f "sha=$CURRENT_SHA") | |
| fi | |
| if gh api "${API_ARGS[@]}" >/dev/null; then | |
| echo "Pushed state via API on attempt $attempt" | |
| exit 0 | |
| fi | |
| echo "State write attempt $attempt failed (likely concurrent run); retrying..." >&2 | |
| sleep $((RANDOM % 5 + 2)) | |
| done | |
| echo "::error::Failed to write state via API after 5 attempts" >&2 | |
| exit 1 | |
| - name: Check for Failures | |
| if: contains(fromJSON('["failure", "cancelled"]'), needs.build-lib.result) | |
| run: | | |
| echo "One or more critical callbacks failed or were cancelled" | |
| exit 1 |