Repository navigation
fix(reassembly): bound TCP holes with a platform-independent open bound (#1959) #7478
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: Project Status | ||
|
Check warning on line 1 in .github/workflows/project-status.yml
|
||
| # Keeps the *Status* field of the project board (users/JarryShaw/projects/2) in | ||
| # line with each item's state labels. The board's built-in workflows add items and | ||
| # set Done on close and merge, but no built-in workflow observes a label change, so | ||
| # this one makes every other transition. The mapping lives in | ||
| # util/project_status.py:status_for and is documented under "Milestones and the | ||
| # Project Board" in docs/source/contributing/conventions/process.rst; the tests in | ||
| # tests/project/test_project_status.py hold the two to each other. | ||
| # | ||
| # SECURITY. `pull_request_target` is used for one reason only: a pull request from | ||
| # a fork gets no secrets under `pull_request`, and PROJECT_TOKEN is a secret. The | ||
| # price is that this workflow runs with that secret *on events a stranger can | ||
| # cause*, so it must never execute anything the pull request supplies -- the | ||
| # classic "pwn request". Concretely, the job below: | ||
| # | ||
| # * never checks out the repository, at any ref, and has no `uses:` step at all; | ||
| # * fetches util/project_status.py through the API at `github.sha`, which under | ||
| # `pull_request_target` is the tip of the *base* branch, never the PR head -- | ||
| # so editing the script in a pull request changes nothing about its own run; | ||
| # * takes nothing from the event payload except the item number, passed through | ||
| # `env:` rather than interpolated into the script, and re-reads everything else | ||
| # (labels, state) from the API. | ||
| # | ||
| # tests/project/test_project_status.py asserts each of them over this file -- no | ||
| # checkout or `uses:`, no `${{ }}` inside a `run:` script, and no event-payload | ||
| # reference outside `env:` other than the item number -- so a later edit that | ||
| # breaks one fails a test rather than a security review. | ||
| # | ||
| # `workflow_dispatch` is the other way in, and it is not a fork boundary: it runs | ||
| # the workflow file *of the branch selected* with PROJECT_TOKEN in scope. Only | ||
| # someone with write access can dispatch, and write access can already change any | ||
| # workflow, so this trusts collaborators rather than forks -- the same trust the | ||
| # rest of the repository's secrets rely on. | ||
| # | ||
| # Event runs and reconciles are in different concurrency groups, so they can | ||
| # overlap. A reconcile reads an item, an event run then sets a fresher Status, | ||
| # and the reconcile writes its older answer over it. The window is the length of | ||
| # one reconcile, and the damage is self-repairing: the item's next event, or the | ||
| # next nightly run, re-reads the labels and sets it right. Serializing the two | ||
| # would mean one global group, which lets a reconcile hold every event run. | ||
| # | ||
| # PROJECT_TOKEN is a classic PAT with `project` and `repo` scopes, because | ||
| # GITHUB_TOKEN cannot write a user-owned project. Without it every run skips with a | ||
| # `::notice::` instead of failing. | ||
| on: | ||
| issues: | ||
| types: [opened, reopened, labeled, unlabeled, closed] | ||
| pull_request_target: | ||
| types: [opened, reopened, labeled, unlabeled, closed, ready_for_review] | ||
| schedule: | ||
| # Nightly backstop for events this workflow missed -- a run dropped because | ||
| # its item's concurrency queue below was full, a GitHub outage, or a label | ||
| # changed while the secret was absent. 00:37 UTC sits well clear of the | ||
| # Saturday jobs (CodeQL and GitHub Pages at 02:00, Python Compatibility at | ||
| # 04:00, Lint at 06:00, Vendor and Conda Update at 10:00), and off the hour | ||
| # mark that GitHub's scheduler queues most heavily. | ||
| - cron: '37 0 * * *' | ||
| workflow_dispatch: | ||
| # The job authenticates with PROJECT_TOKEN throughout and never uses GITHUB_TOKEN, | ||
| # so it is granted nothing. | ||
| permissions: {} | ||
| # Event runs are keyed on the item number, so a label swap -- which fires an | ||
| # `unlabeled` and a `labeled` within the same second -- serializes on one item. | ||
| # Issues and pull requests share one number space, so the key cannot collide | ||
| # across the two event types. | ||
| # | ||
| # Nothing in a group is cancelled: a cancelled run stays on the PR head as a | ||
| # failed Sync Status check even after a newer run has fixed the board. So there | ||
| # is no `cancel-in-progress`, and that is not enough on its own: by default a | ||
| # group holds one pending run and cancels it when a newer one arrives. | ||
| # `queue: max` lets up to 100 wait instead; only a run past that is cancelled. | ||
| # GitHub documents both, and that the two keys together are a validation error: | ||
| # https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency | ||
| # | ||
| # The board still ends right, whatever order the queue runs in -- GitHub states | ||
| # that order is not guaranteed. Runs on one item never overlap, and each re-reads | ||
| # the labels rather than trusting its payload. So the last run to execute starts | ||
| # after every other run on the item has finished, including the one started by | ||
| # the final label event. It therefore reads the final labels, and its write is | ||
| # the last. The cost is that a burst of N events makes N runs instead of | ||
| # collapsing into one, each a handful of API calls. | ||
| # | ||
| # Scheduled and manual reconciles share their own group and queue the same way. | ||
| # A second reconcile waiting behind the first repeats its work, which is cheap at | ||
| # nightly cadence. | ||
| concurrency: | ||
| group: project-status-${{ (github.event.issue.number || github.event.pull_request.number) && format('item-{0}', github.event.issue.number || github.event.pull_request.number) || 'reconcile' }} | ||
| queue: max | ||
| jobs: | ||
| status: | ||
| name: Sync Status | ||
| runs-on: ubuntu-latest | ||
| # A single item is a handful of API calls; a full reconcile is one search | ||
| # page per 50 items plus one mutation per item that changed. | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - name: Sync the project board | ||
| env: | ||
| GH_TOKEN: ${{ secrets.PROJECT_TOKEN }} | ||
| ITEM: ${{ github.event.issue.number || github.event.pull_request.number }} | ||
| SCRIPT_REF: ${{ github.sha }} | ||
| run: | | ||
| if [ -z "$GH_TOKEN" ]; then | ||
| echo '::notice title=Project Status::The PROJECT_TOKEN secret is not set, so the project board was not updated.' | ||
| exit 0 | ||
| fi | ||
| # The base branch's copy of the script -- see the security note above. | ||
| gh api -H 'Accept: application/vnd.github.raw' \ | ||
| "repos/${GITHUB_REPOSITORY}/contents/util/project_status.py?ref=${SCRIPT_REF}" \ | ||
| > "${RUNNER_TEMP}/project_status.py" | ||
| if [ -n "$ITEM" ]; then | ||
| python3 "${RUNNER_TEMP}/project_status.py" --item "$ITEM" | ||
| else | ||
| python3 "${RUNNER_TEMP}/project_status.py" --reconcile | ||
| fi | ||