Skip to content

Commit c628d35

Browse files
authored
Merge pull request #131 from ptr727/develop
Release: sync to ProjectTemplate standards (#130)
2 parents 9d8b94c + 2c0c2d5 commit c628d35

13 files changed

Lines changed: 980 additions & 510 deletions

.github/copilot-instructions.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,108 @@
11
# GitHub Copilot Instructions for LanguageTags
22

3+
The **canonical guide is [AGENTS.md](../AGENTS.md)** at the repo root — read it first. It covers branching, PR review etiquette, workflow YAML conventions, and the release pipeline.
4+
5+
This file is intentionally focused: the GitHub Copilot Review Runbook (provider-specific mechanics behind the review-loop contract defined in AGENTS.md), followed by the LanguageTags-specific code conventions and public-API contract notes that VS Code's AI generators pick up directly from this path.
6+
7+
For C# style rules, see [`CODESTYLE.md`](../CODESTYLE.md) at the repo root. Do not duplicate those rules here.
8+
9+
## GitHub Copilot Review Runbook
10+
11+
Use this section for provider-specific mechanics. The expected review loop *contract* (request review on every push, verify head-SHA coverage, triage findings, reply + resolve, escalate when stuck) is defined in [AGENTS.md → PR Review Etiquette](../AGENTS.md#pr-review-etiquette). This section only describes how to make GitHub Copilot reliably execute it.
12+
13+
### Triggering and Polling
14+
15+
Auto-review on push is configured (via the branch ruleset's `copilot_code_review` rule with `review_on_push: true`) but fires inconsistently in practice — treat it as best-effort, not guaranteed. Request review explicitly through the GitHub PR UI (request `Copilot` as a reviewer) after every push.
16+
17+
**Do NOT post `@Copilot review` as a PR comment.** That comment triggers the Copilot *coding agent* (`copilot-swe-agent[bot]`), which makes code changes rather than posting a review.
18+
19+
Known non-working request paths (don't rely on them):
20+
21+
- `POST /requested_reviewers` with `reviewers=[Copilot]` can return 200 but no-op.
22+
- `copilot-pull-request-reviewer` as a requested reviewer slug returns 422.
23+
- GraphQL `requestReviews` rejects Copilot's bot node.
24+
25+
### Verify Review Covered Current Head
26+
27+
Before merging, confirm Copilot reviewed the current PR head SHA. Copilot may respond as either a formal review (carries an exact commit SHA) or an issue comment (no SHA — use the most recent Copilot comment for manual confirmation). Check both.
28+
29+
```sh
30+
PR_HEAD=$(gh pr view <N> --json headRefOid --jq '.headRefOid')
31+
32+
# 1. Formal review — exact SHA match.
33+
gh pr view <N> --json reviews --jq \
34+
'.reviews[] | select(.author.login=="copilot-pull-request-reviewer") | .commit.oid' \
35+
| grep -q "$PR_HEAD" && echo "covered via formal review"
36+
37+
# 2. Issue comment — show the most recent Copilot comment for manual confirmation.
38+
gh api repos/ptr727/LanguageTags/issues/<N>/comments --jq \
39+
'[.[] | select(.user.login=="copilot-pull-request-reviewer")] | last | {created_at, body: .body[:200]}'
40+
```
41+
42+
Coverage is confirmed when (1) exits 0. For issue comments (path 2), body content is the only reliable signal — `created_at` is not: `git log -1 --format=%cI` is the **commit** timestamp, not the push timestamp, so amended or rebased commits can have an earlier timestamp and an older Copilot comment could satisfy a time check even though Copilot never saw the current head. Treat path (2) as confirmed only when the comment body explicitly refers to the current changes.
43+
44+
### Bounded Retry Workflow
45+
46+
If a review did not run on the current head, retry:
47+
48+
1. Wait briefly and check head-SHA coverage (see above).
49+
1. Request review again via the GitHub PR UI.
50+
1. Retry up to two more times (three total).
51+
1. If still missing, mark review as blocked and escalate to the user/maintainer with what was attempted.
52+
53+
### Reply and Thread Resolution Workflow
54+
55+
List unresolved threads. Use `first: 100` with cursor-based pagination; if `hasNextPage` is true, re-run with `after: "<endCursor>"` to retrieve the next page:
56+
57+
```sh
58+
gh api graphql -f query='
59+
{
60+
repository(owner: "ptr727", name: "LanguageTags") {
61+
pullRequest(number: <N>) {
62+
reviewThreads(first: 100) {
63+
nodes {
64+
id isResolved path
65+
comments(first: 1) { nodes { author { login } body } }
66+
}
67+
pageInfo { hasNextPage endCursor }
68+
}
69+
}
70+
}
71+
}' | jq '
72+
.data.repository.pullRequest.reviewThreads |
73+
(.pageInfo | "hasNextPage=\(.hasNextPage) endCursor=\(.endCursor)"),
74+
(.nodes[] | select(.isResolved == false))
75+
'
76+
```
77+
78+
Reply on a thread, then resolve it:
79+
80+
```sh
81+
gh api graphql -f query='
82+
mutation($threadId: ID!, $body: String!) {
83+
addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) {
84+
comment { id }
85+
}
86+
}' -F threadId="PRRT_..." -F body="Fixed in <SHA>: <one-line summary>."
87+
88+
gh api graphql -f query='
89+
mutation($threadId: ID!) {
90+
resolveReviewThread(input: { threadId: $threadId }) { thread { id isResolved } }
91+
}' -F threadId="PRRT_..."
92+
```
93+
94+
Issue-level Copilot comments (those in `issues/<N>/comments`) have no resolution action — GitHub provides no API or UI to resolve them. Reply if the finding warrants it; no resolution step is needed or possible.
95+
96+
Reply-body conventions:
97+
98+
- Accepted bug/style fix: include fixing commit SHA and a one-line summary.
99+
- Declined style comment: cite the rule (AGENTS.md or CODESTYLE.md) and the existing-tree precedent.
100+
- Declined architecture proposal: one-sentence rationale.
101+
102+
After the final push, sweep-resolve stale older threads for removed code paths.
103+
104+
---
105+
3106
## Project Overview
4107

5108
**LanguageTags** is a C# .NET library for handling ISO 639-2, ISO 639-3, and RFC 5646 / BCP 47 language tags. The project serves two primary purposes:

.github/dependabot.yml

Lines changed: 74 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,74 @@
1-
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
2-
version: 2
3-
updates:
4-
5-
# main
6-
- package-ecosystem: "nuget"
7-
target-branch: "main"
8-
directory: "/"
9-
schedule:
10-
interval: "daily"
11-
groups:
12-
nuget-deps:
13-
patterns:
14-
- "*"
15-
- package-ecosystem: "github-actions"
16-
target-branch: "main"
17-
directory: "/"
18-
schedule:
19-
interval: "daily"
20-
groups:
21-
actions-deps:
22-
patterns:
23-
- "*"
1+
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
2+
#
3+
# Every ecosystem appears **twice**: once with `target-branch: "main"`
4+
# and once with `target-branch: "develop"`. Dependabot will open
5+
# parallel PRs against each branch, so both stay current on
6+
# dependency versions independently of the develop → main release
7+
# cadence.
8+
#
9+
# Why dual-target and not develop-only:
10+
# - `develop` is the integration branch and ships content forward to
11+
# `main` through merge-commit releases, but the time between releases
12+
# can be long (a feature branch may sit on develop for weeks).
13+
# - Consumers (NuGet.org, GitHub releases) pull from `main` directly.
14+
# If `main` only got dependency bumps via the next develop → main
15+
# release, those consumers would ship outdated code in the interim.
16+
# - The codegen workflow takes the same dual-target shape for the same
17+
# reason — see .github/workflows/run-codegen-pull-request-task.yml.
18+
#
19+
# The merge-bot's `case` statement in
20+
# .github/workflows/merge-bot-pull-request.yml dispatches the merge
21+
# method per base ref (squash on develop, merge on main) so both bases
22+
# auto-merge cleanly. `develop` remains strictly forward-only: there
23+
# are no main → develop back-merges; each branch absorbs its own
24+
# Dependabot PRs and codegen PRs independently.
25+
#
26+
# Security update PRs (CVE-driven) are opened by Dependabot against
27+
# the repo default branch (`main`) regardless of any `target-branch`
28+
# config — the `case` statement handles them in the same code path.
29+
version: 2
30+
updates:
31+
32+
# ----- nuget -----
33+
34+
- package-ecosystem: "nuget"
35+
target-branch: "main"
36+
directory: "/"
37+
schedule:
38+
interval: "daily"
39+
groups:
40+
nuget-deps:
41+
patterns:
42+
- "*"
43+
44+
- package-ecosystem: "nuget"
45+
target-branch: "develop"
46+
directory: "/"
47+
schedule:
48+
interval: "daily"
49+
groups:
50+
nuget-deps:
51+
patterns:
52+
- "*"
53+
54+
# ----- github-actions -----
55+
56+
- package-ecosystem: "github-actions"
57+
target-branch: "main"
58+
directory: "/"
59+
schedule:
60+
interval: "daily"
61+
groups:
62+
actions-deps:
63+
patterns:
64+
- "*"
65+
66+
- package-ecosystem: "github-actions"
67+
target-branch: "develop"
68+
directory: "/"
69+
schedule:
70+
interval: "daily"
71+
groups:
72+
actions-deps:
73+
patterns:
74+
- "*"
Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
1-
name: Build BYOB date badge task
2-
3-
env:
4-
IS_MAIN_BRANCH: ${{ endsWith(github.ref, 'refs/heads/main') }}
5-
6-
on:
7-
workflow_call:
8-
9-
jobs:
10-
11-
date-badge:
12-
name: Build BYOB date badge job
13-
runs-on: ubuntu-latest
14-
15-
steps:
16-
17-
- name: Get current date step
18-
id: date
19-
run: |
20-
echo "date=$(date)" >> $GITHUB_OUTPUT
21-
22-
- name: Build BYOB date badge step
23-
if: ${{ env.IS_MAIN_BRANCH == 'true' }}
24-
uses: RubbaBoy/BYOB@v1
25-
with:
26-
name: lastbuild
27-
label: "Last Build"
28-
icon: "github"
29-
status: ${{ steps.date.outputs.date }}
30-
color: "blue"
31-
github_token: ${{ secrets.GITHUB_TOKEN }}
1+
name: Build BYOB date badge task
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
8+
date-badge:
9+
name: Build BYOB date badge job
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
14+
- name: Get current date step
15+
id: date
16+
run: |
17+
set -euo pipefail
18+
echo "date=$(date)" >> $GITHUB_OUTPUT
19+
20+
- name: Build BYOB date badge step
21+
if: ${{ github.ref_name == 'main' }}
22+
uses: RubbaBoy/BYOB@a4919104bc0ec7cfd7f113e42c405cc45246f2a4 # v1
23+
with:
24+
name: lastbuild
25+
label: "Last Build"
26+
icon: "github"
27+
status: ${{ steps.date.outputs.date }}
28+
color: "blue"
29+
github_token: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)