Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .claude/commands/repo-maintenance.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
description: Comprehensive repository maintenance - run all health checks and updates
allowed-tools: Read, Bash(script/repo-maintenance.sh:*), Bash(git:*), Bash(gh:*), Bash(npm:*), Bash(pnpm:*), Bash(jq:*), Skill
argument-hint: '[--mode full|quick|check-only] [--skip CATEGORY] [--create-pr] [--check-actions-pr-settings]'
argument-hint: '[--mode full|quick|check-only] [--skip CATEGORY] [--create-pr] [--check-actions-pr-settings] [--check-scheduled-maintenance] [--check-artifact-retention]'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Include --check-required-workflows in argument-hint.

The hint now lists new --check-* flags but omits --check-required-workflows, which is still documented as required behavior (Line 41). Keeping the hint complete avoids inconsistent command guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/commands/repo-maintenance.md at line 4, The argument-hint on line 4
is missing the --check-required-workflows flag that is documented as required
behavior later in the file. Add --check-required-workflows to the argument-hint
list alongside the other --check-* flags (--check-actions-pr-settings,
--check-scheduled-maintenance, --check-artifact-retention) to keep the command
hints consistent with the documented functionality.

---

# Repository Maintenance Workflow
Expand Down Expand Up @@ -31,6 +31,8 @@ Repository state guard runs before updates. Archived repositories switch to `che
- Private repositories allow Dependency Review to be optional or skipped.
- GitHub Actions PR creation settings are checked with `script/repo-maintenance.sh --check-actions-pr-settings`.
- Automated issue and maintenance PR creation expects `default_workflow_permissions=write` and `can_approve_pull_request_reviews=true`.
- Scheduled Maintenance configuration is checked with `script/repo-maintenance.sh --check-scheduled-maintenance`.
- Artifact retention is checked with `script/repo-maintenance.sh --check-artifact-retention` and should be 30 days or less.
- Managed workflow templates are checked against `templates/workflows/` with `npm run workflow:sync:check`.
- Workflow Lint coverage checks verify `.github/workflows/`, `.github/workflows/templates/`, and `templates/workflows/` are collected without static unmatched globs.

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/container-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,4 @@ jobs:
with:
name: sbom
path: sbom.spdx.json
retention-days: 90
retention-days: 30
12 changes: 7 additions & 5 deletions .github/workflows/scheduled-maintenance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ jobs:
steps:
- name: Validate maintenance token
env:
CLAUDE_PR_GITHUB_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
CLAUDE_PR_GITHUB_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
run: |
if [ -z "$CLAUDE_PR_GITHUB_TOKEN" ]; then
echo "CLAUDE_PR_GITHUB_TOKEN is required for scheduled maintenance because it may update workflow files."
echo "CLAUDE_PR_GITHUB_TOKEN or CLAUDE_PAT is required for scheduled maintenance because it may update workflow files."
exit 1
fi

- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
Comment on lines 46 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/scheduled-maintenance.yml

Repository: keito4/config

Length of output: 6426


🌐 Web query:

actions/checkout v6 persist-credentials default behavior github actions

💡 Result:

For actions/checkout v6, the input persist-credentials default behavior is effectively “true” (credentials are persisted so authenticated git commands work during the job). The credentials are stored in a separate file under $RUNNER_TEMP, wired into the repo’s git config via includeIf; they are removed during post-job cleanup. To opt out, set persist-credentials: false. Details grounded in the v6 action docs: - The action documents that the auth token is persisted to enable scripts to run authenticated git commands, and that you can opt out with persist-credentials: false [1][2][3]. - The v6 documentation also states that persist-credentials now stores credentials in a separate file under $RUNNER_TEMP instead of directly in.git/config [1][2][3]. - The action’s README also notes the token is removed during post-job cleanup [1][2][3]. Important behavioral implication: - Because v6 persists credentials via git includeIf pointing at the $RUNNER_TEMP credentials file, authenticated git operations that rely on those credentials should work in the workflow after checkout, but may require compatible runner/container behavior as described by the action changes [4][5]. Answer in one line: actions/checkout@v6 persists credentials by default (persist-credentials enabled), stores them under $RUNNER_TEMP, and removes them after the job; use persist-credentials: false to disable.

Citations:


Add persist-credentials: false to the checkout step to prevent implicit credential availability.

The actions/checkout@v6 action persists credentials by default, making the PAT available to all subsequent steps via git config. Since the workflow passes authentication explicitly via GH_TOKEN environment variables where needed (lines 88, 119), add persist-credentials: false to the checkout step to enforce explicit token passing and limit unintended credential exposure.

Proposed hardening
       - name: Checkout repository
         uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
         with:
           fetch-depth: 1
+          persist-credentials: false
           token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
persist-credentials: false
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 46-50: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/scheduled-maintenance.yml around lines 46 - 50, The
actions/checkout@v6 step in the "Checkout repository" task persists credentials
to git config by default, making the PAT token available to all subsequent
workflow steps. Since the workflow already passes authentication explicitly via
GH_TOKEN environment variables in later steps, add persist-credentials: false as
a new parameter in the checkout step's with block to prevent implicit credential
availability and enforce explicit token passing for improved security.

Source: Linters/SAST tools


- name: Prepare maintenance branch
run: git checkout -b "$CLAUDE_BRANCH"
Expand All @@ -62,7 +62,7 @@ jobs:
CLAUDE_BRANCH: ${{ env.CLAUDE_BRANCH }}
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
github_token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
prompt: |
Run `script/check-trivyignore-review.sh` first and include any due `.trivyignore` entries in the final summary.

Expand All @@ -85,7 +85,7 @@ jobs:
- name: Create maintenance pull request
if: env.CLAUDE_BRANCH != ''
env:
GH_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
MODE: ${{ inputs.mode || 'full' }}
run: |
Expand Down Expand Up @@ -117,6 +117,7 @@ jobs:
if: failure()
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
EXISTING=$(gh issue list --label "maintenance" --state open --json number --jq 'length')
if [ "$EXISTING" -gt 0 ]; then
Expand All @@ -135,6 +136,7 @@ jobs:
**Mode:** ${{ inputs.mode || 'full' }}

Please check the workflow logs and re-run manually if needed.
If this failed during token validation, configure \`CLAUDE_PR_GITHUB_TOKEN\` or \`CLAUDE_PAT\` in repository Actions secrets.

---
*Auto-generated by [scheduled-maintenance.yml](.github/workflows/scheduled-maintenance.yml)*"
3 changes: 3 additions & 0 deletions script/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ Runs repository maintenance checks and managed updates. This is the executable s
./script/repo-maintenance.sh --mode full
./script/repo-maintenance.sh --mode check-only
./script/repo-maintenance.sh --check-required-workflows
./script/repo-maintenance.sh --check-actions-pr-settings
./script/repo-maintenance.sh --check-scheduled-maintenance
./script/repo-maintenance.sh --check-artifact-retention
```

