1import * as Config from "effect/Config";
2import * as Effect from "effect/Effect";
3import * as Redacted from "effect/Redacted";
4
5import type { Input } from "../Input.ts";
6import * as Output from "../Output.ts";
7import type { Environment } from "./Environment.ts";
8import { Secret } from "./Secret.ts";
9
10export interface SecretsProps {
11 /**
12 * Repository owner (user or organization).
13 */
14 owner: string;
15
16 /**
17 * Repository name.
18 */
19 repository: string;
20
21 /**
22 * Optional environment. When set every secret is scoped to that GitHub
23 * Actions environment instead of the whole repository. Accepts an
24 * environment name or a `GitHub.Environment` resource.
25 */
26 environment?: string | Environment;
27
28 /**
29 * Map of secret name to value. Plain strings are wrapped with
30 * `Redacted.make`; already-redacted values are passed through.
31 */
32 secrets: Record<string, Input<string | Redacted.Redacted<string>>>;
33}
34
35/**
36 * Bulk-creates a set of {@link Secret}s in the same repository (and
37 * optionally the same environment).
38 *
39 * Each entry in `secrets` becomes one `GitHub.Secret` resource, using the
40 * map key as both the alchemy logical id and the secret name.
41 * **Example:** Example
42 * ```ts
43 * yield* GitHub.Secrets({
44 * owner: "my-org",
45 * repository: "my-repo",
46 * secrets: {
47 * AXIOM_INGEST_TOKEN: tokenValue,
48 * AXIOM_DATASET_TRACES: traces.name,
49 * },
50 * });
51 * ```
52 *
53 * @resource
54 * @product Actions
55 */
56export const Secrets = ({
57 owner,
58 repository,
59 environment,
60 secrets,
61}: SecretsProps) =>
62 Effect.all(
63 Object.entries(secrets).map(([name, value]) =>
64 Secret(name, {
65 owner,
66 repository,
67 environment,
68 name,
69 value: liftValue(value),
70 }),
71 ),
72 );
73
74// Accepts a plain string, an existing `Redacted<string>`, or a lazy `Input`
75// of either. We must lift through lazy inputs so the inner string gets wrapped
76// after the engine resolves it — otherwise `Redacted.make(input)` produces an
77// opaque `Redacted<Config | Effect | Output>` that the plan cannot resolve.
78const liftValue = (
79 value: Input<string | Redacted.Redacted<string>>,
80): Input<Redacted.Redacted<string>> =>
81 Config.isConfig(value)
82 ? Config.map(value, toRedacted)
83 : Effect.isEffect(value)
84 ? Effect.map(value, toRedacted)
85 : Output.isOutput(value)
86 ? Output.map(
87 value as Output.Output<string | Redacted.Redacted<string>>,
88 toRedacted,
89 )
90 : toRedacted(value as string | Redacted.Redacted<string>);
91
92const toRedacted = (
93 value: string | Redacted.Redacted<string>,
94): Redacted.Redacted<string> =>
95 Redacted.isRedacted(value)
96 ? (value as Redacted.Redacted<string>)
97 : Redacted.make(value);