From 886889487676eb8db594c8a33f0a3435c88a5a00 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah Date: Wed, 8 Jul 2026 15:17:30 -0700 Subject: [PATCH] Harden project intake token handling --- .github/workflows/project-intake.yml | 27 ++++++++++++++++++++++++- CONTRIBUTING.md | 20 +++++++++++++++++++ tests/validate.sh | 30 ++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/.github/workflows/project-intake.yml b/.github/workflows/project-intake.yml index 0efe16d..3d4426e 100644 --- a/.github/workflows/project-intake.yml +++ b/.github/workflows/project-intake.yml @@ -28,7 +28,8 @@ jobs: BASE_PROJECT_DEFAULT_SIZE: S BASE_PROJECT_DEFAULT_AREA: Product BASE_PROJECT_DEFAULT_INITIATIVE: Adoption Polish - GH_TOKEN: ${{ secrets.BASE_PROJECT_TOKEN || github.token }} + BASE_PROJECT_MIN_GRAPHQL_REMAINING: 500 + GH_TOKEN: ${{ secrets.BASE_PROJECT_TOKEN }} steps: - name: Reconcile Project item shell: bash @@ -41,6 +42,30 @@ jobs: exit 1 fi + if [[ -z "${GH_TOKEN:-}" ]]; then + echo "::error::BASE_PROJECT_TOKEN is required for Project intake because github.token cannot write org Project fields." + exit 1 + fi + + graphql_min_remaining="${BASE_PROJECT_MIN_GRAPHQL_REMAINING:-500}" + if ! [[ "$graphql_min_remaining" =~ ^[0-9]+$ ]] || (( graphql_min_remaining == 0 )); then + echo "::error::BASE_PROJECT_MIN_GRAPHQL_REMAINING must be a positive integer." + exit 1 + fi + + rate_limit_json="$(gh api graphql -f query='query { rateLimit { remaining resetAt } }')" + graphql_remaining="$(jq -r '.data.rateLimit.remaining // 0' <<<"$rate_limit_json")" + graphql_reset_at="$(jq -r '.data.rateLimit.resetAt // "unknown"' <<<"$rate_limit_json")" + if ! [[ "$graphql_remaining" =~ ^[0-9]+$ ]]; then + echo "::error::Unable to read GitHub GraphQL quota for Project intake." + exit 1 + fi + + if (( graphql_remaining < graphql_min_remaining )); then + echo "::error::GitHub GraphQL quota is too low for Project intake (${graphql_remaining} remaining; need at least ${graphql_min_remaining}; retry after ${graphql_reset_at})." + exit 1 + fi + issue_json="$(gh issue view "$issue_number" --repo "$GITHUB_REPOSITORY" --json state,url)" issue_state="$(jq -r '.state' <<<"$issue_json")" issue_url="$(jq -r '.url' <<<"$issue_json")" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8186899..6f01c6b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,3 +50,23 @@ basectl check base-bash-libs basectl doctor base-bash-libs basectl test base-bash-libs ``` + +## Project intake backfill + +The `Project Intake` workflow uses the `BASE_PROJECT_TOKEN` repository secret +to write to the organization Project. If issues are missing from the Project, +check GraphQL quota before starting a backfill: + +```bash +gh api graphql -f query='query { rateLimit { remaining resetAt } }' +``` + +Dispatch missed issues slowly so Project field mutations do not exhaust the +GraphQL quota: + +```bash +for issue in ; do + gh workflow run project-intake.yml --repo basefoundry/base-bash-libs -f issue_number="$issue" + sleep 12 +done +``` diff --git a/tests/validate.sh b/tests/validate.sh index b3064eb..705ebd7 100755 --- a/tests/validate.sh +++ b/tests/validate.sh @@ -82,6 +82,36 @@ if ! grep -F ' - main' .github/workflows/tests.yml >/dev/null; then exit 1 fi +if grep -F 'secrets.BASE_PROJECT_TOKEN || github.token' .github/workflows/project-intake.yml >/dev/null; then + printf 'Project intake workflow must not fall back to github.token for org Project writes.\n' >&2 + exit 1 +fi + +if ! grep -F 'GH_TOKEN: ${{ secrets.BASE_PROJECT_TOKEN }}' .github/workflows/project-intake.yml >/dev/null; then + printf 'Project intake workflow must use BASE_PROJECT_TOKEN directly for gh commands.\n' >&2 + exit 1 +fi + +if ! grep -F 'BASE_PROJECT_MIN_GRAPHQL_REMAINING' .github/workflows/project-intake.yml >/dev/null; then + printf 'Project intake workflow must define a minimum GraphQL quota before Project mutations.\n' >&2 + exit 1 +fi + +if ! grep -F 'rateLimit { remaining resetAt }' .github/workflows/project-intake.yml >/dev/null; then + printf 'Project intake workflow must check GitHub GraphQL quota before Project mutations.\n' >&2 + exit 1 +fi + +if ! grep -F 'Project intake backfill' CONTRIBUTING.md >/dev/null; then + printf 'CONTRIBUTING.md must document the throttled Project intake backfill workflow.\n' >&2 + exit 1 +fi + +if ! grep -F 'gh workflow run project-intake.yml' CONTRIBUTING.md >/dev/null; then + printf 'CONTRIBUTING.md must include the manual Project intake workflow dispatch command.\n' >&2 + exit 1 +fi + fix_comments="$(grep -R -n '# FIX:' lib/bash || true)" if [[ -n "$fix_comments" ]]; then printf 'Production library files must not contain development # FIX: comments:\n%s\n' "$fix_comments" >&2