@effected/github-actions 0.6.1 → 0.7.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/DetachedProcess.js +64 -1
- package/Secret.js +79 -10
- package/index.d.ts +159 -11
- package/package.json +3 -3
package/DetachedProcess.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
|
+
import { HttpClient } from "effect/unstable/http";
|
|
2
3
|
import { ChildProcessSpawner } from "effect/unstable/process";
|
|
3
4
|
import { spawn } from "node:child_process";
|
|
4
5
|
import { closeSync, openSync } from "node:fs";
|
|
@@ -79,6 +80,10 @@ var DetachedProcessError = class extends Schema.TaggedError()("DetachedProcessEr
|
|
|
79
80
|
const ProcessId = Schema.Number.pipe(Schema.check(Schema.makeFilter((value) => Number.isInteger(value) && value > 0 ? void 0 : "Expected a positive integer process id")), Schema.brand("ProcessId"));
|
|
80
81
|
/** Whether a thrown value is a Node errno error with the given code. */
|
|
81
82
|
const isErrno = (cause, code) => typeof cause === "object" && cause !== null && cause.code === code;
|
|
83
|
+
/** See {@link DetachedProcess.makeTestOps}: an unstubbed member dies naming itself. */
|
|
84
|
+
const unimplemented = (member) => {
|
|
85
|
+
throw new Error(`DetachedProcess.makeTestOps: ${member}() was called but not stubbed — pass a \`${member}\` override.`);
|
|
86
|
+
};
|
|
82
87
|
/**
|
|
83
88
|
* A long-lived child that outlives the phase that started it.
|
|
84
89
|
*
|
|
@@ -111,7 +116,7 @@ const isErrno = (cause, code) => typeof cause === "object" && cause !== null &&
|
|
|
111
116
|
*
|
|
112
117
|
* @public
|
|
113
118
|
*/
|
|
114
|
-
var DetachedProcess = class {
|
|
119
|
+
var DetachedProcess = class DetachedProcess {
|
|
115
120
|
constructor() {}
|
|
116
121
|
/**
|
|
117
122
|
* Start a detached child with its output routed to a log file.
|
|
@@ -187,6 +192,34 @@ var DetachedProcess = class {
|
|
|
187
192
|
return yield* poll(attempts);
|
|
188
193
|
});
|
|
189
194
|
/**
|
|
195
|
+
* A readiness probe over HTTP: `true` when a `GET` answers 2xx.
|
|
196
|
+
*
|
|
197
|
+
* @remarks
|
|
198
|
+
* The probe {@link DetachedProcess.awaitReady} almost always wants: an HTTP
|
|
199
|
+
* `GET` at a port the child is still binding, where connection-refused *is*
|
|
200
|
+
* "not up yet". The consumers that hand-rolled the polling wrote exactly
|
|
201
|
+
* this probe around it, and each had to discover the same subtlety — the
|
|
202
|
+
* probe's error channel must stay empty, because `awaitReady` deliberately
|
|
203
|
+
* propagates probe failures, and a refused connection is not a failure of
|
|
204
|
+
* the probe.
|
|
205
|
+
*
|
|
206
|
+
* So this probe never fails: a refused connection, any other transport
|
|
207
|
+
* error and a non-2xx answer all collapse to `false`, leaving
|
|
208
|
+
* `awaitReady`'s own exhaustion as the only failure left. That is a
|
|
209
|
+
* deliberate opt-out of the propagate-probe-failures posture, and it is
|
|
210
|
+
* only sound because every failure an HTTP readiness check can see means
|
|
211
|
+
* the same thing here. The cost is the documented one: a *misconfigured*
|
|
212
|
+
* probe — a typo'd port, a wrong path — is indistinguishable from a child
|
|
213
|
+
* that never came up, and reports as `notReady` only after the full budget.
|
|
214
|
+
* A probe whose failures are meaningfully distinct should stay a
|
|
215
|
+
* hand-written effect with those failures in `E`.
|
|
216
|
+
*
|
|
217
|
+
* The request goes through core's `HttpClient` — `Action.run` provides it
|
|
218
|
+
* ambiently — rather than a bare `fetch`, per the package rule that
|
|
219
|
+
* everything that *can* go through a core contract does.
|
|
220
|
+
*/
|
|
221
|
+
static httpProbe = (url) => HttpClient.get(url).pipe(Effect.map((response) => response.status >= 200 && response.status < 300), Effect.catch(() => Effect.succeed(false)));
|
|
222
|
+
/**
|
|
190
223
|
* Signal a pid that came back across the phase boundary.
|
|
191
224
|
*
|
|
192
225
|
* @remarks
|
|
@@ -225,6 +258,36 @@ var DetachedProcess = class {
|
|
|
225
258
|
}
|
|
226
259
|
});
|
|
227
260
|
});
|
|
261
|
+
/**
|
|
262
|
+
* The real operations: the production default for a
|
|
263
|
+
* {@link DetachedProcessOps} parameter.
|
|
264
|
+
*
|
|
265
|
+
* @remarks
|
|
266
|
+
* The members ARE the statics — same references, not wrappers — so taking
|
|
267
|
+
* the seam costs a consumer nothing over calling the statics directly.
|
|
268
|
+
*/
|
|
269
|
+
static ops = {
|
|
270
|
+
spawn: DetachedProcess.spawn,
|
|
271
|
+
awaitReady: DetachedProcess.awaitReady,
|
|
272
|
+
reap: DetachedProcess.reap
|
|
273
|
+
};
|
|
274
|
+
/**
|
|
275
|
+
* A test double. Unstubbed members die rather than silently succeeding.
|
|
276
|
+
*
|
|
277
|
+
* @remarks
|
|
278
|
+
* The package's `makeTest` convention, on the module's one non-service.
|
|
279
|
+
* Every member a test does not stub dies loudly **naming the member**,
|
|
280
|
+
* because silence is worse here than anywhere else in the package: a spawn
|
|
281
|
+
* that silently "succeeds" fakes a child that does not exist, and a reap
|
|
282
|
+
* that silently succeeds asserts nothing about the one call whose
|
|
283
|
+
* production form signals a real pid.
|
|
284
|
+
*/
|
|
285
|
+
static makeTestOps = (overrides = {}) => ({
|
|
286
|
+
spawn: () => Effect.sync(() => unimplemented("spawn")),
|
|
287
|
+
awaitReady: () => Effect.sync(() => unimplemented("awaitReady")),
|
|
288
|
+
reap: () => Effect.sync(() => unimplemented("reap")),
|
|
289
|
+
...overrides
|
|
290
|
+
});
|
|
228
291
|
};
|
|
229
292
|
|
|
230
293
|
//#endregion
|
package/Secret.js
CHANGED
|
@@ -14,10 +14,18 @@ import { Config, Effect, Redacted } from "effect";
|
|
|
14
14
|
* it makes declassification **explicit, auditable, and impossible to do
|
|
15
15
|
* quietly**.
|
|
16
16
|
*
|
|
17
|
-
* The invariant: **masking and declassification
|
|
18
|
-
* member
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* The invariant: **masking is the floor, and declassification implies it.**
|
|
18
|
+
* Every member that touches plaintext routes through
|
|
19
|
+
* `ActionOutputs.setSecret`, registering the value with the runner's log
|
|
20
|
+
* filter (`::add-mask::`) *before* any plaintext is returned — and the one
|
|
21
|
+
* member that returns nothing, {@link Secret.mask}, registers and stops there
|
|
22
|
+
* — so under the normal runner layer a secret cannot reach a child's
|
|
23
|
+
* environment or a state file without the runner already knowing to redact it
|
|
24
|
+
* from logs. Two scoped exceptions: {@link Secret.adopt} is a `Config` that
|
|
25
|
+
* re-wraps an inherited plaintext without involving `ActionOutputs` at all —
|
|
26
|
+
* the parent masked that value before the handoff — and under
|
|
27
|
+
* `ActionOutputs.layerDetached` the registration is a documented no-op,
|
|
28
|
+
* because a mask emitted inside a detached worker would itself be the leak.
|
|
21
29
|
*
|
|
22
30
|
* `Redacted.value` appears nowhere else in this package, and a test asserts
|
|
23
31
|
* that structurally — a reintroduction elsewhere fails the suite rather than
|
|
@@ -97,12 +105,9 @@ var Secret = class Secret {
|
|
|
97
105
|
* dividing line is where the value goes, not what it is for: a value
|
|
98
106
|
* crossing into a **child's** environment is {@link Secret.forChildEnv},
|
|
99
107
|
* one written to `GITHUB_STATE` or `GITHUB_OUTPUT` is
|
|
100
|
-
* {@link Secret.forRunnerFile},
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* once through `ActionEnvironment`), so if a consumer chooses that bridge,
|
|
104
|
-
* the mutation — and its restore discipline — lives in consumer code as
|
|
105
|
-
* the consumer's own tradeoff, not as a pattern this member recommends.
|
|
108
|
+
* {@link Secret.forRunnerFile}, one bound for the ambient environment is
|
|
109
|
+
* {@link Secret.forProcessEnv}, and one that stays in this process — held
|
|
110
|
+
* by this code, passed as an argument — is this member.
|
|
106
111
|
*
|
|
107
112
|
* It masks even though the value is never *written* anywhere, and that is
|
|
108
113
|
* deliberate rather than superstitious: a signing key that leaks does so
|
|
@@ -126,6 +131,70 @@ var Secret = class Secret {
|
|
|
126
131
|
*/
|
|
127
132
|
static forSigning = (secret) => Secret.forRunnerFile(secret);
|
|
128
133
|
/**
|
|
134
|
+
* Declassify one secret for the caller to bridge into `process.env`.
|
|
135
|
+
*
|
|
136
|
+
* @remarks
|
|
137
|
+
* The third-party-SDK case, under its own auditable name: an SDK that reads
|
|
138
|
+
* only the ambient environment cannot take the plaintext as an argument, so
|
|
139
|
+
* the value has to cross into `process.env` before the SDK looks. The
|
|
140
|
+
* mechanism is identical to {@link Secret.forRunnerFile} — mask first, then
|
|
141
|
+
* return plaintext — and the name is what earns the member: these names are
|
|
142
|
+
* the audit vocabulary, and a search for this one finds every place a
|
|
143
|
+
* secret enters the ambient environment without wading through signing
|
|
144
|
+
* keys and runner files.
|
|
145
|
+
*
|
|
146
|
+
* The ruling on who mutates is unchanged by the name existing: **this
|
|
147
|
+
* package never mutates `process.env`.** Reads are seeded once, at layer
|
|
148
|
+
* construction, through `ActionEnvironment`, and nothing here writes back
|
|
149
|
+
* — a write from inside the kit would be invisible to that seeding and to
|
|
150
|
+
* every consumer's assumptions about when the environment is stable. This
|
|
151
|
+
* member declassifies and masks; the assignment to `process.env`, and the
|
|
152
|
+
* restore discipline that unwinds it when the bridged scope ends, live in
|
|
153
|
+
* the **caller**, as the caller's own explicit tradeoff.
|
|
154
|
+
*
|
|
155
|
+
* The detached-worker inversion applies exactly as it does to
|
|
156
|
+
* {@link Secret.forSigning}: the mask only works where the runner parses
|
|
157
|
+
* stdout, so a worker that needs a bridged environment gets it from the
|
|
158
|
+
* parent — masked before the spawn via {@link Secret.forChildEnv} — rather
|
|
159
|
+
* than declassifying inside itself.
|
|
160
|
+
*/
|
|
161
|
+
static forProcessEnv = (secret) => Secret.forRunnerFile(secret);
|
|
162
|
+
/**
|
|
163
|
+
* Register a secret with the runner's log filter — and return nothing.
|
|
164
|
+
*
|
|
165
|
+
* @remarks
|
|
166
|
+
* Masking without declassification: the value is registered with
|
|
167
|
+
* `::add-mask::` through the same route as every other member, and the
|
|
168
|
+
* success channel is `void`, so a caller cannot come away holding the raw
|
|
169
|
+
* value at all. This is the register-only shape consumers previously spelled
|
|
170
|
+
* as a {@link Secret.forSigning} whose result was discarded — a spelling
|
|
171
|
+
* that lied to the audit vocabulary (a grep for signing found masking),
|
|
172
|
+
* contradicted forSigning's own once-at-construction guidance, and needed a
|
|
173
|
+
* comment to apologize for itself. The names are the vocabulary: a grep for
|
|
174
|
+
* this member finds every register-only site, and a grep for `forSigning`
|
|
175
|
+
* again finds only signing.
|
|
176
|
+
*
|
|
177
|
+
* The canonical caller masks every supplied credential input
|
|
178
|
+
* unconditionally, before the logic that decides which of them will
|
|
179
|
+
* actually be used — a secret the workflow supplied deserves redaction from
|
|
180
|
+
* the log whether or not resolution reaches for it.
|
|
181
|
+
*
|
|
182
|
+
* One value per call is the blessed shape; there is deliberately no
|
|
183
|
+
* set-taking form. {@link Secret.forChildEnv} takes a set because its
|
|
184
|
+
* mask-everything-before-returning-anything ordering is load-bearing; a
|
|
185
|
+
* plain mask returns nothing to order against, and a loop is fine.
|
|
186
|
+
*
|
|
187
|
+
* **In a detached worker, the mask this member emits is a leak.** The
|
|
188
|
+
* `::add-mask::` command only masks when the runner parses this process's
|
|
189
|
+
* stdout; a detached worker's stdout is a log file no runner parses, so
|
|
190
|
+
* there the command masks nothing *and* spells the plaintext into the log
|
|
191
|
+
* — a signing key shipped exactly that way for one round. A worker must
|
|
192
|
+
* compose `ActionOutputs.layerDetached`, under which the mask is a
|
|
193
|
+
* documented no-op; the masking itself is the **parent's** job, done
|
|
194
|
+
* before the spawn via {@link Secret.forChildEnv} under the real layer.
|
|
195
|
+
*/
|
|
196
|
+
static mask = (secret) => Effect.asVoid(Secret.forRunnerFile(secret));
|
|
197
|
+
/**
|
|
129
198
|
* The far side of a handoff: re-wrap a plaintext environment variable.
|
|
130
199
|
*
|
|
131
200
|
* @remarks
|
package/index.d.ts
CHANGED
|
@@ -2897,6 +2897,36 @@ interface ReadinessOptions {
|
|
|
2897
2897
|
/** How many times to retry after the first probe. Defaults to 40. */
|
|
2898
2898
|
readonly attempts?: number | undefined;
|
|
2899
2899
|
}
|
|
2900
|
+
/**
|
|
2901
|
+
* The module's operations as a value: the seam a consumer's tests inject.
|
|
2902
|
+
*
|
|
2903
|
+
* @remarks
|
|
2904
|
+
* `DetachedProcess` is statics on a class, deliberately — there is no scope to
|
|
2905
|
+
* hang a service off, because the whole point is a child that outlives the
|
|
2906
|
+
* process holding any handle to it. The cost of that choice is that the
|
|
2907
|
+
* package's `makeTest`/`layerTest` convention has nothing to attach to:
|
|
2908
|
+
* statics cannot be swapped by a layer, and a test must never spawn a real
|
|
2909
|
+
* child, poll a real port, or — above all — signal a real pid. This interface
|
|
2910
|
+
* restores the seam as **parameter injection**: production code takes a
|
|
2911
|
+
* `DetachedProcessOps`, defaulting to {@link DetachedProcess.ops}, and a test
|
|
2912
|
+
* passes {@link DetachedProcess.makeTestOps} with only the members it means to
|
|
2913
|
+
* observe.
|
|
2914
|
+
*
|
|
2915
|
+
* {@link DetachedProcess.httpProbe} is deliberately not a member. It is a
|
|
2916
|
+
* probe *constructor* with no process side effect of its own; a test stubs
|
|
2917
|
+
* `awaitReady` — the operation that runs the probe — or provides a stub
|
|
2918
|
+
* `HttpClient`.
|
|
2919
|
+
*
|
|
2920
|
+
* @public
|
|
2921
|
+
*/
|
|
2922
|
+
interface DetachedProcessOps {
|
|
2923
|
+
/** {@link DetachedProcess.spawn}. */
|
|
2924
|
+
readonly spawn: (options: DetachedSpawnOptions) => Effect.Effect<ChildProcessSpawner.ProcessId, DetachedProcessError>;
|
|
2925
|
+
/** {@link DetachedProcess.awaitReady}. */
|
|
2926
|
+
readonly awaitReady: <E, R>(probe: Effect.Effect<boolean, E, R>, options?: ReadinessOptions) => Effect.Effect<void, E | DetachedProcessError, R>;
|
|
2927
|
+
/** {@link DetachedProcess.reap}. */
|
|
2928
|
+
readonly reap: (pid: number, signal?: NodeJS.Signals) => Effect.Effect<boolean, DetachedProcessError>;
|
|
2929
|
+
}
|
|
2900
2930
|
/**
|
|
2901
2931
|
* A long-lived child that outlives the phase that started it.
|
|
2902
2932
|
*
|
|
@@ -2955,6 +2985,34 @@ declare class DetachedProcess {
|
|
|
2955
2985
|
* timeout six seconds later with nothing to show for it.
|
|
2956
2986
|
*/
|
|
2957
2987
|
static readonly awaitReady: <E, R>(probe: Effect.Effect<boolean, E, R>, options?: ReadinessOptions | undefined) => Effect.Effect<void, E | DetachedProcessError, R>;
|
|
2988
|
+
/**
|
|
2989
|
+
* A readiness probe over HTTP: `true` when a `GET` answers 2xx.
|
|
2990
|
+
*
|
|
2991
|
+
* @remarks
|
|
2992
|
+
* The probe {@link DetachedProcess.awaitReady} almost always wants: an HTTP
|
|
2993
|
+
* `GET` at a port the child is still binding, where connection-refused *is*
|
|
2994
|
+
* "not up yet". The consumers that hand-rolled the polling wrote exactly
|
|
2995
|
+
* this probe around it, and each had to discover the same subtlety — the
|
|
2996
|
+
* probe's error channel must stay empty, because `awaitReady` deliberately
|
|
2997
|
+
* propagates probe failures, and a refused connection is not a failure of
|
|
2998
|
+
* the probe.
|
|
2999
|
+
*
|
|
3000
|
+
* So this probe never fails: a refused connection, any other transport
|
|
3001
|
+
* error and a non-2xx answer all collapse to `false`, leaving
|
|
3002
|
+
* `awaitReady`'s own exhaustion as the only failure left. That is a
|
|
3003
|
+
* deliberate opt-out of the propagate-probe-failures posture, and it is
|
|
3004
|
+
* only sound because every failure an HTTP readiness check can see means
|
|
3005
|
+
* the same thing here. The cost is the documented one: a *misconfigured*
|
|
3006
|
+
* probe — a typo'd port, a wrong path — is indistinguishable from a child
|
|
3007
|
+
* that never came up, and reports as `notReady` only after the full budget.
|
|
3008
|
+
* A probe whose failures are meaningfully distinct should stay a
|
|
3009
|
+
* hand-written effect with those failures in `E`.
|
|
3010
|
+
*
|
|
3011
|
+
* The request goes through core's `HttpClient` — `Action.run` provides it
|
|
3012
|
+
* ambiently — rather than a bare `fetch`, per the package rule that
|
|
3013
|
+
* everything that *can* go through a core contract does.
|
|
3014
|
+
*/
|
|
3015
|
+
static readonly httpProbe: (url: string | URL) => Effect.Effect<boolean, never, HttpClient.HttpClient>;
|
|
2958
3016
|
/**
|
|
2959
3017
|
* Signal a pid that came back across the phase boundary.
|
|
2960
3018
|
*
|
|
@@ -2976,6 +3034,27 @@ declare class DetachedProcess {
|
|
|
2976
3034
|
* finding its child already dead is the normal ending, not a failure.
|
|
2977
3035
|
*/
|
|
2978
3036
|
static readonly reap: (pid: number, signal?: NodeJS.Signals | undefined) => Effect.Effect<boolean, DetachedProcessError, never>;
|
|
3037
|
+
/**
|
|
3038
|
+
* The real operations: the production default for a
|
|
3039
|
+
* {@link DetachedProcessOps} parameter.
|
|
3040
|
+
*
|
|
3041
|
+
* @remarks
|
|
3042
|
+
* The members ARE the statics — same references, not wrappers — so taking
|
|
3043
|
+
* the seam costs a consumer nothing over calling the statics directly.
|
|
3044
|
+
*/
|
|
3045
|
+
static readonly ops: DetachedProcessOps;
|
|
3046
|
+
/**
|
|
3047
|
+
* A test double. Unstubbed members die rather than silently succeeding.
|
|
3048
|
+
*
|
|
3049
|
+
* @remarks
|
|
3050
|
+
* The package's `makeTest` convention, on the module's one non-service.
|
|
3051
|
+
* Every member a test does not stub dies loudly **naming the member**,
|
|
3052
|
+
* because silence is worse here than anywhere else in the package: a spawn
|
|
3053
|
+
* that silently "succeeds" fakes a child that does not exist, and a reap
|
|
3054
|
+
* that silently succeeds asserts nothing about the one call whose
|
|
3055
|
+
* production form signals a real pid.
|
|
3056
|
+
*/
|
|
3057
|
+
static readonly makeTestOps: (overrides?: Partial<DetachedProcessOps>) => DetachedProcessOps;
|
|
2979
3058
|
}
|
|
2980
3059
|
//#endregion
|
|
2981
3060
|
//#region src/DryRun.d.ts
|
|
@@ -4134,10 +4213,18 @@ declare class PackageManagerInstaller extends PackageManagerInstaller_base {
|
|
|
4134
4213
|
* it makes declassification **explicit, auditable, and impossible to do
|
|
4135
4214
|
* quietly**.
|
|
4136
4215
|
*
|
|
4137
|
-
* The invariant: **masking and declassification
|
|
4138
|
-
* member
|
|
4139
|
-
*
|
|
4140
|
-
*
|
|
4216
|
+
* The invariant: **masking is the floor, and declassification implies it.**
|
|
4217
|
+
* Every member that touches plaintext routes through
|
|
4218
|
+
* `ActionOutputs.setSecret`, registering the value with the runner's log
|
|
4219
|
+
* filter (`::add-mask::`) *before* any plaintext is returned — and the one
|
|
4220
|
+
* member that returns nothing, {@link Secret.mask}, registers and stops there
|
|
4221
|
+
* — so under the normal runner layer a secret cannot reach a child's
|
|
4222
|
+
* environment or a state file without the runner already knowing to redact it
|
|
4223
|
+
* from logs. Two scoped exceptions: {@link Secret.adopt} is a `Config` that
|
|
4224
|
+
* re-wraps an inherited plaintext without involving `ActionOutputs` at all —
|
|
4225
|
+
* the parent masked that value before the handoff — and under
|
|
4226
|
+
* `ActionOutputs.layerDetached` the registration is a documented no-op,
|
|
4227
|
+
* because a mask emitted inside a detached worker would itself be the leak.
|
|
4141
4228
|
*
|
|
4142
4229
|
* `Redacted.value` appears nowhere else in this package, and a test asserts
|
|
4143
4230
|
* that structurally — a reintroduction elsewhere fails the suite rather than
|
|
@@ -4203,12 +4290,9 @@ declare class Secret {
|
|
|
4203
4290
|
* dividing line is where the value goes, not what it is for: a value
|
|
4204
4291
|
* crossing into a **child's** environment is {@link Secret.forChildEnv},
|
|
4205
4292
|
* one written to `GITHUB_STATE` or `GITHUB_OUTPUT` is
|
|
4206
|
-
* {@link Secret.forRunnerFile},
|
|
4207
|
-
*
|
|
4208
|
-
*
|
|
4209
|
-
* once through `ActionEnvironment`), so if a consumer chooses that bridge,
|
|
4210
|
-
* the mutation — and its restore discipline — lives in consumer code as
|
|
4211
|
-
* the consumer's own tradeoff, not as a pattern this member recommends.
|
|
4293
|
+
* {@link Secret.forRunnerFile}, one bound for the ambient environment is
|
|
4294
|
+
* {@link Secret.forProcessEnv}, and one that stays in this process — held
|
|
4295
|
+
* by this code, passed as an argument — is this member.
|
|
4212
4296
|
*
|
|
4213
4297
|
* It masks even though the value is never *written* anywhere, and that is
|
|
4214
4298
|
* deliberate rather than superstitious: a signing key that leaks does so
|
|
@@ -4231,6 +4315,70 @@ declare class Secret {
|
|
|
4231
4315
|
* {@link Secret.forChildEnv} under the real layer.
|
|
4232
4316
|
*/
|
|
4233
4317
|
static readonly forSigning: (secret: Redacted.Redacted<string>) => Effect.Effect<string, never, ActionOutputs>;
|
|
4318
|
+
/**
|
|
4319
|
+
* Declassify one secret for the caller to bridge into `process.env`.
|
|
4320
|
+
*
|
|
4321
|
+
* @remarks
|
|
4322
|
+
* The third-party-SDK case, under its own auditable name: an SDK that reads
|
|
4323
|
+
* only the ambient environment cannot take the plaintext as an argument, so
|
|
4324
|
+
* the value has to cross into `process.env` before the SDK looks. The
|
|
4325
|
+
* mechanism is identical to {@link Secret.forRunnerFile} — mask first, then
|
|
4326
|
+
* return plaintext — and the name is what earns the member: these names are
|
|
4327
|
+
* the audit vocabulary, and a search for this one finds every place a
|
|
4328
|
+
* secret enters the ambient environment without wading through signing
|
|
4329
|
+
* keys and runner files.
|
|
4330
|
+
*
|
|
4331
|
+
* The ruling on who mutates is unchanged by the name existing: **this
|
|
4332
|
+
* package never mutates `process.env`.** Reads are seeded once, at layer
|
|
4333
|
+
* construction, through `ActionEnvironment`, and nothing here writes back
|
|
4334
|
+
* — a write from inside the kit would be invisible to that seeding and to
|
|
4335
|
+
* every consumer's assumptions about when the environment is stable. This
|
|
4336
|
+
* member declassifies and masks; the assignment to `process.env`, and the
|
|
4337
|
+
* restore discipline that unwinds it when the bridged scope ends, live in
|
|
4338
|
+
* the **caller**, as the caller's own explicit tradeoff.
|
|
4339
|
+
*
|
|
4340
|
+
* The detached-worker inversion applies exactly as it does to
|
|
4341
|
+
* {@link Secret.forSigning}: the mask only works where the runner parses
|
|
4342
|
+
* stdout, so a worker that needs a bridged environment gets it from the
|
|
4343
|
+
* parent — masked before the spawn via {@link Secret.forChildEnv} — rather
|
|
4344
|
+
* than declassifying inside itself.
|
|
4345
|
+
*/
|
|
4346
|
+
static readonly forProcessEnv: (secret: Redacted.Redacted<string>) => Effect.Effect<string, never, ActionOutputs>;
|
|
4347
|
+
/**
|
|
4348
|
+
* Register a secret with the runner's log filter — and return nothing.
|
|
4349
|
+
*
|
|
4350
|
+
* @remarks
|
|
4351
|
+
* Masking without declassification: the value is registered with
|
|
4352
|
+
* `::add-mask::` through the same route as every other member, and the
|
|
4353
|
+
* success channel is `void`, so a caller cannot come away holding the raw
|
|
4354
|
+
* value at all. This is the register-only shape consumers previously spelled
|
|
4355
|
+
* as a {@link Secret.forSigning} whose result was discarded — a spelling
|
|
4356
|
+
* that lied to the audit vocabulary (a grep for signing found masking),
|
|
4357
|
+
* contradicted forSigning's own once-at-construction guidance, and needed a
|
|
4358
|
+
* comment to apologize for itself. The names are the vocabulary: a grep for
|
|
4359
|
+
* this member finds every register-only site, and a grep for `forSigning`
|
|
4360
|
+
* again finds only signing.
|
|
4361
|
+
*
|
|
4362
|
+
* The canonical caller masks every supplied credential input
|
|
4363
|
+
* unconditionally, before the logic that decides which of them will
|
|
4364
|
+
* actually be used — a secret the workflow supplied deserves redaction from
|
|
4365
|
+
* the log whether or not resolution reaches for it.
|
|
4366
|
+
*
|
|
4367
|
+
* One value per call is the blessed shape; there is deliberately no
|
|
4368
|
+
* set-taking form. {@link Secret.forChildEnv} takes a set because its
|
|
4369
|
+
* mask-everything-before-returning-anything ordering is load-bearing; a
|
|
4370
|
+
* plain mask returns nothing to order against, and a loop is fine.
|
|
4371
|
+
*
|
|
4372
|
+
* **In a detached worker, the mask this member emits is a leak.** The
|
|
4373
|
+
* `::add-mask::` command only masks when the runner parses this process's
|
|
4374
|
+
* stdout; a detached worker's stdout is a log file no runner parses, so
|
|
4375
|
+
* there the command masks nothing *and* spells the plaintext into the log
|
|
4376
|
+
* — a signing key shipped exactly that way for one round. A worker must
|
|
4377
|
+
* compose `ActionOutputs.layerDetached`, under which the mask is a
|
|
4378
|
+
* documented no-op; the masking itself is the **parent's** job, done
|
|
4379
|
+
* before the spawn via {@link Secret.forChildEnv} under the real layer.
|
|
4380
|
+
*/
|
|
4381
|
+
static readonly mask: (secret: Redacted.Redacted<string>) => Effect.Effect<void, never, ActionOutputs>;
|
|
4234
4382
|
/**
|
|
4235
4383
|
* The far side of a handoff: re-wrap a plaintext environment variable.
|
|
4236
4384
|
*
|
|
@@ -4242,5 +4390,5 @@ declare class Secret {
|
|
|
4242
4390
|
static readonly adopt: (name: string) => Config.Config<Redacted.Redacted<string>>;
|
|
4243
4391
|
}
|
|
4244
4392
|
//#endregion
|
|
4245
|
-
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, ChildEnv, 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, type PathPrependEnv, type PathPrependOptions, 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, type WithStepOptions, WorkflowCommand, describeCause, projectCheckState };
|
|
4393
|
+
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, ChildEnv, type ClientLayerOptions, type DataBlobTransfer, DetachedProcess, DetachedProcessError, type DetachedProcessOps, 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, type PathPrependEnv, type PathPrependOptions, 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, type WithStepOptions, WorkflowCommand, describeCause, projectCheckState };
|
|
4246
4394
|
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/github-actions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The GitHub Actions runtime: env, inputs, outputs, state, logging, cache, artifacts, blob storage, OIDC and the token bridge.",
|
|
6
6
|
"keywords": [
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@azure/storage-blob": "^12.33.0",
|
|
42
|
-
"@effected/github": "^0.4.
|
|
42
|
+
"@effected/github": "^0.4.2",
|
|
43
43
|
"@effected/glob": "^0.3.0",
|
|
44
44
|
"@effected/markdown": "^0.5.1",
|
|
45
45
|
"@effected/npm": "^0.9.0",
|
|
46
|
-
"@effected/sbom": "^0.3.
|
|
46
|
+
"@effected/sbom": "^0.3.1",
|
|
47
47
|
"@effected/templates": "^0.2.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|