1import * as Array from "effect/Array";
2import * as Config from "effect/Config";
3import * as Effect from "effect/Effect";
4import { flow } from "effect/Function";
5import * as Option from "effect/Option";
6import * as String from "effect/String";
7
8export interface GitHubEnv {
9 /** The commit SHA that triggered the workflow run. */
10 readonly sha: string;
11 /** The repository owner from `GITHUB_REPOSITORY_OWNER`. */
12 readonly owner: string;
13 /** The repository name parsed from `GITHUB_REPOSITORY`. */
14 readonly repository: string;
15 /** The pull request number exported by the Alchemy GitHub Action. */
16 readonly pr: number | undefined;
17}
18
19/**
20 * GitHub Actions metadata for conditional resources in `alchemy.run.ts`.
21 *
22 * Resolves to `undefined` outside GitHub Actions. In GitHub Actions, it reads
23 * `GITHUB_SHA`, `GITHUB_REPOSITORY_OWNER`, `GITHUB_REPOSITORY`, and the optional
24 * `PULL_REQUEST` variable exported by the Alchemy GitHub Action.
25 */
26export const GitHubEnv: Config.Config<GitHubEnv | undefined> = Config.Boolean(
27 "GITHUB_ACTIONS",
28).pipe(
29 Config.withDefault(false),
30 // Config has no flatMap in Effect 4; a Config is itself an Effect, so the
31 // enabled branch returns the inner config for mapEffect to evaluate.
32 Config.mapEffect(
33 (enabled): Effect.Effect<GitHubEnv | undefined, Config.ConfigError> =>
34 enabled
35 ? Config.all({
36 sha: Config.String("GITHUB_SHA"),
37 owner: Config.String("GITHUB_REPOSITORY_OWNER"),
38 repository: Config.String("GITHUB_REPOSITORY").pipe(
39 Config.mapEffect(
40 flow(
41 String.split("/"),
42 Array.get(1),
43 Effect.fromOption,
44 Effect.catchTags({
45 NoSuchElementError: Effect.die,
46 }),
47 ),
48 ),
49 ),
50 pr: Config.Number("PULL_REQUEST").pipe(
51 Config.option,
52 Config.map(Option.getOrUndefined),
53 ),
54 })
55 : Effect.succeed(undefined),
56 ),
57);