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
82 changes: 82 additions & 0 deletions .github/workflows/lint-workflows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Workflow lint

# START OF COMMON SECTION
on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '**' ]
types: [opened, synchronize, reopened, ready_for_review]
paths:
- '.github/workflows/**'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# END OF COMMON SECTION

permissions:
contents: read

jobs:
reusable-concurrency:
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
name: Reusable workflows must not self-cancel
runs-on: ubuntu-22.04
timeout-minutes: 5
steps:
- name: Checkout wolfProvider
uses: actions/checkout@v4

- name: Install PyYAML
run: pip install --quiet pyyaml

- name: Check concurrency in reusable workflows
run: |
python3 - <<'EOF'
import glob, sys, yaml

paths = sorted(glob.glob('.github/workflows/*.yml')
+ glob.glob('.github/workflows/*.yaml'))
bad = []
for path in paths:
try:
with open(path) as f:
wf = yaml.safe_load(f)
except yaml.YAMLError as e:
print(f'{path}: unparsable: {e}')
sys.exit(1)
if not isinstance(wf, dict):
continue
# YAML 1.1 parses a bare `on:` key as the boolean True.
triggers = wf.get('on', wf.get(True)) or {}
# `on:` is legal as a mapping, a list, or a bare string.
if isinstance(triggers, str):
names = {triggers}
elif isinstance(triggers, list):
names = set(triggers)
elif isinstance(triggers, dict):
names = set(triggers)
else:
names = set()
if 'workflow_call' not in names:
continue
conc = wf.get('concurrency') or {}
if not isinstance(conc, dict):
continue
# Anything not literally false counts: "true"/${{ }} still cancel.
cip = conc.get('cancel-in-progress', False)
if cip is not False and str(cip).strip().lower() != 'false':
bad.append(f"{path}: cancel-in-progress={cip!r} "
f"group={conc.get('group')!r}")

if bad:
print('Reusable workflow sets cancel-in-progress to something other')
print('than false. github.workflow resolves to the CALLER, so sibling')
print('calls in one run share a group and cancel each other. Set')
print('cancel-in-progress: false, or drop the concurrency block.')
for b in bad:
print(' ' + b)
sys.exit(1)
print('ok: no reusable workflow sets cancel-in-progress')
EOF
4 changes: 1 addition & 3 deletions .github/workflows/nightly-multi-compiler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ on:
workflow_call: {}
workflow_dispatch: {}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# No concurrency group: github.workflow is the caller's here, so any group collides with sibling calls.

jobs:
build_wolfprovider:
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/perf-regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ on:
workflow_dispatch:
workflow_call:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# No concurrency group: github.workflow is the caller's here, so any group collides with sibling calls.

jobs:
discover_versions:
Expand Down
55 changes: 55 additions & 0 deletions .github/workflows/pr-osp-retry.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: PR OSP retry

# No concurrency group: workflow_run reports github.ref as the default branch, so any group self-cancels.

on:
workflow_run:
workflows: ["PR OSP (label-selected)"]
types: [completed]

permissions:
actions: write

jobs:
retry:
name: Rerun failed jobs once
runs-on: ubuntu-latest
timeout-minutes: 5
# 'failure' skips superseded runs (those land as 'cancelled'); attempt 1 bounds this to one retry.
if: >
github.event.workflow_run.conclusion == 'failure' &&
github.event.workflow_run.run_attempt == 1
steps:
- name: Verify trigger is trusted
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
ACTOR: ${{ github.event.workflow_run.actor.login }}
WF_PATH: ${{ github.event.workflow_run.path }}
run: |
# A fork controls both the workflow name and a new file's path, so pin the path.
if [ "$WF_PATH" != ".github/workflows/pr-osp-select.yml" ]; then
echo "::error::untrusted triggering workflow: $WF_PATH"
exit 1
fi
# Distinguish "not a collaborator" (404) from an API failure; never conflate them.
if ! body=$(gh api "/repos/$REPO/collaborators/$ACTOR/permission" 2>&1); then
case "$body" in
*"Not Found"*) echo "::error::actor $ACTOR is not a collaborator"; exit 1 ;;
*) echo "::error::could not verify $ACTOR: $body"; exit 1 ;;
esac
fi
perm=$(printf '%s' "$body" | jq -r '.permission')
# triage can add labels, so it can legitimately start a PR OSP run.
case "$perm" in
admin|write|maintain|triage) echo "actor $ACTOR ok ($perm)" ;;
*) echo "::error::actor $ACTOR cannot label ($perm)"; exit 1 ;;
esac

- name: Rerun failed jobs
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RUN_ID: ${{ github.event.workflow_run.id }}
run: |
gh api -X POST \
"/repos/${{ github.repository }}/actions/runs/$RUN_ID/rerun-failed-jobs"
Loading