Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e528e29
beep boop
grahamc Mar 28, 2025
5454c6b
bep
grahamc Mar 28, 2025
a41118a
?
grahamc Mar 28, 2025
560caa9
?
grahamc Mar 28, 2025
66d8ccf
Render hash mismatches as feedback
gustavderdrache Mar 28, 2025
f46552d
Escape all the metacharacters
gustavderdrache Mar 28, 2025
dca7cc6
Cleanup child processes
gustavderdrache Mar 28, 2025
ad5d306
Use NUL-terminated ls-files output
gustavderdrache Mar 28, 2025
8503756
Downgrade warning to info
gustavderdrache Mar 28, 2025
99e2bd0
Update messaging
gustavderdrache Mar 28, 2025
300c83d
Update dist
gustavderdrache Mar 28, 2025
6c9a2e8
Don't leak file handles
gustavderdrache Mar 28, 2025
da6d450
Remove superfluous punctuation
gustavderdrache Mar 29, 2025
9cd39de
Make extra sure we can hit curl with a SIGTERM
gustavderdrache Mar 29, 2025
c256f70
Be more selective about event ingestion
gustavderdrache Mar 29, 2025
85da126
Use determinate-nixd's hash fix feature
gustavderdrache Apr 14, 2025
f09b111
Feature gate annotations
gustavderdrache Apr 14, 2025
4ce71dd
Add annotation telemetry
gustavderdrache Apr 14, 2025
5947325
build
gustavderdrache Apr 14, 2025
00365ee
Change annotateMismatches() call order
gustavderdrache Apr 14, 2025
06d97ff
Fix property name
gustavderdrache Apr 14, 2025
030ef1f
Remove markdown
gustavderdrache Apr 14, 2025
7f64b08
Add more logging & telemetry
gustavderdrache Apr 14, 2025
b8a2731
pnpm run all
gustavderdrache Apr 14, 2025
2650528
missed a spot
gustavderdrache Apr 14, 2025
0a7860e
Remove .drv when rendering pretty derivation names
gustavderdrache Apr 14, 2025
f4819c9
Don't log determinate-nixd output when fixing hashes
gustavderdrache Apr 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 98 additions & 5 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 66 additions & 0 deletions src/annotate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as core from "@actions/core";

import type { Fix, FixHashesOutputV1, Mismatch } from "./fixHashes.js";

function prettyDerivation(derivation: string): string {
return derivation.replace(/\/nix\/store\/\w+-/, "").replace(/.drv$/, "");
}

function annotateSingle(
file: string,
line: number,
{ derivation, replacement }: Mismatch,
): void {
const pretty = prettyDerivation(derivation);
core.error(`To correct the hash mismatch for ${pretty}, use ${replacement}`, {
file,
startLine: line,
});
}

function annotateMultiple(
file: string,
{ line, found, mismatches }: Fix,
): void {
const matches = mismatches
.map(({ derivation, replacement }) => {
const pretty = prettyDerivation(derivation);
return `* For the derivation ${pretty}, use ${replacement}`;
})
.join("\n");

core.error(
`There are multiple replacements for the expression ${found}:\n${matches}`,
{
file,
startLine: line,
},
);
}

function annotate(file: string, fix: Fix): void {
if (fix.mismatches.length === 1) {
annotateSingle(file, fix.line, fix.mismatches[0]);
} else {
annotateMultiple(file, fix);
}
}

/**
* Annotates fixed-output derivation hash mismatches using GitHub Actions'
*
* @param output The output of `determinate-nixd fix hashes --json`
* @returns The number of annotations reported to the user
*/
export function annotateMismatches(output: FixHashesOutputV1): number {
let count = 0;

for (const { file, fixes } of output.files) {
for (const fix of fixes) {
annotate(file, fix);
count++;
}
}

return count;
}
38 changes: 38 additions & 0 deletions src/fixHashes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { getExecOutput } from "@actions/exec";

export interface Mismatch {
readonly derivation: string;
readonly replacement: string;
}

export interface Fix {
readonly line: number;
readonly found: string;
readonly mismatches: readonly Mismatch[];
}

export interface FileFix {
readonly file: string;
readonly fixes: readonly Fix[];
}

export interface FixHashesOutputV1 {
readonly version: "v1";
readonly files: readonly FileFix[];
}

export async function getFixHashes(): Promise<FixHashesOutputV1> {
const output = await getExecOutput("determinate-nixd", [
"fix",
"hashes",
"--json",
]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const output = await getExecOutput("determinate-nixd", [
"fix",
"hashes",
"--json",
]);
const output = await getExecOutput("determinate-nixd", [
"fix",
"hashes",
"--json",
], {
silent: true,
});

without silent: true the log gets littered with ugly output:

CleanShot 2025-04-14 at 11 50 47


if (output.exitCode !== 0) {
throw new Error(
`determinate-nixd fix hashes returned non-zero exit code ${output.exitCode} with the following error output:\n${output.stderr}`,
);
}

return JSON.parse(output.stdout);
}
39 changes: 39 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { DetSysAction, inputs, platform, stringifyError } from "detsys-ts";
import { randomUUID } from "node:crypto";
import got from "got";
import { setTimeout } from "node:timers/promises";
import { getFixHashes } from "./fixHashes.js";
import { annotateMismatches } from "./annotate.js";

// Nix installation events
const EVENT_INSTALL_NIX_FAILURE = "install_nix_failure";
Expand All @@ -27,6 +29,10 @@ const EVENT_LOGIN_TO_FLAKEHUB = "login_to_flakehub";

// Other events
const EVENT_CONCLUDE_JOB = "conclude_job";
const EVENT_FOD_ANNOTATE = "fod_annotate";

// Feature flag names
const FEAT_ANNOTATIONS = "hash-mismatch-annotations";

// Facts
const FACT_DETERMINATE_NIX = "determinate_nix";
Expand Down Expand Up @@ -124,6 +130,7 @@ class NixInstallerAction extends DetSysAction {
}

async post(): Promise<void> {
await this.annotateMismatches();
await this.cleanupDockerShim();
await this.reportOverall();
}
Expand Down Expand Up @@ -1104,6 +1111,38 @@ class NixInstallerAction extends DetSysAction {
);
}
}

private async annotateMismatches(): Promise<void> {
if (!this.determinate) {
return;
}

const active = this.getFeature(FEAT_ANNOTATIONS)?.variant;
if (!active) {
actionsCore.debug("The annotations feature is disabled for this run");
return;
}

try {
actionsCore.debug("Getting hash fixes from determinate-nixd");
const mismatches = await getFixHashes();
if (mismatches.version !== "v1") {
throw new Error(
`Unsupported \`determinate-nixd fix hashes\` output (got ${mismatches.version}, expected v1)`,
);
}

actionsCore.debug("Annotating mismatches");
const count = annotateMismatches(mismatches);
this.recordEvent(EVENT_FOD_ANNOTATE, { count });
} catch (error) {
// Don't hard fail the action if something exploded; this feature is only a nice-to-have
actionsCore.warning(`Could not consume hash mismatch events: ${error}`);
this.recordEvent("annotation-mismatch-execution:error", {
exception: stringifyError(error),
});
}
}
}

type ExecuteEnvironment = {
Expand Down
Loading