diff --git a/src/exec-stack.ts b/src/exec-stack.ts index c394c9f..a049586 100644 --- a/src/exec-stack.ts +++ b/src/exec-stack.ts @@ -15,6 +15,10 @@ export class ExecStack { .concat(args); } + async upgrade(): Promise { + return await exec.exec("stack", ["upgrade"]); + } + async exec(args: string[], options?: exec.ExecOptions): Promise { return await exec.exec("stack", this.stackArguments.concat(args), options); } diff --git a/src/main.ts b/src/main.ts index a97d537..d9a834a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,3 @@ -import * as path from "path"; import * as core from "@actions/core"; import { ExecStack } from "./exec-stack"; @@ -6,19 +5,18 @@ import { getCacheKeys } from "./get-cache-keys"; import { hashProject } from "./hash-project"; import type { Inputs } from "./inputs"; import { getInputs } from "./inputs"; -import type { StackPath } from "./parse-stack-path"; import { parseStackPath } from "./parse-stack-path"; -import type { StackQuery } from "./parse-stack-query"; -import { getLocalStackWorks, parseStackQuery } from "./parse-stack-query"; +import { parseStackQuery } from "./parse-stack-query"; +import { readStackYamlSync, packagesStackWorks } from "./stack-yaml"; import { DEFAULT_CACHE_OPTIONS, withCache } from "./with-cache"; -function setOutputs(stackQuery: StackQuery, stackPath: StackPath): void { - core.setOutput("compiler", stackQuery.compiler.actual); - core.setOutput( - "compiler-version", - stackQuery.compiler.actual.replace(/^ghc-/, ""), - ); -} +// function setOutputs(stackQuery: StackQuery, stackPath: StackPath): void { +// core.setOutput("compiler", stackQuery.compiler.actual); +// core.setOutput( +// "compiler-version", +// stackQuery.compiler.actual.replace(/^ghc-/, ""), +// ); +// } async function dependencies(stack: ExecStack, inputs: Inputs): Promise { await stack.exec(["setup"].concat(inputs.stackSetupArguments)); @@ -54,28 +52,35 @@ async function run() { process.chdir(inputs.workingDirectory); } + const hashes = await hashProject(inputs.stackYaml); + core.info(`Snapshot hash ${hashes.snapshot}`); + core.info(`Package hash ${hashes.package}`); + core.info(`Sources hash ${hashes.sources}`); + const stack = new ExecStack(inputs.stackYaml, inputs.stackArguments); - const stackPath = await stack.parse(["path"], parseStackPath); - const stackQuery = await stack.parse(["query"], parseStackQuery); - setOutputs(stackQuery, stackPath); + await core.group("Stack upgrade", async () => { + await stack.upgrade(); + }); - const stackWork = path.join(process.cwd(), ".stack-work"); - const stackWorks = getLocalStackWorks(stackQuery, [stackWork]); - core.info(`Found .stack-works:\n - ${stackWorks.join("\n - ")}`); + // Only use --stack-root, which (as of stack v2.15) won't load the + // environment and install GHC, etc. It's the only option currently safe to + // make outside of caching. + const stackRoot = ( + await stack.parse(["path", "--stack-root]"], parseStackPath) + )["stack-root"]; - const hashes = await hashProject(inputs.stackYaml); + const stackYaml = readStackYamlSync(inputs.stackYaml); + const stackWorks = packagesStackWorks(stackYaml); await core.group("Dependencies", async () => { const cacheKeys = getCacheKeys([ - inputs.cachePrefix("stack-deps", stackQuery.compiler.actual), + inputs.cachePrefix("stack-deps", stackYaml.resolver), hashes.snapshot, hashes.package, ]); - const cachePaths = [stackPath["stack-root"], stackPath.programs].concat( - stackWorks, - ); + const cachePaths = [stackRoot].concat(stackWorks); await withCache(cachePaths, cacheKeys, dependencies(stack, inputs), { ...DEFAULT_CACHE_OPTIONS, @@ -85,7 +90,7 @@ async function run() { await core.group("Build", async () => { const cacheKeys = getCacheKeys([ - inputs.cachePrefix("stack-deps", stackQuery.compiler.actual), + inputs.cachePrefix("stack-deps", stackYaml.resolver), hashes.snapshot, hashes.package, hashes.sources, @@ -93,13 +98,30 @@ async function run() { await withCache(stackWorks, cacheKeys, build(stack, inputs), { ...DEFAULT_CACHE_OPTIONS, - skipOnHit: !inputs.cacheSaveAlways, + skipOnHit: false, // always Build }); }); if (inputs.test) { await core.group("Test", async () => await test(stack, inputs)); } + + const stackQuery = await stack.parse( + ["query", "compiler"], + parseStackQuery, + ); + + core.setOutput("compiler", stackQuery.compiler.actual); + core.setOutput( + "compiler-version", + stackQuery.compiler.actual.replace(/^ghc-/, ""), + ); + + const stackPath = await stack.parse(["path"], parseStackPath); + + for (const k in stackPath) { + core.setOutput(k, stackPath[k]); + } } catch (error) { if (error instanceof Error) { core.error(error); diff --git a/src/parse-stack-path.ts b/src/parse-stack-path.ts index a0d187b..5a1fb0a 100644 --- a/src/parse-stack-path.ts +++ b/src/parse-stack-path.ts @@ -2,29 +2,7 @@ import * as yaml from "js-yaml"; export type StackPath = { [key: string]: string | null; - "snapshot-doc-root": string; - "local-doc-root": string; - "local-hoogle-root": string; - "stack-root": string; - "global-config": string; - "project-root": string; - "config-location": string; - "bin-path": string; - programs: string; - "compiler-exe": string; - "compiler-bin": string; - "compiler-tools-bin": string; - "local-bin": string; - "extra-include-dirs": string | null; - "extra-library-dirs": string | null; - "snapshot-pkg-db": string; - "local-pkg-db": string; - "global-pkg-db": string; - "ghc-package-path": string; - "snapshot-install-root": string; - "local-install-root": string; - "dist-dir": string; - "local-hpc-root": string; + "stack-root": string; // we only require this one }; export function parseStackPath(stdout: string): StackPath { diff --git a/src/parse-stack-query.test.ts b/src/parse-stack-query.test.ts index dfacbee..5dde394 100644 --- a/src/parse-stack-query.test.ts +++ b/src/parse-stack-query.test.ts @@ -1,4 +1,4 @@ -import { getLocalStackWorks, parseStackQuery } from "./parse-stack-query"; +import { parseStackQuery } from "./parse-stack-query"; const EXAMPLE = [ "compiler:", @@ -18,33 +18,6 @@ const EXAMPLE = [ test("parseStackQuery", () => { const stackQuery = parseStackQuery(EXAMPLE); - expect(stackQuery.compiler.actual).toBe("ghc-9.2.7"); expect(stackQuery.compiler.wanted).toBe("ghc-9.2.7"); }); - -test("getLocalStackWorks", () => { - const stackQuery = parseStackQuery(EXAMPLE); - - const stackWorks = getLocalStackWorks(stackQuery, []); - - expect(stackWorks).toEqual([ - "/home/patrick/code/freckle/megarepo/backend/core/.stack-work", - "/home/patrick/code/freckle/megarepo/backend/entities/.stack-work", - "/home/patrick/code/freckle/megarepo/backend/fancy-api/.stack-work", - ]); -}); - -test("getLocalStackWorks avoids duplicates", () => { - const stackQuery = parseStackQuery(EXAMPLE); - - const stackWorks = getLocalStackWorks(stackQuery, [ - "/home/patrick/code/freckle/megarepo/backend/core/.stack-work", - ]); - - expect(stackWorks).toEqual([ - "/home/patrick/code/freckle/megarepo/backend/core/.stack-work", - "/home/patrick/code/freckle/megarepo/backend/entities/.stack-work", - "/home/patrick/code/freckle/megarepo/backend/fancy-api/.stack-work", - ]); -}); diff --git a/src/parse-stack-query.ts b/src/parse-stack-query.ts index e64fc8a..90c4d7f 100644 --- a/src/parse-stack-query.ts +++ b/src/parse-stack-query.ts @@ -1,9 +1,7 @@ -import * as osPath from "path"; import * as yaml from "js-yaml"; export type StackQuery = { compiler: Compiler; - locals: Locals; }; export type Compiler = { @@ -11,29 +9,6 @@ export type Compiler = { wanted: string; }; -export type Locals = { [key: string]: Local }; - -export type Local = { - path: string; - version: string; -}; - export function parseStackQuery(stdout: string): StackQuery { return yaml.load(stdout) as StackQuery; } - -export function getLocalStackWorks( - stackQuery: StackQuery, - stackWorks: string[] = [], -): string[] { - for (const k in stackQuery.locals) { - const { path } = stackQuery.locals[k]; - const stackWork = osPath.join(path, ".stack-work"); - - if (!stackWorks.includes(stackWork)) { - stackWorks.push(stackWork); - } - } - - return stackWorks; -} diff --git a/src/stack-yaml.ts b/src/stack-yaml.ts new file mode 100644 index 0000000..976545d --- /dev/null +++ b/src/stack-yaml.ts @@ -0,0 +1,21 @@ +import * as fs from "fs"; +import { join as joinPath } from "path"; +import * as yaml from "js-yaml"; + +export type StackYaml = { + resolver: string; + packages: string[] | null; +}; + +export function readStackYamlSync(path: string): StackYaml { + const contents = fs.readFileSync(path, { encoding: "utf-8" }); + return yaml.load(contents) as StackYaml; +} + +export function packagesStackWorks(stackYaml: StackYaml): string[] { + const cwd = process.cwd(); + + return [".stack-work"] + .concat((stackYaml.packages ?? []).map((p) => joinPath(p, ".stack-work"))) + .map((sw) => joinPath(cwd, sw)); +}