mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
fix(ci): authenticate skill updater pull requests (#3404)
Punchcard-Session: amber-orchard-valley-s4
This commit is contained in:
@@ -7,7 +7,6 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: update-skills
|
||||
@@ -99,11 +98,13 @@ jobs:
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Open or update pull request
|
||||
- name: Commit and push update branch
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
branch="automation/update-skills"
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
@@ -112,6 +113,41 @@ jobs:
|
||||
git commit -m "chore: update skills"
|
||||
git push --force-with-lease origin "$branch"
|
||||
|
||||
- uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
id: app-token
|
||||
continue-on-error: true
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
with:
|
||||
app-id: "2729701"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: ${{ github.event.repository.name }}
|
||||
permission-pull-requests: write
|
||||
|
||||
- uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
id: app-token-fallback
|
||||
continue-on-error: true
|
||||
if: steps.changes.outputs.changed == 'true' && steps.app-token.outcome == 'failure'
|
||||
with:
|
||||
app-id: "2971289"
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
repositories: ${{ github.event.repository.name }}
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Open or update pull request
|
||||
if: steps.changes.outputs.changed == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -z "${GH_TOKEN:-}" ]]; then
|
||||
echo "::error::Unable to create a Barnacle GitHub App token. Check the primary GH_APP_PRIVATE_KEY and fallback GH_APP_PRIVATE_KEY_FALLBACK credentials and their pull-request permissions." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
branch="automation/update-skills"
|
||||
body="$RUNNER_TEMP/update-skills-pr.md"
|
||||
{
|
||||
echo "## Summary"
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
### Fixes
|
||||
|
||||
- CI: authenticate the scheduled project-skill updater's pull request mutations with the Barnacle GitHub App while retaining the workflow token for provenance lookup and branch publication.
|
||||
- Workers: materialize zero-byte directory markers without colliding with descendant files, while retaining real empty files and verifying every downloaded artifact digest.
|
||||
- Deploy: allow an explicitly confirmed backend-only deploy to pause and reliably restore active external-skill rollouts instead of requiring a manual dashboard toggle.
|
||||
- GitHub Actions/CLI: trigger exact pre-publication checks immediately and wait for package publication to finish, so a pending staged upload no longer reports a successful release.
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/* @vitest-environment node */
|
||||
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parse as parseYaml } from "yaml";
|
||||
|
||||
type WorkflowStep = {
|
||||
"continue-on-error"?: boolean;
|
||||
env?: Record<string, unknown>;
|
||||
id?: string;
|
||||
if?: string;
|
||||
name?: string;
|
||||
run?: string;
|
||||
uses?: string;
|
||||
with?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
describe("update skills workflow", () => {
|
||||
it("uses GitHub App auth only for pull request mutations", async () => {
|
||||
const workflow = parseYaml(await readFile(".github/workflows/update-skills.yml", "utf8")) as {
|
||||
jobs: {
|
||||
update: {
|
||||
steps: WorkflowStep[];
|
||||
};
|
||||
};
|
||||
permissions?: Record<string, string>;
|
||||
};
|
||||
|
||||
expect(workflow.permissions).toEqual({ contents: "write" });
|
||||
|
||||
const steps = workflow.jobs.update.steps;
|
||||
const provenanceStep = steps.find((step) => step.name === "Refresh changed skill provenance");
|
||||
expect(provenanceStep?.env).toEqual({ GH_TOKEN: "${{ github.token }}" });
|
||||
|
||||
const pushStep = steps.find((step) => step.name === "Commit and push update branch");
|
||||
expect(pushStep?.env).toEqual({ GH_TOKEN: "${{ github.token }}" });
|
||||
expect(pushStep?.run).toContain('git push --force-with-lease origin "$branch"');
|
||||
expect(pushStep?.run).not.toContain("gh pr ");
|
||||
|
||||
const primaryTokenStep = steps.find((step) => step.id === "app-token");
|
||||
expect(primaryTokenStep).toMatchObject({
|
||||
uses: "actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3",
|
||||
"continue-on-error": true,
|
||||
if: "steps.changes.outputs.changed == 'true'",
|
||||
with: {
|
||||
"app-id": "2729701",
|
||||
"private-key": "${{ secrets.GH_APP_PRIVATE_KEY }}",
|
||||
owner: "${{ github.repository_owner }}",
|
||||
repositories: "${{ github.event.repository.name }}",
|
||||
"permission-pull-requests": "write",
|
||||
},
|
||||
});
|
||||
|
||||
const fallbackTokenStep = steps.find((step) => step.id === "app-token-fallback");
|
||||
expect(fallbackTokenStep).toMatchObject({
|
||||
uses: "actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3",
|
||||
"continue-on-error": true,
|
||||
if: "steps.changes.outputs.changed == 'true' && steps.app-token.outcome == 'failure'",
|
||||
with: {
|
||||
"app-id": "2971289",
|
||||
"private-key": "${{ secrets.GH_APP_PRIVATE_KEY_FALLBACK }}",
|
||||
owner: "${{ github.repository_owner }}",
|
||||
repositories: "${{ github.event.repository.name }}",
|
||||
"permission-pull-requests": "write",
|
||||
},
|
||||
});
|
||||
|
||||
const pullRequestStep = steps.find((step) => step.name === "Open or update pull request");
|
||||
expect(pullRequestStep?.env).toEqual({
|
||||
GH_TOKEN: "${{ steps.app-token.outputs.token || steps.app-token-fallback.outputs.token }}",
|
||||
});
|
||||
const pullRequestRun = pullRequestStep?.run ?? "";
|
||||
expect(pullRequestRun).toContain("gh pr list");
|
||||
expect(pullRequestRun).toContain("gh pr edit");
|
||||
expect(pullRequestRun).toContain("gh pr create");
|
||||
expect(pullRequestRun).not.toContain("git push");
|
||||
expect(JSON.stringify(pullRequestStep)).not.toContain("github.token");
|
||||
|
||||
const tokenGuard = 'if [[ -z "${GH_TOKEN:-}" ]]';
|
||||
expect(pullRequestRun).toContain(tokenGuard);
|
||||
expect(pullRequestRun).toContain("primary GH_APP_PRIVATE_KEY");
|
||||
expect(pullRequestRun).toContain("fallback GH_APP_PRIVATE_KEY_FALLBACK");
|
||||
expect(pullRequestRun.indexOf(tokenGuard)).toBeLessThan(pullRequestRun.indexOf("gh pr list"));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user