Skip to content

Commit eca6327

Browse files
authored
Add an AGENTS.md to orient agents and developers (#221)
AI-assisted contributions are a reality of open source, and that includes the maintainers themselves. Over recent months, AI has proven increasingly useful for the kind of menial, tedious work that does not require much creativity but is highly boring when done by hand: chasing down webhook delivery failures, tracing how a single slash command fans out into cross-repository workflow dispatches and check-run cascades, and adapting to GitHub API changes that quietly break the automation. This `AGENTS.md` provides enough context about the repository's architecture, conventions, and contracts that AI tools can produce reasonable results even when a human contributor fails to steer carefully. It documents the thin-Azure-Function-that-delegates-to-`git-for-windows-automation` architecture and the two contracts holding it together (workflows dispatched by file name; check-run names and summaries parsed by `cascading-runs.js`), the slash commands and how `/deploy` maps packages to architectures, the `embargoed-builds/` worktree that must _not_ be edited from `main`, and the build/test/lint commands and test-harness quirks. It mirrors `git-for-windows-automation`'s `AGENTS.md` and points back at it, since the two describe opposite ends of the same contract. Similar `AGENTS.md` files have been added to other repositories in the Git for Windows project: [git](git-for-windows/git#6198), [git-for-windows-automation](git-for-windows/git-for-windows-automation#170), [build-extra](git-for-windows/build-extra#718), [MINGW-packages](git-for-windows/MINGW-packages#194), and [MSYS2-packages](git-for-windows/MSYS2-packages#289). It is stacked on #220 on purpose, so that what it says about `/deploy` matches the code there; until that lands, the diff below also includes #220's commit.
2 parents f73ee11 + 88aca54 commit eca6327

1 file changed

Lines changed: 240 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
# AGENTS.md
2+
3+
This file provides guidance for AI agents and developers working with the
4+
`gfw-helper-github-app` repository.
5+
6+
## Repository Purpose
7+
8+
This repository implements the **GitForWindowsHelper GitHub App**, an
9+
[Azure Function](https://learn.microsoft.com/en-us/azure/azure-functions/)
10+
that serves the automation needs of the
11+
[Git for Windows](https://github.com/git-for-windows/git) project. It
12+
receives GitHub webhook events, performs quick tasks itself, and hands off
13+
more complex tasks to GitHub workflows in the
14+
[`git-for-windows-automation`](https://github.com/git-for-windows/git-for-windows-automation)
15+
repository (which has its own `AGENTS.md` describing the other side of
16+
this relationship).
17+
18+
The most visible feature is "slash commands": commands such as `/deploy`
19+
that maintainers issue via comments on GitHub Issues and Pull Requests.
20+
21+
## Architecture Overview
22+
23+
```
24+
GitHub webhook (issue_comment, check_run, push, workflow_run)
25+
|
26+
v
27+
GitForWindowsHelper (this Azure Function)
28+
| quick tasks done inline (e.g., /hi, queue check-runs)
29+
|
30+
v
31+
Dispatches a workflow BY FILENAME in git-for-windows-automation
32+
(or build-extra / git-sdk-*) via workflow_dispatch
33+
|
34+
v
35+
That workflow runs and "mirrors" check-runs back to the source PR;
36+
the App reacts to those check-run completions to drive cascades
37+
```
38+
39+
The App is intentionally thin: it validates the webhook, decides what to
40+
do, and either answers directly or triggers a workflow elsewhere. The
41+
heavy lifting (building packages, tagging Git, building installers) lives
42+
in `git-for-windows-automation`.
43+
44+
## Critical Contracts with git-for-windows-automation
45+
46+
This App is the counterpart to `git-for-windows-automation`. Two contracts
47+
must stay in sync across both repositories:
48+
49+
1. **Workflows are dispatched by filename.** Renaming a workflow there
50+
requires a matching change here. The App dispatches:
51+
52+
| Filename | Target repo | Triggered by |
53+
|----------|-------------|--------------|
54+
| `open-pr.yml` | git-for-windows-automation | `/open pr` |
55+
| `updpkgsums.yml` | git-for-windows-automation | `/updpkgsums` |
56+
| `build-and-deploy.yml` | git-for-windows-automation | `/deploy` |
57+
| `tag-git.yml` | git-for-windows-automation | `/snapshot`, `/git-artifacts` |
58+
| `git-artifacts.yml` | git-for-windows-automation | cascading after `tag-git` |
59+
| `release-git.yml` | git-for-windows-automation | `/release` |
60+
| `upload-snapshot.yml` | git-for-windows-automation | cascading after `git-artifacts` |
61+
| `add-release-note.yml` | build-extra | `/add release note` |
62+
| `sync.yml` | git-sdk-32 / git-sdk-64 / git-sdk-arm64 | `/synchronize-sdks` |
63+
64+
2. **Check-run names and summaries are a parsing contract.** The App
65+
queues check-runs with specific names and later parses the names,
66+
summaries and `text` of completed check-runs to drive cascades (see
67+
`cascading-runs.js`). These patterns must remain stable on both sides:
68+
69+
- Check-run names: `deploy`, `deploy_<arch>` (e.g. `deploy_x86_64`,
70+
`deploy_ucrt64`, `deploy_aarch64`), `tag-git`,
71+
`git-artifacts-<arch>` (`x86_64`/`i686`/`aarch64`), `upload-snapshot`.
72+
- Summary patterns parsed in `cascading-runs.js`:
73+
`Tag Git <version> @<sha>` and
74+
`Build Git <version> artifacts from commit <sha> (tag-git run #<id>)`.
75+
- The `text` field is parsed for
76+
`For details, see [this run](<.../actions/runs/<id>>)` to recover the
77+
dispatched workflow run id.
78+
79+
Changing any of these without updating `git-for-windows-automation` will
80+
break the automation.
81+
82+
## Slash Commands
83+
84+
Implemented in `slash-commands.js`; documented for users in `README.md`.
85+
86+
| Command | Where | What it does |
87+
|---------|-------|--------------|
88+
| `/hi` | any repo the App is installed in | Replies "Hi @&lt;login&gt;" |
89+
| `/add release note <type> <message>` (alias `/add relnote`) | git, build-extra, MINGW-packages, MSYS2-packages | Dispatches `add-release-note.yml` in build-extra |
90+
| `/open pr` | git issues | Dispatches `open-pr.yml` to open a component-update PR |
91+
| `/updpkgsums` | build-extra, MINGW-packages, MSYS2-packages PRs | Dispatches `updpkgsums.yml` to refresh PKGBUILD checksums |
92+
| `/deploy [<package>]` | build-extra, MINGW-packages, MSYS2-packages PRs | Dispatches one or more `build-and-deploy.yml` runs to build & deploy Pacman packages |
93+
| `/synchronize-sdks` | any G4W repo | Dispatches `sync.yml` in each `git-sdk-*` repo |
94+
| `/snapshot` | git PRs | Builds a snapshot from the PR merge commit (no upload) |
95+
| `/git-artifacts` | git PRs | Builds all release artifacts |
96+
| `/release` | git PRs | Publishes the artifacts as a GitHub Release |
97+
98+
### How `/deploy` maps packages to architectures
99+
100+
`/deploy` decides which `build-and-deploy.yml` runs to dispatch based on
101+
the package, using `isMSYSPackage` and `buildsAllArchitecturesInOneRun`
102+
from `component-updates.js`:
103+
104+
- **MSYS packages**: separate `x86_64` and/or `i686` runs.
105+
- **`mingw-w64-git-credential-manager`, `mingw-w64-git-lfs`,
106+
`mingw-w64-wintoast`**: a single combined run (no explicit
107+
architecture). These are cross-compiled via Visual Studio or downloaded
108+
as pre-built artifacts for every architecture (including `clangarm64`)
109+
at once, so they must not be split.
110+
- **`mingw-w64-llvm`**: only the `aarch64` run.
111+
- **All other MINGW packages (and `git-extra`)**: separate `i686`,
112+
`x86_64`, `ucrt64` and `aarch64` runs, so they build in parallel. The
113+
set of `MINGW_ARCH`s the dispatched workflow builds for each pseudo
114+
architecture lives in `build-and-deploy.yml` over in
115+
`git-for-windows-automation`.
116+
117+
## Event Routing
118+
119+
`index.js` is the Azure Function entry point. After validating the webhook
120+
signature (`validate-github-webhook.js`) it routes by `x-github-event`:
121+
122+
| Event | Condition | Handler |
123+
|-------|-----------|---------|
124+
| `issue_comment` | `action == created` and body starts with `/` | `slash-commands.js` |
125+
| `workflow_run` | completed `release-git.yml` run on `main` | `finalize-g4w-release.js` |
126+
| `check_run` | completed, app is the active bot, repo is `git` | `cascadingRuns` in `cascading-runs.js` |
127+
| `push` | to `git-for-windows/git` | `handlePush` in `cascading-runs.js` |
128+
129+
## Directory Structure
130+
131+
```
132+
GitForWindowsHelper/ # the Azure Function code
133+
├── index.js # entry point / webhook router
134+
├── function.json # Azure Functions binding
135+
├── slash-commands.js # /deploy, /release, /snapshot, ... handlers
136+
├── cascading-runs.js # tag-git -> git-artifacts -> upload-snapshot cascade
137+
├── component-updates.js # package classification + release-note helpers
138+
├── finalize-g4w-release.js # post-release follow-up
139+
├── check-runs.js # queue/update/list check-runs in other repos
140+
├── issues.js # comment helpers (append, react, ...)
141+
├── trigger-workflow-dispatch.js
142+
├── github-api-request.js / github-api-request-as-app.js
143+
├── get-installation-access-token.js / get-installation-id-for-repo.js
144+
├── get-collaborator-permissions.js / get-user-access-token.js
145+
├── https-request.js / search.js / gently.js / org.js
146+
└── validate-github-webhook.js
147+
148+
__tests__/ # Jest tests for the App (index + component-updates)
149+
.github/workflows/deploy.yml # deploys the Function to Azure on push to main
150+
embargoed-builds/ # SEPARATE git worktree (see below) -- do not edit here
151+
```
152+
153+
## The `embargoed-builds` Worktree
154+
155+
`embargoed-builds/` is **not** part of the `main` branch. It is a separate
156+
long-lived branch (`embargoed-builds`) that is typically checked out as a
157+
[git worktree](https://git-scm.com/docs/git-worktree) nested in the
158+
working directory. It is a parallel variant of this App used to build
159+
**embargoed security releases**: it deploys to private Azure Blob Storage
160+
(`wingit.blob.core.windows.net`) instead of the public `pacman-repo`, and
161+
relies on self-hosted Windows/ARM64 runners.
162+
163+
Consequences for agents:
164+
165+
- When working on `main`, **do not edit files under `embargoed-builds/`**.
166+
It belongs to a different branch and is maintained separately; the two
167+
variants are reconciled deliberately, not by editing both at once.
168+
- Because Jest's default `testMatch` is `**/__tests__/**`, running
169+
`npm test` from the repo root also collects
170+
`embargoed-builds/__tests__/` when that worktree is present (you will
171+
see four suites instead of two). To run only this App's tests, scope it:
172+
`npx jest __tests__/`.
173+
174+
## Building, Testing and Linting
175+
176+
There is no build step; the Function is deployed as-is. Verified commands:
177+
178+
- `npm run lint` — ESLint over `**/*.js` (config in `eslint.config.js`).
179+
- `npm run lint:fix` — ESLint with `--fix`.
180+
- `npm test` — Jest. To restrict to this App (excluding the
181+
`embargoed-builds` worktree), use `npx jest __tests__/`.
182+
183+
Always run lint and tests before committing.
184+
185+
## Testing Conventions
186+
187+
The tests in `__tests__/index.test.js` exercise the webhook handler
188+
end-to-end with heavily mocked dependencies. When adding `/deploy`-style
189+
tests, keep these harness facts in mind:
190+
191+
- `afterEach` calls `jest.clearAllMocks()` and empties `dispatchedWorkflows`,
192+
so `toHaveBeenCalledTimes(...)` counts are per-test.
193+
- The GitHub API is mocked in `mockGitHubApiRequest`. Looking up a PR's
194+
head SHA goes through `GET .../pulls/<number>`, so **each PR number used
195+
in a test needs its own `pulls/<number>` entry** returning
196+
`{ head: { sha: ... } }`, or the mock throws "Unhandled GET ...".
197+
- `dispatchedWorkflows` is built with `unshift`, so it is in **reverse**
198+
dispatch order. A `/deploy` that dispatches `i686, x86_64, ucrt64,
199+
aarch64` yields `['aarch64', 'ucrt64', 'x86_64', 'i686']`.
200+
- The user-facing comment text is assembled in `slash-commands.js`: a
201+
single dispatch reads "The workflow run [was started]"; multiple
202+
dispatches read "The [<arch>](...), the [<arch>](...) and the
203+
[<arch>](...) workflow runs were started." `displayArchitecture`
204+
overrides the shown label (e.g. `arm64` for the `aarch64` run).
205+
206+
## Coding Conventions
207+
208+
- CommonJS modules (`require()` / `module.exports`); `async`/`await`.
209+
- Handlers `require()` their dependencies lazily, inside the function
210+
body, rather than at the top of the file.
211+
- Modules are written to be usable both from the Azure Function and from
212+
the command line (see the `test-*.js` and `get-*.js` helper scripts at
213+
the repo root), with a `context`/`console` argument used for logging.
214+
- Errors are surfaced by throwing; `index.js` turns thrown errors into 500
215+
responses.
216+
- Follow the surrounding style: small, single-purpose modules; minimal,
217+
surgical changes; no duplicated API/auth logic (reuse the existing
218+
`github-api-request*` / token modules).
219+
- Commit subjects follow `<area>: <imperative>`, e.g.
220+
`slash-commands: ...`, `component-updates: ...`.
221+
222+
## Deployment
223+
224+
`.github/workflows/deploy.yml` deploys the Function to Azure on every push
225+
to `main` that touches `deploy.yml` or `GitForWindowsHelper/**`. It
226+
authenticates with Azure via OIDC (`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`,
227+
`AZURE_SUBSCRIPTION_ID`) in the `deploy-to-azure` environment and publishes
228+
with `Azure/functions-action`, respecting `.funcignore`. The GitHub App
229+
secrets (`GITHUB_APP_*`, `GITHUB_WEBHOOK_SECRET`) are configured on the
230+
Azure Function itself, not in this repository; see `README.md` for the
231+
one-time setup.
232+
233+
## Validating Changes
234+
235+
1. **Lint and test**: `npm run lint` and `npx jest __tests__/`.
236+
2. **Dispatch contracts**: if you change a dispatched workflow filename or
237+
a queued check-run name/summary, update `git-for-windows-automation`
238+
(and re-check its `AGENTS.md`) in lockstep.
239+
3. **Leave `embargoed-builds/` alone** unless you are deliberately working
240+
on that branch.

0 commit comments

Comments
 (0)