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
17 changes: 17 additions & 0 deletions skills/github-project/references/actionlint-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,20 @@ Complex matrix expressions with `fromJSON()` may cause type-check warnings. Thes
ignore:
- 'fromJSON'
```

### yamllint `empty-lines` rejects trailing blank lines

Unrelated to actionlint itself, but bites right next to it in CI: if you run `yamllint` in the same lane, its default `empty-lines` rule rejects a file that ends with more than one newline. Workflow files end with exactly one newline.

Generators that use `echo "$CONTENT" > file.yml` frequently add a trailing blank; prefer:

```bash
printf '%s\n' "$CONTENT" > file.yml
```

Verify after writing:

```bash
# Bytes at end of file — should be `0a` (one newline), not `0a0a`.
tail -c 2 file.yml | xxd -p
```
42 changes: 42 additions & 0 deletions skills/github-project/references/auto-merge-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,48 @@ For each unresolved thread:

**Re-sweep after follow-up PRs merge.** Copilot often reviews the follow-up PR itself and posts new threads. The sweep isn't one-shot — run it again until the count hits zero across all touched PRs.

## CI Annotations — Always Check Before Declaring a PR Clean

CI checks can report `success` at the status-run level while still emitting **warning annotations** (typical for actionlint / shellcheck via reviewdog, CodeQL deprecation notices, YAML-lint). These annotations don't show up in `gh pr checks` or in the PR summary page — they only appear on the job's detail page or on the Files-Changed tab. Declaring a PR "clean" based on `gh pr checks` alone leaves real findings un-addressed.

**Check explicitly:**

```bash
# Annotations on a specific check run:
gh api repos/OWNER/REPO/check-runs/CHECK_RUN_ID/annotations --jq \
'.[] | {message, annotation_level, path, start_line}'

# All check runs for a commit that have any annotations:
gh api "repos/OWNER/REPO/commits/SHA/check-runs" --jq \
'.check_runs[] | select(.output.annotations_count > 0) |
{name, annotations: .output.annotations_count}'
```

**Make warnings blocking.** reviewdog-based linters default to posting warnings that don't fail the workflow. Configure them to fail:

```yaml
- uses: reviewdog/action-actionlint@v1 # or -shellcheck, -yamllint, etc.
with:
fail_level: error
```

`fail_level: error` is the modern input; the deprecated `fail_on_error` + `level` combination still works but is going away. When a new reviewdog-based linter is added, grep the caller for `fail_level:` and set it to `error` up front — otherwise real findings silently accumulate.

## CI Re-runs Replay the Same Commit

`gh run rerun <run-id>` **re-executes the ORIGINAL commit SHA**, not `HEAD`. If you push a fix and re-run a failed old workflow, the rerun still fails against the pre-fix code.

**Right way:** push the fix, then either wait for the automatic run triggered by the push, or re-run the LATEST run:

```bash
# Latest run ID for a workflow on a branch:
gh api "repos/OWNER/REPO/actions/runs?per_page=5" --jq \
'.workflow_runs[] | select(.name == "CI") | {id, head_sha: .head_sha[:7]}' \
| head -1

gh api repos/OWNER/REPO/actions/runs/RUN_ID/rerun -X POST
```

## Merge Queue Behavior and Pitfalls

### Sequential Processing
Expand Down
20 changes: 20 additions & 0 deletions skills/github-project/references/merge-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,26 @@ EOF

**Fix:** Update auto-merge workflow to use `--merge` flag instead of `--rebase` or `--squash`.

### Rulesets cannot block merge on a pending review

Neither branch protection nor rulesets support "block merge while any requested reviewer hasn't submitted yet". The options available are adjacent but not equivalent:

| Setting | What it does | Not what you want |
|---|---|---|
| `required_approving_review_count: 1` | Needs **one approval** | Doesn't wait for other requested reviewers |
| `required_review_thread_resolution: true` | Blocks on **unresolved threads** | Doesn't block before any thread is created |

If you need to hold merge until Copilot (or any other requested reviewer) has actually posted its review, the workaround is a custom GitHub Actions status check that queries pending reviewers and fails if any are outstanding — then require that check in branch protection.

```bash
# Example: fail the check if any reviewer is still requested.
pending=$(gh api "repos/$REPO/pulls/$PR" --jq '.requested_reviewers | length')
Comment thread
CybotTM marked this conversation as resolved.
if [[ "$pending" -gt 0 ]]; then
echo "::error::Still waiting on $pending requested reviewer(s)"
exit 1
fi
```

## References

- [GitHub Branch Protection](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches)
Expand Down
34 changes: 34 additions & 0 deletions skills/github-project/references/multi-repo-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,40 @@ name specific repos to skip.)

Wait for "go" (or an equivalent affirmative). Silence is not approval.

## Pre-Flight Per-Repo Checks

Before touching each repo in the batch, check three things the dry-run manifest won't catch — they tend to surface as 30 silent failures across a fleet loop:

### 1. Default branch name

Not every repo uses `main`. Some legacy repos are on `master`; forks can be on anything.

```bash
DEFAULT_BRANCH=$(gh api "repos/$REPO" --jq '.default_branch')
```

Use `$DEFAULT_BRANCH` everywhere a script would otherwise hard-code `main`. Pushing to the wrong branch either silently creates a new branch or fails with a confusing rejection.

### 2. Archived repos

Archived repos reject most writes with a generic permission error. Dependabot/Renovate sometimes still open PRs on them (via the pre-archive config), and a batch loop that tries to merge them fails cryptically.

```bash
ARCHIVED=$(gh api "repos/$REPO" --jq '.archived')
if [[ "$ARCHIVED" == "true" ]]; then
# Skip, or handle specially: unarchive → close PR → re-archive.
continue
fi
```

Never enable auto-merge on archived repos — the auto-merge plumbing fails at set-up time with "archived" errors.

### 3. Contents API vs branch protection

`gh api -X PUT repos/.../contents/...` is the fastest path for tiny single-file edits across a fleet — but it returns HTTP 409 on any repo that requires PRs, a merge queue, or signed commits. If your batch mixes repos with and without branch protection, this path breaks mid-loop and leaves half the fleet updated.

**Safer default for batch file edits**: open a one-commit PR per repo even when the Contents API would work. Gives you a reviewable diff, matches any future signing/protection rule tightening, and keeps behavior consistent across the fleet.

## Parallel PR Rebasing

For N PRs that need rebasing on their default branches, dispatch parallel subagents — one per PR — with failure isolation.
Expand Down
Loading