You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 6bc1674
Browse filesBrowse the repository at this point in the historyBrowse files
Vivswan Shah
committed
refactor(arch): move the run flows to src/flows and add the public entry src/index.ts
The single-, multi-, and merge-run flows, delivery, redaction, settings reading, and the step summaries move from src/action/ to src/flows/; the secret-reference module moves to src/engine/, the private-report channel list and the redacted-detail types to src/report/delivery.ts, and the @actions/artifact uploader to src/action/artifact.ts behind the report layer's ArtifactUploader port. src/index.ts is the public entry, and src/action/ reaches the rest of src/ only through it: architecture.yml grants the action that one edge, so the action is a consumer of the library. No behavior changes: every flow, message, output, and test keeps its meaning, and the single-repo body of run() becomes runSingle, symmetric with runMulti.
- `lib/settings.schema.json` is the COMMITTED JSON Schema for settings.yml, generated from the zod slices in `src/sections/<key>/schema.ts` as composed into the settings document by `src/schema.ts`. Each slice is ONE declaration that produces its config type (z.infer), the section's tolerant runtime shape (the section module derives `loosen(<slice>)` itself; the factory-minted families - secrets, variables, the two setup sections, and the list sections on `listSection` - get it from their shared factory in `src/sections/shared/`, which does that derivation once), and its part of the published schema (`.meta({id})` names its definitions; the descriptions come from the `schema:` map in the section's `<key>.docs.yml`, keyed `Definition.field`, which the generator attaches and fails on when a site or a key is unmatched); `src/schema.ts` adds only the document-level wrappers and pins each property to its slice with a lockstep type. Regenerate the schema with `bun run build:schema` after a schema-affecting `src/` change - CI's schema-check job fails when it drifts. `lib/index.js`, the bundled entrypoint the action runs (node24), is NOT committed on main: every workflow that executes the action builds it first (`bun run build:bundle`), the e2e runner builds it to a temp path, and a release builds and commits it on a packaged child of the release commit, which is what every `vX.Y.Z` tag and the moving major point at (main itself stays source-only). `lib/` is exempt from the typography check (third-party unicode in the bundle; the schema descriptions are checked at source) and excluded from [biome](https://biomejs.dev).
38
38
- The import layering of `src/` is declared in `architecture.yml` and enforced by `bun run lint:arch`, so a new cross-layer import is a deliberate edit to that file; the module map in `docs/reference/architecture.md` is generated from it.
39
-
- The apply/check engine layout: `src/main.ts` is the thin bundled entrypoint; `src/io.ts` is the Io output port (action-layer-free; the action implements it over @actions/core); `src/types.ts` holds the leaf type vocabulary; `src/action/` is the GitHub Actions layer (inputs, settings reading, step summaries, redaction, the single- and multi-repo run flows); `src/engine/` is the per-repo pipeline (orchestrate, validate, merge, diff); `src/github/` is the REST client, pagination, and repo-file fetch; `src/discovery/` resolves multi-repo targets; `src/report/` composes the private report. Under `src/sections/`, each settings section is one self-contained DIRECTORY `src/sections/<key>/`: `index.ts` (the `SectionModule` - key, PAT grant advice, the loose shape derived from the slice, handler), `schema.ts` (the zod slice), `mock.ts` (the e2e mock fragment), `generators.ts` (the fuzz fragment; the secret and variable factory families share theirs in `test/e2e/generators.ts` instead), `<key>.test.ts` (absent where a shared factory's suite under `test/sections/` covers the section), and `scenarios/`. Beside the section directories sit `contract/` (the layered section contract: permissions/endpoints/graphql/module/errors/requests/live), `shared/` (the cross-section engines and factories, plus the schema helpers), and `registry.ts` (the single registration point). All GitHub API list calls must go through `listAll()` (bare-array endpoints) or `listAllEnveloped()` (endpoints that wrap the list in a `{total_count, <key>: []}` envelope), both backed by the single page loop in `src/github/paginate.ts`; errors through `call()`/`throwFor()` so the permission policy (`on-missing-permission`, `required-sections`) works.
39
+
- The apply/check engine layout: `src/main.ts` is the thin bundled entrypoint; `src/index.ts` is the public entry (declaration-only re-exports, pinned by test/library/public-surface.test.ts); `src/action/` is the GitHub Actions layer and a CONSUMER of that entry: its only import outside itself is `../index.js`, the one edge `architecture.yml` grants it; `src/flows/` holds the run flows the action and a library caller share (single, multi, merge, delivery, redaction); `src/io.ts` is the import-free Io output port; `src/types.ts` holds the leaf type vocabulary; `src/engine/` is the per-repo pipeline (orchestrate, validate, merge, diff, secret references); `src/github/` is the REST client, pagination, and repo-file fetch; `src/discovery/` resolves multi-repo targets; `src/report/` composes and delivers the private report. Under `src/sections/`, each settings section is one self-contained DIRECTORY `src/sections/<key>/`: `index.ts` (the `SectionModule` - key, PAT grant advice, the loose shape derived from the slice, handler), `schema.ts` (the zod slice), `mock.ts` (the e2e mock fragment), `generators.ts` (the fuzz fragment; the secret and variable factory families share theirs in `test/e2e/generators.ts` instead), `<key>.test.ts` (absent where a shared factory's suite under `test/sections/` covers the section), and `scenarios/`. Beside the section directories sit `contract/` (the layered section contract: permissions/endpoints/graphql/module/errors/requests/live), `shared/` (the cross-section engines and factories, plus the schema helpers), and `registry.ts` (the single registration point). All GitHub API list calls must go through `listAll()` (bare-array endpoints) or `listAllEnveloped()` (endpoints that wrap the list in a `{total_count, <key>: []}` envelope), both backed by the single page loop in `src/github/paginate.ts`; errors through `call()`/`throwFor()` so the permission policy (`on-missing-permission`, `required-sections`) works.
40
40
- Adding a section: create `src/sections/<key>/` with the six pieces above plus its `<key>.docs.yml` (the Sections table cells for docs/reference/sections.md, the COVERAGE.md prose, and the schema descriptions the generators render; the docs loader fails without it), compose the `SettingsFile` property from the slice and add the key to `SECTION_KEYS` in `src/schema.ts`, and add one line in `src/sections/registry.ts` - the compiler flags each forgotten step (the registry's mapped type, the mock fragment registration, the slice composition pin, and the generator aggregation are all keyed by SectionKey).
41
41
- Adding a section endpoint: declare it in the section module's `ENDPOINTS`, add the matching handler under its `section.role` key in the section's `mock.ts` (the fragment's handler record is typed over the section's exact key union, so a missing or phantom handler fails to compile, and `assertHandlerCompleteness()` backstops at construction), add a scenario that reaches it (the coverage tripwire fails on a cold route), and regenerate the trimmed spec (`bun .github/scripts/trim-openapi.ts`; the artifact stays gitignored). `USED_PATHS` (in `test/e2e/openapi/paths.ts`) picks the new path up automatically. Core routes the action calls outside the sections (the repo fetch, the settings-file contents read, `repos: "*"` discovery, and the private-report issue channel) are served by a separate core-path handler, and a request that matches no registered section or core route fails loudly.
42
42
- Two declarations on each section are single sources the rest of the system reads: `permission` (a `SectionPermission`) drives the PAT grant prose (`grantFor`), the mock's permission gate, and the fuzz oracle; the `ENDPOINTS` dictionary drives the paths, the mock routes, and `USED_PATHS`. `undeclaredDefault` drives the Undeclared-default column of the generated Sections table on docs/reference/sections.md (`bun run build:docs`; `build:check` fails on drift). Change one and its consumers follow.
@@ -140,7 +140,7 @@ Two kinds of document reach the engine:
140
140
141
141
A `$NAME` secret reference resolves from the workflow step's environment, so it is honored only in operator documents. A target repository must never be able to route the operator's secrets into itself. Provenance is a property of the source document, decided once where the document is chosen, and every value in it shares that one source.
each["one target at a time<br>src/action/multi.ts runMulti()"]
194
+
each["one target at a time<br>src/flows/multi.ts runMulti()"]
195
195
own["applied from that file, as written"]
196
196
fetch["src/github/repo-file.ts<br>getRepoFile()"]
197
197
probe["the repository object names the default branch"]
@@ -219,7 +219,7 @@ Targets come from checked-in files under `repos-dir`, from the `repos` list, or
219
219
- A contents 404 alone proves nothing (a missing file, a missing grant, or an invisible repository all look the same). The proof is the default-branch ref read, a call that needs Contents: read and succeeds whether or not the file exists: 200 means the token could have read the file, so the file is absent.
220
220
- A ref read that is denied leaves the proof inconclusive, and the target fails naming the grant. A denial must never look like a missing file.
// DefaultArtifactClient emits its own core.warning before it throws on a
16
+
// missing runtime token, so invoking it without one would double-warn.
17
+
// Fail before constructing it; deliverArtifactReport turns this into the
18
+
// single warning callers see.
19
+
if(!process.env.ACTIONS_RUNTIME_TOKEN){
20
+
thrownewError(
21
+
"the artifact service is unavailable: no ACTIONS_RUNTIME_TOKEN in the environment. Artifact upload needs a GitHub-hosted or self-hosted Actions runner (it is not available on GitHub Enterprise Server or outside Actions)",
0 commit comments