@effected/github-actions 0.19.1 → 0.20.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 +13 -12
- package/ActionCache.js +1 -2
- package/ActionEnvironment.js +21 -6
- package/ActionInput.js +37 -55
- package/ActionLogger.js +27 -10
- package/ActionOutputs.js +27 -8
- package/ActionState.js +31 -2
- package/ActionsIdentityToken.js +2 -1
- package/ActionsProvenance.js +2 -1
- package/Artifact.js +4 -8
- package/BlobEnvelope.js +41 -15
- package/BlobStore.githubCache.js +11 -4
- package/BlobStore.js +8 -7
- package/CacheKey.js +6 -7
- package/ChildEnv.js +2 -5
- package/DetachedProcess.js +8 -11
- package/GitHubMarkdown.js +8 -10
- package/GitHubToken.js +12 -14
- package/ManagedDocument.js +3 -4
- package/OidcTokenIssuer.js +11 -7
- package/PackageManagerInstaller.js +10 -3
- package/README.md +4 -4
- package/Secret.js +10 -17
- package/ToolInstaller.js +12 -6
- package/index.d.ts +334 -293
- package/index.js +1 -1
- package/internal/actionsResults.js +0 -12
- package/internal/archiveCommands.js +0 -66
- package/internal/cacheService.js +0 -16
- package/internal/digest.js +0 -13
- package/internal/jwt.js +0 -17
- package/internal/pnpmExe.js +0 -15
- package/internal/runnerFile.js +0 -16
- package/internal/sigv4.js +1 -4
- package/internal/unstubbed.js +0 -12
- package/package.json +12 -11
- package/WorkflowCommand.js +0 -100
package/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ActionEnvironment, ActionEnvironmentError, GitHubContext, RunnerContext } from "./ActionEnvironment.js";
|
|
2
2
|
import { ActionInput } from "./ActionInput.js";
|
|
3
|
-
import { WorkflowCommand } from "./WorkflowCommand.js";
|
|
4
3
|
import { ActionLogger } from "./ActionLogger.js";
|
|
5
4
|
import { ActionOutputs, DetachedOutputError, InvalidOutputNameError, OutputEncodeError, RecordedOutput, RunnerFileUnavailableError, RunnerFileWriteError } from "./ActionOutputs.js";
|
|
6
5
|
import { ActionState, ActionStateError } from "./ActionState.js";
|
|
@@ -26,5 +25,6 @@ import { GitHubMarkdown } from "./GitHubMarkdown.js";
|
|
|
26
25
|
import { GitHubToken, GitHubTokenError } from "./GitHubToken.js";
|
|
27
26
|
import { ToolInstaller, ToolInstallerError } from "./ToolInstaller.js";
|
|
28
27
|
import { AmbientPackageManager, CachedPackageManager, InstalledPackageManager, PackageManagerInstaller, PackageManagerInstallerError } from "./PackageManagerInstaller.js";
|
|
28
|
+
import { WorkflowCommand } from "@effected/github-commands";
|
|
29
29
|
|
|
30
30
|
export { Action, ActionCache, ActionCacheError, ActionEnvironment, ActionEnvironmentError, ActionInput, ActionLogger, ActionOutputs, ActionRuntime, ActionState, ActionStateError, ActionsIdentityToken, ActionsProvenance, AmbientPackageManager, Artifact, ArtifactError, BlobEnvelope, BlobMetadataDecodeError, BlobMetadataEncodeError, BlobStore, BlobStoreError, BlobTransferError, CacheKey, CacheKeyBadPatternError, CacheKeyReadError, CachedPackageManager, CheckDocument, CheckDocumentError, CheckDocumentStamp, CheckReport, CheckState, ChildEnv, DetachedLogUnavailableError, DetachedNotReadyError, DetachedOutputError, DetachedProcess, DetachedSignalFailedError, DetachedSpawnFailedError, DryRun, GitHubCacheBlobStore, GitHubContext, GitHubMarkdown, GitHubToken, GitHubTokenError, InstalledPackageManager, InvalidOutputNameError, InvalidPidError, ManagedDocument, ManagedDocumentError, NotABlobEnvelopeError, OidcClaims, OidcTokenError, OidcTokenIssuer, OutputEncodeError, PackageManagerInstaller, PackageManagerInstallerError, ProcessId, RecordedOutput, RunnerContext, RunnerFileUnavailableError, RunnerFileWriteError, Secret, ToolInstaller, ToolInstallerError, TruncatedBlobEnvelopeError, UnsupportedBlobEnvelopeVersionError, WorkflowCommand, describeCause, projectCheckState };
|
|
@@ -2,18 +2,6 @@ import { payloadOf } from "./jwt.js";
|
|
|
2
2
|
import { Effect, Option, Redacted, Result } from "effect";
|
|
3
3
|
|
|
4
4
|
//#region src/internal/actionsResults.ts
|
|
5
|
-
/**
|
|
6
|
-
* Reading the runner's *results backend* coordinates — the two variables the
|
|
7
|
-
* cache, artifact and blob-store protocols all speak through.
|
|
8
|
-
*
|
|
9
|
-
* @remarks
|
|
10
|
-
* Shared by `ActionCache`, `Artifact` and `BlobStore.githubCache`, and
|
|
11
|
-
* deliberately free of `@azure/storage-blob`: an internal helper is exactly how
|
|
12
|
-
* a heavy import leaks into a light module's graph, so the one piece those
|
|
13
|
-
* three modules share is the piece with no dependencies.
|
|
14
|
-
*
|
|
15
|
-
* @internal
|
|
16
|
-
*/
|
|
17
5
|
/** The variable naming the backend. Absent outside a `uses:` step. */
|
|
18
6
|
const RESULTS_URL = "ACTIONS_RESULTS_URL";
|
|
19
7
|
/** The variable holding the run-scoped credential. Absent outside a `uses:` step. */
|
|
@@ -1,72 +1,6 @@
|
|
|
1
1
|
import { ChildProcess } from "effect/process";
|
|
2
2
|
|
|
3
3
|
//#region src/internal/archiveCommands.ts
|
|
4
|
-
/**
|
|
5
|
-
* The archiver command lines this package spawns, spelled once.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* `Artifact` (pack and unpack) and `ToolInstaller` (unpack only) each spawn
|
|
9
|
-
* the same three shapes — a zip, an unzip, a tar extraction — and each has a
|
|
10
|
-
* Windows half that no test on a POSIX host can execute. Two hand-written
|
|
11
|
-
* copies drifted: `ToolInstaller.extractZip` learned the overwrite overload
|
|
12
|
-
* and the stderr capture below, `Artifact.download` did not, and a download
|
|
13
|
-
* into a non-empty directory kept failing on Windows with an EMPTY stderr.
|
|
14
|
-
* This module is the one spelling; the tests pin its text verbatim, which
|
|
15
|
-
* is the only proof the Windows halves get.
|
|
16
|
-
*
|
|
17
|
-
* **The pwsh unzip is deliberately belt-and-braces.** The THREE-argument
|
|
18
|
-
* `ExtractToDirectory(src, dest, $true)` overload overwrites existing files —
|
|
19
|
-
* the two-argument one refuses, which is what turned a repeated extraction
|
|
20
|
-
* into a hard failure — and the `$ErrorActionPreference = 'Stop'` plus
|
|
21
|
-
* try/catch writes the actual .NET exception text plainly to stderr and
|
|
22
|
-
* exits 1. pwsh's own error rendering does not reliably reach a captured
|
|
23
|
-
* stream, and an empty complaint costs a source dive to even hypothesize
|
|
24
|
-
* about.
|
|
25
|
-
*
|
|
26
|
-
* **The pwsh zip drives .NET's `ZipFile` directly, not `Compress-Archive`.**
|
|
27
|
-
* Microsoft's own reference for `Compress-Archive -Path` states both faults:
|
|
28
|
-
* handed individual file paths it stores every entry under its bare file
|
|
29
|
-
* name — directory structure is kept only when `-Path` names a directory —
|
|
30
|
-
* so `dir\b.txt` landed as `b.txt`, same-named files in different
|
|
31
|
-
* directories collided, and a Windows artifact was structurally different
|
|
32
|
-
* from the POSIX `zip -qr` one; and `-Path` expands wildcards, so a literal
|
|
33
|
-
* `report[1].txt` matched nothing and failed the upload. Stating every entry
|
|
34
|
-
* name explicitly through `CreateEntryFromFile(zip, source, entryName)`
|
|
35
|
-
* removes both: the source is `Path.Combine(root, rel)`, taken literally,
|
|
36
|
-
* and the entry name is `rel` with `\` turned to `/`, which is what `zip`
|
|
37
|
-
* records on POSIX.
|
|
38
|
-
*
|
|
39
|
-
* **The Windows script is constant-size: the file list travels in a manifest
|
|
40
|
-
* file, never in `-Command`.** `CreateProcessW` caps the whole command line
|
|
41
|
-
* at 32,767 characters, and inlining one `CreateEntryFromFile` statement per
|
|
42
|
-
* file (~214 characters each) hit that ceiling at roughly 150 files with
|
|
43
|
-
* `ENAMETOOLONG` — fewer than the `Compress-Archive` form it replaced carried.
|
|
44
|
-
* The script now reads {@link zipManifest}'s output (one relative path per
|
|
45
|
-
* line) with `File.ReadAllLines` and loops. **The two branches deliberately
|
|
46
|
-
* differ in mechanism**: POSIX `zip` takes the files as argv, whose ceiling
|
|
47
|
-
* (`ARG_MAX`, megabytes on the hosted runners) is far larger, so the only
|
|
48
|
-
* file-count ceiling left is that POSIX argv limit. The manifest is written by
|
|
49
|
-
* the caller (`Artifact.zip`) as UTF-8 WITHOUT a BOM, which is what
|
|
50
|
-
* `ReadAllLines` reads by default. Its BOM detection would consume a BOM
|
|
51
|
-
* rather than leak it into the first path (measured), so this is a
|
|
52
|
-
* plain-bytes preference, not a correctness guard.
|
|
53
|
-
*
|
|
54
|
-
* **Every `CreateEntryFromFile` is assigned to `$null`.** Unassigned, pwsh
|
|
55
|
-
* writes the returned `ZipArchiveEntry` to stdout — roughly 430 bytes of
|
|
56
|
-
* formatted object per file — and `internal/spawn.ts` interleaves stdout with
|
|
57
|
-
* stderr into the captured output, which is what `ArtifactError.stderr`
|
|
58
|
-
* carries. Left in, that chatter buried the one line that matters, the .NET
|
|
59
|
-
* exception, under kilobytes of entry listings.
|
|
60
|
-
*
|
|
61
|
-
* Every path handed to PowerShell is single-quoted, and a single quote inside
|
|
62
|
-
* one is doubled — the only escape a single-quoted PowerShell literal has.
|
|
63
|
-
*
|
|
64
|
-
* Pure: no filesystem, no environment, no spawn. `internal/spawn.ts` is the
|
|
65
|
-
* execution half. Reaches `effect/process` alone, so it is safe for
|
|
66
|
-
* any module to import (`__test__/reachability.test.ts`).
|
|
67
|
-
*
|
|
68
|
-
* @internal
|
|
69
|
-
*/
|
|
70
4
|
/** A PowerShell single-quoted literal: `'` becomes `''`, nothing else is special. */
|
|
71
5
|
const pwshLiteral = (value) => `'${value.replaceAll("'", "''")}'`;
|
|
72
6
|
/** A non-interactive pwsh invocation of one command line. */
|
package/internal/cacheService.js
CHANGED
|
@@ -2,22 +2,6 @@ import { CONFLICT, isOk, stringField } from "./twirp.js";
|
|
|
2
2
|
import { Effect, Option } from "effect";
|
|
3
3
|
|
|
4
4
|
//#region src/internal/cacheService.ts
|
|
5
|
-
/**
|
|
6
|
-
* The Actions `CacheService` choreography — reserve, upload, finalize; look up
|
|
7
|
-
* — spelled once for the two services that speak it.
|
|
8
|
-
*
|
|
9
|
-
* @remarks
|
|
10
|
-
* `ActionCache` files a tar archive under a path-derived version and
|
|
11
|
-
* `BlobStore.githubCache` files an envelope under a constant one, but the
|
|
12
|
-
* three RPCs between them and their conflict semantics are identical. Each
|
|
13
|
-
* caller supplies its own per-key `call` and its own `refused` constructor, so
|
|
14
|
-
* the errors stay the caller's; this module owns only the protocol. It reaches
|
|
15
|
-
* `effect` and the Twirp client and nothing heavier — the Azure transfer of
|
|
16
|
-
* the bytes between the two RPCs stays in the three modules licensed to import
|
|
17
|
-
* it (`__test__/reachability.test.ts`).
|
|
18
|
-
*
|
|
19
|
-
* @internal
|
|
20
|
-
*/
|
|
21
5
|
/** The Twirp service the Actions cache protocol lives under. @internal */
|
|
22
6
|
const CACHE_SERVICE = "github.actions.results.api.v1.CacheService";
|
|
23
7
|
/**
|
package/internal/digest.js
CHANGED
|
@@ -3,19 +3,6 @@ import { createHash } from "node:crypto";
|
|
|
3
3
|
import * as Hex from "effect/encoding/Hex";
|
|
4
4
|
|
|
5
5
|
//#region src/internal/digest.ts
|
|
6
|
-
/**
|
|
7
|
-
* The sanctioned `node:crypto` digests, spelled once.
|
|
8
|
-
*
|
|
9
|
-
* @remarks
|
|
10
|
-
* Core `Crypto` offers one-shot SHA digests over bytes already in memory, but
|
|
11
|
-
* no HMAC and no incremental hasher, so this package's licence for a `node:`
|
|
12
|
-
* import covers hashing (see the Module concept in `okf/`). Keeping every
|
|
13
|
-
* `createHash` behind this module is what makes that licence auditable in one
|
|
14
|
-
* place rather than six, and keeps the streamed file digest — the shape a
|
|
15
|
-
* multi-gigabyte toolchain archive needs — from being re-derived per caller.
|
|
16
|
-
*
|
|
17
|
-
* @internal
|
|
18
|
-
*/
|
|
19
6
|
/** The raw SHA-256 of a string or byte array held in memory. @internal */
|
|
20
7
|
const sha256 = (value) => new Uint8Array(createHash("sha256").update(value).digest());
|
|
21
8
|
/** The hex SHA-256 of a string or byte array held in memory. @internal */
|
package/internal/jwt.js
CHANGED
|
@@ -3,23 +3,6 @@ import * as Base64Url from "effect/encoding/Base64Url";
|
|
|
3
3
|
|
|
4
4
|
//#region src/internal/jwt.ts
|
|
5
5
|
/**
|
|
6
|
-
* The JWT segment codec — read a payload, or frame an unsigned token — for the
|
|
7
|
-
* two places this package reads a runner-issued token's claims.
|
|
8
|
-
*
|
|
9
|
-
* @remarks
|
|
10
|
-
* **No signature verification, deliberately.** Every token read here was
|
|
11
|
-
* handed to this process by the runner that started it, and the claim read
|
|
12
|
-
* scopes a request (`actionsResults`) or is republished for a verifier
|
|
13
|
-
* elsewhere (`OidcTokenIssuer`); neither authorizes anything on the strength
|
|
14
|
-
* of the claim alone. Do not "fix" it here.
|
|
15
|
-
*
|
|
16
|
-
* Core `Base64Url` does the base64url work, so no `Buffer` is involved: it is
|
|
17
|
-
* strict about the alphabet where Node's decoder is forgiving, which is why
|
|
18
|
-
* there is a test whose payload actually contains `-` and `_`.
|
|
19
|
-
*
|
|
20
|
-
* @internal
|
|
21
|
-
*/
|
|
22
|
-
/**
|
|
23
6
|
* The decoded payload of `token` — the middle segment, base64url JSON —
|
|
24
7
|
* without any check on the signature.
|
|
25
8
|
*
|
package/internal/pnpmExe.js
CHANGED
|
@@ -3,21 +3,6 @@ import * as Hex from "effect/encoding/Hex";
|
|
|
3
3
|
import * as Base64 from "effect/encoding/Base64";
|
|
4
4
|
|
|
5
5
|
//#region src/internal/pnpmExe.ts
|
|
6
|
-
/**
|
|
7
|
-
* The pnpm native-binary layout: from pnpm 12 the `pnpm` registry package is a
|
|
8
|
-
* thin wrapper whose `pnpm` bin is a shebang-less placeholder that the
|
|
9
|
-
* package's own install script overwrites with the host's native executable,
|
|
10
|
-
* fetched as an optional dependency named `@pnpm/exe.<os>-<arch>[-musl]`.
|
|
11
|
-
* The installer never runs lifecycle scripts, so it performs that overlay
|
|
12
|
-
* itself; the pure pieces of that live here, where they are testable without
|
|
13
|
-
* a tarball.
|
|
14
|
-
*
|
|
15
|
-
* Detection is by LAYOUT (the manifest's `optionalDependencies` naming an
|
|
16
|
-
* `@pnpm/exe.*` package), never by major version — pnpm 11 and earlier ship
|
|
17
|
-
* no such dependency and keep their Node entry points.
|
|
18
|
-
*
|
|
19
|
-
* @internal
|
|
20
|
-
*/
|
|
21
6
|
/** The prefix every native-binary package name carries. @internal */
|
|
22
7
|
const PNPM_EXE_PREFIX = "@pnpm/exe.";
|
|
23
8
|
/**
|
package/internal/runnerFile.js
CHANGED
|
@@ -1,20 +1,4 @@
|
|
|
1
1
|
//#region src/internal/runnerFile.ts
|
|
2
|
-
/**
|
|
3
|
-
* The runner-file heredoc protocol — `GITHUB_OUTPUT`, `GITHUB_ENV` and
|
|
4
|
-
* `GITHUB_STATE` all take `name<<DELIM\nvalue\nDELIM\n` — spelled once for
|
|
5
|
-
* `ActionOutputs` and `ActionState`.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* **Delimiters are derived, never random.** GitHub's own toolkit uses a
|
|
9
|
-
* random UUID here and accepts the (tiny) chance of collision. Deriving the
|
|
10
|
-
* delimiter instead makes collision **impossible** rather than improbable,
|
|
11
|
-
* needs no randomness — so no `Crypto` in `R` and no unreproducible output —
|
|
12
|
-
* and is trivially testable. A value that contains a delimiter would
|
|
13
|
-
* terminate its block early and corrupt every entry after it, which is a
|
|
14
|
-
* value-controlled injection into the runner's own file.
|
|
15
|
-
*
|
|
16
|
-
* @internal
|
|
17
|
-
*/
|
|
18
2
|
/** The base delimiter for a heredoc block. */
|
|
19
3
|
const BASE_DELIMITER = "EFFECTED_EOF";
|
|
20
4
|
/** A delimiter guaranteed absent from `value`. @internal */
|
package/internal/sigv4.js
CHANGED
|
@@ -45,10 +45,7 @@ const timestamps = (now) => {
|
|
|
45
45
|
* @remarks
|
|
46
46
|
* Exported separately from {@link sign} so a test can pin it against AWS's
|
|
47
47
|
* **own documented** canonical request rather than against this
|
|
48
|
-
* implementation's output.
|
|
49
|
-
* this module was checked against a remembered signature constant that turned
|
|
50
|
-
* out to be from a different example, and the only thing that settled which side
|
|
51
|
-
* was wrong was reproducing a value AWS publishes.
|
|
48
|
+
* implementation's output.
|
|
52
49
|
*/
|
|
53
50
|
const canonicalize = (request, credentials) => {
|
|
54
51
|
const { amzDate, dateStamp } = timestamps(request.now);
|
package/internal/unstubbed.js
CHANGED
|
@@ -2,18 +2,6 @@ import { Effect } from "effect";
|
|
|
2
2
|
|
|
3
3
|
//#region src/internal/unstubbed.ts
|
|
4
4
|
/**
|
|
5
|
-
* The one way an unstubbed test-double member dies, for every `makeTest` in
|
|
6
|
-
* this package.
|
|
7
|
-
*
|
|
8
|
-
* @remarks
|
|
9
|
-
* A double's unstubbed member must die rather than silently succeed — a stub
|
|
10
|
-
* that returns nothing teaches a test that nothing happened. The message names
|
|
11
|
-
* the double, the member and the override that fixes it, and it is spelled
|
|
12
|
-
* here once so a wording change is one edit rather than ten.
|
|
13
|
-
*
|
|
14
|
-
* @internal
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
5
|
* The die-on-call members of `<double>.makeTest`: `dies("save")` is an
|
|
18
6
|
* `Effect` that defects naming `save` when run. Each package double binds its
|
|
19
7
|
* own name once, then lists its members.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/github-actions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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,18 +39,19 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@azure/storage-blob": "^12.33.0",
|
|
42
|
-
"@effected/github": "^0.
|
|
43
|
-
"@effected/
|
|
44
|
-
"@effected/
|
|
45
|
-
"@effected/
|
|
46
|
-
"@effected/
|
|
47
|
-
"@effected/
|
|
48
|
-
"@effected/
|
|
49
|
-
"@effected/
|
|
42
|
+
"@effected/github": "^0.15.0",
|
|
43
|
+
"@effected/github-commands": "^0.1.0",
|
|
44
|
+
"@effected/glob": "^0.10.0",
|
|
45
|
+
"@effected/markdown": "^0.15.0",
|
|
46
|
+
"@effected/npm": "^0.20.0",
|
|
47
|
+
"@effected/sbom": "^0.10.0",
|
|
48
|
+
"@effected/semver": "^0.11.0",
|
|
49
|
+
"@effected/templates": "^0.10.0",
|
|
50
|
+
"@effected/walker": "^0.15.0"
|
|
50
51
|
},
|
|
51
52
|
"peerDependencies": {
|
|
52
|
-
"@effect/platform-node": "4.0.0
|
|
53
|
-
"effect": "4.0.0
|
|
53
|
+
"@effect/platform-node": "^4.0.0",
|
|
54
|
+
"effect": "^4.0.0"
|
|
54
55
|
},
|
|
55
56
|
"engines": {
|
|
56
57
|
"node": ">=24.11.0"
|
package/WorkflowCommand.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
//#region src/WorkflowCommand.ts
|
|
2
|
-
/**
|
|
3
|
-
* Escape a command's message.
|
|
4
|
-
*
|
|
5
|
-
* @remarks
|
|
6
|
-
* The percent sign is replaced **first**, and the order is load-bearing: doing
|
|
7
|
-
* it last would re-escape the `%` of an escape sequence this function just
|
|
8
|
-
* produced, turning `%0A` into `%250A`.
|
|
9
|
-
*/
|
|
10
|
-
const escapeMessage = (value) => value.replaceAll("%", "%25").replaceAll("\r", "%0D").replaceAll("\n", "%0A");
|
|
11
|
-
/**
|
|
12
|
-
* Escape a property value.
|
|
13
|
-
*
|
|
14
|
-
* @remarks
|
|
15
|
-
* Everything a message escapes, plus `:` and `,` — the two characters that
|
|
16
|
-
* delimit the property list and terminate it. An unescaped one truncates the
|
|
17
|
-
* command or lets a value be read as a new property.
|
|
18
|
-
*/
|
|
19
|
-
const escapeProperty = (value) => escapeMessage(value).replaceAll(":", "%3A").replaceAll(",", "%2C");
|
|
20
|
-
/**
|
|
21
|
-
* The GitHub Actions workflow-command wire protocol: `::name key=value::message`.
|
|
22
|
-
*
|
|
23
|
-
* @remarks
|
|
24
|
-
* **Pure.** This module renders strings and nothing else — it performs no IO
|
|
25
|
-
* and holds no service — which is what makes the escaping rules testable
|
|
26
|
-
* without a runner, and what lets a non-Actions consumer reuse the protocol.
|
|
27
|
-
* Writing a rendered command to stdout is {@link ActionOutputs}'s job.
|
|
28
|
-
*
|
|
29
|
-
* The escaping is the whole point of the module. A message carrying a raw
|
|
30
|
-
* newline does not merely render oddly: the runner reads the text after it as
|
|
31
|
-
* a **new command**, so an unescaped log line is a command-injection vector.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```ts
|
|
35
|
-
* import { WorkflowCommand } from "@effected/github-actions";
|
|
36
|
-
*
|
|
37
|
-
* WorkflowCommand.error("build failed", { file: "src/main.ts", startLine: 12 });
|
|
38
|
-
* // "::error file=src/main.ts,line=12::build failed"
|
|
39
|
-
* ```
|
|
40
|
-
*
|
|
41
|
-
* @public
|
|
42
|
-
*/
|
|
43
|
-
var WorkflowCommand = class WorkflowCommand {
|
|
44
|
-
constructor() {}
|
|
45
|
-
/**
|
|
46
|
-
* Render an arbitrary command. The primitive every other member uses.
|
|
47
|
-
*
|
|
48
|
-
* @remarks
|
|
49
|
-
* The property type is written out structurally rather than as the module's
|
|
50
|
-
* `CommandProperties` alias: an internal named type on a `@public` signature
|
|
51
|
-
* fails the API Extractor gate, and neither an `@internal` tag nor a second
|
|
52
|
-
* alias helps — only inlining does.
|
|
53
|
-
*/
|
|
54
|
-
static render(name, properties, message) {
|
|
55
|
-
const rendered = Object.entries(properties).filter((entry) => entry[1] !== void 0).map(([key, value]) => `${key}=${escapeProperty(String(value))}`).join(",");
|
|
56
|
-
return `${rendered === "" ? `::${name}::` : `::${name} ${rendered}::`}${escapeMessage(message)}`;
|
|
57
|
-
}
|
|
58
|
-
/** `::debug::` — shown only when `ACTIONS_STEP_DEBUG` is enabled. */
|
|
59
|
-
static debug(message) {
|
|
60
|
-
return WorkflowCommand.render("debug", {}, message);
|
|
61
|
-
}
|
|
62
|
-
/** `::notice::` with optional source annotation. */
|
|
63
|
-
static notice(message, properties = {}) {
|
|
64
|
-
return WorkflowCommand.render("notice", WorkflowCommand.annotation(properties), message);
|
|
65
|
-
}
|
|
66
|
-
/** `::warning::` with optional source annotation. */
|
|
67
|
-
static warning(message, properties = {}) {
|
|
68
|
-
return WorkflowCommand.render("warning", WorkflowCommand.annotation(properties), message);
|
|
69
|
-
}
|
|
70
|
-
/** `::error::` with optional source annotation. */
|
|
71
|
-
static error(message, properties = {}) {
|
|
72
|
-
return WorkflowCommand.render("error", WorkflowCommand.annotation(properties), message);
|
|
73
|
-
}
|
|
74
|
-
/** `::group::` — opens a collapsible section in the runner log. */
|
|
75
|
-
static group(name) {
|
|
76
|
-
return WorkflowCommand.render("group", {}, name);
|
|
77
|
-
}
|
|
78
|
-
/** `::endgroup::` — closes the innermost open group. */
|
|
79
|
-
static endGroup() {
|
|
80
|
-
return WorkflowCommand.render("endgroup", {}, "");
|
|
81
|
-
}
|
|
82
|
-
/** `::add-mask::` — registers a value for redaction in the runner log. */
|
|
83
|
-
static addMask(value) {
|
|
84
|
-
return WorkflowCommand.render("add-mask", {}, value);
|
|
85
|
-
}
|
|
86
|
-
/** Map readable annotation fields onto GitHub's abbreviated wire names. */
|
|
87
|
-
static annotation(properties) {
|
|
88
|
-
return {
|
|
89
|
-
title: properties.title,
|
|
90
|
-
file: properties.file,
|
|
91
|
-
line: properties.startLine,
|
|
92
|
-
endLine: properties.endLine,
|
|
93
|
-
col: properties.startColumn,
|
|
94
|
-
endColumn: properties.endColumn
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
//#endregion
|
|
100
|
-
export { WorkflowCommand };
|