### setup-ci.sh
Expand Down
94 changes: 94 additions & 0 deletions script/lib/repo_maintenance_checks.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
# Additional checks used by script/repo-maintenance.sh.

check_scheduled_maintenance_configuration() {
local workflow=".github/workflows/scheduled-maintenance.yml"
local repo secrets issue_count=0
local has_pr_token=false has_legacy_pat=false

[[ -f "$workflow" ]] || return 0

if grep -q "CLAUDE_PR_GITHUB_TOKEN" "$workflow"; then
if command -v gh >/dev/null 2>&1; then
repo="$(gh repo view --json nameWithOwner --jq '.nameWithOwner' 2>/dev/null || true)"
if [[ -n "$repo" && "$repo" != "null" ]]; then
secrets="$(gh secret list --repo "$repo" --json name --jq '.[].name' 2>/dev/null || true)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize org-level maintenance secrets

When CLAUDE_PR_GITHUB_TOKEN or CLAUDE_PAT is configured as an organization Actions secret with this repository selected, the workflow's ${{ secrets.* }} references are valid, but this check only runs gh secret list --repo and therefore only sees repository-level secrets (gh secret list treats organization secrets as a separate level). In that setup --check-scheduled-maintenance fails and tells users to add a repo secret even though scheduled maintenance would run, so the check should also account for org-level secrets or treat an absent repo secret as inconclusive.

Useful? React with 👍 / 👎.

grep -Fxq "CLAUDE_PR_GITHUB_TOKEN" <<<"$secrets" && has_pr_token=true
if grep -q "CLAUDE_PAT" "$workflow" && grep -Fxq "CLAUDE_PAT" <<<"$secrets"; then
has_legacy_pat=true
fi
if [[ "$has_pr_token" != "true" && "$has_legacy_pat" != "true" ]]; then
output::warning "scheduled-maintenance.yml requires CLAUDE_PR_GITHUB_TOKEN or CLAUDE_PAT secret"
echo "Settings: https://github.com/$repo/settings/secrets/actions"
issue_count=$((issue_count + 1))
fi
else
output::warning "Scheduled Maintenance secret check skipped: repository unavailable"
fi
else
output::warning "Scheduled Maintenance secret check skipped: gh not found"
fi
fi

if grep -q "name: Post failure issue" "$workflow" \
&& ! grep -q "GH_REPO: \${{ github.repository }}" "$workflow" \
&& ! grep -q -- "--repo \"\$GITHUB_REPOSITORY\"" "$workflow"; then
output::warning "scheduled-maintenance.yml failure issue step needs GH_REPO or --repo"
issue_count=$((issue_count + 1))
fi

if [[ "$issue_count" -gt 0 ]]; then
return 1
fi

output::success "Scheduled Maintenance configuration ok"
}

