@effected/github-actions 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ActionCache.js +128 -17
- package/ActionInput.js +61 -8
- package/ActionLogger.js +7 -3
- package/ActionOutputs.js +71 -2
- package/ActionState.js +33 -6
- package/ActionsIdentityToken.js +61 -0
- package/ActionsProvenance.js +103 -0
- package/Artifact.js +2 -2
- package/BlobStore.githubCache.js +2 -1
- package/CacheKey.js +106 -8
- package/CheckDocument.js +193 -0
- package/CheckState.js +75 -0
- package/GitHubMarkdown.js +197 -0
- package/ManagedDocument.js +281 -0
- package/PackageManagerInstaller.js +494 -0
- package/README.md +241 -0
- package/Secret.js +34 -0
- package/ToolInstaller.js +98 -52
- package/index.d.ts +1612 -230
- package/index.js +9 -2
- package/package.json +7 -3
package/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ import { NodeServices } from "@effect/platform-node";
|
|
|
2
2
|
import { Cause, Config, ConfigProvider, Context, Duration, Effect, FileSystem, Layer, Logger, Option, Path, Redacted, Result, Schema } from "effect";
|
|
3
3
|
import { HttpClient } from "effect/unstable/http";
|
|
4
4
|
import { ChildProcessSpawner } from "effect/unstable/process";
|
|
5
|
+
import { IdentityToken, SlsaProvenance } from "@effected/sbom";
|
|
5
6
|
import { BotIdentity, GitHubApp, GitHubClient, InstallationToken, PermissionLevel, RetryPolicy } from "@effected/github";
|
|
7
|
+
import { PackageManagerPin } from "@effected/npm";
|
|
6
8
|
//#region src/ActionEnvironment.d.ts
|
|
7
9
|
declare const ActionEnvironmentError_base: Schema.Class<ActionEnvironmentError, Schema.TaggedStruct<"ActionEnvironmentError", {
|
|
8
10
|
readonly reason: Schema.Literals<readonly ["missing", "malformed"]>;
|
|
@@ -237,6 +239,24 @@ declare class WorkflowCommand {
|
|
|
237
239
|
}
|
|
238
240
|
//#endregion
|
|
239
241
|
//#region src/ActionLogger.d.ts
|
|
242
|
+
/**
|
|
243
|
+
* Options for {@link ActionLoggerShape.withBuffer}.
|
|
244
|
+
*
|
|
245
|
+
* @public
|
|
246
|
+
*/
|
|
247
|
+
interface WithBufferOptions {
|
|
248
|
+
/**
|
|
249
|
+
* What happens to the captured transcript when the step **succeeds**.
|
|
250
|
+
*
|
|
251
|
+
* @remarks
|
|
252
|
+
* `"flush"` — the default — replays it, so a clean run still prints what it
|
|
253
|
+
* did. `"discard"` drops it, which is what keeps a green release log to one
|
|
254
|
+
* line per step: the transcript exists only as the failure report. A failure,
|
|
255
|
+
* a defect or an interruption flushes under either setting — the choice is
|
|
256
|
+
* only about what a success is worth in the log.
|
|
257
|
+
*/
|
|
258
|
+
readonly onSuccess?: "flush" | "discard";
|
|
259
|
+
}
|
|
240
260
|
/**
|
|
241
261
|
* The {@link ActionLogger} service shape.
|
|
242
262
|
*
|
|
@@ -252,13 +272,18 @@ interface ActionLoggerShape {
|
|
|
252
272
|
* Warnings and errors are **not** buffered — they go out as they happen, so a
|
|
253
273
|
* long step still reports trouble while it is running. Everything at `Info`
|
|
254
274
|
* and below is held and flushed on every exit path, including a defect or an
|
|
255
|
-
* interruption, so a clean run still prints its transcript.
|
|
275
|
+
* interruption, so a clean run still prints its transcript. A step that
|
|
276
|
+
* should be **quiet** when it succeeds passes
|
|
277
|
+
* `{ onSuccess: "discard" }` ({@link WithBufferOptions}); failure still
|
|
278
|
+
* spills the transcript either way.
|
|
256
279
|
*
|
|
257
280
|
* Buffering is skipped entirely when the runner has step debugging enabled or
|
|
258
281
|
* the ambient minimum log level is already `Debug` or lower — someone asking
|
|
259
|
-
* for verbose output wants it live
|
|
282
|
+
* for verbose output wants it live, and that overrides
|
|
283
|
+
* `onSuccess: "discard"` too: asking for debug output means wanting to see
|
|
284
|
+
* what a green step did.
|
|
260
285
|
*/
|
|
261
|
-
readonly withBuffer: <A, E, R>(label: string, effect: Effect.Effect<A, E, R
|
|
286
|
+
readonly withBuffer: <A, E, R>(label: string, effect: Effect.Effect<A, E, R>, options?: WithBufferOptions) => Effect.Effect<A, E, R>;
|
|
262
287
|
/**
|
|
263
288
|
* Emit a `::notice::` annotation.
|
|
264
289
|
*
|
|
@@ -349,9 +374,11 @@ declare const ActionOutputError_base: Schema.Class<ActionOutputError, Schema.Tag
|
|
|
349
374
|
* the code is running outside a GitHub runner. `writeFailed` — the file
|
|
350
375
|
* exists but could not be appended to. `invalidName` — the name would
|
|
351
376
|
* corrupt the file's block structure. `encodeFailed` — a value did not
|
|
352
|
-
* satisfy its schema.
|
|
377
|
+
* satisfy its schema. `detached` — the member was called under
|
|
378
|
+
* {@link ActionOutputs.layerDetached}, where the channel it writes cannot
|
|
379
|
+
* reach the job that would read it.
|
|
353
380
|
*/
|
|
354
|
-
readonly reason: Schema.Literals<readonly ["unavailable", "writeFailed", "invalidName", "encodeFailed"]>;
|
|
381
|
+
readonly reason: Schema.Literals<readonly ["unavailable", "writeFailed", "invalidName", "encodeFailed", "detached"]>;
|
|
355
382
|
/** Which runner file was involved, by environment variable name. */
|
|
356
383
|
readonly file: Schema.optionalKey<Schema.String>;
|
|
357
384
|
/** The output or variable name, when one is involved. */
|
|
@@ -381,7 +408,17 @@ interface ActionOutputsShape {
|
|
|
381
408
|
readonly summary: (content: string) => Effect.Effect<void, ActionOutputError>;
|
|
382
409
|
/** Export an environment variable to **subsequent** steps. */
|
|
383
410
|
readonly exportVariable: (name: string, value: string) => Effect.Effect<void, ActionOutputError>;
|
|
384
|
-
/**
|
|
411
|
+
/**
|
|
412
|
+
* Prepend a directory to `PATH` for **subsequent** steps.
|
|
413
|
+
*
|
|
414
|
+
* @remarks
|
|
415
|
+
* This appends to the `GITHUB_PATH` runner file and does **nothing else** —
|
|
416
|
+
* unlike `@actions/core`, it does NOT mutate the live `process.env.PATH` of
|
|
417
|
+
* the current process. A same-process probe of a tool that was just added
|
|
418
|
+
* (`pnpm --version`, a shim, a binary) will not find it by name; probe by
|
|
419
|
+
* absolute path instead. The runner applies the addition when the next step
|
|
420
|
+
* starts, exactly as it does for {@link ActionOutputsShape.exportVariable}.
|
|
421
|
+
*/
|
|
385
422
|
readonly addPath: (path: string) => Effect.Effect<void, ActionOutputError>;
|
|
386
423
|
/** Emit an error annotation. Does not itself set the exit code. */
|
|
387
424
|
readonly setFailed: (message: string) => Effect.Effect<void>;
|
|
@@ -406,6 +443,45 @@ declare const ActionOutputs_base: Context.ServiceClass<ActionOutputs, "@effected
|
|
|
406
443
|
*/
|
|
407
444
|
declare class ActionOutputs extends ActionOutputs_base {
|
|
408
445
|
static readonly layer: Layer.Layer<ActionOutputs, never, ActionEnvironment | FileSystem.FileSystem>;
|
|
446
|
+
/**
|
|
447
|
+
* The outputs surface that is correct inside a **detached worker**.
|
|
448
|
+
*
|
|
449
|
+
* @remarks
|
|
450
|
+
* The masking model assumes the runner parses stdout. A detached worker's
|
|
451
|
+
* stdout is a **log file no runner parses**, so under the real layer
|
|
452
|
+
* `setSecret` emits `::add-mask::<plaintext>` into that file — a command
|
|
453
|
+
* that is simultaneously **inert** (nothing reads it, so nothing is masked)
|
|
454
|
+
* and a **secret leak** (the plaintext is now sitting verbatim in a log). A
|
|
455
|
+
* production action shipped exactly that for one round: an S3 secret and
|
|
456
|
+
* session token written into a worker's log by the masking call itself.
|
|
457
|
+
* This layer is the worker-side fix; the parent-side rule is that every
|
|
458
|
+
* secret a worker will hold is masked **before** the spawn, by
|
|
459
|
+
* `Secret.forChildEnv` under the real layer.
|
|
460
|
+
*
|
|
461
|
+
* The layer needs no environment and no filesystem — its `R` is `never` —
|
|
462
|
+
* so a worker composing it structurally *cannot* write a runner file.
|
|
463
|
+
* Per-member behavior:
|
|
464
|
+
*
|
|
465
|
+
* - **`setSecret` — silent no-op.** The value must not be written
|
|
466
|
+
* *anywhere*: there is no runner on this channel to hand it to, and any
|
|
467
|
+
* spelling of it in the log IS the leak. Masking is the parent's job,
|
|
468
|
+
* before the worker exists.
|
|
469
|
+
* - **`set`, `setJson`, `exportVariable`, `addPath`, `summary` — fail
|
|
470
|
+
* typed** (`reason: "detached"`, naming the file). Each writes a runner
|
|
471
|
+
* file that configures the parent job's *later steps* — `GITHUB_OUTPUT`,
|
|
472
|
+
* `GITHUB_ENV`, `GITHUB_PATH` — or is collected when the step completes
|
|
473
|
+
* (`GITHUB_STEP_SUMMARY`). A detached worker has no later steps, may
|
|
474
|
+
* outlive the job entirely, and even an inherited file path would be
|
|
475
|
+
* read at a moment unrelated to the write — so a worker calling one of
|
|
476
|
+
* these is a program error worth surfacing, not a write to degrade
|
|
477
|
+
* silently.
|
|
478
|
+
* - **`setFailed` — degrades to a plain log line** (`Console.error`,
|
|
479
|
+
* without the `::error::` syntax nothing here would parse). Unlike the
|
|
480
|
+
* file members it is log-like: "record that something failed" still
|
|
481
|
+
* means something in a worker's log, and its signature carries no error
|
|
482
|
+
* channel to fail through.
|
|
483
|
+
*/
|
|
484
|
+
static readonly layerDetached: Layer.Layer<ActionOutputs>;
|
|
409
485
|
/** A test double. Unstubbed members die rather than silently succeeding. */
|
|
410
486
|
static readonly makeTest: (overrides?: Partial<ActionOutputsShape>) => ActionOutputsShape;
|
|
411
487
|
/** {@link ActionOutputs.makeTest} behind `Layer.succeed`. */
|
|
@@ -417,10 +493,13 @@ declare const ActionStateError_base: Schema.Class<ActionStateError, Schema.Tagge
|
|
|
417
493
|
/**
|
|
418
494
|
* `missing` — no value was saved under this key in an earlier phase.
|
|
419
495
|
* `malformed` — a value is there but is not JSON, or does not satisfy the
|
|
420
|
-
* schema it was read with. `
|
|
496
|
+
* schema it was read with. `notPlainJson` — caught at SAVE time: the
|
|
497
|
+
* schema's encoded form does not survive `JSON.stringify`/`JSON.parse`, so
|
|
498
|
+
* persisting it would present one phase later as a `malformed` mystery with
|
|
499
|
+
* no pointer to the cause. `writeFailed` — the state file could not be
|
|
421
500
|
* appended to.
|
|
422
501
|
*/
|
|
423
|
-
readonly reason: Schema.Literals<readonly ["missing", "malformed", "writeFailed"]>;
|
|
502
|
+
readonly reason: Schema.Literals<readonly ["missing", "malformed", "notPlainJson", "writeFailed"]>;
|
|
424
503
|
readonly key: Schema.String;
|
|
425
504
|
readonly cause: Schema.optionalKey<Schema.Defect>;
|
|
426
505
|
}>, import("effect/Cause").YieldableError>;
|
|
@@ -438,7 +517,24 @@ declare class ActionStateError extends ActionStateError_base {
|
|
|
438
517
|
* @public
|
|
439
518
|
*/
|
|
440
519
|
interface ActionStateShape {
|
|
441
|
-
/**
|
|
520
|
+
/**
|
|
521
|
+
* Persist a value for a later phase.
|
|
522
|
+
*
|
|
523
|
+
* @remarks
|
|
524
|
+
* **The schema's ENCODED form must be plain JSON** — `GITHUB_STATE` is a
|
|
525
|
+
* text file and the value crosses it as `JSON.stringify(encoded)`. A schema
|
|
526
|
+
* whose encoded side is a class instance (`Schema.Option`'s is an `Option`,
|
|
527
|
+
* serialized via its `toJSON` to `{"_id":"Option",…}`) writes something no
|
|
528
|
+
* later phase can decode; use the plain-JSON codec instead —
|
|
529
|
+
* `Schema.OptionFromNullOr` for an optional value.
|
|
530
|
+
*
|
|
531
|
+
* Every save proves the rule: the encoded value is round-tripped through
|
|
532
|
+
* `JSON.stringify`/`JSON.parse` and re-decoded, and a value that does not
|
|
533
|
+
* survive fails HERE, typed (`reason: "notPlainJson"`, naming the key) —
|
|
534
|
+
* rather than one phase later as a `malformed` mystery in `post` that
|
|
535
|
+
* `main` believed it saved. Action state is small by protocol, so the
|
|
536
|
+
* per-save round-trip costs effectively nothing.
|
|
537
|
+
*/
|
|
442
538
|
readonly save: <A, I>(key: string, value: A, schema: Schema.Codec<A, I>) => Effect.Effect<void, ActionStateError>;
|
|
443
539
|
/** Read a value saved by an earlier phase. */
|
|
444
540
|
readonly get: <A, I>(key: string, schema: Schema.Codec<A, I>) => Effect.Effect<A, ActionStateError>;
|
|
@@ -685,6 +781,14 @@ declare class CacheKeyError extends CacheKeyError_base {
|
|
|
685
781
|
declare const CacheKey_base: Schema.Class<CacheKey, Schema.Struct<{
|
|
686
782
|
/** The components, most general first. */
|
|
687
783
|
readonly segments: Schema.NonEmptyArray<Schema.String>;
|
|
784
|
+
/**
|
|
785
|
+
* The explicit ladder policy, when one was given: how many leading
|
|
786
|
+
* segments each rung keeps, in the order the rungs are tried. Absent for
|
|
787
|
+
* the default every-prefix ladder; **empty for no ladder at all** —
|
|
788
|
+
* exact-match-only. See {@link CacheKey.withRestoreDepths} and
|
|
789
|
+
* {@link CacheKey.withoutRestoreKeys}.
|
|
790
|
+
*/
|
|
791
|
+
readonly restoreDepths: Schema.optionalKey<Schema.$Array<Schema.Number>>;
|
|
688
792
|
}>, {}>;
|
|
689
793
|
/**
|
|
690
794
|
* A GitHub Actions cache key and the restore-key ladder that goes with it.
|
|
@@ -697,6 +801,12 @@ declare const CacheKey_base: Schema.Class<CacheKey, Schema.Struct<{
|
|
|
697
801
|
* the primary key and its fallbacks impossible to drift apart, which is the
|
|
698
802
|
* failure that produces a cache that never hits and never says why.
|
|
699
803
|
*
|
|
804
|
+
* By default every prefix is a rung. When some segments must never be dropped
|
|
805
|
+
* — a rung without a version digest resurrects stale cross-version caches —
|
|
806
|
+
* give the key an explicit ladder policy with
|
|
807
|
+
* {@link CacheKey.withRestoreDepths}, or none at all with
|
|
808
|
+
* {@link CacheKey.withoutRestoreKeys}, instead of bypassing the typed key.
|
|
809
|
+
*
|
|
700
810
|
* @example
|
|
701
811
|
* ```ts
|
|
702
812
|
* import { CacheKey } from "@effected/github-actions";
|
|
@@ -715,12 +825,69 @@ declare class CacheKey extends CacheKey_base {
|
|
|
715
825
|
* The restore keys, most specific first.
|
|
716
826
|
*
|
|
717
827
|
* @remarks
|
|
718
|
-
*
|
|
719
|
-
* three-segment key falls back to two prefixes and a
|
|
720
|
-
* back to nothing — there is no rung that would match
|
|
721
|
-
* repository.
|
|
828
|
+
* By default each rung drops the last segment and keeps the trailing
|
|
829
|
+
* separator, so a three-segment key falls back to two prefixes and a
|
|
830
|
+
* one-segment key falls back to nothing — there is no rung that would match
|
|
831
|
+
* every cache in the repository.
|
|
832
|
+
*
|
|
833
|
+
* **The default ladder is every prefix**, which is wrong for a key whose
|
|
834
|
+
* later segments must never be dropped alone: a
|
|
835
|
+
* `platform-arch-versionHash-branchHash-lockfileHash` key derives rungs
|
|
836
|
+
* without the version digest, and those resurrect stale cross-version
|
|
837
|
+
* caches. A key built with {@link CacheKey.withRestoreDepths} carries its
|
|
838
|
+
* own ladder instead, and this getter answers exactly that ladder — so
|
|
839
|
+
* `ActionCache.restore` picks the policy up through the same typed-key
|
|
840
|
+
* path, no hand-built restore-key list required. An **empty** policy
|
|
841
|
+
* ({@link CacheKey.withoutRestoreKeys}) answers no rungs at all: only
|
|
842
|
+
* *absence* selects the default ladder, so "none" is never mistaken for
|
|
843
|
+
* "every prefix".
|
|
722
844
|
*/
|
|
723
845
|
get restoreKeys(): ReadonlyArray<string>;
|
|
846
|
+
/**
|
|
847
|
+
* A copy of this key carrying an explicit restore-key ladder policy.
|
|
848
|
+
*
|
|
849
|
+
* @remarks
|
|
850
|
+
* Each depth is the number of **leading segments** the rung keeps, and the
|
|
851
|
+
* rungs are emitted in exactly the order given — GitHub tries restore keys
|
|
852
|
+
* in order, so the order IS the policy. Descending depths (most specific
|
|
853
|
+
* first) are what a fallback ladder almost always wants; that convention is
|
|
854
|
+
* recommended rather than enforced, because rung ordering is the consumer's
|
|
855
|
+
* policy where commas, newlines and the length limit are the runner's
|
|
856
|
+
* protocol. A depth outside `1..segments.length - 1` is refused at
|
|
857
|
+
* construction: `0` would match every cache in the repository, and
|
|
858
|
+
* `segments.length` would just repeat the primary key.
|
|
859
|
+
*
|
|
860
|
+
* @example
|
|
861
|
+
* ```ts
|
|
862
|
+
* import { CacheKey } from "@effected/github-actions";
|
|
863
|
+
*
|
|
864
|
+
* const key = CacheKey.of("Linux", "X64", "v1hash", "main", "lockhash").withRestoreDepths([4, 3]);
|
|
865
|
+
* key.restoreKeys; // ["Linux-X64-v1hash-main-", "Linux-X64-v1hash-"]
|
|
866
|
+
* ```
|
|
867
|
+
*/
|
|
868
|
+
withRestoreDepths(depths: readonly [number, ...Array<number>]): CacheKey;
|
|
869
|
+
/**
|
|
870
|
+
* A copy of this key that restores from its **exact key only** — no ladder.
|
|
871
|
+
*
|
|
872
|
+
* @remarks
|
|
873
|
+
* The third point in the policy space, and the one the typed key had no
|
|
874
|
+
* spelling for: absence of a policy means the default every-prefix ladder,
|
|
875
|
+
* so exact-match-only restores (a cache-bust mode, where a stale partial
|
|
876
|
+
* hit is worse than a cold start) previously forced the caller back to the
|
|
877
|
+
* string form. This is the same `restoreDepths` field carrying **zero
|
|
878
|
+
* rungs** — an honest value, not a sentinel: `ActionCache.restore` sends no
|
|
879
|
+
* `restore_keys` at all, so the backend answers this key or a miss.
|
|
880
|
+
*
|
|
881
|
+
* @example
|
|
882
|
+
* ```ts
|
|
883
|
+
* import { CacheKey } from "@effected/github-actions";
|
|
884
|
+
*
|
|
885
|
+
* const key = CacheKey.of("Linux", "pnpm-store", "abc123").withoutRestoreKeys();
|
|
886
|
+
* key.key; // "Linux-pnpm-store-abc123"
|
|
887
|
+
* key.restoreKeys; // []
|
|
888
|
+
* ```
|
|
889
|
+
*/
|
|
890
|
+
withoutRestoreKeys(): CacheKey;
|
|
724
891
|
/** Build a key from its segments. */
|
|
725
892
|
static of(...segments: readonly [string, ...ReadonlyArray<string>]): CacheKey;
|
|
726
893
|
/**
|
|
@@ -866,9 +1033,31 @@ declare class ActionCacheError extends ActionCacheError_base {
|
|
|
866
1033
|
*/
|
|
867
1034
|
interface ActionCacheShape {
|
|
868
1035
|
/**
|
|
869
|
-
* Archive `paths` and store them under
|
|
1036
|
+
* Archive the files and directories matching `paths` and store them under
|
|
1037
|
+
* `key`.
|
|
870
1038
|
*
|
|
871
1039
|
* @remarks
|
|
1040
|
+
* `paths` is a list of glob **patterns**, resolved with `actions/cache`
|
|
1041
|
+
* parity before `tar` ever sees them: a pattern may match directories (a
|
|
1042
|
+
* matched directory is archived recursively, never expanded into its
|
|
1043
|
+
* descendants), a pattern that matches nothing — including a literal path
|
|
1044
|
+
* that is not on disk — is silently dropped, and a list that resolves to
|
|
1045
|
+
* nothing at all fails typed rather than reserving an entry no archive will
|
|
1046
|
+
* ever back. A leading `~` expands to the caller's home directory, relative
|
|
1047
|
+
* patterns root at `GITHUB_WORKSPACE` (falling back to the working
|
|
1048
|
+
* directory), and a leading `!` marks an exclusion.
|
|
1049
|
+
*
|
|
1050
|
+
* The entry **version** is a digest of the literal pattern list, exactly as
|
|
1051
|
+
* `actions/cache` computes it — resolution feeds `tar` only, never the
|
|
1052
|
+
* version — so {@link ActionCacheShape.restore | restore}, which resolves
|
|
1053
|
+
* nothing, derives the same version from the same list for free.
|
|
1054
|
+
*
|
|
1055
|
+
* One deliberate divergence from the toolkit: where `actions/cache` re-roots
|
|
1056
|
+
* every match relative to the workspace, resolved paths here stay
|
|
1057
|
+
* **absolute** and are archived with `tar -P`, so a restore puts every file
|
|
1058
|
+
* back where it came from regardless of the restoring step's working
|
|
1059
|
+
* directory.
|
|
1060
|
+
*
|
|
872
1061
|
* A key that already exists is a **success**: cache entries are immutable, so
|
|
873
1062
|
* another job having written it first is the outcome the caller wanted.
|
|
874
1063
|
*/
|
|
@@ -881,6 +1070,11 @@ interface ActionCacheShape {
|
|
|
881
1070
|
* `Option.none()` is a miss, not a failure — a cold cache is the normal state
|
|
882
1071
|
* of a new branch. Passing a {@link CacheKey} supplies its restore-key ladder
|
|
883
1072
|
* automatically; passing a `string` and no ladder means "this key or nothing".
|
|
1073
|
+
*
|
|
1074
|
+
* `paths` only derives the entry version and MUST be the same literal list
|
|
1075
|
+
* the save used — no glob resolution happens here, because none is needed:
|
|
1076
|
+
* extraction consumes no path list (the archive already names its contents),
|
|
1077
|
+
* and `actions/cache` likewise hashes the un-resolved list on both sides.
|
|
884
1078
|
*/
|
|
885
1079
|
readonly restore: (paths: ReadonlyArray<string>, key: string | CacheKey, restoreKeys?: ReadonlyArray<string>) => Effect.Effect<Option.Option<string>, ActionCacheError>;
|
|
886
1080
|
}
|
|
@@ -987,6 +1181,21 @@ declare class ActionCache extends ActionCache_base {
|
|
|
987
1181
|
*/
|
|
988
1182
|
declare class ActionInput {
|
|
989
1183
|
private constructor();
|
|
1184
|
+
/**
|
|
1185
|
+
* The exact environment variable the runner publishes an input under.
|
|
1186
|
+
*
|
|
1187
|
+
* @remarks
|
|
1188
|
+
* `variable("biome-version")` is `"INPUT_BIOME-VERSION"` — GitHub
|
|
1189
|
+
* uppercases, replaces **spaces** with underscores, and leaves every other
|
|
1190
|
+
* character (**dashes included**) alone. This is the same derivation every
|
|
1191
|
+
* accessor on this class resolves through, exported so a test that must
|
|
1192
|
+
* spell a runner variable can derive it instead of hand-writing it: a
|
|
1193
|
+
* hand-written `INPUT_BIOME_VERSION` reads as absent on a real runner, the
|
|
1194
|
+
* default silently applies, and the suite goes green against the wrong
|
|
1195
|
+
* value. Prefer keying the test environment by input name (see
|
|
1196
|
+
* {@link ActionInput.provider}) so no variable is spelled at all.
|
|
1197
|
+
*/
|
|
1198
|
+
static readonly variable: (name: string) => string;
|
|
990
1199
|
/**
|
|
991
1200
|
* A required string input.
|
|
992
1201
|
*
|
|
@@ -1096,14 +1305,37 @@ declare class ActionInput {
|
|
|
1096
1305
|
*/
|
|
1097
1306
|
static schema<A, I>(name: string, schema: Schema.Codec<A, I>): Config.Config<A>;
|
|
1098
1307
|
/**
|
|
1099
|
-
* A `ConfigProvider` over a record of runner variables
|
|
1308
|
+
* A `ConfigProvider` over a record of runner variables **or input names**.
|
|
1100
1309
|
*
|
|
1101
1310
|
* @remarks
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1104
|
-
*
|
|
1105
|
-
*
|
|
1106
|
-
*
|
|
1311
|
+
* The record dual-accepts, and the split is the `INPUT_` prefix:
|
|
1312
|
+
*
|
|
1313
|
+
* - A key already spelled as a runner variable (`INPUT_BIOME-VERSION`,
|
|
1314
|
+
* `PLAIN_VAR`) is read verbatim, exactly as before.
|
|
1315
|
+
* - Any other key is treated as an **input name**, `with:`-block style —
|
|
1316
|
+
* `{ "biome-version": "2.3.14" }` — and serves the variable this module
|
|
1317
|
+
* derives for it (`ActionInput.variable`). This is the spelling to
|
|
1318
|
+
* prefer in tests: a hand-written `INPUT_BIOME_VERSION` (underscore where
|
|
1319
|
+
* the runner keeps the dash) reads as absent and the default silently
|
|
1320
|
+
* applies, and the input-name form makes that class of bug
|
|
1321
|
+
* unrepresentable — the mangling never leaves this module.
|
|
1322
|
+
*
|
|
1323
|
+
* When both spellings of the same input are present and non-empty, the
|
|
1324
|
+
* explicit `INPUT_`-spelled entry wins.
|
|
1325
|
+
*
|
|
1326
|
+
* A **flat single-segment** lookup additionally tries the `INPUT_`
|
|
1327
|
+
* derivation of the name first, mirroring the order the production runtime
|
|
1328
|
+
* installs ({@link ActionInput.providerOver} via `layerDefault`) — so a
|
|
1329
|
+
* bare `Config.string("biome-version")` resolves under this provider
|
|
1330
|
+
* exactly as it does inside `Action.run`. Nested and numeric paths pass
|
|
1331
|
+
* through as the joined spelling only, as ever.
|
|
1332
|
+
*
|
|
1333
|
+
* The source package's `ActionsConfigProvider` behaviors are otherwise
|
|
1334
|
+
* preserved exactly: the path is joined with `_`, spaces become underscores
|
|
1335
|
+
* and the whole is uppercased; and an **empty string reads as absent**,
|
|
1336
|
+
* because the runner sets unsupplied inputs to `""` and treating that as
|
|
1337
|
+
* present would make every optional input look supplied. That rule applies
|
|
1338
|
+
* to input-name entries too: `{ "flag": "" }` is an unsupplied input.
|
|
1107
1339
|
*
|
|
1108
1340
|
* Taking the environment as an argument rather than reading `process.env`
|
|
1109
1341
|
* is what makes inputs testable without mutating the test process.
|
|
@@ -1176,6 +1408,288 @@ declare class ActionInput {
|
|
|
1176
1408
|
static readonly layerDefault: Layer.Layer<never>;
|
|
1177
1409
|
}
|
|
1178
1410
|
//#endregion
|
|
1411
|
+
//#region src/OidcTokenIssuer.d.ts
|
|
1412
|
+
declare const OidcTokenError_base: Schema.Class<OidcTokenError, Schema.TaggedStruct<"OidcTokenError", {
|
|
1413
|
+
/**
|
|
1414
|
+
* `unavailable` — the runner did not publish the token-service variables,
|
|
1415
|
+
* which almost always means the workflow is missing `permissions: id-token:
|
|
1416
|
+
* write`. `requestFailed` — the token service could not be reached or refused
|
|
1417
|
+
* the request. `malformedResponse` — it answered with something that is not a
|
|
1418
|
+
* token envelope. `malformedToken` — the token is not a decodable JWT.
|
|
1419
|
+
* `missingClaims` — it decoded, but without the claims a provenance statement
|
|
1420
|
+
* needs.
|
|
1421
|
+
*/
|
|
1422
|
+
readonly reason: Schema.Literals<readonly ["unavailable", "requestFailed", "malformedResponse", "malformedToken", "missingClaims"]>;
|
|
1423
|
+
/** What was wrong, in one line. */
|
|
1424
|
+
readonly detail: Schema.optionalKey<Schema.String>;
|
|
1425
|
+
/** The HTTP status, when the token service answered. */
|
|
1426
|
+
readonly status: Schema.optionalKey<Schema.Number>;
|
|
1427
|
+
/** The underlying failure, preserved structurally. */
|
|
1428
|
+
readonly cause: Schema.optionalKey<Schema.Defect>;
|
|
1429
|
+
}>, import("effect/Cause").YieldableError>;
|
|
1430
|
+
/**
|
|
1431
|
+
* Raised when an OIDC token cannot be issued or read.
|
|
1432
|
+
*
|
|
1433
|
+
* @public
|
|
1434
|
+
*/
|
|
1435
|
+
declare class OidcTokenError extends OidcTokenError_base {
|
|
1436
|
+
get message(): string;
|
|
1437
|
+
}
|
|
1438
|
+
declare const OidcClaims_base: Schema.Class<OidcClaims, Schema.Struct<{
|
|
1439
|
+
/** The issuer, e.g. `https://token.actions.githubusercontent.com`. */
|
|
1440
|
+
readonly iss: Schema.String;
|
|
1441
|
+
/** The full ref the workflow ran on. */
|
|
1442
|
+
readonly ref: Schema.String;
|
|
1443
|
+
/** The commit. */
|
|
1444
|
+
readonly sha: Schema.String;
|
|
1445
|
+
/** `owner/repo`. */
|
|
1446
|
+
readonly repository: Schema.String;
|
|
1447
|
+
/** The event that triggered the run. */
|
|
1448
|
+
readonly event_name: Schema.String;
|
|
1449
|
+
/** The reusable-workflow ref, which is what a verifier pins against. */
|
|
1450
|
+
readonly job_workflow_ref: Schema.String;
|
|
1451
|
+
/** The calling workflow's ref. */
|
|
1452
|
+
readonly workflow_ref: Schema.String;
|
|
1453
|
+
/** The numeric repository id, as a string. */
|
|
1454
|
+
readonly repository_id: Schema.String;
|
|
1455
|
+
/** The numeric owner id, as a string. */
|
|
1456
|
+
readonly repository_owner_id: Schema.String;
|
|
1457
|
+
/** `github-hosted` or `self-hosted`. */
|
|
1458
|
+
readonly runner_environment: Schema.String;
|
|
1459
|
+
/** The run id, as a string. */
|
|
1460
|
+
readonly run_id: Schema.String;
|
|
1461
|
+
/** The run attempt, as a string. */
|
|
1462
|
+
readonly run_attempt: Schema.String;
|
|
1463
|
+
}>, {}>;
|
|
1464
|
+
/**
|
|
1465
|
+
* The claims a GitHub Actions OIDC token carries about the workflow that ran.
|
|
1466
|
+
*
|
|
1467
|
+
* @remarks
|
|
1468
|
+
* The field names are GitHub's own JWT claim names, kept verbatim rather than
|
|
1469
|
+
* translated to the kit's casing. They are an **external vocabulary** — the
|
|
1470
|
+
* same strings that appear in a SLSA provenance predicate and in every
|
|
1471
|
+
* published example of an Actions OIDC policy — and renaming them would put a
|
|
1472
|
+
* translation layer between a consumer and the specification they are reading.
|
|
1473
|
+
*
|
|
1474
|
+
* @public
|
|
1475
|
+
*/
|
|
1476
|
+
declare class OidcClaims extends OidcClaims_base {}
|
|
1477
|
+
/**
|
|
1478
|
+
* The {@link OidcTokenIssuer} service shape.
|
|
1479
|
+
*
|
|
1480
|
+
* @public
|
|
1481
|
+
*/
|
|
1482
|
+
interface OidcTokenIssuerShape {
|
|
1483
|
+
/**
|
|
1484
|
+
* Request an ID token, optionally bound to an audience.
|
|
1485
|
+
*
|
|
1486
|
+
* @remarks
|
|
1487
|
+
* Omitting the audience sends no `audience` parameter at all, matching
|
|
1488
|
+
* `@actions/core.getIDToken` — the runner's default audience is not the empty
|
|
1489
|
+
* string.
|
|
1490
|
+
*/
|
|
1491
|
+
readonly token: (audience?: string) => Effect.Effect<Redacted.Redacted<string>, OidcTokenError>;
|
|
1492
|
+
/**
|
|
1493
|
+
* The token's claims, decoded.
|
|
1494
|
+
*
|
|
1495
|
+
* @remarks
|
|
1496
|
+
* **The signature is deliberately not verified**, for three reasons that are
|
|
1497
|
+
* recorded so nobody "fixes" it:
|
|
1498
|
+
*
|
|
1499
|
+
* 1. The token comes from the runner's own token-service endpoint over TLS.
|
|
1500
|
+
* The transport is the trust boundary, and the process asking for the
|
|
1501
|
+
* token is the process that received it.
|
|
1502
|
+
* 2. The claims populate a provenance predicate, not a trust decision.
|
|
1503
|
+
* Nothing branches on them for authorization; they are recorded as
|
|
1504
|
+
* attested facts about the workflow that ran.
|
|
1505
|
+
* 3. Verifying would need a JWKS fetch, which turns a decode into a network
|
|
1506
|
+
* call — untestable without a fixture server, and dependent on GitHub's
|
|
1507
|
+
* key endpoint being reachable at attestation time.
|
|
1508
|
+
*
|
|
1509
|
+
* A consumer that needs a *verified* token needs a different operation with a
|
|
1510
|
+
* different name and a different error channel, not an option on this one.
|
|
1511
|
+
*
|
|
1512
|
+
* Claims are on the surface rather than left to the call site because that is
|
|
1513
|
+
* what makes the provenance path reachable in a test: a double built with
|
|
1514
|
+
* {@link OidcTokenIssuer.layerFor} answers with real, decodable claims, where
|
|
1515
|
+
* the synthetic non-JWT its predecessor returned made every consumer's
|
|
1516
|
+
* `decodeJwtClaims` yield nothing and silently skipped the path under test.
|
|
1517
|
+
*/
|
|
1518
|
+
readonly claims: (audience?: string) => Effect.Effect<OidcClaims, OidcTokenError>;
|
|
1519
|
+
}
|
|
1520
|
+
declare const OidcTokenIssuer_base: Context.ServiceClass<OidcTokenIssuer, "@effected/github-actions/OidcTokenIssuer", OidcTokenIssuerShape>;
|
|
1521
|
+
/**
|
|
1522
|
+
* The runner's OIDC token service.
|
|
1523
|
+
*
|
|
1524
|
+
* @remarks
|
|
1525
|
+
* Lives here rather than with attestation because it reads
|
|
1526
|
+
* `ACTIONS_ID_TOKEN_REQUEST_TOKEN` and `ACTIONS_ID_TOKEN_REQUEST_URL`, which
|
|
1527
|
+
* exist only when a workflow declares `permissions: id-token: write` — that is
|
|
1528
|
+
* a fact about the runner, not about signing.
|
|
1529
|
+
*
|
|
1530
|
+
* @example
|
|
1531
|
+
* ```ts
|
|
1532
|
+
* import { OidcTokenIssuer } from "@effected/github-actions";
|
|
1533
|
+
* import { Effect } from "effect";
|
|
1534
|
+
*
|
|
1535
|
+
* const program = Effect.gen(function* () {
|
|
1536
|
+
* const issuer = yield* OidcTokenIssuer;
|
|
1537
|
+
* const claims = yield* issuer.claims("sigstore");
|
|
1538
|
+
* return claims.job_workflow_ref;
|
|
1539
|
+
* });
|
|
1540
|
+
* ```
|
|
1541
|
+
*
|
|
1542
|
+
* @public
|
|
1543
|
+
*/
|
|
1544
|
+
declare class OidcTokenIssuer extends OidcTokenIssuer_base {
|
|
1545
|
+
static readonly layer: Layer.Layer<OidcTokenIssuer, never, ActionEnvironment | HttpClient.HttpClient>;
|
|
1546
|
+
/**
|
|
1547
|
+
* An **unsigned** JWT carrying these claims.
|
|
1548
|
+
*
|
|
1549
|
+
* @remarks
|
|
1550
|
+
* For building test doubles, and nothing else: the signature segment is a
|
|
1551
|
+
* placeholder, so this token would fail any verifier. It exists because the
|
|
1552
|
+
* alternative — a double returning a synthetic non-JWT — is what made the
|
|
1553
|
+
* provenance path structurally unreachable in the source package, drawing
|
|
1554
|
+
* four separate apologetic comments from one consumer.
|
|
1555
|
+
*/
|
|
1556
|
+
static readonly unsignedTokenFor: (claims: OidcClaims) => Redacted.Redacted<string>;
|
|
1557
|
+
/** A test double. Unstubbed members die rather than answering with a non-token. */
|
|
1558
|
+
static readonly makeTest: (overrides?: Partial<OidcTokenIssuerShape>) => OidcTokenIssuerShape;
|
|
1559
|
+
/** {@link OidcTokenIssuer.makeTest} behind `Layer.succeed`. */
|
|
1560
|
+
static readonly layerTest: (overrides?: Partial<OidcTokenIssuerShape>) => Layer.Layer<OidcTokenIssuer>;
|
|
1561
|
+
/**
|
|
1562
|
+
* A double that answers with these claims, consistently on both members.
|
|
1563
|
+
*
|
|
1564
|
+
* @remarks
|
|
1565
|
+
* `token()` returns a real decodable JWT built from the same claims
|
|
1566
|
+
* `claims()` returns, so a consumer that decodes the token itself and a
|
|
1567
|
+
* consumer that asks the service both see the same thing. A double whose two
|
|
1568
|
+
* members can disagree is how a test proves a path works while production
|
|
1569
|
+
* takes the other one.
|
|
1570
|
+
*/
|
|
1571
|
+
static readonly layerFor: (claims: OidcClaims) => Layer.Layer<OidcTokenIssuer>;
|
|
1572
|
+
}
|
|
1573
|
+
//#endregion
|
|
1574
|
+
//#region src/ActionsIdentityToken.d.ts
|
|
1575
|
+
/**
|
|
1576
|
+
* The Actions side of `@effected/sbom`'s inverted OIDC contract.
|
|
1577
|
+
*
|
|
1578
|
+
* @remarks
|
|
1579
|
+
* `@effected/sbom` declares `IdentityToken` — one method, one audience,
|
|
1580
|
+
* one redacted token — precisely so that signing does not drag the Actions
|
|
1581
|
+
* runtime into every consumer that only wants to emit an SBOM. This is the
|
|
1582
|
+
* layer that closes the inversion from the other side: it serves that contract
|
|
1583
|
+
* from the runner's own OIDC token service ({@link OidcTokenIssuer}), the same
|
|
1584
|
+
* arrangement as `@effected/workspaces` implementing `@effected/commands`'
|
|
1585
|
+
* `LocalExec`.
|
|
1586
|
+
*
|
|
1587
|
+
* The audience is forwarded verbatim. `SigstoreSigner` asks its identity
|
|
1588
|
+
* contract for the `sigstore` audience itself (`SIGSTORE_OIDC_AUDIENCE` is the
|
|
1589
|
+
* signing protocol's requirement, owned there), so nothing here — and nothing
|
|
1590
|
+
* in a consumer — needs to spell it.
|
|
1591
|
+
*
|
|
1592
|
+
* An `OidcTokenError` is rewrapped as `IdentityTokenError` with the
|
|
1593
|
+
* original preserved structurally in `cause`, so a refused exchange still says
|
|
1594
|
+
* which audience it was minting for **and** why the runner declined —
|
|
1595
|
+
* `reason: "unavailable"` almost always means the workflow is missing
|
|
1596
|
+
* `permissions: id-token: write`.
|
|
1597
|
+
*
|
|
1598
|
+
* @example
|
|
1599
|
+
* ```ts
|
|
1600
|
+
* import { ActionsIdentityToken, OidcTokenIssuer } from "@effected/github-actions";
|
|
1601
|
+
* import { SigstoreSigner } from "@effected/sbom";
|
|
1602
|
+
* import { Layer } from "effect";
|
|
1603
|
+
*
|
|
1604
|
+
* const signing = SigstoreSigner.layer.pipe(
|
|
1605
|
+
* Layer.provide(ActionsIdentityToken.layer),
|
|
1606
|
+
* Layer.provide(OidcTokenIssuer.layer),
|
|
1607
|
+
* );
|
|
1608
|
+
* ```
|
|
1609
|
+
*
|
|
1610
|
+
* @public
|
|
1611
|
+
*/
|
|
1612
|
+
declare class ActionsIdentityToken {
|
|
1613
|
+
private constructor();
|
|
1614
|
+
/**
|
|
1615
|
+
* `IdentityToken` served by the runner's OIDC token service.
|
|
1616
|
+
*
|
|
1617
|
+
* @remarks
|
|
1618
|
+
* Requires {@link OidcTokenIssuer} rather than composing
|
|
1619
|
+
* `OidcTokenIssuer.layer` in, so an action that already wired the issuer —
|
|
1620
|
+
* every action using `ActionRuntime.layer` has its requirements at hand —
|
|
1621
|
+
* does not construct a second one.
|
|
1622
|
+
*/
|
|
1623
|
+
static readonly layer: Layer.Layer<IdentityToken, never, OidcTokenIssuer>;
|
|
1624
|
+
}
|
|
1625
|
+
//#endregion
|
|
1626
|
+
//#region src/ActionsProvenance.d.ts
|
|
1627
|
+
/**
|
|
1628
|
+
* The Actions side of `@effected/sbom`'s SLSA provenance constructor.
|
|
1629
|
+
*
|
|
1630
|
+
* @remarks
|
|
1631
|
+
* `SlsaProvenance.forGitHubWorkflow` is total and does the real work, but it
|
|
1632
|
+
* takes a camelCase input record — and the only input anyone inside a workflow
|
|
1633
|
+
* actually holds is the runner's own OIDC claims, spelled in GitHub's
|
|
1634
|
+
* snake_case claim names. The rename between the two is eleven all-string
|
|
1635
|
+
* fields, so transposing `repository_id` and `repository_owner_id` compiles,
|
|
1636
|
+
* passes every type check, and produces a validly signed attestation asserting
|
|
1637
|
+
* the **wrong** provenance. This module owns that rename once, on the same
|
|
1638
|
+
* `github-actions` ↔ `sbom` seam as {@link ActionsIdentityToken}: `sbom` must
|
|
1639
|
+
* not depend on the Actions runtime, so the adapter lives here, beside the
|
|
1640
|
+
* services it reads.
|
|
1641
|
+
*
|
|
1642
|
+
* The construct ends at the predicate. Assembling the in-toto statement,
|
|
1643
|
+
* signing it and uploading the bundle stay with the consumer — that pipeline
|
|
1644
|
+
* is deliberately consumer glue (see `@effected/sbom`'s `SigstoreSigner` and
|
|
1645
|
+
* `@effected/github`'s `Attestation.upload`).
|
|
1646
|
+
*
|
|
1647
|
+
* @example
|
|
1648
|
+
* ```ts
|
|
1649
|
+
* import { ActionsProvenance } from "@effected/github-actions";
|
|
1650
|
+
* import { Effect } from "effect";
|
|
1651
|
+
*
|
|
1652
|
+
* const program = Effect.gen(function* () {
|
|
1653
|
+
* const provenance = yield* ActionsProvenance.capture();
|
|
1654
|
+
* return provenance.runDetails.builder.id;
|
|
1655
|
+
* });
|
|
1656
|
+
* ```
|
|
1657
|
+
*
|
|
1658
|
+
* @public
|
|
1659
|
+
*/
|
|
1660
|
+
declare class ActionsProvenance {
|
|
1661
|
+
private constructor();
|
|
1662
|
+
/**
|
|
1663
|
+
* The current run's SLSA provenance, built from the runner's OIDC claims.
|
|
1664
|
+
*
|
|
1665
|
+
* @remarks
|
|
1666
|
+
* Every field `GitHubWorkflowProvenance` declares is populated from
|
|
1667
|
+
* {@link OidcClaims}, except `serverUrl`, which is read from
|
|
1668
|
+
* `GITHUB_SERVER_URL` through {@link ActionEnvironment}'s `getOptional`
|
|
1669
|
+
* with `https://github.com` as the default — **not** through the
|
|
1670
|
+
* `GitHubContext` projection. The projection fails typed when a `GITHUB_*`
|
|
1671
|
+
* variable is missing, and a missing server URL is not a failure: it has a
|
|
1672
|
+
* correct default. GHES consumers set the variable, github.com consumers
|
|
1673
|
+
* never should have to, and neither should think about it. (Upstream
|
|
1674
|
+
* `@actions/attest` reads the same variable with no default and writes the
|
|
1675
|
+
* literal string `undefined` into every URL it builds — the hazard
|
|
1676
|
+
* `GitHubWorkflowProvenance.serverUrl` being a required field exists to
|
|
1677
|
+
* prevent.)
|
|
1678
|
+
*
|
|
1679
|
+
* The audience is forwarded to {@link OidcTokenIssuerShape.claims}
|
|
1680
|
+
* verbatim; the claims a provenance reads do not depend on it, so omitting
|
|
1681
|
+
* it is fine and spends no extra token mint.
|
|
1682
|
+
*
|
|
1683
|
+
* An {@link OidcTokenError} passes through **untouched** — not caught, not
|
|
1684
|
+
* defaulted, not wrapped. Whether attestation is mandatory or best-effort
|
|
1685
|
+
* is the consumer's policy: an action that must not publish unattested
|
|
1686
|
+
* lets the error propagate, and one that publishes anyway catches it at
|
|
1687
|
+
* its own boundary. `reason: "unavailable"` almost always means the
|
|
1688
|
+
* workflow is missing `permissions: id-token: write`.
|
|
1689
|
+
*/
|
|
1690
|
+
static readonly capture: (audience?: string) => Effect.Effect<SlsaProvenance, OidcTokenError, OidcTokenIssuer | ActionEnvironment>;
|
|
1691
|
+
}
|
|
1692
|
+
//#endregion
|
|
1179
1693
|
//#region src/Artifact.d.ts
|
|
1180
1694
|
declare const ArtifactError_base: Schema.Class<ArtifactError, Schema.TaggedStruct<"ArtifactError", {
|
|
1181
1695
|
/**
|
|
@@ -1625,9 +2139,292 @@ declare class GitHubCacheBlobStore {
|
|
|
1625
2139
|
static readonly layerWith: (transfer: DataBlobTransfer) => Layer.Layer<BlobStore, never, HttpClient.HttpClient | ActionEnvironment>;
|
|
1626
2140
|
}
|
|
1627
2141
|
//#endregion
|
|
1628
|
-
//#region src/
|
|
1629
|
-
declare const
|
|
1630
|
-
/**
|
|
2142
|
+
//#region src/CheckDocument.d.ts
|
|
2143
|
+
declare const CheckReport_base: Schema.Class<CheckReport, Schema.Struct<{
|
|
2144
|
+
/** Where the check is now. The last reported state is the authoritative one. */
|
|
2145
|
+
readonly state: Schema.Literals<readonly ["running", "pass", "fail", "warn", "user_interaction_required", "skipped", "timeout"]>;
|
|
2146
|
+
/** Display name, when it differs from the registry key. */
|
|
2147
|
+
readonly title: Schema.optionalKey<Schema.String>;
|
|
2148
|
+
/** A one-line verdict, e.g. `in progress...` or `3 packages ready`. */
|
|
2149
|
+
readonly outcome: Schema.optionalKey<Schema.String>;
|
|
2150
|
+
/** A pre-rendered markdown block of per-check detail. */
|
|
2151
|
+
readonly detail: Schema.optionalKey<Schema.String>;
|
|
2152
|
+
/** Where the check's name should link. */
|
|
2153
|
+
readonly url: Schema.optionalKey<Schema.String>;
|
|
2154
|
+
}>, {}>;
|
|
2155
|
+
/**
|
|
2156
|
+
* What a run knows about one of its checks: the authoritative state, and the
|
|
2157
|
+
* presentation facts a renderer projects into the document.
|
|
2158
|
+
*
|
|
2159
|
+
* @remarks
|
|
2160
|
+
* Only `state` is required. The rest exist because a status table needs them:
|
|
2161
|
+
* `title` when the display name differs from the registry key, `outcome` for
|
|
2162
|
+
* the one-line verdict cell, `url` for linking the check's name, and `detail`
|
|
2163
|
+
* for the per-check block a body region carries. A later report for the same
|
|
2164
|
+
* key **replaces** the whole entry — last write wins, per field and per check.
|
|
2165
|
+
*
|
|
2166
|
+
* @public
|
|
2167
|
+
*/
|
|
2168
|
+
declare class CheckReport extends CheckReport_base {}
|
|
2169
|
+
declare const CheckDocumentError_base: Schema.Class<CheckDocumentError, Schema.TaggedStruct<"CheckDocumentError", {
|
|
2170
|
+
/** Which half failed: regenerating the document, or writing it. */
|
|
2171
|
+
readonly kind: Schema.Literals<readonly ["render", "sink"]>;
|
|
2172
|
+
/** The underlying failure. */
|
|
2173
|
+
readonly cause: Schema.optionalKey<Schema.Defect>;
|
|
2174
|
+
}>, import("effect/Cause").YieldableError>;
|
|
2175
|
+
/**
|
|
2176
|
+
* Raised when the check document cannot be regenerated or written.
|
|
2177
|
+
*
|
|
2178
|
+
* @remarks
|
|
2179
|
+
* `render` — projecting the registry onto the document failed, almost always
|
|
2180
|
+
* because a reported `detail` contains a line the region scanner would read
|
|
2181
|
+
* as a marker; the underlying `ManagedDocumentError` rides in `cause`.
|
|
2182
|
+
* `sink` — the consumer's write effect failed; its error rides in `cause`.
|
|
2183
|
+
* Callers of `flush` branch on `kind`; the background reconciler never fails
|
|
2184
|
+
* a fiber with this — it logs and retries on the next report.
|
|
2185
|
+
*
|
|
2186
|
+
* @public
|
|
2187
|
+
*/
|
|
2188
|
+
declare class CheckDocumentError extends CheckDocumentError_base {
|
|
2189
|
+
get message(): string;
|
|
2190
|
+
}
|
|
2191
|
+
/**
|
|
2192
|
+
* How a rendered document leaves the process: the narrow sink contract.
|
|
2193
|
+
*
|
|
2194
|
+
* @remarks
|
|
2195
|
+
* One rendered document in, one write effect out — nothing else. That is
|
|
2196
|
+
* what lets the sticky PR comment (`PullRequestComment.upsert`), the PR
|
|
2197
|
+
* description (`PATCH` on the pull request) and a test's recording `Ref` all
|
|
2198
|
+
* fit the same reconciler. The success value is ignored; a failure surfaces
|
|
2199
|
+
* as {@link CheckDocumentError} with `kind: "sink"`. Provide any services the
|
|
2200
|
+
* write needs when constructing the sink — the reconciler runs it as given.
|
|
2201
|
+
*
|
|
2202
|
+
* @public
|
|
2203
|
+
*/
|
|
2204
|
+
type CheckDocumentSink = (rendered: string) => Effect.Effect<unknown, unknown>;
|
|
2205
|
+
/**
|
|
2206
|
+
* Options for {@link CheckDocument.layer}.
|
|
2207
|
+
*
|
|
2208
|
+
* @public
|
|
2209
|
+
*/
|
|
2210
|
+
interface CheckDocumentOptions {
|
|
2211
|
+
/** The managed document's namespace, e.g. your action's name. */
|
|
2212
|
+
readonly namespace: string;
|
|
2213
|
+
/** Which document, within that namespace. */
|
|
2214
|
+
readonly key: string;
|
|
2215
|
+
/**
|
|
2216
|
+
* The document text to reconcile against initially — a PR body fetched
|
|
2217
|
+
* once at startup, so human content outside the managed regions survives.
|
|
2218
|
+
* Defaults to empty: a fresh document.
|
|
2219
|
+
*/
|
|
2220
|
+
readonly initial?: string | undefined;
|
|
2221
|
+
/**
|
|
2222
|
+
* The pure projection from registry state to region entries.
|
|
2223
|
+
*
|
|
2224
|
+
* @remarks
|
|
2225
|
+
* Called with the full snapshot on every reconcile pass; returns the
|
|
2226
|
+
* regions to replace, keyed however the consumer's document is laid out —
|
|
2227
|
+
* typically one region per check plus a shared status table. It must be a
|
|
2228
|
+
* pure function of the snapshot: rendering the same state twice must
|
|
2229
|
+
* produce the same entries, because byte-identical output is what
|
|
2230
|
+
* suppresses the write.
|
|
2231
|
+
*/
|
|
2232
|
+
readonly render: (checks: ReadonlyMap<string, CheckReport>) => ReadonlyArray<readonly [key: string, content: string]>;
|
|
2233
|
+
/** Where the rendered document goes. */
|
|
2234
|
+
readonly sink: CheckDocumentSink;
|
|
2235
|
+
/**
|
|
2236
|
+
* Debounce parameters: `quiet` is the trailing settle window, `maxWait`
|
|
2237
|
+
* the staleness bound under a steady stream of reports. Defaults: 500
|
|
2238
|
+
* milliseconds and 3 seconds.
|
|
2239
|
+
*/
|
|
2240
|
+
readonly debounce?: {
|
|
2241
|
+
readonly quiet?: Duration.Input | undefined;
|
|
2242
|
+
readonly maxWait?: Duration.Input | undefined;
|
|
2243
|
+
} | undefined;
|
|
2244
|
+
/**
|
|
2245
|
+
* How long one sink write may run before the pass fails with
|
|
2246
|
+
* `kind: "sink"`. Default: 30 seconds.
|
|
2247
|
+
*
|
|
2248
|
+
* @remarks
|
|
2249
|
+
* The reconciler serializes passes behind one permit, and the finalizer's
|
|
2250
|
+
* last flush waits on that same permit — so a sink that never resolves
|
|
2251
|
+
* would otherwise stall every later pass AND scope teardown. The bound
|
|
2252
|
+
* turns a hung write into the ordinary sink failure path: a background
|
|
2253
|
+
* pass logs and retries on the next report, `flush` surfaces the typed
|
|
2254
|
+
* error.
|
|
2255
|
+
*/
|
|
2256
|
+
readonly sinkTimeout?: Duration.Input | undefined;
|
|
2257
|
+
}
|
|
2258
|
+
/**
|
|
2259
|
+
* The check-run → document reconciler's surface.
|
|
2260
|
+
*
|
|
2261
|
+
* @public
|
|
2262
|
+
*/
|
|
2263
|
+
interface CheckDocumentShape {
|
|
2264
|
+
/**
|
|
2265
|
+
* Record a check's current state and schedule a reconcile.
|
|
2266
|
+
*
|
|
2267
|
+
* @remarks
|
|
2268
|
+
* Never fails and never blocks on the write — the state lands in the
|
|
2269
|
+
* registry, and the debounced background pass projects it. Reporting the
|
|
2270
|
+
* same key again replaces the entry; resolution is **not** terminal, so
|
|
2271
|
+
* `pass` followed by `fail` leaves `fail` even inside one debounce window.
|
|
2272
|
+
*/
|
|
2273
|
+
readonly report: (check: string, report: CheckReport) => Effect.Effect<void>;
|
|
2274
|
+
/** The registry as of now, in first-report order. */
|
|
2275
|
+
readonly checks: Effect.Effect<ReadonlyMap<string, CheckReport>>;
|
|
2276
|
+
/**
|
|
2277
|
+
* Reconcile immediately, skipping the debounce.
|
|
2278
|
+
*
|
|
2279
|
+
* @remarks
|
|
2280
|
+
* Rendering unchanged state issues no write. Call it at the end of a run;
|
|
2281
|
+
* the layer's finalizer also runs it, so the last burst of reports lands
|
|
2282
|
+
* even when the scope closes inside the quiet window.
|
|
2283
|
+
*/
|
|
2284
|
+
readonly flush: Effect.Effect<void, CheckDocumentError>;
|
|
2285
|
+
}
|
|
2286
|
+
declare const CheckDocument_base: Context.ServiceClass<CheckDocument, "@effected/github-actions/CheckDocument", CheckDocumentShape>;
|
|
2287
|
+
/**
|
|
2288
|
+
* A living document of check state: an in-process registry the run reports
|
|
2289
|
+
* into, debounced onto a marker-delimited document, written through a narrow
|
|
2290
|
+
* sink.
|
|
2291
|
+
*
|
|
2292
|
+
* @remarks
|
|
2293
|
+
* **Push, not pull.** The run owns its checks and knows when they resolve;
|
|
2294
|
+
* nothing here polls GitHub. Consumers call `report` as states change, and a
|
|
2295
|
+
* background fiber projects the registry onto a {@link ManagedDocument} —
|
|
2296
|
+
* regions replaced from current state, so a check that resolves many times
|
|
2297
|
+
* updates its region in place instead of appending.
|
|
2298
|
+
*
|
|
2299
|
+
* The debounce is **trailing with a max-wait, never leading**: a burst of
|
|
2300
|
+
* reports coalesces into one write carrying the *final* state, and a steady
|
|
2301
|
+
* stream still surfaces progress every `maxWait`. A pass that renders
|
|
2302
|
+
* byte-identical text issues no write at all.
|
|
2303
|
+
*
|
|
2304
|
+
* `layer` mints fresh state per call — one layer per document. Bind it to a
|
|
2305
|
+
* `const` if two parts of a program must share one registry.
|
|
2306
|
+
*
|
|
2307
|
+
* @example
|
|
2308
|
+
* ```ts
|
|
2309
|
+
* import { CheckDocument, CheckReport, GitHubMarkdown } from "@effected/github-actions";
|
|
2310
|
+
* import { Effect } from "effect";
|
|
2311
|
+
*
|
|
2312
|
+
* const layer = CheckDocument.layer({
|
|
2313
|
+
* namespace: "my-action",
|
|
2314
|
+
* key: "release-validation",
|
|
2315
|
+
* render: (checks) => [
|
|
2316
|
+
* [
|
|
2317
|
+
* "header",
|
|
2318
|
+
* GitHubMarkdown.table(
|
|
2319
|
+
* ["Check", "Outcome"],
|
|
2320
|
+
* [...checks].map(([key, check]) => [key, check.outcome ?? check.state]),
|
|
2321
|
+
* ),
|
|
2322
|
+
* ],
|
|
2323
|
+
* ],
|
|
2324
|
+
* sink: (rendered) => Effect.log(rendered),
|
|
2325
|
+
* });
|
|
2326
|
+
*
|
|
2327
|
+
* const program = Effect.gen(function* () {
|
|
2328
|
+
* const doc = yield* CheckDocument;
|
|
2329
|
+
* yield* doc.report("build", CheckReport.make({ state: "running" }));
|
|
2330
|
+
* yield* doc.report("build", CheckReport.make({ state: "pass", outcome: "clean" }));
|
|
2331
|
+
* yield* doc.flush;
|
|
2332
|
+
* });
|
|
2333
|
+
* ```
|
|
2334
|
+
*
|
|
2335
|
+
* @public
|
|
2336
|
+
*/
|
|
2337
|
+
declare class CheckDocument extends CheckDocument_base {
|
|
2338
|
+
/**
|
|
2339
|
+
* The reconciler over one document.
|
|
2340
|
+
*
|
|
2341
|
+
* @remarks
|
|
2342
|
+
* Forks the debounce fiber into the layer's scope and registers a
|
|
2343
|
+
* finalizer that flushes once more on the way out, so the scope closing
|
|
2344
|
+
* mid-window cannot strand the final state. A background pass that fails
|
|
2345
|
+
* logs a structured warning and leaves the registry intact — the next
|
|
2346
|
+
* report retries it; only `flush` surfaces the typed error.
|
|
2347
|
+
*/
|
|
2348
|
+
static layer(options: CheckDocumentOptions): Layer.Layer<CheckDocument>;
|
|
2349
|
+
/** An inert double; unstubbed members die naming themselves. */
|
|
2350
|
+
static readonly makeTest: (overrides?: Partial<CheckDocumentShape>) => CheckDocumentShape;
|
|
2351
|
+
/** {@link CheckDocument.makeTest} behind a `Layer`. */
|
|
2352
|
+
static readonly layerTest: (overrides?: Partial<CheckDocumentShape>) => Layer.Layer<CheckDocument>;
|
|
2353
|
+
}
|
|
2354
|
+
//#endregion
|
|
2355
|
+
//#region src/CheckState.d.ts
|
|
2356
|
+
/**
|
|
2357
|
+
* The kit's check-state vocabulary.
|
|
2358
|
+
*
|
|
2359
|
+
* @remarks
|
|
2360
|
+
* Deliberately **wider than GitHub's own check-run conclusions**: `running` is
|
|
2361
|
+
* a first-class state rather than the absence of a conclusion, and
|
|
2362
|
+
* `user_interaction_required` names a release pipeline waiting on a human. A
|
|
2363
|
+
* check's lifecycle is a *sequence* of these states, and only the last one is
|
|
2364
|
+
* authoritative — resolution is not a terminal transition, so a check may run
|
|
2365
|
+
* `running`, `pass`, `running`, `fail` in order, and that means `fail`.
|
|
2366
|
+
*
|
|
2367
|
+
* Every state projects onto GitHub's check-run wire vocabulary through
|
|
2368
|
+
* {@link projectCheckState}. GitHub's `cancelled` conclusion has no
|
|
2369
|
+
* counterpart here on purpose: cancellation is something the runner does *to*
|
|
2370
|
+
* a run, not a state a pipeline reports about its own checks.
|
|
2371
|
+
*
|
|
2372
|
+
* @public
|
|
2373
|
+
*/
|
|
2374
|
+
declare const CheckState: Schema.Literals<readonly ["running", "pass", "fail", "warn", "user_interaction_required", "skipped", "timeout"]>;
|
|
2375
|
+
/**
|
|
2376
|
+
* The type of `CheckState`.
|
|
2377
|
+
*
|
|
2378
|
+
* @public
|
|
2379
|
+
*/
|
|
2380
|
+
type CheckState = typeof CheckState.Type;
|
|
2381
|
+
/**
|
|
2382
|
+
* The check-run conclusions the kit vocabulary can produce.
|
|
2383
|
+
*
|
|
2384
|
+
* @remarks
|
|
2385
|
+
* A subset of GitHub's conclusion set, spelled structurally so a pure
|
|
2386
|
+
* vocabulary module does not put `@effected/github` — and through it the
|
|
2387
|
+
* octokit runtime — on its import graph. A structural assertion in the test
|
|
2388
|
+
* suite pins these literals against `CheckConclusion` in `@effected/github`,
|
|
2389
|
+
* so drift between the two spellings fails a test rather than a consumer.
|
|
2390
|
+
*
|
|
2391
|
+
* @public
|
|
2392
|
+
*/
|
|
2393
|
+
type CheckRunConclusion = "success" | "failure" | "neutral" | "skipped" | "timed_out" | "action_required";
|
|
2394
|
+
/**
|
|
2395
|
+
* A `CheckState` on GitHub's check-run wire: a status, and a conclusion
|
|
2396
|
+
* exactly when the status is `completed`.
|
|
2397
|
+
*
|
|
2398
|
+
* @remarks
|
|
2399
|
+
* A discriminated union rather than two optional fields, because the wire
|
|
2400
|
+
* protocol's own invariant is conditional: an in-progress check run *has* no
|
|
2401
|
+
* conclusion, and a completed one always does.
|
|
2402
|
+
*
|
|
2403
|
+
* @public
|
|
2404
|
+
*/
|
|
2405
|
+
type CheckRunProjection = {
|
|
2406
|
+
readonly status: "in_progress";
|
|
2407
|
+
} | {
|
|
2408
|
+
readonly status: "completed";
|
|
2409
|
+
readonly conclusion: CheckRunConclusion;
|
|
2410
|
+
};
|
|
2411
|
+
/**
|
|
2412
|
+
* Project a kit check state onto GitHub's check-run wire vocabulary.
|
|
2413
|
+
*
|
|
2414
|
+
* @remarks
|
|
2415
|
+
* The canonical mapping: `running` is `in_progress`; everything else is
|
|
2416
|
+
* `completed` with `pass → success`, `fail → failure`, `warn → neutral`
|
|
2417
|
+
* (GitHub has no warning conclusion, and `neutral` is its non-failing,
|
|
2418
|
+
* non-success verdict), `user_interaction_required → action_required`,
|
|
2419
|
+
* `skipped → skipped` and `timeout → timed_out`.
|
|
2420
|
+
*
|
|
2421
|
+
* @public
|
|
2422
|
+
*/
|
|
2423
|
+
declare const projectCheckState: (state: CheckState) => CheckRunProjection;
|
|
2424
|
+
//#endregion
|
|
2425
|
+
//#region src/DetachedProcess.d.ts
|
|
2426
|
+
declare const DetachedProcessError_base: Schema.Class<DetachedProcessError, Schema.TaggedStruct<"DetachedProcessError", {
|
|
2427
|
+
/**
|
|
1631
2428
|
* `logUnavailable` — the log file could not be opened, so there is nowhere
|
|
1632
2429
|
* for the child's output to go. `spawnFailed` — the child did not start.
|
|
1633
2430
|
* `invalidPid` — a non-positive pid was handed to {@link DetachedProcess.reap};
|
|
@@ -1896,6 +2693,230 @@ declare class DryRun extends DryRun_base {
|
|
|
1896
2693
|
static readonly layerTest: (overrides?: Partial<DryRunShape>) => Layer.Layer<DryRun>;
|
|
1897
2694
|
}
|
|
1898
2695
|
//#endregion
|
|
2696
|
+
//#region src/GitHubMarkdown.d.ts
|
|
2697
|
+
/**
|
|
2698
|
+
* A heading level GitHub renders, `1` through `6`.
|
|
2699
|
+
*
|
|
2700
|
+
* @public
|
|
2701
|
+
*/
|
|
2702
|
+
type GitHubHeadingDepth = 1 | 2 | 3 | 4 | 5 | 6;
|
|
2703
|
+
/**
|
|
2704
|
+
* Options for {@link GitHubMarkdown.list}.
|
|
2705
|
+
*
|
|
2706
|
+
* @public
|
|
2707
|
+
*/
|
|
2708
|
+
interface GitHubListOptions {
|
|
2709
|
+
/** Render `1.`-numbered items instead of bullets. */
|
|
2710
|
+
readonly ordered?: boolean | undefined;
|
|
2711
|
+
}
|
|
2712
|
+
/**
|
|
2713
|
+
* The row-schema constraint {@link GitHubMarkdown.tableFor} accepts: any
|
|
2714
|
+
* schema exposing a struct field map — `Schema.Struct` and `Schema.Class`
|
|
2715
|
+
* both qualify.
|
|
2716
|
+
*
|
|
2717
|
+
* @public
|
|
2718
|
+
*/
|
|
2719
|
+
type GitHubRowSchema = Schema.Constraint & {
|
|
2720
|
+
readonly fields: Schema.Struct.Fields;
|
|
2721
|
+
};
|
|
2722
|
+
/**
|
|
2723
|
+
* The field keys of a row schema whose column REQUIRES an explicit
|
|
2724
|
+
* `format`: fields whose encoded side is not a string (their codec has no
|
|
2725
|
+
* string projection to borrow), and fields whose encoding needs services
|
|
2726
|
+
* (a sync cell render cannot provide them).
|
|
2727
|
+
*
|
|
2728
|
+
* @public
|
|
2729
|
+
*/
|
|
2730
|
+
type GitHubSchemaTableFormatRequiredKeys<Fields extends Schema.Struct.Fields> = { [K in keyof Fields]: [Fields[K]["Encoded"]] extends [string | undefined] ? [Fields[K]["EncodingServices"]] extends [never] ? never : K : K; }[keyof Fields];
|
|
2731
|
+
/**
|
|
2732
|
+
* Per-column configuration for {@link GitHubMarkdown.tableFor}.
|
|
2733
|
+
*
|
|
2734
|
+
* @public
|
|
2735
|
+
*/
|
|
2736
|
+
interface GitHubSchemaTableColumn<Value> {
|
|
2737
|
+
/**
|
|
2738
|
+
* The column header, overriding the field schema's `title` annotation
|
|
2739
|
+
* and the field-name fallback. Pre-rendered markdown, like every cell.
|
|
2740
|
+
*/
|
|
2741
|
+
readonly header?: string | undefined;
|
|
2742
|
+
/**
|
|
2743
|
+
* Render a cell from the decoded field value instead of encoding it
|
|
2744
|
+
* through the field's codec. Required — by type — for a field whose
|
|
2745
|
+
* encoded side is not a string.
|
|
2746
|
+
*/
|
|
2747
|
+
readonly format?: ((value: Value) => string) | undefined;
|
|
2748
|
+
}
|
|
2749
|
+
/**
|
|
2750
|
+
* {@link GitHubSchemaTableColumn} with the `format` obligation discharged —
|
|
2751
|
+
* the entry shape a non-string-encoded field demands.
|
|
2752
|
+
*
|
|
2753
|
+
* @public
|
|
2754
|
+
*/
|
|
2755
|
+
interface GitHubSchemaTableFormattedColumn<Value> extends GitHubSchemaTableColumn<Value> {
|
|
2756
|
+
readonly format: (value: Value) => string;
|
|
2757
|
+
}
|
|
2758
|
+
/**
|
|
2759
|
+
* The per-field column map for {@link GitHubMarkdown.tableFor}: entries are
|
|
2760
|
+
* optional except where the field's encoded side is not a string, where the
|
|
2761
|
+
* entry — and its `format` — are required.
|
|
2762
|
+
*
|
|
2763
|
+
* @public
|
|
2764
|
+
*/
|
|
2765
|
+
type GitHubSchemaTableColumns<Fields extends Schema.Struct.Fields> = { readonly [K in GitHubSchemaTableFormatRequiredKeys<Fields>]: GitHubSchemaTableFormattedColumn<Exclude<Fields[K]["Type"], undefined>>; } & { readonly [K in Exclude<keyof Fields, GitHubSchemaTableFormatRequiredKeys<Fields>>]?: GitHubSchemaTableColumn<Exclude<Fields[K]["Type"], undefined>>; };
|
|
2766
|
+
/**
|
|
2767
|
+
* Options for {@link GitHubMarkdown.tableFor}. The whole bag is optional
|
|
2768
|
+
* exactly when every field's encoded side is a string; one field without a
|
|
2769
|
+
* string projection makes `columns` — and the entry carrying its `format` —
|
|
2770
|
+
* required.
|
|
2771
|
+
*
|
|
2772
|
+
* @public
|
|
2773
|
+
*/
|
|
2774
|
+
type GitHubSchemaTableOptions<Fields extends Schema.Struct.Fields> = [GitHubSchemaTableFormatRequiredKeys<Fields>] extends [never] ? {
|
|
2775
|
+
readonly columns?: GitHubSchemaTableColumns<Fields> | undefined;
|
|
2776
|
+
} : {
|
|
2777
|
+
readonly columns: GitHubSchemaTableColumns<Fields>;
|
|
2778
|
+
};
|
|
2779
|
+
/**
|
|
2780
|
+
* A renderer minted by {@link GitHubMarkdown.tableFor}: columns are fixed by
|
|
2781
|
+
* the schema, rows are supplied at each render.
|
|
2782
|
+
*
|
|
2783
|
+
* @public
|
|
2784
|
+
*/
|
|
2785
|
+
interface GitHubSchemaTable<Row> {
|
|
2786
|
+
/**
|
|
2787
|
+
* Render the current rows as a GFM table. Pure and repeatable — identical
|
|
2788
|
+
* rows produce identical output, so re-rendering from changed state is
|
|
2789
|
+
* just calling it again.
|
|
2790
|
+
*/
|
|
2791
|
+
render(rows: ReadonlyArray<Row>): string;
|
|
2792
|
+
}
|
|
2793
|
+
/**
|
|
2794
|
+
* The fluent markdown writer for GitHub surfaces — PR comments, check-run
|
|
2795
|
+
* summaries, job summaries.
|
|
2796
|
+
*
|
|
2797
|
+
* @remarks
|
|
2798
|
+
* Every member takes **pre-rendered markdown** (links, emphasis, emoji — the
|
|
2799
|
+
* caller's fragments pass through verbatim) and returns a `string`, so
|
|
2800
|
+
* compositions read as plain string assembly. What the writer owns is the
|
|
2801
|
+
* *structure*: wherever a fragment could break the construct around it, the
|
|
2802
|
+
* output goes through `@effected/markdown`'s node classes and serializer
|
|
2803
|
+
* rather than string joining. A table cell carrying `>=1 || <2` renders with
|
|
2804
|
+
* its pipes escaped instead of shifting every column after it; a code block
|
|
2805
|
+
* whose content contains a fence gets a longer fence; inline code containing
|
|
2806
|
+
* a backtick gets a wider delimiter; a URL containing spaces or parentheses
|
|
2807
|
+
* is angle-bracketed.
|
|
2808
|
+
*
|
|
2809
|
+
* This module is the package's **only** importer of `@effected/markdown` — a
|
|
2810
|
+
* structural test pins that, on the same terms as the Azure confinement — so
|
|
2811
|
+
* an action that never renders markdown never links the engine.
|
|
2812
|
+
*
|
|
2813
|
+
* @example
|
|
2814
|
+
* ```ts
|
|
2815
|
+
* import { GitHubMarkdown } from "@effected/github-actions";
|
|
2816
|
+
*
|
|
2817
|
+
* const body = [
|
|
2818
|
+
* GitHubMarkdown.heading("Release Validation"),
|
|
2819
|
+
* GitHubMarkdown.table(["Check", "Outcome"], [["build", "✅ passed"]]),
|
|
2820
|
+
* GitHubMarkdown.details("Logs", GitHubMarkdown.codeBlock(log, "text")),
|
|
2821
|
+
* ].join("\n\n");
|
|
2822
|
+
* ```
|
|
2823
|
+
*
|
|
2824
|
+
* @public
|
|
2825
|
+
*/
|
|
2826
|
+
declare class GitHubMarkdown {
|
|
2827
|
+
private constructor();
|
|
2828
|
+
/**
|
|
2829
|
+
* A GFM table.
|
|
2830
|
+
*
|
|
2831
|
+
* @remarks
|
|
2832
|
+
* Cells are pre-rendered markdown; the serializer escapes the pipes GFM
|
|
2833
|
+
* would otherwise read as column breaks. Rows shorter than the header are
|
|
2834
|
+
* padded with empty cells rather than shifting.
|
|
2835
|
+
*/
|
|
2836
|
+
static table(headers: ReadonlyArray<string>, rows: ReadonlyArray<ReadonlyArray<string>>): string;
|
|
2837
|
+
/**
|
|
2838
|
+
* A GFM table whose columns are defined ONCE by a row schema.
|
|
2839
|
+
*
|
|
2840
|
+
* @remarks
|
|
2841
|
+
* The schema is the single authority for the table's shape: column order
|
|
2842
|
+
* is field declaration order, each header is the field's `title`
|
|
2843
|
+
* annotation (fall back: the property name, overridable per column), and
|
|
2844
|
+
* each cell is the field value's **encoded** string form — a branded or
|
|
2845
|
+
* typed field projects through its own codec, so the vocabulary lives
|
|
2846
|
+
* with the schema instead of being respelled at every call site, and a
|
|
2847
|
+
* row can no longer transpose columns because it is a typed object, not
|
|
2848
|
+
* a positional array.
|
|
2849
|
+
*
|
|
2850
|
+
* A field whose encoded side is not a string has no string projection to
|
|
2851
|
+
* borrow; the type of `options` makes its column's `format` — and
|
|
2852
|
+
* therefore `columns` and `options` themselves — required. An absent or
|
|
2853
|
+
* `undefined` optional field renders as an empty cell without consulting
|
|
2854
|
+
* codec or formatter.
|
|
2855
|
+
*
|
|
2856
|
+
* The renderer is a pure value: `render` maps current rows through
|
|
2857
|
+
* {@link GitHubMarkdown.table}, so escaping is inherited and identical
|
|
2858
|
+
* rows produce identical output. Rows are already-decoded values of the
|
|
2859
|
+
* schema's type, so encoding them is total in practice; a value smuggled
|
|
2860
|
+
* past the types throws the codec's `SchemaError` as a defect — same
|
|
2861
|
+
* posture as the serializer arm above, **not** a silent fallback.
|
|
2862
|
+
*
|
|
2863
|
+
* Only string-keyed fields become columns; symbol keys are not
|
|
2864
|
+
* enumerable table columns.
|
|
2865
|
+
*
|
|
2866
|
+
* @example
|
|
2867
|
+
* ```ts
|
|
2868
|
+
* import { GitHubMarkdown } from "@effected/github-actions";
|
|
2869
|
+
* import { Schema } from "effect";
|
|
2870
|
+
*
|
|
2871
|
+
* const CheckRow = Schema.Struct({
|
|
2872
|
+
* name: Schema.String.annotate({ title: "Check" }),
|
|
2873
|
+
* outcome: Schema.Literals(["passed", "failed"]),
|
|
2874
|
+
* });
|
|
2875
|
+
* const checks = GitHubMarkdown.tableFor(CheckRow);
|
|
2876
|
+
* const body = checks.render([{ name: "build", outcome: "passed" }]);
|
|
2877
|
+
* ```
|
|
2878
|
+
*/
|
|
2879
|
+
static tableFor<S extends GitHubRowSchema>(schema: S, ...options: [GitHubSchemaTableFormatRequiredKeys<S["fields"]>] extends [never] ? [options?: GitHubSchemaTableOptions<S["fields"]>] : [options: GitHubSchemaTableOptions<S["fields"]>]): GitHubSchemaTable<S["Type"]>;
|
|
2880
|
+
/** An ATX heading; the level defaults to `2`. */
|
|
2881
|
+
static heading(text: string, level?: GitHubHeadingDepth): string;
|
|
2882
|
+
/** A link. URLs the destination cannot carry raw are angle-bracketed. */
|
|
2883
|
+
static link(text: string, url: string): string;
|
|
2884
|
+
/** Inline code. Content containing backticks widens the delimiter. */
|
|
2885
|
+
static code(text: string): string;
|
|
2886
|
+
/** A fenced code block. Content containing a fence lengthens the fence. */
|
|
2887
|
+
static codeBlock(content: string, language?: string): string;
|
|
2888
|
+
/**
|
|
2889
|
+
* A list of pre-rendered markdown items.
|
|
2890
|
+
*
|
|
2891
|
+
* @remarks
|
|
2892
|
+
* A multi-line item is indented onto continuation lines, so it stays part
|
|
2893
|
+
* of its bullet instead of terminating the list.
|
|
2894
|
+
*/
|
|
2895
|
+
static list(items: ReadonlyArray<string>, options?: GitHubListOptions): string;
|
|
2896
|
+
/**
|
|
2897
|
+
* A collapsible `<details>` block.
|
|
2898
|
+
*
|
|
2899
|
+
* @remarks
|
|
2900
|
+
* mdast has no details node, so this is the one construct the writer emits
|
|
2901
|
+
* as raw HTML. The blank lines around `body` are what make GitHub render
|
|
2902
|
+
* the markdown inside it; `summary` is HTML context — markdown is **not**
|
|
2903
|
+
* rendered there, so bold a summary with `<strong>`, not `**`.
|
|
2904
|
+
*/
|
|
2905
|
+
static details(summary: string, body: string): string;
|
|
2906
|
+
/**
|
|
2907
|
+
* Pre-rendered markdown, passed through by name.
|
|
2908
|
+
*
|
|
2909
|
+
* @remarks
|
|
2910
|
+
* The identity function, deliberately: every writer member already treats
|
|
2911
|
+
* its fragments as markdown, so `raw` adds no behavior — it adds the
|
|
2912
|
+
* *statement* that a value is already rendered. Use it where a fragment
|
|
2913
|
+
* from elsewhere enters a composition and a reader would otherwise wonder
|
|
2914
|
+
* whether it still needs escaping. It replaces the `Html.make` idiom,
|
|
2915
|
+
* which passes markdown through correctly but announces the wrong intent.
|
|
2916
|
+
*/
|
|
2917
|
+
static raw(markdown: string): string;
|
|
2918
|
+
}
|
|
2919
|
+
//#endregion
|
|
1899
2920
|
//#region src/GitHubToken.d.ts
|
|
1900
2921
|
declare const GitHubTokenError_base: Schema.Class<GitHubTokenError, Schema.TaggedStruct<"GitHubTokenError", {
|
|
1901
2922
|
/**
|
|
@@ -2087,254 +3108,183 @@ declare class GitHubToken {
|
|
|
2087
3108
|
static readonly dispose: (options?: ReadOptions | undefined) => Effect.Effect<void, ActionStateError | import("@effected/github").GitHubAppError, ActionState | GitHubApp>;
|
|
2088
3109
|
}
|
|
2089
3110
|
//#endregion
|
|
2090
|
-
//#region src/
|
|
2091
|
-
declare const
|
|
2092
|
-
/**
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
* `missingClaims` — it decoded, but without the claims a provenance statement
|
|
2099
|
-
* needs.
|
|
2100
|
-
*/
|
|
2101
|
-
readonly reason: Schema.Literals<readonly ["unavailable", "requestFailed", "malformedResponse", "malformedToken", "missingClaims"]>;
|
|
2102
|
-
/** What was wrong, in one line. */
|
|
2103
|
-
readonly detail: Schema.optionalKey<Schema.String>;
|
|
2104
|
-
/** The HTTP status, when the token service answered. */
|
|
2105
|
-
readonly status: Schema.optionalKey<Schema.Number>;
|
|
2106
|
-
/** The underlying failure, preserved structurally. */
|
|
2107
|
-
readonly cause: Schema.optionalKey<Schema.Defect>;
|
|
3111
|
+
//#region src/ManagedDocument.d.ts
|
|
3112
|
+
declare const ManagedDocumentError_base: Schema.Class<ManagedDocumentError, Schema.TaggedStruct<"ManagedDocumentError", {
|
|
3113
|
+
/** Which ambiguity or refusal was found. */
|
|
3114
|
+
readonly kind: Schema.Literals<readonly ["unterminatedRegion", "orphanedEnd", "overlappingRegions", "duplicateRegion", "markerInContent", "duplicateDeclaration"]>;
|
|
3115
|
+
/** 1-based line of the offending marker, for the structural kinds. */
|
|
3116
|
+
readonly line: Schema.optionalKey<Schema.Number>;
|
|
3117
|
+
/** The region key involved, when the failure names one. */
|
|
3118
|
+
readonly key: Schema.optionalKey<Schema.String>;
|
|
2108
3119
|
}>, import("effect/Cause").YieldableError>;
|
|
2109
3120
|
/**
|
|
2110
|
-
* Raised when
|
|
3121
|
+
* Raised when a managed document cannot be read or regenerated without
|
|
3122
|
+
* guessing.
|
|
3123
|
+
*
|
|
3124
|
+
* @remarks
|
|
3125
|
+
* The four structural kinds (`unterminatedRegion`, `orphanedEnd`,
|
|
3126
|
+
* `overlappingRegions`, `duplicateRegion`) surface at parse time and name an
|
|
3127
|
+
* ambiguity whose silent resolution would corrupt content a human wrote by
|
|
3128
|
+
* hand. The two declaration kinds surface when regions are applied:
|
|
3129
|
+
* `markerInContent` refuses region content that itself contains a line the
|
|
3130
|
+
* scanner would read as a region marker, and `duplicateDeclaration` refuses
|
|
3131
|
+
* one call that states two intentions for the same region.
|
|
2111
3132
|
*
|
|
2112
3133
|
* @public
|
|
2113
3134
|
*/
|
|
2114
|
-
declare class
|
|
3135
|
+
declare class ManagedDocumentError extends ManagedDocumentError_base {
|
|
2115
3136
|
get message(): string;
|
|
2116
3137
|
}
|
|
2117
|
-
declare const OidcClaims_base: Schema.Class<OidcClaims, Schema.Struct<{
|
|
2118
|
-
/** The issuer, e.g. `https://token.actions.githubusercontent.com`. */
|
|
2119
|
-
readonly iss: Schema.String;
|
|
2120
|
-
/** The full ref the workflow ran on. */
|
|
2121
|
-
readonly ref: Schema.String;
|
|
2122
|
-
/** The commit. */
|
|
2123
|
-
readonly sha: Schema.String;
|
|
2124
|
-
/** `owner/repo`. */
|
|
2125
|
-
readonly repository: Schema.String;
|
|
2126
|
-
/** The event that triggered the run. */
|
|
2127
|
-
readonly event_name: Schema.String;
|
|
2128
|
-
/** The reusable-workflow ref, which is what a verifier pins against. */
|
|
2129
|
-
readonly job_workflow_ref: Schema.String;
|
|
2130
|
-
/** The calling workflow's ref. */
|
|
2131
|
-
readonly workflow_ref: Schema.String;
|
|
2132
|
-
/** The numeric repository id, as a string. */
|
|
2133
|
-
readonly repository_id: Schema.String;
|
|
2134
|
-
/** The numeric owner id, as a string. */
|
|
2135
|
-
readonly repository_owner_id: Schema.String;
|
|
2136
|
-
/** `github-hosted` or `self-hosted`. */
|
|
2137
|
-
readonly runner_environment: Schema.String;
|
|
2138
|
-
/** The run id, as a string. */
|
|
2139
|
-
readonly run_id: Schema.String;
|
|
2140
|
-
/** The run attempt, as a string. */
|
|
2141
|
-
readonly run_attempt: Schema.String;
|
|
2142
|
-
}>, {}>;
|
|
2143
3138
|
/**
|
|
2144
|
-
*
|
|
2145
|
-
*
|
|
2146
|
-
* @remarks
|
|
2147
|
-
* The field names are GitHub's own JWT claim names, kept verbatim rather than
|
|
2148
|
-
* translated to the kit's casing. They are an **external vocabulary** — the
|
|
2149
|
-
* same strings that appear in a SLSA provenance predicate and in every
|
|
2150
|
-
* published example of an Actions OIDC policy — and renaming them would put a
|
|
2151
|
-
* translation layer between a consumer and the specification they are reading.
|
|
3139
|
+
* Options identifying a managed document inside a text that may not carry one
|
|
3140
|
+
* yet.
|
|
2152
3141
|
*
|
|
2153
3142
|
* @public
|
|
2154
3143
|
*/
|
|
2155
|
-
|
|
2156
|
-
/**
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
*/
|
|
2161
|
-
interface OidcTokenIssuerShape {
|
|
3144
|
+
interface ManagedDocumentSource {
|
|
3145
|
+
/** Whose document this is, e.g. your action's name. */
|
|
3146
|
+
readonly namespace: string;
|
|
3147
|
+
/** Which document, within that namespace. */
|
|
3148
|
+
readonly key: string;
|
|
2162
3149
|
/**
|
|
2163
|
-
*
|
|
2164
|
-
*
|
|
2165
|
-
*
|
|
2166
|
-
* Omitting the audience sends no `audience` parameter at all, matching
|
|
2167
|
-
* `@actions/core.getIDToken` — the runner's default audience is not the empty
|
|
2168
|
-
* string.
|
|
3150
|
+
* The current text — a fetched comment body, a PR description — or absent
|
|
3151
|
+
* when there is nothing yet. An absent text and an empty one mean the same
|
|
3152
|
+
* thing: a fresh document.
|
|
2169
3153
|
*/
|
|
2170
|
-
readonly
|
|
2171
|
-
/**
|
|
2172
|
-
* The token's claims, decoded.
|
|
2173
|
-
*
|
|
2174
|
-
* @remarks
|
|
2175
|
-
* **The signature is deliberately not verified**, for three reasons that are
|
|
2176
|
-
* recorded so nobody "fixes" it:
|
|
2177
|
-
*
|
|
2178
|
-
* 1. The token comes from the runner's own token-service endpoint over TLS.
|
|
2179
|
-
* The transport is the trust boundary, and the process asking for the
|
|
2180
|
-
* token is the process that received it.
|
|
2181
|
-
* 2. The claims populate a provenance predicate, not a trust decision.
|
|
2182
|
-
* Nothing branches on them for authorization; they are recorded as
|
|
2183
|
-
* attested facts about the workflow that ran.
|
|
2184
|
-
* 3. Verifying would need a JWKS fetch, which turns a decode into a network
|
|
2185
|
-
* call — untestable without a fixture server, and dependent on GitHub's
|
|
2186
|
-
* key endpoint being reachable at attestation time.
|
|
2187
|
-
*
|
|
2188
|
-
* A consumer that needs a *verified* token needs a different operation with a
|
|
2189
|
-
* different name and a different error channel, not an option on this one.
|
|
2190
|
-
*
|
|
2191
|
-
* Claims are on the surface rather than left to the call site because that is
|
|
2192
|
-
* what makes the provenance path reachable in a test: a double built with
|
|
2193
|
-
* {@link OidcTokenIssuer.layerFor} answers with real, decodable claims, where
|
|
2194
|
-
* the synthetic non-JWT its predecessor returned made every consumer's
|
|
2195
|
-
* `decodeJwtClaims` yield nothing and silently skipped the path under test.
|
|
2196
|
-
*/
|
|
2197
|
-
readonly claims: (audience?: string) => Effect.Effect<OidcClaims, OidcTokenError>;
|
|
3154
|
+
readonly text?: string | undefined;
|
|
2198
3155
|
}
|
|
2199
|
-
declare const
|
|
3156
|
+
declare const ManagedDocument_base: Schema.Class<ManagedDocument, Schema.Struct<{
|
|
3157
|
+
/** Whose document this is. */
|
|
3158
|
+
readonly namespace: Schema.String;
|
|
3159
|
+
/** Which document, within that namespace. */
|
|
3160
|
+
readonly key: Schema.String;
|
|
3161
|
+
/** The document's full text, sentinel and regions included. */
|
|
3162
|
+
readonly text: Schema.String;
|
|
3163
|
+
}>, {}>;
|
|
2200
3164
|
/**
|
|
2201
|
-
*
|
|
3165
|
+
* A marker-delimited document: named regions a tool owns, inside text a human
|
|
3166
|
+
* may also edit.
|
|
2202
3167
|
*
|
|
2203
3168
|
* @remarks
|
|
2204
|
-
*
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
*
|
|
3169
|
+
* The one primitive behind both the sticky PR comment and the managed PR
|
|
3170
|
+
* description. A sentinel HTML comment (`<!-- namespace:key -->`) identifies
|
|
3171
|
+
* the document; each region is delimited by HTML-comment markers keyed
|
|
3172
|
+
* `namespace.key.region`; and everything outside the managed regions is
|
|
3173
|
+
* preserved byte-for-byte on regeneration. Regions are **replaced from
|
|
3174
|
+
* current state, never appended** — applying the same region twice yields one
|
|
3175
|
+
* region carrying the latest content, which is what makes a check that
|
|
3176
|
+
* resolves many times safe to re-render.
|
|
3177
|
+
*
|
|
3178
|
+
* The region engine is `@effected/templates`' pure `SectionDocument` —
|
|
3179
|
+
* reused, not reinvented — fixed to the HTML comment style and a
|
|
3180
|
+
* region-specific phrase. Everything here is string → string; the GitHub
|
|
3181
|
+
* write path composes this with `PullRequestComment` (whose `CommentMarker`
|
|
3182
|
+
* renders the *same* sentinel for the same namespace and key) or a PR-body
|
|
3183
|
+
* update in `@effected/github`.
|
|
3184
|
+
*
|
|
3185
|
+
* Construct with {@link ManagedDocument.parseResult} — create-or-update in
|
|
3186
|
+
* one call, so the consumer never branches on whether the document exists
|
|
3187
|
+
* yet. `ManagedDocument.make` is the raw schema constructor and performs no
|
|
3188
|
+
* structural validation.
|
|
2208
3189
|
*
|
|
2209
3190
|
* @example
|
|
2210
3191
|
* ```ts
|
|
2211
|
-
* import {
|
|
2212
|
-
* import {
|
|
2213
|
-
*
|
|
2214
|
-
* const
|
|
2215
|
-
*
|
|
2216
|
-
* const
|
|
2217
|
-
*
|
|
2218
|
-
*
|
|
3192
|
+
* import { ManagedDocument } from "@effected/github-actions";
|
|
3193
|
+
* import { Result } from "effect";
|
|
3194
|
+
*
|
|
3195
|
+
* const doc = ManagedDocument.parseResult({ namespace: "my-action", key: "release", text: fetched });
|
|
3196
|
+
* if (Result.isSuccess(doc)) {
|
|
3197
|
+
* const next = doc.success.withRegionsResult([
|
|
3198
|
+
* ["header", "## Validating Release"],
|
|
3199
|
+
* ["footer", "generated by my-action"],
|
|
3200
|
+
* ]);
|
|
3201
|
+
* }
|
|
2219
3202
|
* ```
|
|
2220
3203
|
*
|
|
2221
3204
|
* @public
|
|
2222
3205
|
*/
|
|
2223
|
-
declare class
|
|
2224
|
-
static readonly layer: Layer.Layer<OidcTokenIssuer, never, ActionEnvironment | HttpClient.HttpClient>;
|
|
3206
|
+
declare class ManagedDocument extends ManagedDocument_base {
|
|
2225
3207
|
/**
|
|
2226
|
-
*
|
|
3208
|
+
* Read a document out of existing text, or begin a fresh one.
|
|
2227
3209
|
*
|
|
2228
3210
|
* @remarks
|
|
2229
|
-
*
|
|
2230
|
-
*
|
|
2231
|
-
*
|
|
2232
|
-
*
|
|
2233
|
-
*
|
|
3211
|
+
* The synchronous primitive; {@link ManagedDocument.parse} derives from it.
|
|
3212
|
+
* Absent text, empty text and text that carries no sentinel are all legal —
|
|
3213
|
+
* the sentinel and regions are added by the first
|
|
3214
|
+
* {@link ManagedDocument.withRegionsResult}. Only a *structurally corrupt*
|
|
3215
|
+
* region layout fails, because every structural ambiguity is a case where
|
|
3216
|
+
* a silent choice would destroy content a human wrote.
|
|
2234
3217
|
*/
|
|
2235
|
-
static
|
|
2236
|
-
/** A test double. Unstubbed members die rather than answering with a non-token. */
|
|
2237
|
-
static readonly makeTest: (overrides?: Partial<OidcTokenIssuerShape>) => OidcTokenIssuerShape;
|
|
2238
|
-
/** {@link OidcTokenIssuer.makeTest} behind `Layer.succeed`. */
|
|
2239
|
-
static readonly layerTest: (overrides?: Partial<OidcTokenIssuerShape>) => Layer.Layer<OidcTokenIssuer>;
|
|
3218
|
+
static parseResult(source: ManagedDocumentSource): Result.Result<ManagedDocument, ManagedDocumentError>;
|
|
2240
3219
|
/**
|
|
2241
|
-
*
|
|
3220
|
+
* Read a document out of existing text, in `Effect`.
|
|
2242
3221
|
*
|
|
2243
3222
|
* @remarks
|
|
2244
|
-
*
|
|
2245
|
-
*
|
|
2246
|
-
* consumer that asks the service both see the same thing. A double whose two
|
|
2247
|
-
* members can disagree is how a test proves a path works while production
|
|
2248
|
-
* takes the other one.
|
|
3223
|
+
* Defined in terms of {@link ManagedDocument.parseResult} — synchronous
|
|
3224
|
+
* callers can use that variant directly.
|
|
2249
3225
|
*/
|
|
2250
|
-
static readonly
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
* value of the type, so this module does not try to make it serializable —
|
|
2263
|
-
* it makes declassification **explicit, auditable, and impossible to do
|
|
2264
|
-
* quietly**.
|
|
2265
|
-
*
|
|
2266
|
-
* The invariant: **masking and declassification are the same call.** Every
|
|
2267
|
-
* member registers the value with the runner's log filter (`::add-mask::`)
|
|
2268
|
-
* *before* returning plaintext, so a secret cannot reach a child's environment
|
|
2269
|
-
* or a state file without the runner already knowing to redact it from logs.
|
|
2270
|
-
*
|
|
2271
|
-
* `Redacted.value` appears nowhere else in this package, and a test asserts
|
|
2272
|
-
* that structurally — a reintroduction elsewhere fails the suite rather than
|
|
2273
|
-
* review.
|
|
2274
|
-
*
|
|
2275
|
-
* What this deliberately does **not** do is encrypt the handoff. The child
|
|
2276
|
-
* runs on the same runner, as the same user, as the parent; a key would have
|
|
2277
|
-
* to travel the same channel as the secret it protects.
|
|
2278
|
-
*
|
|
2279
|
-
* @example
|
|
2280
|
-
* ```ts
|
|
2281
|
-
* import { Secret } from "@effected/github-actions";
|
|
2282
|
-
* import { Effect } from "effect";
|
|
2283
|
-
*
|
|
2284
|
-
* const program = Effect.gen(function* () {
|
|
2285
|
-
* const env = yield* Secret.forChildEnv({ MY_TOKEN: theToken });
|
|
2286
|
-
* // every value in `env` is already masked in the runner log
|
|
2287
|
-
* return env;
|
|
2288
|
-
* });
|
|
2289
|
-
* ```
|
|
2290
|
-
*
|
|
2291
|
-
* @public
|
|
2292
|
-
*/
|
|
2293
|
-
declare class Secret {
|
|
2294
|
-
private constructor();
|
|
3226
|
+
static readonly parse: (source: ManagedDocumentSource) => Effect.Effect<ManagedDocument, ManagedDocumentError, never>;
|
|
3227
|
+
/** The HTML comment identifying this document, `<!-- namespace:key -->`. */
|
|
3228
|
+
get sentinel(): string;
|
|
3229
|
+
/** Does this text carry the document's sentinel? */
|
|
3230
|
+
matches(text: string): boolean;
|
|
3231
|
+
/** The content of one region, if the document carries it. */
|
|
3232
|
+
region(key: string): Option.Option<string>;
|
|
3233
|
+
/** Every region this document carries, in document order. */
|
|
3234
|
+
get regions(): ReadonlyArray<{
|
|
3235
|
+
readonly key: string;
|
|
3236
|
+
readonly content: string;
|
|
3237
|
+
}>;
|
|
2295
3238
|
/**
|
|
2296
|
-
*
|
|
3239
|
+
* The document with these regions replaced from current state.
|
|
2297
3240
|
*
|
|
2298
3241
|
* @remarks
|
|
2299
|
-
*
|
|
2300
|
-
*
|
|
3242
|
+
* The synchronous primitive; {@link ManagedDocument.withRegions} derives
|
|
3243
|
+
* from it. Each entry pairs a region key with its full new content. A
|
|
3244
|
+
* region already present is **replaced in place**; a region not present
|
|
3245
|
+
* yet is added; regions not named are left exactly as they are; and every
|
|
3246
|
+
* byte outside a managed region survives untouched. Applying entries that
|
|
3247
|
+
* change nothing returns byte-identical text, which is what lets a caller
|
|
3248
|
+
* compare-and-skip instead of issuing a write.
|
|
3249
|
+
*
|
|
3250
|
+
* Region keys share the name grammar `namespace` and `key` obey; a key
|
|
3251
|
+
* outside it is a wiring error and throws at construction rather than
|
|
3252
|
+
* surfacing in the typed channel.
|
|
2301
3253
|
*/
|
|
2302
|
-
|
|
3254
|
+
withRegionsResult(entries: ReadonlyArray<readonly [key: string, content: string]>): Result.Result<ManagedDocument, ManagedDocumentError>;
|
|
2303
3255
|
/**
|
|
2304
|
-
*
|
|
3256
|
+
* The document with these regions replaced, in `Effect`.
|
|
2305
3257
|
*
|
|
2306
3258
|
* @remarks
|
|
2307
|
-
*
|
|
2308
|
-
*
|
|
3259
|
+
* Defined in terms of {@link ManagedDocument.withRegionsResult} —
|
|
3260
|
+
* synchronous callers can use that variant directly.
|
|
2309
3261
|
*/
|
|
2310
|
-
|
|
3262
|
+
withRegions(entries: ReadonlyArray<readonly [key: string, content: string]>): Effect.Effect<ManagedDocument, ManagedDocumentError>;
|
|
3263
|
+
/** The marker key a region is written under: `namespace.key.region`. */
|
|
3264
|
+
private wireKey;
|
|
3265
|
+
/** The region key back out of a wire key, for error reporting. */
|
|
3266
|
+
private regionKeyOf;
|
|
2311
3267
|
/**
|
|
2312
|
-
*
|
|
3268
|
+
* This document's own regions, in document order.
|
|
2313
3269
|
*
|
|
2314
3270
|
* @remarks
|
|
2315
|
-
*
|
|
2316
|
-
*
|
|
2317
|
-
*
|
|
2318
|
-
*
|
|
2319
|
-
*
|
|
2320
|
-
*
|
|
2321
|
-
* through something nobody audited — a debug log of outgoing headers, a
|
|
2322
|
-
* serialized error, a stack trace carrying the closure. Registering it with
|
|
2323
|
-
* the runner's filter costs one call and makes every one of those redacted.
|
|
2324
|
-
*
|
|
2325
|
-
* Call it **once, at layer construction**, not per operation: masking is
|
|
2326
|
-
* idempotent but a workflow command per request is noise in the log.
|
|
3271
|
+
* A document a human already edited can be structurally corrupt, and a
|
|
3272
|
+
* read has no error channel worth having — a corrupt document simply has
|
|
3273
|
+
* no readable regions; {@link ManagedDocument.parseResult} is where
|
|
3274
|
+
* corruption fails typed. Regions belonging to a *different* document in
|
|
3275
|
+
* the same text (another namespace or key) are not this document's and are
|
|
3276
|
+
* never reported.
|
|
2327
3277
|
*/
|
|
2328
|
-
|
|
3278
|
+
private ownRegions;
|
|
2329
3279
|
/**
|
|
2330
|
-
*
|
|
3280
|
+
* Append the sentinel when the text does not carry it yet.
|
|
2331
3281
|
*
|
|
2332
3282
|
* @remarks
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2335
|
-
*
|
|
3283
|
+
* Appended at the end — the same position `PullRequestComment.upsert`
|
|
3284
|
+
* writes its marker to — so a document rendered here and a comment written
|
|
3285
|
+
* there agree about where the identity line lives.
|
|
2336
3286
|
*/
|
|
2337
|
-
|
|
3287
|
+
private ensureSentinel;
|
|
2338
3288
|
}
|
|
2339
3289
|
//#endregion
|
|
2340
3290
|
//#region src/ToolInstaller.d.ts
|
|
@@ -2380,9 +3330,75 @@ declare class ToolInstallerError extends ToolInstallerError_base {
|
|
|
2380
3330
|
interface ExtractOptions {
|
|
2381
3331
|
/** The destination. A fresh temporary directory when omitted. */
|
|
2382
3332
|
readonly destination?: string | undefined;
|
|
2383
|
-
/**
|
|
3333
|
+
/**
|
|
3334
|
+
* Flags for `tar`. Defaults to `["xzf"]`, i.e. a gzipped tarball.
|
|
3335
|
+
*
|
|
3336
|
+
* @remarks
|
|
3337
|
+
* The argv is assembled as `[...flags, archive, "-C", destination]` — the
|
|
3338
|
+
* archive path is appended directly AFTER the flags, so the LAST flag must
|
|
3339
|
+
* be the one that consumes the archive operand. `["xzf"]` works because `f`
|
|
3340
|
+
* is last inside the cluster; a custom set like `["xz", "--strip=1", "-f"]`
|
|
3341
|
+
* must end the same way, deliberately rather than by ordering luck.
|
|
3342
|
+
*/
|
|
2384
3343
|
readonly flags?: ReadonlyArray<string> | undefined;
|
|
2385
3344
|
}
|
|
3345
|
+
/**
|
|
3346
|
+
* How a {@link ToolInstallerShape.download} should behave.
|
|
3347
|
+
*
|
|
3348
|
+
* @public
|
|
3349
|
+
*/
|
|
3350
|
+
interface ToolDownloadOptions {
|
|
3351
|
+
/**
|
|
3352
|
+
* The overall time budget for ONE download attempt (request plus streaming
|
|
3353
|
+
* the body to disk). Defaults to five minutes.
|
|
3354
|
+
*
|
|
3355
|
+
* @remarks
|
|
3356
|
+
* The default is deliberately generous: toolchain archives run to hundreds
|
|
3357
|
+
* of megabytes, and a healthy runner connection moves tens of megabytes a
|
|
3358
|
+
* second — even a 500 MB archive on a degraded 2 MB/s link fits in five
|
|
3359
|
+
* minutes. What the budget exists for is the connection that stops moving
|
|
3360
|
+
* entirely: without it, a dead connection is a silent CI hang until the job
|
|
3361
|
+
* timeout; with it, the attempt fails typed as `downloadFailed` with no
|
|
3362
|
+
* status — which `retryable` classifies as a transport fault — and the
|
|
3363
|
+
* download's own retry policy tries again.
|
|
3364
|
+
*/
|
|
3365
|
+
readonly timeout?: Duration.Input | undefined;
|
|
3366
|
+
}
|
|
3367
|
+
/**
|
|
3368
|
+
* What {@link ToolInstallerShape.provisionFile} should provision.
|
|
3369
|
+
*
|
|
3370
|
+
* @public
|
|
3371
|
+
*/
|
|
3372
|
+
interface ProvisionFileOptions {
|
|
3373
|
+
/** The tool-cache name the binary is provisioned under, e.g. `"biome"`. */
|
|
3374
|
+
readonly tool: string;
|
|
3375
|
+
/** The exact version — a tool-cache path segment, so spell it canonically. */
|
|
3376
|
+
readonly version: string;
|
|
3377
|
+
/** The url the binary downloads from on a cache miss. */
|
|
3378
|
+
readonly url: string;
|
|
3379
|
+
/**
|
|
3380
|
+
* The cached file's name — what the tool is invoked as once its directory
|
|
3381
|
+
* is on the `PATH` (e.g. `"biome"`, or `"biome.exe"` on Windows).
|
|
3382
|
+
*/
|
|
3383
|
+
readonly binary: string;
|
|
3384
|
+
}
|
|
3385
|
+
/**
|
|
3386
|
+
* Where {@link ToolInstallerShape.provisionFile} put a single-binary tool.
|
|
3387
|
+
*
|
|
3388
|
+
* @remarks
|
|
3389
|
+
* The same `directory`/`binDir` shape as `PackageManagerInstaller`'s
|
|
3390
|
+
* `CachedPackageManager`, so a consumer publishes either the same way. For a
|
|
3391
|
+
* single binary the two coincide: the cached directory contains exactly the
|
|
3392
|
+
* executable, so it IS the directory to hand to `ActionOutputs.addPath`.
|
|
3393
|
+
*
|
|
3394
|
+
* @public
|
|
3395
|
+
*/
|
|
3396
|
+
interface ProvisionedFile {
|
|
3397
|
+
/** The cached entry: `<tool-cache>/<tool>/<version>/<arch>`. */
|
|
3398
|
+
readonly directory: string;
|
|
3399
|
+
/** The directory to `addPath` — for a single binary, the cached directory itself. */
|
|
3400
|
+
readonly binDir: string;
|
|
3401
|
+
}
|
|
2386
3402
|
/**
|
|
2387
3403
|
* The {@link ToolInstaller} service shape.
|
|
2388
3404
|
*
|
|
@@ -2395,10 +3411,20 @@ interface ToolInstallerShape {
|
|
|
2395
3411
|
* @remarks
|
|
2396
3412
|
* Absent is not an error, and neither is an unreadable cache: both mean
|
|
2397
3413
|
* "install it", which is the only thing a caller can do about either.
|
|
3414
|
+
*
|
|
3415
|
+
* **The tool cache is SHARED.** The hosted runner image pre-populates it and
|
|
3416
|
+
* every `setup-*` action writes into it — this package is a co-writer, not
|
|
3417
|
+
* the owner. A hit guarantees only the `<root>/<tool>/<version>/<arch>`
|
|
3418
|
+
* location contract ({@link ToolInstaller.cachePath}); the directory's
|
|
3419
|
+
* *interior* layout is whatever its writer produced, which is not
|
|
3420
|
+
* necessarily what this consumer's own `cacheDir` would have written.
|
|
3421
|
+
* Validate the layout of a foreign hit before relying on it — applying an
|
|
3422
|
+
* assumption from your own install path (an archive-wrapper subdirectory,
|
|
3423
|
+
* say) to a foreign entry is a real shipped bug.
|
|
2398
3424
|
*/
|
|
2399
3425
|
readonly find: (tool: string, version: string) => Effect.Effect<Option.Option<string>>;
|
|
2400
3426
|
/** Download a url to a temporary file, retrying what is worth retrying. */
|
|
2401
|
-
readonly download: (url: string) => Effect.Effect<string, ToolInstallerError>;
|
|
3427
|
+
readonly download: (url: string, options?: ToolDownloadOptions) => Effect.Effect<string, ToolInstallerError>;
|
|
2402
3428
|
/** Extract a tarball. Returns the directory its contents landed in. */
|
|
2403
3429
|
readonly extractTar: (archive: string, options?: ExtractOptions) => Effect.Effect<string, ToolInstallerError>;
|
|
2404
3430
|
/** Extract a zip. Returns the directory its contents landed in. */
|
|
@@ -2407,16 +3433,38 @@ interface ToolInstallerShape {
|
|
|
2407
3433
|
readonly cacheDir: (source: string, tool: string, version: string) => Effect.Effect<string, ToolInstallerError>;
|
|
2408
3434
|
/** Install a single file into the tool cache under `name`. Returns the cached directory. */
|
|
2409
3435
|
readonly cacheFile: (source: string, name: string, tool: string, version: string) => Effect.Effect<string, ToolInstallerError>;
|
|
3436
|
+
/**
|
|
3437
|
+
* Provision a single-binary tool: the whole `find` → `download` →
|
|
3438
|
+
* chmod → `cacheFile` composition as one call.
|
|
3439
|
+
*
|
|
3440
|
+
* @remarks
|
|
3441
|
+
* The one packaged workflow among the primitives, because the single-binary
|
|
3442
|
+
* shape (biome, and every Rust/Go tool distributed as a bare executable) has
|
|
3443
|
+
* no per-tool variation left to compose: there is no archive, so no
|
|
3444
|
+
* extraction and no layout fixup. A cache hit whose entry contains the
|
|
3445
|
+
* named `binary` short-circuits; a hit *missing* it — a foreign or partial
|
|
3446
|
+
* entry, see {@link ToolInstallerShape.find}'s shared-cache warning — is
|
|
3447
|
+
* treated as a miss and reinstalled over, rather than handing back a
|
|
3448
|
+
* directory that cannot run the tool. On the install path the downloaded
|
|
3449
|
+
* file is made executable (`0o755`) BEFORE caching — skipped when
|
|
3450
|
+
* `RUNNER_OS` is Windows, where the bit does not exist — so the cache only
|
|
3451
|
+
* ever contains a runnable tool; a chmod failure is `cacheFailed` with the
|
|
3452
|
+
* file as its subject.
|
|
3453
|
+
*/
|
|
3454
|
+
readonly provisionFile: (options: ProvisionFileOptions) => Effect.Effect<ProvisionedFile, ToolInstallerError>;
|
|
2410
3455
|
}
|
|
2411
3456
|
declare const ToolInstaller_base: Context.ServiceClass<ToolInstaller, "@effected/github-actions/ToolInstaller", ToolInstallerShape>;
|
|
2412
3457
|
/**
|
|
2413
3458
|
* Download, extract and cache a toolchain in the runner's tool cache.
|
|
2414
3459
|
*
|
|
2415
3460
|
* @remarks
|
|
2416
|
-
* The primitives,
|
|
2417
|
-
* `extractTar` → `cacheDir` in whatever order its tool
|
|
2418
|
-
* orders genuinely differ (a
|
|
2419
|
-
*
|
|
3461
|
+
* The primitives, plus exactly one packaged workflow: a consumer composes
|
|
3462
|
+
* `find` → `download` → `extractTar` → `cacheDir` in whatever order its tool
|
|
3463
|
+
* needs, because the orders genuinely differ (a tarball with a nested root
|
|
3464
|
+
* directory needs a path fixup between extract and cache). The one shape with
|
|
3465
|
+
* no per-tool variation — a single bare binary, which skips extraction and
|
|
3466
|
+
* needs only the executable bit — ships whole as
|
|
3467
|
+
* {@link ToolInstallerShape.provisionFile}.
|
|
2420
3468
|
*
|
|
2421
3469
|
* No `@actions/tool-cache` dependency — the cache is a directory layout, and
|
|
2422
3470
|
* this reproduces the layout rather than importing a package to compute it.
|
|
@@ -2463,5 +3511,339 @@ declare class ToolInstaller extends ToolInstaller_base {
|
|
|
2463
3511
|
static readonly layerTest: (overrides?: Partial<ToolInstallerShape>) => Layer.Layer<ToolInstaller>;
|
|
2464
3512
|
}
|
|
2465
3513
|
//#endregion
|
|
2466
|
-
|
|
3514
|
+
//#region src/PackageManagerInstaller.d.ts
|
|
3515
|
+
declare const PackageManagerInstallerError_base: Schema.Class<PackageManagerInstallerError, Schema.TaggedStruct<"PackageManagerInstallerError", {
|
|
3516
|
+
/**
|
|
3517
|
+
* `downloadFailed`, `extractFailed` and `cacheFailed` mirror the
|
|
3518
|
+
* {@link ToolInstallerError} that caused them, preserved on `cause`
|
|
3519
|
+
* (`cacheFailed` also covers a shim that could not be written into the
|
|
3520
|
+
* entry). `integrityMismatch` — the downloaded artifact does not hash to
|
|
3521
|
+
* what the pin declares; nothing was cached. `integrityMissing` — the pin
|
|
3522
|
+
* carries no integrity and the caller asked for `requireIntegrity`.
|
|
3523
|
+
* `unsupportedPlatform` — bun publishes no build for this runner's
|
|
3524
|
+
* OS/architecture pair. `layoutUnexpected` — the artifact extracted, but
|
|
3525
|
+
* its contents are not shaped like the package manager it claims to be.
|
|
3526
|
+
*/
|
|
3527
|
+
readonly reason: Schema.Literals<readonly ["downloadFailed", "extractFailed", "integrityMismatch", "integrityMissing", "unsupportedPlatform", "layoutUnexpected", "cacheFailed"]>;
|
|
3528
|
+
/** The package manager being installed. */
|
|
3529
|
+
readonly name: Schema.Literals<readonly ["npm", "pnpm", "yarn", "bun"]>;
|
|
3530
|
+
/** The exact version being installed, in string form. */
|
|
3531
|
+
readonly version: Schema.String;
|
|
3532
|
+
/** What was being worked on — a url, a path, or an OS/arch pair. */
|
|
3533
|
+
readonly subject: Schema.optionalKey<Schema.String>;
|
|
3534
|
+
/** The integrity the pin declares (`<algo>.<hex>`), on a mismatch. */
|
|
3535
|
+
readonly expected: Schema.optionalKey<Schema.String>;
|
|
3536
|
+
/** The integrity the artifact actually hashed to, on a mismatch. */
|
|
3537
|
+
readonly actual: Schema.optionalKey<Schema.String>;
|
|
3538
|
+
/** The underlying failure, preserved structurally. */
|
|
3539
|
+
readonly cause: Schema.optionalKey<Schema.Defect>;
|
|
3540
|
+
}>, import("effect/Cause").YieldableError>;
|
|
3541
|
+
/**
|
|
3542
|
+
* Raised when a package manager cannot be provisioned on the runner.
|
|
3543
|
+
*
|
|
3544
|
+
* @public
|
|
3545
|
+
*/
|
|
3546
|
+
declare class PackageManagerInstallerError extends PackageManagerInstallerError_base {
|
|
3547
|
+
get message(): string;
|
|
3548
|
+
}
|
|
3549
|
+
/**
|
|
3550
|
+
* How {@link PackageManagerInstallerShape.install} should behave.
|
|
3551
|
+
*
|
|
3552
|
+
* @public
|
|
3553
|
+
*/
|
|
3554
|
+
interface PackageManagerInstallOptions {
|
|
3555
|
+
/**
|
|
3556
|
+
* Fail with `integrityMissing` when the pin carries no integrity hash,
|
|
3557
|
+
* instead of proceeding with a logged warning. Off by default, because
|
|
3558
|
+
* in-the-wild `devEngines` pins routinely carry no integrity.
|
|
3559
|
+
*/
|
|
3560
|
+
readonly requireIntegrity?: boolean | undefined;
|
|
3561
|
+
/**
|
|
3562
|
+
* The npm registry host the npm, pnpm and yarn tarballs download from.
|
|
3563
|
+
* Defaults to `https://registry.npmjs.org`; a corporate mirror that serves
|
|
3564
|
+
* the standard `/<name>/-/<basename>-<version>.tgz` tarball routes works
|
|
3565
|
+
* unchanged. bun does not ship through a registry — its per-platform zip
|
|
3566
|
+
* always comes from GitHub releases.
|
|
3567
|
+
*/
|
|
3568
|
+
readonly registry?: string | undefined;
|
|
3569
|
+
/**
|
|
3570
|
+
* Whether an npm pin may be answered by the runner's own ambient npm.
|
|
3571
|
+
* Defaults to `true`.
|
|
3572
|
+
*
|
|
3573
|
+
* @remarks
|
|
3574
|
+
* Set `false` when the run REPLACES node: a consumer that installs a pinned
|
|
3575
|
+
* node in the same run puts that node's bundled npm ahead of the runner's on
|
|
3576
|
+
* every path that matters afterward (the install child's `PATH`, and every
|
|
3577
|
+
* later workflow step via `GITHUB_PATH`). The ambient probe interrogates the
|
|
3578
|
+
* RUNNER's npm, so its exact-version match can diverge from the npm that
|
|
3579
|
+
* actually executes once the pinned node shadows it. Suppressing the probe
|
|
3580
|
+
* skips it entirely — no `npm --version` is ever spawned — and the install
|
|
3581
|
+
* goes straight to the tool-cache/dist path, answering
|
|
3582
|
+
* `source: "tool-cache"` with its own `binDir` as usual.
|
|
3583
|
+
*/
|
|
3584
|
+
readonly allowAmbient?: boolean | undefined;
|
|
3585
|
+
}
|
|
3586
|
+
declare const AmbientPackageManager_base: Schema.Class<AmbientPackageManager, Schema.Struct<{
|
|
3587
|
+
/** The discriminant: the runner's own toolchain answered. */
|
|
3588
|
+
readonly source: Schema.tag<"ambient">;
|
|
3589
|
+
/** The package-manager name (`npm`, `pnpm`, `yarn` or `bun`). */
|
|
3590
|
+
readonly name: Schema.Literals<readonly ["npm", "pnpm", "yarn", "bun"]>;
|
|
3591
|
+
/** The exact installed version, in string form. */
|
|
3592
|
+
readonly version: Schema.String;
|
|
3593
|
+
/** Bin name → the ambient command name that invokes it. */
|
|
3594
|
+
readonly bins: Schema.$Record<Schema.String, Schema.String>;
|
|
3595
|
+
}>, {}>;
|
|
3596
|
+
/**
|
|
3597
|
+
* A package manager the runner's own toolchain already had: nothing was
|
|
3598
|
+
* downloaded and nothing was cached, so there is no directory to publish.
|
|
3599
|
+
*
|
|
3600
|
+
* @remarks
|
|
3601
|
+
* `bins` values are bare command names (`npm`, `npx`) resolved through the
|
|
3602
|
+
* ambient `PATH` — the toolchain already put them there.
|
|
3603
|
+
*
|
|
3604
|
+
* @public
|
|
3605
|
+
*/
|
|
3606
|
+
declare class AmbientPackageManager extends AmbientPackageManager_base {}
|
|
3607
|
+
declare const CachedPackageManager_base: Schema.Class<CachedPackageManager, Schema.Struct<{
|
|
3608
|
+
/** The discriminant: the manager lives in the tool cache. */
|
|
3609
|
+
readonly source: Schema.tag<"tool-cache">;
|
|
3610
|
+
/** The package-manager name (`npm`, `pnpm`, `yarn` or `bun`). */
|
|
3611
|
+
readonly name: Schema.Literals<readonly ["npm", "pnpm", "yarn", "bun"]>;
|
|
3612
|
+
/** The exact installed version, in string form. */
|
|
3613
|
+
readonly version: Schema.String;
|
|
3614
|
+
/** The cached entry: `<tool-cache>/<name>/<version>/<arch>`. */
|
|
3615
|
+
readonly directory: Schema.String;
|
|
3616
|
+
/** The directory to `addPath` — executable shims, or bun's own directory. */
|
|
3617
|
+
readonly binDir: Schema.String;
|
|
3618
|
+
/** Bin name → the absolute path of the underlying entry point. */
|
|
3619
|
+
readonly bins: Schema.$Record<Schema.String, Schema.String>;
|
|
3620
|
+
}>, {}>;
|
|
3621
|
+
/**
|
|
3622
|
+
* A package manager living in the runner's tool cache — found there, or
|
|
3623
|
+
* installed into it by this call.
|
|
3624
|
+
*
|
|
3625
|
+
* @remarks
|
|
3626
|
+
* `binDir` is the directory a consumer hands to `ActionOutputs.addPath` to
|
|
3627
|
+
* make the manager invokable by name in subsequent workflow steps. For the
|
|
3628
|
+
* npm-registry managers it is the entry's `.bin` directory of executable
|
|
3629
|
+
* shims this installer writes (`#!/bin/sh` exec wrappers, or `.cmd` wrappers
|
|
3630
|
+
* on Windows); for bun it is the entry directory itself, because the cached
|
|
3631
|
+
* `bun` binary is directly executable and a shim would add nothing but a
|
|
3632
|
+
* fork. `bins` still maps each published bin name to the underlying entry —
|
|
3633
|
+
* a Node script to run with `node` for npm/pnpm/yarn, the native binary for
|
|
3634
|
+
* bun. Calling `addPath` remains deliberately the consumer's move, and note
|
|
3635
|
+
* that `addPath` targets *subsequent* steps: a same-process probe must use
|
|
3636
|
+
* the absolute paths in `bins` or `binDir`.
|
|
3637
|
+
*
|
|
3638
|
+
* @public
|
|
3639
|
+
*/
|
|
3640
|
+
declare class CachedPackageManager extends CachedPackageManager_base {}
|
|
3641
|
+
/**
|
|
3642
|
+
* An installed package manager, discriminated by `source`: `ambient` carries
|
|
3643
|
+
* no directory, `tool-cache` carries the cached `directory` and the
|
|
3644
|
+
* `addPath`-able `binDir`. The union is a `Schema`, so the record round-trips
|
|
3645
|
+
* through `ActionState` for a later phase to read back.
|
|
3646
|
+
*
|
|
3647
|
+
* @remarks
|
|
3648
|
+
* **Construct the variant classes, never the union.** The union's inherited
|
|
3649
|
+
* `make` typechecks against *both* variants at once, so a wrong field yields
|
|
3650
|
+
* a confusing two-branch error instead of "this variant wants X" — reach for
|
|
3651
|
+
* {@link AmbientPackageManager}`.make` / {@link CachedPackageManager}`.make`,
|
|
3652
|
+
* whose `Schema.tag` fills `source` for you. The same tag is why an
|
|
3653
|
+
* overrides-style test helper should be typed
|
|
3654
|
+
* `Partial<Omit<typeof CachedPackageManager.Type, "source">>` — a plain
|
|
3655
|
+
* `Partial` offers `source`, which `make` auto-fills and rejects as input.
|
|
3656
|
+
*
|
|
3657
|
+
* @public
|
|
3658
|
+
*/
|
|
3659
|
+
declare const InstalledPackageManager: Schema.Union<readonly [typeof AmbientPackageManager, typeof CachedPackageManager]>;
|
|
3660
|
+
/**
|
|
3661
|
+
* The decoded type of {@link (InstalledPackageManager:variable)}:
|
|
3662
|
+
* {@link AmbientPackageManager} `|` {@link CachedPackageManager}.
|
|
3663
|
+
*
|
|
3664
|
+
* @public
|
|
3665
|
+
*/
|
|
3666
|
+
type InstalledPackageManager = typeof InstalledPackageManager.Type;
|
|
3667
|
+
/**
|
|
3668
|
+
* The {@link PackageManagerInstaller} service shape.
|
|
3669
|
+
*
|
|
3670
|
+
* @public
|
|
3671
|
+
*/
|
|
3672
|
+
interface PackageManagerInstallerShape {
|
|
3673
|
+
/**
|
|
3674
|
+
* Provision the exact package-manager version a pin names, answering with
|
|
3675
|
+
* where it landed and how to invoke it.
|
|
3676
|
+
*/
|
|
3677
|
+
readonly install: (pin: PackageManagerPin, options?: PackageManagerInstallOptions) => Effect.Effect<InstalledPackageManager, PackageManagerInstallerError>;
|
|
3678
|
+
}
|
|
3679
|
+
declare const PackageManagerInstaller_base: Context.ServiceClass<PackageManagerInstaller, "@effected/github-actions/PackageManagerInstaller", PackageManagerInstallerShape>;
|
|
3680
|
+
/**
|
|
3681
|
+
* First-class exact-version package-manager provisioning on a GitHub runner.
|
|
3682
|
+
*
|
|
3683
|
+
* @remarks
|
|
3684
|
+
* Takes a corepack pin (`@effected/npm`'s `PackageManagerPin`) and answers
|
|
3685
|
+
* with an installed manager: tool-cache `find` first, then — for npm — an
|
|
3686
|
+
* ambient `npm --version` probe (every Node toolchain ships npm), and only
|
|
3687
|
+
* then the manager's own dist: npm, pnpm and yarn 1.x from their registry
|
|
3688
|
+
* tarballs, yarn 2+ (Berry) from `@yarnpkg/cli-dist`, bun from its
|
|
3689
|
+
* per-platform GitHub-release zip. A pin that carries integrity is verified
|
|
3690
|
+
* fail-closed against what corepack itself hashes; one that does not proceeds
|
|
3691
|
+
* with a logged warning unless `requireIntegrity` says otherwise.
|
|
3692
|
+
*
|
|
3693
|
+
* Every tool-cache answer carries a `binDir` a consumer can hand straight to
|
|
3694
|
+
* `ActionOutputs.addPath`: the npm-registry managers get a `.bin` directory
|
|
3695
|
+
* of executable shims written as part of the cached entry, bun's entry is its
|
|
3696
|
+
* own `binDir`. Caching goes through {@link ToolInstaller}, so the
|
|
3697
|
+
* stage-then-swap invariant holds here for free — shims included, the tool
|
|
3698
|
+
* cache only ever contains complete package managers.
|
|
3699
|
+
*
|
|
3700
|
+
* @example
|
|
3701
|
+
* ```ts
|
|
3702
|
+
* import { ActionOutputs, PackageManagerInstaller } from "@effected/github-actions";
|
|
3703
|
+
* import { PackageManagerPin } from "@effected/npm";
|
|
3704
|
+
* import { Effect } from "effect";
|
|
3705
|
+
*
|
|
3706
|
+
* const provision = Effect.gen(function* () {
|
|
3707
|
+
* const installer = yield* PackageManagerInstaller;
|
|
3708
|
+
* const outputs = yield* ActionOutputs;
|
|
3709
|
+
* const pin = yield* PackageManagerPin.parse("pnpm@10.13.1");
|
|
3710
|
+
* const installed = yield* installer.install(pin);
|
|
3711
|
+
* if (installed.source === "tool-cache") {
|
|
3712
|
+
* yield* outputs.addPath(installed.binDir);
|
|
3713
|
+
* }
|
|
3714
|
+
* });
|
|
3715
|
+
* ```
|
|
3716
|
+
*
|
|
3717
|
+
* @public
|
|
3718
|
+
*/
|
|
3719
|
+
declare class PackageManagerInstaller extends PackageManagerInstaller_base {
|
|
3720
|
+
static readonly layer: Layer.Layer<PackageManagerInstaller, never, ActionEnvironment | FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner | ToolInstaller>;
|
|
3721
|
+
/** A test double. The unstubbed member dies rather than reporting an install that did not happen. */
|
|
3722
|
+
static readonly makeTest: (overrides?: Partial<PackageManagerInstallerShape>) => PackageManagerInstallerShape;
|
|
3723
|
+
/** {@link PackageManagerInstaller.makeTest} behind `Layer.succeed`. */
|
|
3724
|
+
static readonly layerTest: (overrides?: Partial<PackageManagerInstallerShape>) => Layer.Layer<PackageManagerInstaller>;
|
|
3725
|
+
}
|
|
3726
|
+
//#endregion
|
|
3727
|
+
//#region src/Secret.d.ts
|
|
3728
|
+
/**
|
|
3729
|
+
* The declassification seam: the **only** place in this package where a
|
|
3730
|
+
* `Redacted` value becomes a plain string.
|
|
3731
|
+
*
|
|
3732
|
+
* @remarks
|
|
3733
|
+
* Some boundaries cannot carry a `Redacted`. A detached child process reads
|
|
3734
|
+
* its configuration from environment variables; `GITHUB_STATE` is a plaintext
|
|
3735
|
+
* file by GitHub's protocol. `Redacted` refusing to serialize is the whole
|
|
3736
|
+
* value of the type, so this module does not try to make it serializable —
|
|
3737
|
+
* it makes declassification **explicit, auditable, and impossible to do
|
|
3738
|
+
* quietly**.
|
|
3739
|
+
*
|
|
3740
|
+
* The invariant: **masking and declassification are the same call.** Every
|
|
3741
|
+
* member registers the value with the runner's log filter (`::add-mask::`)
|
|
3742
|
+
* *before* returning plaintext, so a secret cannot reach a child's environment
|
|
3743
|
+
* or a state file without the runner already knowing to redact it from logs.
|
|
3744
|
+
*
|
|
3745
|
+
* `Redacted.value` appears nowhere else in this package, and a test asserts
|
|
3746
|
+
* that structurally — a reintroduction elsewhere fails the suite rather than
|
|
3747
|
+
* review.
|
|
3748
|
+
*
|
|
3749
|
+
* What this deliberately does **not** do is encrypt the handoff. The child
|
|
3750
|
+
* runs on the same runner, as the same user, as the parent; a key would have
|
|
3751
|
+
* to travel the same channel as the secret it protects.
|
|
3752
|
+
*
|
|
3753
|
+
* @example
|
|
3754
|
+
* ```ts
|
|
3755
|
+
* import { Secret } from "@effected/github-actions";
|
|
3756
|
+
* import { Effect } from "effect";
|
|
3757
|
+
*
|
|
3758
|
+
* const program = Effect.gen(function* () {
|
|
3759
|
+
* const env = yield* Secret.forChildEnv({ MY_TOKEN: theToken });
|
|
3760
|
+
* // every value in `env` is already masked in the runner log
|
|
3761
|
+
* return env;
|
|
3762
|
+
* });
|
|
3763
|
+
* ```
|
|
3764
|
+
*
|
|
3765
|
+
* @public
|
|
3766
|
+
*/
|
|
3767
|
+
declare class Secret {
|
|
3768
|
+
private constructor();
|
|
3769
|
+
/**
|
|
3770
|
+
* Declassify a set of secrets for a child process's environment.
|
|
3771
|
+
*
|
|
3772
|
+
* @remarks
|
|
3773
|
+
* Every value is masked before any plaintext is returned — including when
|
|
3774
|
+
* one of them fails, because masking happens for the whole set first.
|
|
3775
|
+
*
|
|
3776
|
+
* **Detached workers invert the masking model.** Masking works because the
|
|
3777
|
+
* runner parses this process's stdout; a *detached* child's stdout is a log
|
|
3778
|
+
* file no runner parses, so a mask emitted **inside** the worker is inert
|
|
3779
|
+
* and writes the plaintext verbatim into that log — a shipped incident, not
|
|
3780
|
+
* a hypothetical. The rule: **the parent masks, before the spawn** — call
|
|
3781
|
+
* this member in the parent, where the runner is listening, and hand the
|
|
3782
|
+
* already-masked plaintext to the worker's environment. Inside the worker,
|
|
3783
|
+
* compose `ActionOutputs.layerDetached`, whose `setSecret` is a documented
|
|
3784
|
+
* no-op, so the same code cannot re-leak what the parent already masked.
|
|
3785
|
+
*/
|
|
3786
|
+
static readonly forChildEnv: (entries: Readonly<Record<string, Redacted.Redacted<string>>>) => Effect.Effect<Record<string, string>, never, ActionOutputs>;
|
|
3787
|
+
/**
|
|
3788
|
+
* Declassify one secret for a runner file (`GITHUB_STATE`, `GITHUB_OUTPUT`).
|
|
3789
|
+
*
|
|
3790
|
+
* @remarks
|
|
3791
|
+
* Masks first. The runner file is plaintext regardless; the mask is what
|
|
3792
|
+
* keeps the value out of the visible log.
|
|
3793
|
+
*/
|
|
3794
|
+
static readonly forRunnerFile: (secret: Redacted.Redacted<string>) => Effect.Effect<string, never, ActionOutputs>;
|
|
3795
|
+
/**
|
|
3796
|
+
* Declassify one secret for an in-process use that needs the raw bytes.
|
|
3797
|
+
*
|
|
3798
|
+
* @remarks
|
|
3799
|
+
* Request signing is the case this exists for: an HMAC cannot be computed
|
|
3800
|
+
* over a `Redacted`, so a signing key has to become a string somewhere, and
|
|
3801
|
+
* the point of this module is that "somewhere" is here and nowhere else.
|
|
3802
|
+
*
|
|
3803
|
+
* Signing named the member; it does not gate it. **Any in-process use that
|
|
3804
|
+
* needs the raw value without writing it to a runner file is this
|
|
3805
|
+
* operation** — handing a key to a library that takes a plain string. The
|
|
3806
|
+
* dividing line is where the value goes, not what it is for: a value
|
|
3807
|
+
* crossing into a **child's** environment is {@link Secret.forChildEnv},
|
|
3808
|
+
* one written to `GITHUB_STATE` or `GITHUB_OUTPUT` is
|
|
3809
|
+
* {@link Secret.forRunnerFile}, and one that stays in this process is this
|
|
3810
|
+
* member. A third-party SDK that reads only the ambient environment is the
|
|
3811
|
+
* hard case: this package never mutates `process.env` (reads are seeded
|
|
3812
|
+
* once through `ActionEnvironment`), so if a consumer chooses that bridge,
|
|
3813
|
+
* the mutation — and its restore discipline — lives in consumer code as
|
|
3814
|
+
* the consumer's own tradeoff, not as a pattern this member recommends.
|
|
3815
|
+
*
|
|
3816
|
+
* It masks even though the value is never *written* anywhere, and that is
|
|
3817
|
+
* deliberate rather than superstitious: a signing key that leaks does so
|
|
3818
|
+
* through something nobody audited — a debug log of outgoing headers, a
|
|
3819
|
+
* serialized error, a stack trace carrying the closure. Registering it with
|
|
3820
|
+
* the runner's filter costs one call and makes every one of those redacted.
|
|
3821
|
+
*
|
|
3822
|
+
* Call it **once, at layer construction**, not per operation: masking is
|
|
3823
|
+
* idempotent but a workflow command per request is noise in the log.
|
|
3824
|
+
*
|
|
3825
|
+
* **In a detached worker, the mask this member emits is a leak.** The
|
|
3826
|
+
* `::add-mask::` command only masks when the runner parses this process's
|
|
3827
|
+
* stdout; a detached worker's stdout is a log file no runner parses, so
|
|
3828
|
+
* there the command masks nothing *and* spells the plaintext into the log
|
|
3829
|
+
* — a signing key shipped exactly that way for one round. A worker that
|
|
3830
|
+
* signs (an S3-style `BlobStore` is the canonical case) must compose
|
|
3831
|
+
* `ActionOutputs.layerDetached`, under which this member still returns the
|
|
3832
|
+
* raw key for the HMAC but the mask is a documented no-op; the masking
|
|
3833
|
+
* itself is the **parent's** job, done before the spawn via
|
|
3834
|
+
* {@link Secret.forChildEnv} under the real layer.
|
|
3835
|
+
*/
|
|
3836
|
+
static readonly forSigning: (secret: Redacted.Redacted<string>) => Effect.Effect<string, never, ActionOutputs>;
|
|
3837
|
+
/**
|
|
3838
|
+
* The far side of a handoff: re-wrap a plaintext environment variable.
|
|
3839
|
+
*
|
|
3840
|
+
* @remarks
|
|
3841
|
+
* A `Config`, so a missing or empty handoff is an honest `ConfigError`
|
|
3842
|
+
* naming the variable rather than an empty `Redacted` that fails much later
|
|
3843
|
+
* as an authentication error.
|
|
3844
|
+
*/
|
|
3845
|
+
static readonly adopt: (name: string) => Config.Config<Redacted.Redacted<string>>;
|
|
3846
|
+
}
|
|
3847
|
+
//#endregion
|
|
3848
|
+
export { Action, ActionCache, ActionCacheError, type ActionCacheShape, ActionEnvironment, ActionEnvironmentError, type ActionEnvironmentShape, ActionInput, ActionLogger, type ActionLoggerShape, ActionOutputError, ActionOutputs, type ActionOutputsShape, type ActionRunOptions, ActionRuntime, type ActionServices, ActionState, ActionStateError, type ActionStateShape, ActionsIdentityToken, ActionsProvenance, AmbientPackageManager, type AnnotationProperties, Artifact, ArtifactError, type ArtifactItem, type ArtifactRef, type ArtifactShape, type Blob, BlobEnvelope, BlobEnvelopeError, BlobStore, BlobStoreError, type BlobStoreShape, BlobTransferError, CacheKey, CacheKeyError, CachedPackageManager, CheckDocument, CheckDocumentError, type CheckDocumentOptions, type CheckDocumentShape, type CheckDocumentSink, CheckReport, type CheckRunConclusion, type CheckRunProjection, CheckState, type ClientLayerOptions, type DataBlobTransfer, DetachedProcess, DetachedProcessError, type DetachedSpawnOptions, type DownloadOptions, type DownloadResult, DryRun, type DryRunShape, type ExtractOptions, type FileBlobTransfer, GitHubCacheBlobStore, GitHubContext, type GitHubHeadingDepth, type GitHubListOptions, GitHubMarkdown, type GitHubRowSchema, type GitHubSchemaTable, type GitHubSchemaTableColumn, type GitHubSchemaTableColumns, type GitHubSchemaTableFormatRequiredKeys, type GitHubSchemaTableFormattedColumn, type GitHubSchemaTableOptions, GitHubToken, GitHubTokenError, InstalledPackageManager, ManagedDocument, ManagedDocumentError, type ManagedDocumentSource, OidcClaims, OidcTokenError, OidcTokenIssuer, type OidcTokenIssuerShape, type PackageManagerInstallOptions, PackageManagerInstaller, PackageManagerInstallerError, type PackageManagerInstallerShape, ProcessId, type ProvisionFileOptions, type ProvisionOptions, type ProvisionedFile, type ReadOptions, type ReadinessOptions, RunnerContext, type S3Config, Secret, type ToolDownloadOptions, ToolInstaller, ToolInstallerError, type ToolInstallerShape, type UploadOptions, type UploadResult, type WithBufferOptions, WorkflowCommand, describeCause, projectCheckState };
|
|
2467
3849
|
//# sourceMappingURL=index.d.ts.map
|