Repository navigation
release: 0.133.0 #4
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: Resolve pending main conflict | |
| # lithic-schemas's push-triggered stlc build can't drive claude-code-action inline (that | |
| # action only recognizes issues/issue_comment/pull_request/pull_request_review/ | |
| # pull_request_review_comment/workflow_dispatch/repository_dispatch/schedule/workflow_run -- | |
| # push isn't in that list, no config bypasses it). So on a push-time custom-code conflict, | |
| # lithic-schemas's "Cut conflict PR" step hands the conflict off here: it pushes ONE commit | |
| # to `stlc/pending-main-conflict`, parented on this repo's `main`, whose tree is the state | |
| # stlc was left in -- regenerated SDK with the custom-code patch applied as far as it would | |
| # go, and conflict markers left in the files it couldn't apply. | |
| # | |
| # Note what that means: git sees NO conflict here (the branch is a clean descendant of | |
| # main). The markers are ordinary file content. So this workflow finds them by text search, | |
| # not `git diff --diff-filter=U`, and there is no merge to perform -- just markers to fix. | |
| # (An earlier version of this pair pushed stlc's own HEAD and let GitHub compute a native | |
| # merge instead. That does not work: stlc keeps a separate internal `seal` lineage that | |
| # shares only the repo's initial commit with the trunk, so the comparison came out as ~1791 | |
| # files of add/add conflicts instead of the handful of files that are really custom code.) | |
| # | |
| # Deliberately does not auto-merge: a human reviews and merges, same as the seal PR pattern | |
| # in stlc-sync.yml. Merging triggers that file's `seal-dispatch` job, which notifies | |
| # lithic-schemas to re-sync. | |
| on: | |
| pull_request: | |
| types: [opened, synchronize] | |
| branches: [main] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| # claude-code-action mints an OIDC token and exchanges it for a GitHub token, so it needs | |
| # id-token: write. Without it every attempt fails with "Unable to get | |
| # ACTIONS_ID_TOKEN_REQUEST_URL env variable" before it ever looks at the conflict -- and | |
| # because the step is continue-on-error, the job still goes green and the PR comment | |
| # reads as "couldn't auto-resolve" rather than "never ran". Same set as lithic-schemas' | |
| # own claude.yaml. | |
| id-token: write | |
| jobs: | |
| resolve: | |
| # Scope to the one branch this is for -- this workflow has no reason to run on any other | |
| # PR into this repo (this repo's main is stlc-managed). | |
| if: github.event.pull_request.head.ref == 'stlc/pending-main-conflict' | |
| runs-on: ${{ vars.STLC_RUNNER || 'ubuntu-latest' }} | |
| concurrency: | |
| # One resolution attempt at a time per PR -- a rapid-fire re-push (lithic-schemas | |
| # force-pushing a fresher conflict) shouldn't race with itself. | |
| group: resolve-pending-conflict-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| steps: | |
| - name: Check out PR branch | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| fetch-depth: 0 | |
| persist-credentials: true | |
| - name: Find conflict markers | |
| id: markers | |
| run: | | |
| # Git's own markers always carry a label ("<<<<<<< ours", ">>>>>>> theirs"), so | |
| # requiring the trailing space keeps this from matching a row of angle brackets in | |
| # generated docs or a test fixture. Require BOTH ends to be present before | |
| # believing it. | |
| if git grep -lE '^<{7} ' -- . > /dev/null 2>&1 && git grep -lE '^>{7} ' -- . > /dev/null 2>&1; then | |
| echo "present=true" >> "$GITHUB_OUTPUT" | |
| echo "Files with conflict markers:" | |
| git grep -lE '^<{7} ' -- . | |
| else | |
| echo "present=false" >> "$GITHUB_OUTPUT" | |
| echo "No conflict markers found -- nothing for this workflow to do." | |
| fi | |
| - name: Resolve conflict markers with Claude | |
| id: resolve | |
| if: steps.markers.outputs.present == 'true' | |
| # continue-on-error: a miss must fall through to the "still conflicted" path below, | |
| # not be swallowed as a silent success. | |
| continue-on-error: true | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | |
| # This PR was opened by the lithic-workflow-dispatcher GitHub App (lithic-schemas's | |
| # "Cut conflict PR" step, same LITHIC_WORKFLOW_APP_ID/PRIVATE_KEY as bundle.yaml), | |
| # so github.actor here is that bot, not a human -- claude-code-action's | |
| # human-actor guard rejects it unless allow-listed. Same bot, same reasoning as the | |
| # inline resolve step in stlc-build-template.yml. | |
| allowed_bots: "lithic-workflow-dispatcher" | |
| # Read/Edit plus read-only git. Committing and pushing happen in the explicit step | |
| # below, where they're auditable from the workflow log rather than folded into | |
| # whatever the model decides to run. | |
| claude_args: | | |
| --allowedTools "Read,Edit,Bash(git grep:*),Bash(git status:*),Bash(git diff:*),Bash(git log:*)" | |
| prompt: | | |
| This repo is a Stainless-generated SDK. The checked-out branch is a single commit | |
| on top of `main` holding a half-finished integration: the SDK was regenerated | |
| from a changed OpenAPI spec, then the repo's hand-written custom code was | |
| replayed on top, and some of it would not apply cleanly. Those spots still have | |
| `<<<<<<<` / `=======` / `>>>>>>>` conflict markers sitting in the files as | |
| ordinary text. Git itself reports no conflict and there is no merge in progress, | |
| so find them with `git grep -nE '^<{7} '`, not `git diff --diff-filter=U`. | |
| In each marker block, the `<<<<<<<` section is the newly generated code and the | |
| `>>>>>>>` section is the hand-written custom code -- but read the labels on the | |
| marker lines to confirm rather than trusting position alone. | |
| 1. Find every file containing conflict markers. | |
| 2. Resolve each block: keep the intent of the hand-written custom code while | |
| taking whatever the new generation legitimately added or changed. When in | |
| doubt, preserve the custom-code behavior and layer the new generated pieces | |
| (new types, fields, methods) around it rather than dropping either side. | |
| 3. Delete the marker lines themselves as you resolve each block. | |
| 4. Do not commit, push, or run a formatter -- later steps handle that. | |
| 5. If a block can't be resolved with confidence (ambiguous intent, or a semantic | |
| conflict a textual merge can't settle), leave its markers in place and say so | |
| plainly in your final summary instead of guessing. | |
| - name: Commit and push resolution | |
| id: finish | |
| if: always() && steps.markers.outputs.present == 'true' | |
| run: | | |
| set -euo pipefail | |
| if git grep -lE '^<{7} ' -- . > /dev/null 2>&1; then | |
| echo "Conflict markers still present -- not committing a half-resolved tree." | |
| echo "resolved=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [ -z "$(git status --porcelain)" ]; then | |
| echo "Markers gone but no changes staged -- nothing to commit." | |
| echo "resolved=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| git config user.name "lithic-workflow[bot]" | |
| git config user.email "lithic-workflow[bot]@users.noreply.github.com" | |
| git add -A | |
| git commit -m "stlc: resolve pending main conflict" | |
| git push origin "HEAD:${{ github.event.pull_request.head.ref }}" | |
| echo "resolved=true" >> "$GITHUB_OUTPUT" | |
| - name: Comment status on the PR | |
| if: always() && steps.markers.outputs.present == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| RESOLVED: ${{ steps.finish.outputs.resolved }} | |
| run: | | |
| merge_note="Merge with \"Create a merge commit\" -- not squash or rebase (breaks stlc's lineage tracking; see stainless/README.md in lithic-schemas)." | |
| if [ "$RESOLVED" = "true" ]; then | |
| body="Conflict markers resolved and pushed. Please review the diff and merge when ready. ${merge_note}" | |
| else | |
| body="Could not fully auto-resolve this conflict -- markers are still present (Claude flagged low confidence, or the resolution step failed). Needs a human: check out this branch, search for \`<<<<<<<\`, fix the blocks, commit and push. ${merge_note}" | |
| fi | |
| gh pr comment "$PR_NUMBER" --body "$body" | |
| - name: Alert on failure | |
| if: failure() | |
| env: | |
| ALERT_WEBHOOK_URL: ${{ secrets.STLC_ALERT_WEBHOOK_URL }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| run: | | |
| msg="Conflict-resolution workflow failed in ${{ github.repository }} for the pending main conflict PR (#${{ github.event.pull_request.number }}). It needs manual resolution now. Run: ${RUN_URL}" | |
| echo "::error title=stlc workflow failed::$msg" | |
| { echo "### ⚠️ stlc workflow failed"; echo ""; echo "$msg"; } >> "$GITHUB_STEP_SUMMARY" | |
| if [ -n "${ALERT_WEBHOOK_URL:-}" ]; then | |
| curl -sS -X POST -H 'Content-Type: application/json' \ | |
| -d "$(jq -n --arg text "$msg" '{text:$text}')" "$ALERT_WEBHOOK_URL" \ | |
| || echo "::warning::Alert webhook POST failed" | |
| fi |