@effected/github-actions 0.23.1 → 0.25.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/Action.js +1 -2
- package/ActionCache.js +1 -2
- package/ActionEnvironment.js +7 -10
- package/ActionInput.js +15 -19
- package/ActionLogger.js +1 -2
- package/ActionOutputs.js +1 -2
- package/ActionState.js +1 -2
- package/ActionsIdentityToken.js +6 -7
- package/ActionsProvenance.js +18 -17
- package/Artifact.js +4 -4
- package/BlobEnvelope.js +1 -2
- package/BlobStore.githubCache.js +1 -2
- package/BlobStore.js +59 -30
- package/BlobTransfer.js +1 -2
- package/CacheKey.js +1 -2
- package/CheckDocument.js +1 -2
- package/CheckState.js +1 -2
- package/ChildEnv.js +1 -2
- package/DetachedProcess.js +1 -2
- package/DryRun.js +1 -2
- package/GitHubMarkdown.js +1 -2
- package/GitHubToken.js +1 -2
- package/ManagedDocument.js +1 -2
- package/OidcTokenIssuer.js +24 -54
- package/PackageManagerInstaller.js +20 -24
- package/README.md +3 -3
- package/Secret.js +11 -8
- package/ToolInstaller.js +4 -4
- package/index.d.ts +51 -125
- package/index.js +2 -2
- package/internal/actionsResults.js +6 -6
- package/internal/archiveCommands.js +1 -2
- package/internal/cacheService.js +1 -2
- package/internal/digest.js +1 -2
- package/internal/fsProbe.js +1 -2
- package/internal/json.js +11 -0
- package/internal/pnpmExe.js +1 -2
- package/internal/runner.js +22 -3
- package/internal/runnerFile.js +1 -2
- package/internal/shim.js +1 -2
- package/internal/spawn.js +1 -2
- package/internal/twirp.js +1 -2
- package/internal/unstubbed.js +1 -2
- package/package.json +13 -10
- package/internal/jwt.js +0 -42
- package/internal/sigv4.js +0 -115
package/OidcTokenIssuer.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { ActionEnvironment } from "./ActionEnvironment.js";
|
|
2
2
|
import { unstubbed } from "./internal/unstubbed.js";
|
|
3
|
-
import { payloadOf, unsignedJwt } from "./internal/jwt.js";
|
|
4
3
|
import { Context, Effect, Layer, Redacted, Schema } from "effect";
|
|
5
4
|
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
5
|
+
import { Oidc } from "@effected/oidc";
|
|
6
|
+
import { ActionsOidcClaimsFromWire } from "@effected/github";
|
|
7
|
+
import * as Base64Url from "effect/encoding/Base64Url";
|
|
6
8
|
import * as HttpClientResponse from "effect/http/HttpClientResponse";
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
// src/OidcTokenIssuer.ts
|
|
9
11
|
/**
|
|
10
12
|
* Raised when an OIDC token cannot be issued or read.
|
|
11
13
|
*
|
|
@@ -45,44 +47,6 @@ var OidcTokenError = class extends Schema.TaggedError()("OidcTokenError", {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
};
|
|
48
|
-
/**
|
|
49
|
-
* The claims a GitHub Actions OIDC token carries about the workflow that ran.
|
|
50
|
-
*
|
|
51
|
-
* @remarks
|
|
52
|
-
* The field names are GitHub's own JWT claim names, kept verbatim rather than
|
|
53
|
-
* translated to the kit's casing. They are an **external vocabulary** — the
|
|
54
|
-
* same strings that appear in a SLSA provenance predicate and in every
|
|
55
|
-
* published example of an Actions OIDC policy — and renaming them would put a
|
|
56
|
-
* translation layer between a consumer and the specification they are reading.
|
|
57
|
-
*
|
|
58
|
-
* @public
|
|
59
|
-
*/
|
|
60
|
-
var OidcClaims = class extends Schema.Class("OidcClaims")({
|
|
61
|
-
/** The issuer, e.g. `https://token.actions.githubusercontent.com`. */
|
|
62
|
-
iss: Schema.String,
|
|
63
|
-
/** The full ref the workflow ran on. */
|
|
64
|
-
ref: Schema.String,
|
|
65
|
-
/** The commit. */
|
|
66
|
-
sha: Schema.String,
|
|
67
|
-
/** `owner/repo`. */
|
|
68
|
-
repository: Schema.String,
|
|
69
|
-
/** The event that triggered the run. */
|
|
70
|
-
event_name: Schema.String,
|
|
71
|
-
/** The reusable-workflow ref, which is what a verifier pins against. */
|
|
72
|
-
job_workflow_ref: Schema.String,
|
|
73
|
-
/** The calling workflow's ref. */
|
|
74
|
-
workflow_ref: Schema.String,
|
|
75
|
-
/** The numeric repository id, as a string. */
|
|
76
|
-
repository_id: Schema.String,
|
|
77
|
-
/** The numeric owner id, as a string. */
|
|
78
|
-
repository_owner_id: Schema.String,
|
|
79
|
-
/** `github-hosted` or `self-hosted`. */
|
|
80
|
-
runner_environment: Schema.String,
|
|
81
|
-
/** The run id, as a string. */
|
|
82
|
-
run_id: Schema.String,
|
|
83
|
-
/** The run attempt, as a string. */
|
|
84
|
-
run_attempt: Schema.String
|
|
85
|
-
}) {};
|
|
86
50
|
/** The token envelope the runner's token service answers with. */
|
|
87
51
|
const TokenEnvelope = Schema.Struct({
|
|
88
52
|
value: Schema.String,
|
|
@@ -97,16 +61,23 @@ const REQUEST_URL = "ACTIONS_ID_TOKEN_REQUEST_URL";
|
|
|
97
61
|
* The absence of verification is deliberate and is documented on
|
|
98
62
|
* {@link OidcTokenIssuerShape.claims}; do not "fix" it here.
|
|
99
63
|
*/
|
|
100
|
-
const readClaims = (token) =>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
64
|
+
const readClaims = (token) => Oidc.decodeUnverified(token, ActionsOidcClaimsFromWire).pipe(Effect.mapError((error) => error.reason === "claims" ? new OidcTokenError({
|
|
65
|
+
reason: "missingClaims",
|
|
66
|
+
cause: error.cause ?? error
|
|
67
|
+
}) : new OidcTokenError({
|
|
68
|
+
reason: "malformedToken",
|
|
69
|
+
detail: error.detail,
|
|
70
|
+
cause: error
|
|
71
|
+
})));
|
|
72
|
+
/**
|
|
73
|
+
* An **unsigned** JWT: `header.payload.unsigned`, each JSON segment
|
|
74
|
+
* base64url-encoded. For test doubles only; the signature segment is a
|
|
75
|
+
* placeholder, so the token fails any verifier.
|
|
76
|
+
*/
|
|
77
|
+
const unsignedJwt = (header, payload) => {
|
|
78
|
+
const segment = (value) => Base64Url.encode(JSON.stringify(value));
|
|
79
|
+
return `${segment(header)}.${segment(payload)}.unsigned`;
|
|
80
|
+
};
|
|
110
81
|
const make = Effect.gen(function* () {
|
|
111
82
|
const env = yield* ActionEnvironment;
|
|
112
83
|
const http = yield* HttpClient.HttpClient;
|
|
@@ -166,7 +137,7 @@ const dies = unstubbed("OidcTokenIssuer.makeTest");
|
|
|
166
137
|
* const program = Effect.gen(function* () {
|
|
167
138
|
* const issuer = yield* OidcTokenIssuer;
|
|
168
139
|
* const claims = yield* issuer.claims("sigstore");
|
|
169
|
-
* return claims.
|
|
140
|
+
* return claims.jobWorkflowRef;
|
|
170
141
|
* });
|
|
171
142
|
* ```
|
|
172
143
|
*
|
|
@@ -193,7 +164,7 @@ var OidcTokenIssuer = class OidcTokenIssuer extends Context.Service()("@effected
|
|
|
193
164
|
static unsignedTokenFor = (claims) => Redacted.make(unsignedJwt({
|
|
194
165
|
alg: "RS256",
|
|
195
166
|
typ: "JWT"
|
|
196
|
-
}, Schema.
|
|
167
|
+
}, Schema.encodeSync(ActionsOidcClaimsFromWire)(claims)));
|
|
197
168
|
/** A test double. Unstubbed members die rather than answering with a non-token. */
|
|
198
169
|
static makeTest = (overrides = {}) => ({
|
|
199
170
|
token: () => dies("token"),
|
|
@@ -218,5 +189,4 @@ var OidcTokenIssuer = class OidcTokenIssuer extends Context.Service()("@effected
|
|
|
218
189
|
});
|
|
219
190
|
};
|
|
220
191
|
|
|
221
|
-
|
|
222
|
-
export { OidcClaims, OidcTokenError, OidcTokenIssuer };
|
|
192
|
+
export { OidcTokenError, OidcTokenIssuer };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ActionEnvironment } from "./ActionEnvironment.js";
|
|
2
2
|
import { unstubbed } from "./internal/unstubbed.js";
|
|
3
3
|
import { digestFileHex } from "./internal/digest.js";
|
|
4
|
-
import { isWindowsRunner } from "./internal/runner.js";
|
|
4
|
+
import { isWindowsRunner, runnerArch } from "./internal/runner.js";
|
|
5
5
|
import { typeAt } from "./internal/fsProbe.js";
|
|
6
6
|
import { PNPM_EXE_PREFIX, detectMusl, isNodeScript, pnpmExeTarget, strongestSri } from "./internal/pnpmExe.js";
|
|
7
7
|
import { cmdShim, gitBashShim, posixShim } from "./internal/shim.js";
|
|
@@ -10,7 +10,18 @@ import { Context, Effect, FileSystem, Layer, Option, Path, Schema } from "effect
|
|
|
10
10
|
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
11
11
|
import { CorepackIntegrityHash, DEFAULT_REGISTRY, PackageManagerPin, PackageManagerPinName } from "@effected/npm";
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// src/PackageManagerInstaller.ts
|
|
14
|
+
/**
|
|
15
|
+
* The two `package.json` members the installer reads, as JSON text decoded to
|
|
16
|
+
* an object. Both stay `unknown`: they are attacker-supplied, and their own
|
|
17
|
+
* normalizers decide what survives.
|
|
18
|
+
*/
|
|
19
|
+
const decodeManifest = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Struct({
|
|
20
|
+
bin: Schema.optionalKey(Schema.Unknown),
|
|
21
|
+
optionalDependencies: Schema.optionalKey(Schema.Unknown)
|
|
22
|
+
})));
|
|
23
|
+
/** A packument's `dist.integrity`, left `unknown` for `expectedFromSri` to judge. */
|
|
24
|
+
const decodePackument = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Struct({ dist: Schema.optionalKey(Schema.Struct({ integrity: Schema.optionalKey(Schema.Unknown) })) })));
|
|
14
25
|
/**
|
|
15
26
|
* Raised when a package manager cannot be provisioned on the runner.
|
|
16
27
|
*
|
|
@@ -202,11 +213,6 @@ const normalizeBins = (bin, fallbackName) => {
|
|
|
202
213
|
}
|
|
203
214
|
return Object.keys(entries).length === 0 ? Option.none() : Option.some(entries);
|
|
204
215
|
};
|
|
205
|
-
/** Map a `RUNNER_ARCH` value (`X64`, `ARM64`) onto the Node arch spelling. */
|
|
206
|
-
const archFromRunner = (runnerArch) => {
|
|
207
|
-
const lowered = runnerArch.toLowerCase();
|
|
208
|
-
return lowered === "x64" || lowered === "arm64" ? lowered : runnerArch;
|
|
209
|
-
};
|
|
210
216
|
const make = Effect.gen(function* () {
|
|
211
217
|
const env = yield* ActionEnvironment;
|
|
212
218
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -214,10 +220,7 @@ const make = Effect.gen(function* () {
|
|
|
214
220
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
215
221
|
const installer = yield* ToolInstaller;
|
|
216
222
|
const runnerOs = yield* Effect.map(env.getOptional("RUNNER_OS"), (found) => Option.getOrElse(found, () => ""));
|
|
217
|
-
const arch = yield*
|
|
218
|
-
onNone: () => process.arch,
|
|
219
|
-
onSome: archFromRunner
|
|
220
|
-
}));
|
|
223
|
+
const arch = yield* runnerArch(env);
|
|
221
224
|
const windows = yield* isWindowsRunner(env);
|
|
222
225
|
const musl = detectMusl();
|
|
223
226
|
const bunBinaryName = windows ? "bun.exe" : "bun";
|
|
@@ -305,14 +308,11 @@ const make = Effect.gen(function* () {
|
|
|
305
308
|
subject: "no package.json",
|
|
306
309
|
cause
|
|
307
310
|
})));
|
|
308
|
-
const manifest = yield* Effect.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
cause
|
|
314
|
-
})
|
|
315
|
-
});
|
|
311
|
+
const manifest = yield* decodeManifest(raw).pipe(Effect.mapError((cause) => errorFor(pin)({
|
|
312
|
+
reason: "layoutUnexpected",
|
|
313
|
+
subject: "unparseable package.json",
|
|
314
|
+
cause
|
|
315
|
+
})));
|
|
316
316
|
const bins = Option.getOrUndefined(normalizeBins(manifest.bin, pin.name));
|
|
317
317
|
if (bins === void 0) return yield* Effect.fail(errorFor(pin)({
|
|
318
318
|
reason: "layoutUnexpected",
|
|
@@ -458,10 +458,7 @@ const make = Effect.gen(function* () {
|
|
|
458
458
|
subject: packumentUrl,
|
|
459
459
|
cause
|
|
460
460
|
});
|
|
461
|
-
const packument = yield* fs.readFileString(packumentFile).pipe(Effect.mapError(unverifiable), Effect.flatMap((raw) => Effect.
|
|
462
|
-
try: () => JSON.parse(raw),
|
|
463
|
-
catch: unverifiable
|
|
464
|
-
})));
|
|
461
|
+
const packument = yield* fs.readFileString(packumentFile).pipe(Effect.mapError(unverifiable), Effect.flatMap((raw) => decodePackument(raw).pipe(Effect.mapError(unverifiable))));
|
|
465
462
|
return yield* expectedFromSri(pin, packument.dist?.integrity, packumentUrl);
|
|
466
463
|
});
|
|
467
464
|
/**
|
|
@@ -734,5 +731,4 @@ var PackageManagerInstaller = class PackageManagerInstaller extends Context.Serv
|
|
|
734
731
|
static layerTest = (overrides = {}) => Layer.succeed(PackageManagerInstaller, PackageManagerInstaller.makeTest(overrides));
|
|
735
732
|
};
|
|
736
733
|
|
|
737
|
-
//#endregion
|
|
738
734
|
export { AmbientPackageManager, CachedPackageManager, InstalledPackageManager, PackageManagerInstaller, PackageManagerInstallerError };
|
package/README.md
CHANGED
|
@@ -155,7 +155,7 @@ Two decisions make that safe for any segment value, with no prefix reasoning at
|
|
|
155
155
|
|
|
156
156
|
`CacheKey.hashFiles` is byte-compatible with `@actions/glob`'s `hashFiles`, and `OidcTokenIssuer.claims` decodes the runner's OIDC token for a SLSA provenance predicate without verifying its signature — the token arrived over TLS from the runner's own token service, so the transport is the trust boundary and nothing here branches on the claims for authorization.
|
|
157
157
|
|
|
158
|
-
`ActionsProvenance.capture` turns those claims into an `@effected/sbom` `SlsaProvenance` predicate, and `ActionsIdentityToken.layer` serves
|
|
158
|
+
`ActionsProvenance.capture` turns those claims into an `@effected/sbom` `SlsaProvenance` predicate, and `ActionsIdentityToken.layer` serves `@effected/oidc`'s narrow `IdentityToken` contract, which that package's signer consumes, from the same issuer — the inversion that keeps the Actions runtime out of every consumer that only wants to emit an SBOM:
|
|
159
159
|
|
|
160
160
|
```ts
|
|
161
161
|
import { ActionsIdentityToken, ActionsProvenance, OidcTokenIssuer } from "@effected/github-actions";
|
|
@@ -303,13 +303,13 @@ const TestOutputs = ActionOutputs.layerTest({
|
|
|
303
303
|
- `ActionCache` / `Artifact` — the runner's cache and artifact protocols over Twirp v2, excluded from the default runtime so a consumer that skips them never links Azure.
|
|
304
304
|
- `CacheKey` — cache keys and their restore-key ladder, `withNamespace(segment)` for a bust token or a shared-runner prefix, plus `hashFiles`/`matchingFiles`/`hashMatching` byte-compatible with `@actions/glob`.
|
|
305
305
|
- `OidcTokenIssuer` — the runner's OIDC token service, with unverified claim decoding for provenance predicates.
|
|
306
|
-
- `ActionsIdentityToken` / `ActionsProvenance` — the `@effected/sbom` seam: `layer` serves
|
|
306
|
+
- `ActionsIdentityToken` / `ActionsProvenance` — the `@effected/sbom` seam: `layer` serves `@effected/oidc`'s `IdentityToken` contract from the runner's issuer, and `capture` builds a `SlsaProvenance` predicate out of the runner's OIDC claims.
|
|
307
307
|
- `GitHubMarkdown` — the escaping-safe writer for GitHub surfaces: `table`, `tableFor(schema)`, `heading`, `link`, `code`, `codeBlock`, `list`, `details` and `raw`, and the package's only importer of `@effected/markdown`.
|
|
308
308
|
- `ManagedDocument` — marker-delimited named regions in text a human also edits, for a sticky comment or a managed pull request description; content outside the regions survives byte-for-byte, and each region can carry marker metadata that round-trips through `entry(key)` without ever addressing the region.
|
|
309
309
|
- `CheckState` / `projectCheckState` — the kit's check vocabulary, wider than GitHub's conclusions, and its projection onto the check-run wire form.
|
|
310
310
|
- `CheckDocument` — the debounced check-to-document reconciler: report states as they change, and one write lands carrying the final one. `stamp` plus a readable sink lets two runs share one document, dropping a strictly older run's pass instead of clobbering the newer one; `flush` reports `written`, `unchanged` or `stale`.
|
|
311
311
|
- `ToolInstaller` — download, extract and cache a toolchain in the runner's tool cache, with no `@actions/tool-cache` dependency; `ToolInstallerError` always names its `subject`.
|
|
312
|
-
- `BlobStore` — durable `get`/`put`/`has` over `StoredBlob<A>` values (bytes plus caller-schema metadata; the type was named `Blob<A>` before, which collided with the global), backed by `layerS3` (SigV4-signed
|
|
312
|
+
- `BlobStore` — durable `get`/`put`/`has` over `StoredBlob<A>` values (bytes plus caller-schema metadata; the type was named `Blob<A>` before, which collided with the global), backed by `layerS3` (SigV4-signed through `@effected/crypto`, so it needs core `Crypto` in `R`; no `@aws-sdk/*` dependency) or `GitHubCacheBlobStore.layer` (the runner's own cache); `layerMemory` runs the real envelope framing for tests.
|
|
313
313
|
|
|
314
314
|
## License
|
|
315
315
|
|
package/Secret.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ActionOutputs } from "./ActionOutputs.js";
|
|
2
2
|
import { Config, Effect, Redacted } from "effect";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// src/Secret.ts
|
|
5
5
|
/**
|
|
6
6
|
* The declassification seam: the **only** place in this package where a
|
|
7
7
|
* `Redacted` value becomes a plain string.
|
|
@@ -94,9 +94,13 @@ var Secret = class Secret {
|
|
|
94
94
|
* Declassify one secret for an in-process use that needs the raw bytes.
|
|
95
95
|
*
|
|
96
96
|
* @remarks
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
97
|
+
* For a consumer that must hand the raw key to code taking a plain string,
|
|
98
|
+
* such as a third-party signing library. A signer that takes a `Redacted`
|
|
99
|
+
* needs no declassification at all: `@effected/crypto`'s `SigV4` and `Hmac`
|
|
100
|
+
* unwrap the key only where they compute, so a caller of those (this
|
|
101
|
+
* package's own S3 `BlobStore` is one) registers the value with
|
|
102
|
+
* {@link Secret.mask} and passes the `Redacted` on. No module in this
|
|
103
|
+
* package calls this member any more.
|
|
100
104
|
*
|
|
101
105
|
* Signing named the member; it does not gate it. **Any in-process use that
|
|
102
106
|
* needs the raw value without writing it to a runner file is this
|
|
@@ -121,9 +125,9 @@ var Secret = class Secret {
|
|
|
121
125
|
* `::add-mask::` command only masks when the runner parses this process's
|
|
122
126
|
* stdout; a detached worker's stdout is a log file no runner parses, so
|
|
123
127
|
* there the command masks nothing *and* spells the plaintext into the log.
|
|
124
|
-
* A worker that signs
|
|
125
|
-
*
|
|
126
|
-
*
|
|
128
|
+
* A worker that signs must compose `ActionOutputs.layerDetached`, under
|
|
129
|
+
* which this member still returns the raw key but the mask is a
|
|
130
|
+
* documented no-op; the masking
|
|
127
131
|
* itself is the **parent's** job, done before the spawn via
|
|
128
132
|
* {@link Secret.forChildEnv} under the real layer.
|
|
129
133
|
*/
|
|
@@ -198,5 +202,4 @@ var Secret = class Secret {
|
|
|
198
202
|
static adopt = (name) => Config.Redacted(name);
|
|
199
203
|
};
|
|
200
204
|
|
|
201
|
-
//#endregion
|
|
202
205
|
export { Secret };
|
package/ToolInstaller.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ActionEnvironment } from "./ActionEnvironment.js";
|
|
2
2
|
import { unstubbed } from "./internal/unstubbed.js";
|
|
3
|
-
import { isWindowsRunner, toolCacheRoot } from "./internal/runner.js";
|
|
3
|
+
import { isWindowsRunner, runnerArch, toolCacheRoot } from "./internal/runner.js";
|
|
4
4
|
import { spawnOnce } from "./internal/spawn.js";
|
|
5
5
|
import { tarExtractCommand, unzipCommand } from "./internal/archiveCommands.js";
|
|
6
6
|
import { isErrno, typeAt } from "./internal/fsProbe.js";
|
|
@@ -8,7 +8,7 @@ import { Context, Effect, FileSystem, Layer, Option, Path, Schedule, Schema, Str
|
|
|
8
8
|
import { HttpClient } from "effect/http";
|
|
9
9
|
import { ChildProcessSpawner } from "effect/process";
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// src/ToolInstaller.ts
|
|
12
12
|
/**
|
|
13
13
|
* Raised when a tool cannot be downloaded, extracted or cached.
|
|
14
14
|
*
|
|
@@ -68,11 +68,12 @@ const make = Effect.gen(function* () {
|
|
|
68
68
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
69
69
|
const root = yield* toolCacheRoot(env, path);
|
|
70
70
|
const windows = yield* isWindowsRunner(env);
|
|
71
|
+
const arch = yield* runnerArch(env);
|
|
71
72
|
const cachePath = (tool, version) => ToolInstaller.cachePath({
|
|
72
73
|
root,
|
|
73
74
|
tool,
|
|
74
75
|
version,
|
|
75
|
-
arch
|
|
76
|
+
arch
|
|
76
77
|
});
|
|
77
78
|
const destinationFor = (options) => {
|
|
78
79
|
const requested = options?.destination;
|
|
@@ -376,5 +377,4 @@ var ToolInstaller = class ToolInstaller extends Context.Service()("@effected/git
|
|
|
376
377
|
static layerTest = (overrides = {}) => Layer.succeed(ToolInstaller, ToolInstaller.makeTest(overrides));
|
|
377
378
|
};
|
|
378
379
|
|
|
379
|
-
//#endregion
|
|
380
380
|
export { ToolInstaller, ToolInstallerError };
|