Table of Contents generated with DocToc

GitHub — CLI and API operation catalogue

Shared reference for the gh CLI and gh api / gh api graphql invocations the skills use against the project‘s tracker repository. The skills reference this file for the recipe shape; each inline command in a skill already substitutes the tracker repo slug from the adopting project’s manifest (see ../../<project-config>/project.md).

Placeholder convention used below:

  • <tracker> — the tracker repository slug from <project manifest>.tracker_repo (for Airflow, <tracker>).
  • <upstream> — the upstream codebase slug from <project manifest>.upstream_repo (for Airflow, <upstream>).
  • <N> — issue or PR number.

Authentication

Every skill's Step 0 pre-flight must verify that gh is authenticated and has collaborator access to <tracker>:

gh auth status                          # must show logged-in user + scopes
gh api repos/<tracker> --jq .name       # must return the repo name; 401/403/404 means stop

A non-zero exit on either command is a hard stop — the skill reports the failure and asks the user to gh auth login (or to ask for collaborator access to the tracker) rather than retrying.

Collaborator lookup (security-team roster)

gh api repos/<tracker>/collaborators --jq '.[].login'

The authoritative “who is on the security team” list. Every collaborator counts regardless of permission level (read / triage / write / maintain / admin). Roster snapshots maintained in the project manifest files (for Airflow, release-trains.md) are caches of this command's output and can drift between changes.

Issues

Read

gh issue view <N> --repo <tracker> \
  --json number,title,state,body,labels,milestone,assignees,author

Add --json comments when the skill needs the comment trail, and --json projectItems when it needs to see which project boards the issue sits on.

Create

gh issue create --repo <tracker> \
  --title '<title>' \
  --body-file <path> \
  --label '<label-1>' --label '<label-2>'

Always write the body to a temp file and pass --body-file — shell quoting silently corrupts anything with literal backticks, $(…), or newlines inside a multi-paragraph body.

Edit — labels

gh issue edit <N> --repo <tracker> \
  --add-label '<label-a>,<label-b>' \
  --remove-label '<label-c>'

Apply every add + remove in one call so the change lands as a single audit-trail entry rather than as N separate events.

Edit — assignees

gh issue edit <N> --repo <tracker> --add-assignee @me         # self-assign
gh issue edit <N> --repo <tracker> --add-assignee <handle>    # named user

Edit — body

gh issue edit <N> --repo <tracker> --body-file <tmpfile>

Write the edited body to a temp file first. The skills that perform “body-field surgery” (updating one ### <field> section without touching the rest) read the full body, replace the targeted section between its header and the next ### heading, and write the result back via --body-file.

Comment

gh issue comment <N> --repo <tracker> --body-file <tmpfile>

Before posting, scrub the comment body for bare-name mentions of project maintainers / release managers / security-team members and replace with @-handles. See the per-project mention rule (for Airflow, ../../<project-config>/naming-conventions.md#mentioning-airflow-maintainers-and-security-team-members) for the grep-list of names to check.

Close / reopen

gh issue close <N>  --repo <tracker> --reason completed   # or 'not planned'
gh issue reopen <N> --repo <tracker>

Milestones

List

gh api 'repos/<tracker>/milestones?state=all&per_page=100' \
  --jq '.[] | select(.title == "<target>") | {number, state}'

Create

gh api repos/<tracker>/milestones \
  -f title='<target>' \
  -f state=open \
  -f description='<optional one-line description>'

The create call returns the milestone object including its number — capture that in case the milestone is later closed (see fallback below).

Assign to an issue

gh issue edit <N> --repo <tracker> --milestone '<title>'

Closed-milestone fallback. gh issue edit --milestone '<title>' fails with '<title>' not found if the milestone is closed. Fall back to the REST API and reference it by number:

gh api repos/<tracker>/issues/<N> -X PATCH -F milestone=<number>

Labels

List

gh label list --repo <tracker> --limit 100 \
  --json name,description,color --jq '.[].name'

Create

gh label create '<name>' --repo <tracker> \
  --description '<short description>' \
  --color '<hex>'

Do not silently create labels without asking the user. Label names are the shared vocabulary of the security team, and new labels should be discussed.

Pull requests

Create (public PR on the upstream repo)

gh pr create --web --repo <upstream> \
  --base <base-branch> --head <user>:<branch> \
  --title "<neutral title>" \
  --body "$(cat <path-to-body>)"

--web is load-bearing. Per the per-project convention (for Airflow, see ../../<project-config>/fix-workflow.md#pr-creation-convention), always open PRs through the browser so the human reviewer can check the title, body, and Gen-AI disclosure before clicking Create.

Edit — backport / other labels

gh pr edit <N> --repo <upstream> --add-label '<backport-label>'

Safe to run immediately after PR creation; the backport bot acts on the label when the PR merges, not when it is applied.

Cross-link from the public PR back to the private tracker

Forbidden. The public PR body and any follow-up public comment must not reveal the CVE, the security nature, or the private tracker URL. Enforce via the scrub step before writing the PR body — see the per-project scrubbing rule (for Airflow, ../../<project-config>/fix-workflow.md#pr-title--body-scrubbing).

GraphQL (Projects V2)

See project-board.md for the board introspection and updateProjectV2ItemFieldValue patterns.

Error handling

If any state-changing command fails, stop the apply loop, report the failure verbatim, and ask the user how to proceed — do not guess. Most sync-style skills order their apply list so the load-bearing edit (usually the body edit) is first; a failure on a later step leaves the body correct and a subsequent sync run will catch up the rest.