@effected/github-actions 0.2.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/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { HttpClient } from "effect/unstable/http";
4
4
  import { ChildProcessSpawner } from "effect/unstable/process";
5
5
  import { IdentityToken, SlsaProvenance } from "@effected/sbom";
6
6
  import { BotIdentity, GitHubApp, GitHubClient, InstallationToken, PermissionLevel, RetryPolicy } from "@effected/github";
7
+ import { PackageManagerPin } from "@effected/npm";
7
8
  //#region src/ActionEnvironment.d.ts
8
9
  declare const ActionEnvironmentError_base: Schema.Class<ActionEnvironmentError, Schema.TaggedStruct<"ActionEnvironmentError", {
9
10
  readonly reason: Schema.Literals<readonly ["missing", "malformed"]>;
@@ -373,9 +374,11 @@ declare const ActionOutputError_base: Schema.Class<ActionOutputError, Schema.Tag
373
374
  * the code is running outside a GitHub runner. `writeFailed` — the file
374
375
  * exists but could not be appended to. `invalidName` — the name would
375
376
  * corrupt the file's block structure. `encodeFailed` — a value did not
376
- * 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.
377
380
  */
378
- readonly reason: Schema.Literals<readonly ["unavailable", "writeFailed", "invalidName", "encodeFailed"]>;
381
+ readonly reason: Schema.Literals<readonly ["unavailable", "writeFailed", "invalidName", "encodeFailed", "detached"]>;
379
382
  /** Which runner file was involved, by environment variable name. */
380
383
  readonly file: Schema.optionalKey<Schema.String>;
381
384
  /** The output or variable name, when one is involved. */
@@ -405,7 +408,17 @@ interface ActionOutputsShape {
405
408
  readonly summary: (content: string) => Effect.Effect<void, ActionOutputError>;
406
409
  /** Export an environment variable to **subsequent** steps. */
407
410
  readonly exportVariable: (name: string, value: string) => Effect.Effect<void, ActionOutputError>;
408
- /** Prepend a directory to `PATH` for subsequent steps. */
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
+ */
409
422
  readonly addPath: (path: string) => Effect.Effect<void, ActionOutputError>;
410
423
  /** Emit an error annotation. Does not itself set the exit code. */
411
424
  readonly setFailed: (message: string) => Effect.Effect<void>;
@@ -430,6 +443,45 @@ declare const ActionOutputs_base: Context.ServiceClass<ActionOutputs, "@effected
430
443
  */
431
444
  declare class ActionOutputs extends ActionOutputs_base {
432
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>;
433
485
  /** A test double. Unstubbed members die rather than silently succeeding. */
434
486
  static readonly makeTest: (overrides?: Partial<ActionOutputsShape>) => ActionOutputsShape;
435
487
  /** {@link ActionOutputs.makeTest} behind `Layer.succeed`. */
@@ -441,10 +493,13 @@ declare const ActionStateError_base: Schema.Class<ActionStateError, Schema.Tagge
441
493
  /**
442
494
  * `missing` — no value was saved under this key in an earlier phase.
443
495
  * `malformed` — a value is there but is not JSON, or does not satisfy the
444
- * schema it was read with. `writeFailed` — the state file could not be
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
445
500
  * appended to.
446
501
  */
447
- readonly reason: Schema.Literals<readonly ["missing", "malformed", "writeFailed"]>;
502
+ readonly reason: Schema.Literals<readonly ["missing", "malformed", "notPlainJson", "writeFailed"]>;
448
503
  readonly key: Schema.String;
449
504
  readonly cause: Schema.optionalKey<Schema.Defect>;
450
505
  }>, import("effect/Cause").YieldableError>;
@@ -462,7 +517,24 @@ declare class ActionStateError extends ActionStateError_base {
462
517
  * @public
463
518
  */
464
519
  interface ActionStateShape {
465
- /** Persist a value for a later phase. */
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
+ */
466
538
  readonly save: <A, I>(key: string, value: A, schema: Schema.Codec<A, I>) => Effect.Effect<void, ActionStateError>;
467
539
  /** Read a value saved by an earlier phase. */
468
540
  readonly get: <A, I>(key: string, schema: Schema.Codec<A, I>) => Effect.Effect<A, ActionStateError>;
@@ -709,6 +781,14 @@ declare class CacheKeyError extends CacheKeyError_base {
709
781
  declare const CacheKey_base: Schema.Class<CacheKey, Schema.Struct<{
710
782
  /** The components, most general first. */
711
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>>;
712
792
  }>, {}>;
713
793
  /**
714
794
  * A GitHub Actions cache key and the restore-key ladder that goes with it.
@@ -721,6 +801,12 @@ declare const CacheKey_base: Schema.Class<CacheKey, Schema.Struct<{
721
801
  * the primary key and its fallbacks impossible to drift apart, which is the
722
802
  * failure that produces a cache that never hits and never says why.
723
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
+ *
724
810
  * @example
725
811
  * ```ts
726
812
  * import { CacheKey } from "@effected/github-actions";
@@ -739,12 +825,69 @@ declare class CacheKey extends CacheKey_base {
739
825
  * The restore keys, most specific first.
740
826
  *
741
827
  * @remarks
742
- * Each rung drops the last segment and keeps the trailing separator, so a
743
- * three-segment key falls back to two prefixes and a one-segment key falls
744
- * back to nothing — there is no rung that would match every cache in the
745
- * 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".
746
844
  */
747
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;
748
891
  /** Build a key from its segments. */
749
892
  static of(...segments: readonly [string, ...ReadonlyArray<string>]): CacheKey;
750
893
  /**
@@ -890,9 +1033,31 @@ declare class ActionCacheError extends ActionCacheError_base {
890
1033
  */
891
1034
  interface ActionCacheShape {
892
1035
  /**
893
- * Archive `paths` and store them under `key`.
1036
+ * Archive the files and directories matching `paths` and store them under
1037
+ * `key`.
894
1038
  *
895
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
+ *
896
1061
  * A key that already exists is a **success**: cache entries are immutable, so
897
1062
  * another job having written it first is the outcome the caller wanted.
898
1063
  */
@@ -905,6 +1070,11 @@ interface ActionCacheShape {
905
1070
  * `Option.none()` is a miss, not a failure — a cold cache is the normal state
906
1071
  * of a new branch. Passing a {@link CacheKey} supplies its restore-key ladder
907
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.
908
1078
  */
909
1079
  readonly restore: (paths: ReadonlyArray<string>, key: string | CacheKey, restoreKeys?: ReadonlyArray<string>) => Effect.Effect<Option.Option<string>, ActionCacheError>;
910
1080
  }
@@ -1011,6 +1181,21 @@ declare class ActionCache extends ActionCache_base {
1011
1181
  */
1012
1182
  declare class ActionInput {
1013
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;
1014
1199
  /**
1015
1200
  * A required string input.
1016
1201
  *
@@ -1120,14 +1305,37 @@ declare class ActionInput {
1120
1305
  */
1121
1306
  static schema<A, I>(name: string, schema: Schema.Codec<A, I>): Config.Config<A>;
1122
1307
  /**
1123
- * A `ConfigProvider` over a record of runner variables.
1308
+ * A `ConfigProvider` over a record of runner variables **or input names**.
1124
1309
  *
1125
1310
  * @remarks
1126
- * Subsumes the source package's free-standing `ActionsConfigProvider`, whose
1127
- * three behaviors are preserved exactly: the path is joined with `_`, spaces
1128
- * become underscores and the whole is uppercased; and an **empty string
1129
- * reads as absent**, because the runner sets unsupplied inputs to `""` and
1130
- * treating that as present would make every optional input look supplied.
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.
1131
1339
  *
1132
1340
  * Taking the environment as an argument rather than reading `process.env`
1133
1341
  * is what makes inputs testable without mutating the test process.
@@ -3079,106 +3287,6 @@ declare class ManagedDocument extends ManagedDocument_base {
3079
3287
  private ensureSentinel;
3080
3288
  }
3081
3289
  //#endregion
3082
- //#region src/Secret.d.ts
3083
- /**
3084
- * The declassification seam: the **only** place in this package where a
3085
- * `Redacted` value becomes a plain string.
3086
- *
3087
- * @remarks
3088
- * Some boundaries cannot carry a `Redacted`. A detached child process reads
3089
- * its configuration from environment variables; `GITHUB_STATE` is a plaintext
3090
- * file by GitHub's protocol. `Redacted` refusing to serialize is the whole
3091
- * value of the type, so this module does not try to make it serializable —
3092
- * it makes declassification **explicit, auditable, and impossible to do
3093
- * quietly**.
3094
- *
3095
- * The invariant: **masking and declassification are the same call.** Every
3096
- * member registers the value with the runner's log filter (`::add-mask::`)
3097
- * *before* returning plaintext, so a secret cannot reach a child's environment
3098
- * or a state file without the runner already knowing to redact it from logs.
3099
- *
3100
- * `Redacted.value` appears nowhere else in this package, and a test asserts
3101
- * that structurally — a reintroduction elsewhere fails the suite rather than
3102
- * review.
3103
- *
3104
- * What this deliberately does **not** do is encrypt the handoff. The child
3105
- * runs on the same runner, as the same user, as the parent; a key would have
3106
- * to travel the same channel as the secret it protects.
3107
- *
3108
- * @example
3109
- * ```ts
3110
- * import { Secret } from "@effected/github-actions";
3111
- * import { Effect } from "effect";
3112
- *
3113
- * const program = Effect.gen(function* () {
3114
- * const env = yield* Secret.forChildEnv({ MY_TOKEN: theToken });
3115
- * // every value in `env` is already masked in the runner log
3116
- * return env;
3117
- * });
3118
- * ```
3119
- *
3120
- * @public
3121
- */
3122
- declare class Secret {
3123
- private constructor();
3124
- /**
3125
- * Declassify a set of secrets for a child process's environment.
3126
- *
3127
- * @remarks
3128
- * Every value is masked before any plaintext is returned — including when
3129
- * one of them fails, because masking happens for the whole set first.
3130
- */
3131
- static readonly forChildEnv: (entries: Readonly<Record<string, Redacted.Redacted<string>>>) => Effect.Effect<Record<string, string>, never, ActionOutputs>;
3132
- /**
3133
- * Declassify one secret for a runner file (`GITHUB_STATE`, `GITHUB_OUTPUT`).
3134
- *
3135
- * @remarks
3136
- * Masks first. The runner file is plaintext regardless; the mask is what
3137
- * keeps the value out of the visible log.
3138
- */
3139
- static readonly forRunnerFile: (secret: Redacted.Redacted<string>) => Effect.Effect<string, never, ActionOutputs>;
3140
- /**
3141
- * Declassify one secret for an in-process use that needs the raw bytes.
3142
- *
3143
- * @remarks
3144
- * Request signing is the case this exists for: an HMAC cannot be computed
3145
- * over a `Redacted`, so a signing key has to become a string somewhere, and
3146
- * the point of this module is that "somewhere" is here and nowhere else.
3147
- *
3148
- * Signing named the member; it does not gate it. **Any in-process use that
3149
- * needs the raw value without writing it to a runner file is this
3150
- * operation** — handing a key to a library that takes a plain string. The
3151
- * dividing line is where the value goes, not what it is for: a value
3152
- * crossing into a **child's** environment is {@link Secret.forChildEnv},
3153
- * one written to `GITHUB_STATE` or `GITHUB_OUTPUT` is
3154
- * {@link Secret.forRunnerFile}, and one that stays in this process is this
3155
- * member. A third-party SDK that reads only the ambient environment is the
3156
- * hard case: this package never mutates `process.env` (reads are seeded
3157
- * once through `ActionEnvironment`), so if a consumer chooses that bridge,
3158
- * the mutation — and its restore discipline — lives in consumer code as
3159
- * the consumer's own tradeoff, not as a pattern this member recommends.
3160
- *
3161
- * It masks even though the value is never *written* anywhere, and that is
3162
- * deliberate rather than superstitious: a signing key that leaks does so
3163
- * through something nobody audited — a debug log of outgoing headers, a
3164
- * serialized error, a stack trace carrying the closure. Registering it with
3165
- * the runner's filter costs one call and makes every one of those redacted.
3166
- *
3167
- * Call it **once, at layer construction**, not per operation: masking is
3168
- * idempotent but a workflow command per request is noise in the log.
3169
- */
3170
- static readonly forSigning: (secret: Redacted.Redacted<string>) => Effect.Effect<string, never, ActionOutputs>;
3171
- /**
3172
- * The far side of a handoff: re-wrap a plaintext environment variable.
3173
- *
3174
- * @remarks
3175
- * A `Config`, so a missing or empty handoff is an honest `ConfigError`
3176
- * naming the variable rather than an empty `Redacted` that fails much later
3177
- * as an authentication error.
3178
- */
3179
- static readonly adopt: (name: string) => Config.Config<Redacted.Redacted<string>>;
3180
- }
3181
- //#endregion
3182
3290
  //#region src/ToolInstaller.d.ts
3183
3291
  declare const ToolInstallerError_base: Schema.Class<ToolInstallerError, Schema.TaggedStruct<"ToolInstallerError", {
3184
3292
  /**
@@ -3222,9 +3330,75 @@ declare class ToolInstallerError extends ToolInstallerError_base {
3222
3330
  interface ExtractOptions {
3223
3331
  /** The destination. A fresh temporary directory when omitted. */
3224
3332
  readonly destination?: string | undefined;
3225
- /** Flags for `tar`. Defaults to `["xzf"]`, i.e. a gzipped tarball. */
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
+ */
3226
3343
  readonly flags?: ReadonlyArray<string> | undefined;
3227
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
+ }
3228
3402
  /**
3229
3403
  * The {@link ToolInstaller} service shape.
3230
3404
  *
@@ -3237,10 +3411,20 @@ interface ToolInstallerShape {
3237
3411
  * @remarks
3238
3412
  * Absent is not an error, and neither is an unreadable cache: both mean
3239
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.
3240
3424
  */
3241
3425
  readonly find: (tool: string, version: string) => Effect.Effect<Option.Option<string>>;
3242
3426
  /** Download a url to a temporary file, retrying what is worth retrying. */
3243
- readonly download: (url: string) => Effect.Effect<string, ToolInstallerError>;
3427
+ readonly download: (url: string, options?: ToolDownloadOptions) => Effect.Effect<string, ToolInstallerError>;
3244
3428
  /** Extract a tarball. Returns the directory its contents landed in. */
3245
3429
  readonly extractTar: (archive: string, options?: ExtractOptions) => Effect.Effect<string, ToolInstallerError>;
3246
3430
  /** Extract a zip. Returns the directory its contents landed in. */
@@ -3249,16 +3433,38 @@ interface ToolInstallerShape {
3249
3433
  readonly cacheDir: (source: string, tool: string, version: string) => Effect.Effect<string, ToolInstallerError>;
3250
3434
  /** Install a single file into the tool cache under `name`. Returns the cached directory. */
3251
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>;
3252
3455
  }
3253
3456
  declare const ToolInstaller_base: Context.ServiceClass<ToolInstaller, "@effected/github-actions/ToolInstaller", ToolInstallerShape>;
3254
3457
  /**
3255
3458
  * Download, extract and cache a toolchain in the runner's tool cache.
3256
3459
  *
3257
3460
  * @remarks
3258
- * The primitives, not a workflow: a consumer composes `find` → `download` →
3259
- * `extractTar` → `cacheDir` in whatever order its tool needs, because the
3260
- * orders genuinely differ (a single binary skips extraction, a tarball with a
3261
- * nested root directory needs a path fixup between extract and cache).
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}.
3262
3468
  *
3263
3469
  * No `@actions/tool-cache` dependency — the cache is a directory layout, and
3264
3470
  * this reproduces the layout rather than importing a package to compute it.
@@ -3305,5 +3511,339 @@ declare class ToolInstaller extends ToolInstaller_base {
3305
3511
  static readonly layerTest: (overrides?: Partial<ToolInstallerShape>) => Layer.Layer<ToolInstaller>;
3306
3512
  }
3307
3513
  //#endregion
3308
- 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, type AnnotationProperties, Artifact, ArtifactError, type ArtifactItem, type ArtifactRef, type ArtifactShape, type Blob, BlobEnvelope, BlobEnvelopeError, BlobStore, BlobStoreError, type BlobStoreShape, BlobTransferError, CacheKey, CacheKeyError, 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, ManagedDocument, ManagedDocumentError, type ManagedDocumentSource, OidcClaims, OidcTokenError, OidcTokenIssuer, type OidcTokenIssuerShape, ProcessId, type ProvisionOptions, type ReadOptions, type ReadinessOptions, RunnerContext, type S3Config, Secret, ToolInstaller, ToolInstallerError, type ToolInstallerShape, type UploadOptions, type UploadResult, type WithBufferOptions, WorkflowCommand, describeCause, projectCheckState };
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 };
3309
3849
  //# sourceMappingURL=index.d.ts.map