check_artifact_retention() {
local workflow issue_count=0

for workflow in .github/workflows/*.yml .github/workflows/*.yaml; do
[[ -f "$workflow" ]] || continue
if ! awk -v file="$(basename "$workflow")" '
/^[[:space:]]*(-[[:space:]]*)?uses:[[:space:]]*actions\/upload-artifact@/ {
in_upload = 1
has_retention = 0
next
}
in_upload && /^[[:space:]]*retention-days:[[:space:]]*/ {
has_retention = 1
value = $0
sub(/.*retention-days:[[:space:]]*/, "", value)
sub(/[[:space:]#].*/, "", value)
if (value + 0 > 30) {
printf "%s: artifact retention-days is %s (expected <= 30)\n", file, value
Comment on lines +58 to +64

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject non-literal retention values instead of coercing them to zero.

At Line 63, value + 0 coerces non-numeric values to 0, so entries like "90" or ${{ ... }} can slip past the <= 30 gate. Fail explicitly when retention-days is not a literal integer.

🔧 Proposed fix
       in_upload && /^[[:space:]]*retention-days:[[:space:]]*/ {
         has_retention = 1
         value = $0
         sub(/.*retention-days:[[:space:]]*/, "", value)
         sub(/[[:space:]#].*/, "", value)
-        if (value + 0 > 30) {
+        if (value !~ /^"?[0-9]+"?$/) {
+          printf "%s: artifact retention-days must be a literal integer <= 30 (found %s)\n", file, value
+          bad = 1
+          next
+        }
+        gsub(/"/, "", value)
+        if ((value + 0) > 30) {
           printf "%s: artifact retention-days is %s (expected <= 30)\n", file, value
           bad = 1
         }
         next
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
in_upload && /^[[:space:]]*retention-days:[[:space:]]*/ {
has_retention = 1
value = $0
sub(/.*retention-days:[[:space:]]*/, "", value)
sub(/[[:space:]#].*/, "", value)
if (value + 0 > 30) {
printf "%s: artifact retention-days is %s (expected <= 30)\n", file, value
in_upload && /^[[:space:]]*retention-days:[[:space:]]*/ {
has_retention = 1
value = $0
sub(/.*retention-days:[[:space:]]*/, "", value)
sub(/[[:space:]#].*/, "", value)
if (value !~ /^"?[0-9]+"?$/) {
printf "%s: artifact retention-days must be a literal integer <= 30 (found %s)\n", file, value
bad = 1
next
}
gsub(/"/, "", value)
if ((value + 0) > 30) {
printf "%s: artifact retention-days is %s (expected <= 30)\n", file, value
bad = 1
}
next
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@script/lib/repo_maintenance_checks.sh` around lines 58 - 64, The condition at
line 63 using `value + 0 > 30` coerces non-numeric values to zero, allowing
invalid entries like variable references or expressions to bypass validation.
Replace the coercive numeric comparison with an explicit check that validates
the value is a literal integer before comparing it to 30. Add validation logic
to reject cases where value contains non-numeric characters or variable syntax
like dollar signs, and report an error when retention-days is not a proper
literal integer value.

bad = 1
}
next
}
in_upload && /^[[:space:]]*-[[:space:]]*(name|uses):/ {
if (!has_retention) {
printf "%s: upload-artifact missing retention-days\n", file
bad = 1
}
in_upload = ($0 ~ /uses:[[:space:]]*actions\/upload-artifact@/)
has_retention = 0
}
END {
if (in_upload && !has_retention) {
printf "%s: upload-artifact missing retention-days\n", file
bad = 1
}
exit bad ? 1 : 0
}
' "$workflow"; then
issue_count=$((issue_count + 1))
fi
done

if [[ "$issue_count" -gt 0 ]]; then
return 1
fi

output::success "Artifact retention settings ok"
}
49 changes: 43 additions & 6 deletions script/repo-maintenance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_REPO="$(cd "$SCRIPT_DIR/.." && pwd)"
# shellcheck source=script/lib/output.sh
source "$SCRIPT_DIR/lib/output.sh"
# shellcheck source=script/lib/repo_maintenance_checks.sh
source "$SCRIPT_DIR/lib/repo_maintenance_checks.sh"

MODE="full"
SKIP_CATEGORIES=""
CREATE_PR=false
CHECK_REQUIRED_WORKFLOWS_ONLY=false
CHECK_ACTIONS_PR_SETTINGS_ONLY=false
CHECK_SCHEDULED_MAINTENANCE_ONLY=false
CHECK_ARTIFACT_RETENTION_ONLY=false
CONTEXT_DIR="${CONTEXT_DIR:-.context}"

usage() {
cat <<'EOF'
Usage: script/repo-maintenance.sh [--mode full|quick|check-only] [--skip CATEGORY] [--create-pr] [--check-required-workflows] [--check-actions-pr-settings]
Usage: script/repo-maintenance.sh [--mode full|quick|check-only] [--skip CATEGORY] [--create-pr] [--check-required-workflows] [--check-actions-pr-settings] [--check-scheduled-maintenance] [--check-artifact-retention]
EOF
}

Expand Down Expand Up @@ -45,6 +49,16 @@ while [[ $# -gt 0 ]]; do
MODE="check-only"
shift
;;
--check-scheduled-maintenance)
CHECK_SCHEDULED_MAINTENANCE_ONLY=true
MODE="check-only"
shift
;;
--check-artifact-retention)
CHECK_ARTIFACT_RETENTION_ONLY=true
MODE="check-only"
shift
;;
-h|--help)
usage
exit 0
Expand Down Expand Up @@ -372,13 +386,20 @@ create_pr_if_requested() {
return 0
fi

local branch
branch="maintenance/$(date +%Y%m%d-%H%M%S)"
git checkout -b "$branch"
local branch current_branch
branch="${CLAUDE_BRANCH:-maintenance/$(date +%Y%m%d-%H%M%S)}"
current_branch="$(git branch --show-current 2>/dev/null || true)"
if [[ "$current_branch" != "$branch" ]]; then
if git rev-parse --verify "$branch" >/dev/null 2>&1; then
git checkout "$branch"
else
git checkout -b "$branch"
fi
fi
git add -A
git commit -m "chore: repository maintenance"
git push -u origin "$branch"
gh pr create --title "chore: repository maintenance" --body "Automated repository maintenance."
gh pr create --head "$branch" --title "chore: repository maintenance" --body "Automated repository maintenance."
}

if [[ "$CHECK_REQUIRED_WORKFLOWS_ONLY" == "true" ]]; then
Expand All @@ -391,6 +412,16 @@ if [[ "$CHECK_ACTIONS_PR_SETTINGS_ONLY" == "true" ]]; then
exit $?
fi

if [[ "$CHECK_SCHEDULED_MAINTENANCE_ONLY" == "true" ]]; then
check_scheduled_maintenance_configuration
exit $?
fi

if [[ "$CHECK_ARTIFACT_RETENTION_ONLY" == "true" ]]; then
check_artifact_retention
exit $?
fi

cat <<EOF
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Repository Maintenance
Expand All @@ -404,7 +435,13 @@ EOF
check_repository_state

if ! has_skip "setup"; then
check_actions_pr_creation_settings || true
if [[ "$CREATE_PR" == "true" ]]; then
check_actions_pr_creation_settings
else
check_actions_pr_creation_settings || true
fi
check_scheduled_maintenance_configuration || true
check_artifact_retention || true
check_workflow_templates
check_workflow_template_lint_coverage
check_managed_templates
Expand Down
12 changes: 7 additions & 5 deletions templates/workflows/scheduled-maintenance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ jobs:
steps:
- name: Validate maintenance token
env:
CLAUDE_PR_GITHUB_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
CLAUDE_PR_GITHUB_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
run: |
if [ -z "$CLAUDE_PR_GITHUB_TOKEN" ]; then
echo "CLAUDE_PR_GITHUB_TOKEN is required for scheduled maintenance because it may update workflow files."
echo "CLAUDE_PR_GITHUB_TOKEN or CLAUDE_PAT is required for scheduled maintenance because it may update workflow files."
exit 1
fi

- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
Comment on lines 46 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
from pathlib import Path

for path in [
    Path("templates/workflows/scheduled-maintenance.yml"),
    Path(".github/workflows/scheduled-maintenance.yml"),
]:
    if not path.exists():
        continue

    text = path.read_text()
    idx = text.find("uses: actions/checkout")
    if idx == -1:
        continue

    next_step = text.find("\n      - name:", idx + 1)
    block = text[idx: next_step if next_step != -1 else len(text)]

    if "secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT" in block and "persist-credentials: false" not in block:
        print(f"{path}: checkout persists the fallback PAT credentials")
PY

rg -n -C3 '\bgit\s+(push|ls-remote)\b' --glob 'script/**' --glob '.github/workflows/**' --glob 'templates/workflows/**'

Repository: keito4/config

Length of output: 4147


Add persist-credentials: false to both workflow copies to prevent reusing the long-lived fallback PAT.

actions/checkout persists credentials by default; both templates/workflows/scheduled-maintenance.yml and .github/workflows/scheduled-maintenance.yml can use CLAUDE_PAT as a fallback, exposing a long-lived token to later Claude/npm/script commands that can read it from git config. The workflows contain git ls-remote and git push operations that would require explicit credentials if persistence is disabled.

Proposed hardening
       - name: Checkout repository
         uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
         with:
           fetch-depth: 1
+          persist-credentials: false
           token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
persist-credentials: false
token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@templates/workflows/scheduled-maintenance.yml` around lines 46 - 50, Add the
`persist-credentials: false` parameter to the `with:` section of the
`actions/checkout` action in both the
`templates/workflows/scheduled-maintenance.yml` and
`.github/workflows/scheduled-maintenance.yml` files. This prevents the
long-lived fallback PAT token from being persisted in git config where it could
be exposed to subsequent git operations like `git ls-remote` and `git push` in
the workflow.


- name: Prepare maintenance branch
run: git checkout -b "$CLAUDE_BRANCH"
Expand All @@ -62,7 +62,7 @@ jobs:
CLAUDE_BRANCH: ${{ env.CLAUDE_BRANCH }}
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
github_token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
prompt: |
Run `script/check-trivyignore-review.sh` first and include any due `.trivyignore` entries in the final summary.

Expand All @@ -85,7 +85,7 @@ jobs:
- name: Create maintenance pull request
if: env.CLAUDE_BRANCH != ''
env:
GH_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
MODE: ${{ inputs.mode || 'full' }}
run: |
Expand Down Expand Up @@ -117,6 +117,7 @@ jobs:
if: failure()
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
EXISTING=$(gh issue list --label "maintenance" --state open --json number --jq 'length')
if [ "$EXISTING" -gt 0 ]; then
Expand All @@ -135,6 +136,7 @@ jobs:
**Mode:** ${{ inputs.mode || 'full' }}

Please check the workflow logs and re-run manually if needed.
If this failed during token validation, configure \`CLAUDE_PR_GITHUB_TOKEN\` or \`CLAUDE_PAT\` in repository Actions secrets.

---
*Auto-generated by [scheduled-maintenance.yml](.github/workflows/scheduled-maintenance.yml)*"
6 changes: 3 additions & 3 deletions test/claude-workflow-contract.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ describe('Claude workflow contracts', () => {
expect(workflow.indexOf('name: Validate maintenance token')).toBeLessThan(
workflow.indexOf('name: Checkout repository'),
);
expect(workflow).toContain('token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}');
expect(workflow).toContain('github_token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}');
expect(workflow).toContain('token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}');
expect(workflow).toContain('github_token: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}');
expect(workflow).toContain('name: Prepare maintenance branch');
expect(workflow).toContain('git checkout -b "$CLAUDE_BRANCH"');
expect(workflow).toContain('Use branch `${{ env.CLAUDE_BRANCH }}`');
expect(workflow).toContain('GH_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN }}');
expect(workflow).toContain('GH_TOKEN: ${{ secrets.CLAUDE_PR_GITHUB_TOKEN || secrets.CLAUDE_PAT }}');
expect(workflow).toContain('if: env.CLAUDE_BRANCH !=');
expect(workflow).toContain('gh pr create');
expect(workflow).toContain('git ls-remote --exit-code --heads origin "$CLAUDE_BRANCH"');
Expand Down
Loading
Loading