diff --git a/.github/manifest.yaml b/.github/manifest.yaml index 669f29df..0191fee9 100644 --- a/.github/manifest.yaml +++ b/.github/manifest.yaml @@ -1,6 +1,7 @@ ci: config: trunk_branch: main + state_token: ${{ secrets.CASCADE_STATE_TOKEN }} triggers: - cmd/** - internal/** diff --git a/.github/workflows/orchestrate.yaml b/.github/workflows/orchestrate.yaml index 206090c4..f98d18fb 100644 --- a/.github/workflows/orchestrate.yaml +++ b/.github/workflows/orchestrate.yaml @@ -1,5 +1,5 @@ # AUTO-GENERATED by cascade - DO NOT EDIT MANUALLY -# Regenerate with: cascade generate-workflow --config .github/manifest.yaml +# Regenerate with: cascade generate-workflow --config /Users/joshua.temple/go/src/github.com/stablekernel/cascade/.worktrees/activate/.github/manifest.yaml name: Orchestrate CI/CD @@ -31,6 +31,7 @@ jobs: setup: name: Setup runs-on: ubuntu-latest + timeout-minutes: 30 outputs: run_build_cli: ${{ steps.setup.outputs.run_build_cli }} head_sha: ${{ steps.setup.outputs.head_sha }} @@ -76,6 +77,7 @@ jobs: needs: [setup, validate, build-cli] if: always() && needs.setup.result == 'success' runs-on: ubuntu-latest + timeout-minutes: 30 outputs: cli_result: ${{ needs.build-cli.outputs.result }} validate_result: ${{ needs.validate.outputs.result }} @@ -148,6 +150,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Update Manifest env: + GH_TOKEN: ${{ secrets.CASCADE_STATE_TOKEN }} HEAD_SHA: ${{ needs.setup.outputs.head_sha }} VERSION: ${{ needs.setup.outputs.version }} run: | @@ -171,6 +174,33 @@ jobs: 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" @@ -179,19 +209,26 @@ jobs: 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" + 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") + 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 "Push attempt $attempt rejected (likely concurrent run); retrying..." >&2 + echo "State write attempt $attempt failed (likely concurrent run); retrying..." >&2 sleep $((RANDOM % 5 + 2)) done - echo "::error::Failed to push state after 5 attempts" >&2 + echo "::error::Failed to write state via API after 5 attempts" >&2 exit 1 - name: Check for Failures - if: needs.validate.result == 'failure' || needs.build-cli.result == 'failure' + if: contains(fromJSON('["failure", "cancelled"]'), needs.validate.result) || contains(fromJSON('["failure", "cancelled"]'), needs.build-cli.result) run: | - echo "One or more critical callbacks failed" + echo "One or more critical callbacks failed or were cancelled" exit 1 diff --git a/.github/workflows/promote.yaml b/.github/workflows/promote.yaml index 47b3f4ee..9d814256 100644 --- a/.github/workflows/promote.yaml +++ b/.github/workflows/promote.yaml @@ -53,6 +53,10 @@ permissions: contents: write actions: write +concurrency: + group: "${{ github.workflow }}" + cancel-in-progress: false + jobs: preflight: name: Pre-flight Check @@ -269,6 +273,7 @@ jobs: - name: Finalize Promotion if: ${{ github.event.inputs.dry_run != 'true' }} env: + GH_TOKEN: ${{ secrets.CASCADE_STATE_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PROMOTION_RESULT: ${{ needs.preflight.outputs.promotion_result }} run: |