Skip to content

fix(reassembly): bound TCP holes with a platform-independent open bound (#1959) #7478

fix(reassembly): bound TCP holes with a platform-independent open bound (#1959)

fix(reassembly): bound TCP holes with a platform-independent open bound (#1959) #7478

Workflow file for this run

name: Project Status

Check warning on line 1 in .github/workflows/project-status.yml

View workflow run for this annotation

GitHub Actions / Project Status

Workflow execution policy warning (evaluate mode)

On November 2, 2026, GitHub will restrict `pull_request_target` on public repositories by default. To continue allowing the event trigger, configure an Actions policy. Learn more: https://gh.io/securely-using-pull_request_target#default-policy-for-pull_request_target
# 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