Compare commits

..

36 Commits

Author SHA1 Message Date
gustavderdrache 8d7ad706d3 Don't leak file handles
CI / Check the dist/ folder is up to date (push) Failing after 52s
CI / Test: ${{ matrix.runner }}${{ matrix.determinate && ' with determinate' || '' }} (true, ubuntu-latest) (push) Failing after 42s
2025-03-28 19:55:52 -04:00
gustavderdrache 871bc2c1eb Update dist 2025-03-28 19:43:29 -04:00
gustavderdrache 03441dfa7a Update messaging 2025-03-28 19:42:55 -04:00
gustavderdrache d58e92bfa1 Downgrade warning to info 2025-03-28 19:42:10 -04:00
gustavderdrache 1eafba6ccb Use NUL-terminated ls-files output 2025-03-28 19:41:14 -04:00
gustavderdrache 583b0fbb40 Cleanup child processes 2025-03-28 19:38:57 -04:00
gustavderdrache b433f89383 Escape all the metacharacters 2025-03-28 19:35:42 -04:00
gustavderdrache b09ec83579 Render hash mismatches as feedback 2025-03-28 18:37:29 -04:00
gustavderdrache 0e85837c7a prevent breakpoint activation 2025-03-28 18:37:15 -04:00
Graham Christensen 92da2ded77 ? 2025-03-28 16:47:54 -04:00
Graham Christensen 651153b0f5 ? 2025-03-28 16:42:33 -04:00
Graham Christensen f632d22519 bep 2025-03-28 16:33:50 -04:00
Graham Christensen 37394bd1c7 ? 2025-03-28 16:30:15 -04:00
Graham Christensen d9d0dababa ? 2025-03-28 16:29:37 -04:00
Graham Christensen e528e29ddf beep boop 2025-03-28 16:29:16 -04:00
detsys-pr-bot 78d714f6f7 Update detsys-ts: Ignore hyphen-sep'd diags (#83) (#155)
* Update `detsys-ts` for: `Ignore hyphen-sep'd diags (#83)` (`07c7fc924119a8d9879c1c164ae593049d47f648`)

* Wait for the socket to appear

* ...

---------

Co-authored-by: grahamc <76716+grahamc@users.noreply.github.com>
Co-authored-by: Graham Christensen <graham@grahamc.com>
2025-03-28 15:20:00 -04:00
Graham Christensen 3e92d74f28 fail-fast: false (#156) 2025-03-28 14:17:41 -04:00
detsys-pr-bot 84f3b6ba94 Update detsys-ts for: Merge pull request #82 from DeterminateSystems/even-more-crashes (03533d37dcd46f34d9e99385e665615b221a30d9) (#154)
Co-authored-by: grahamc <76716+grahamc@users.noreply.github.com>
2025-03-28 14:19:02 +00:00
detsys-pr-bot 8f9243d6dd Update detsys-ts for: Merge pull request #81 from DeterminateSystems/dont-capture-some-crashes (8d9725c4856301321cd2508f5b8725cfb99366e2) (#153)
Co-authored-by: grahamc <76716+grahamc@users.noreply.github.com>
2025-03-27 16:04:36 +00:00
Luc Perkins 480fcd11bd Merge pull request #152 from detsys-pr-bot/detsys-ts-update-38df301720b69972f084538dd44c181269f264b0
Update `detsys-ts`: Merge pull request #80 from DeterminateSystems/fixup-traces
2025-03-27 11:10:33 -03:00
grahamc 2551261839 Update detsys-ts for: Merge pull request #80 from DeterminateSystems/fixup-traces (38df301720b69972f084538dd44c181269f264b0) 2025-03-27 13:47:59 +00:00
Luc Perkins 7f1ff2b54f Merge pull request #151 from detsys-pr-bot/detsys-ts-update-cf33e9577ca7571bf9ebbfe298b4e3e457a58813
Update `detsys-ts`: Merge pull request #79 from DeterminateSystems/dependabot/npm_and_yarn/npm_and_yarn-14f44f5325
2025-03-26 10:00:20 -03:00
lucperkins 1e22780707 Update detsys-ts for: Merge pull request #79 from DeterminateSystems/dependabot/npm_and_yarn/npm_and_yarn-14f44f5325 (cf33e9577ca7571bf9ebbfe298b4e3e457a58813) 2025-03-26 12:32:03 +00:00
Luc Perkins dc965b7805 Merge pull request #150 from DeterminateSystems/update-deps 2025-03-25 19:27:38 -03:00
Cole Helbling fe228f6faf Update @actions/github to 6.0.0 2025-03-25 14:41:42 -07:00
Luc Perkins 300844e532 Merge pull request #149 from DeterminateSystems/flakehub-cache-action 2025-03-24 15:51:30 -03:00
Luc Perkins 86febfe320 Switch to flakehub-cache-action 2025-03-24 00:49:28 -03:00
Luc Perkins 0c7c6d4acd Merge pull request #148 from detsys-pr-bot/detsys-ts-update-9d2b0e4636787bba8b886ddc720f007850e541fb
Update `detsys-ts`: Merge pull request #78 from DeterminateSystems/dependabot/npm_and_yarn/npm-deps-0af3b8ec11
2025-03-24 00:45:22 -03:00
lucperkins fbb684a4bf Update detsys-ts for: Merge pull request #78 from DeterminateSystems/dependabot/npm_and_yarn/npm-deps-0af3b8ec11 (9d2b0e4636787bba8b886ddc720f007850e541fb) 2025-03-24 01:42:22 +00:00
detsys-pr-bot 37dc9ba6c4 Update detsys-ts for: Merge pull request #74 from DeterminateSystems/dependabot/npm_and_yarn/npm-deps-eb3d92718e (4c7ff9706e6466d7c03c58a3479ed212d6ffb5ba) (#146)
Co-authored-by: grahamc <76716+grahamc@users.noreply.github.com>
2025-03-19 17:30:45 -04:00
detsys-pr-bot a48face581 Update detsys-ts for: Merge pull request #71 from DeterminateSystems/updates (b3319a2c78c46d5ad6bc00d3453266d3d9fdde44) (#138)
Co-authored-by: grahamc <76716+grahamc@users.noreply.github.com>
2025-02-06 16:54:30 +00:00
detsys-pr-bot dea7810afd Update detsys-ts for: Merge pull request #69 from DeterminateSystems/update-deps (eb87094f35072ac911526ad052c3437c9e0c42d6) (#131)
Co-authored-by: grahamc <76716+grahamc@users.noreply.github.com>
2024-11-26 11:32:08 -05:00
Graham Christensen e50d5f73bf Log in to flakehub on existing installs (#129)
* Log in to flakehub if the machine is already installed

* Put the nix store paths first (in the PATH)

* set the path earlier

* Warn on login failures
2024-11-14 11:41:23 -05:00
detsys-pr-bot 25431d2798 Update detsys-ts for: Merge pull request #67 from DeterminateSystems/allow-obliterating-id-token-privs (4280bc94c9545f31ccf08001cc16f20ccb91b770) (#128)
Co-authored-by: grahamc <76716+grahamc@users.noreply.github.com>
2024-11-06 14:54:15 -05:00
Graham Christensen b92f66560d Add the job-status option (#125) 2024-10-03 11:40:44 -04:00
Graham Christensen ddfca32d6f Convert flakehub: true to determinate: true (#123)
* Drop the flakehub param to deprecated, use determinate, and log in to flakehub
* Expand the test suite to cover determinate on all our targets

---------

Co-authored-by: Luc Perkins <lucperkins@gmail.com>
2024-09-13 12:36:49 -07:00
7 changed files with 34262 additions and 48105 deletions
+21 -130
View File
@@ -3,32 +3,17 @@ name: CI
on:
pull_request:
push:
branches: [main]
branches: [main, curl-data]
workflow_dispatch:
jobs:
tests:
runs-on: ubuntu-22.04
needs:
- check-dist-up-to-date
- install-nix-linux
- install-nix-macos
- install-with-non-default-source-inputs
# NOTE(cole-h): GitHub treats "skipped" as "OK" for the purposes of required checks on branch
# protection, so we take advantage of this fact and fail if any of the dependent actions failed,
# or "skip" (which is a success for GHA's purposes) if none of them did.
if: failure()
steps:
- name: Dependent checks failed
run: exit 1
check-dist-up-to-date:
name: Check the dist/ folder is up to date
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- uses: DeterminateSystems/flakehub-cache-action@main
- name: pnpm install
run: nix develop --command pnpm install
- name: prettier format
@@ -44,14 +29,15 @@ jobs:
- name: Ensure no staged changes
run: git diff --exit-code
install-nix-linux:
name: Run test suite for Linux systems
install-nix:
name: "Test: ${{ matrix.runner }}${{ matrix.determinate && ' with determinate' || '' }}"
strategy:
fail-fast: false
matrix:
runner:
- ubuntu-latest
- nscloud-ubuntu-22.04-amd64-4x16
- namespace-profile-default-arm64
determinate:
- true
runs-on: ${{ matrix.runner }}
permissions:
contents: read
@@ -65,6 +51,13 @@ jobs:
log-directives: nix_installer=trace
backtrace: full
_internal-strict-mode: true
determinate: ${{ matrix.determinate }}
# - name: Breakpoint if tests failed
# uses: namespacelabs/breakpoint-action@v0
# with:
# duration: 30m
# authorized-users: grahamc
- name: echo $PATH
run: echo $PATH
@@ -82,85 +75,6 @@ jobs:
nix store gc
nix run nixpkgs#hello
- name: Test bash
run: nix-instantiate -E 'builtins.currentTime' --eval
if: success() || failure()
shell: bash --login {0}
- name: Test sh
run: nix-instantiate -E 'builtins.currentTime' --eval
if: success() || failure()
shell: sh -l {0}
- name: Install Nix again (noop)
uses: ./
with:
logger: pretty
log-directives: nix_installer=trace
backtrace: full
_internal-strict-mode: true
- name: Test `nix` with `$GITHUB_PATH`
if: success() || failure()
run: |
nix run nixpkgs#hello
nix profile install nixpkgs#hello
hello
nix store gc
nix run nixpkgs#hello
- name: Reinstall Nix
uses: ./
with:
logger: pretty
log-directives: nix_installer=trace
backtrace: full
reinstall: true
extra-conf: |
use-sqlite-wal = true
_internal-strict-mode: true
- name: Test `nix` with `$GITHUB_PATH`
if: success() || failure()
run: |
nix run nixpkgs#hello
nix profile install nixpkgs#hello
hello
nix store gc
nix run nixpkgs#hello
- name: Verify the generated nix.conf
run: |
cat -n /etc/nix/nix.conf
grep -E "^trusted-users = .*$USER" /etc/nix/nix.conf
grep -E "^use-sqlite-wal = true" /etc/nix/nix.conf
install-nix-macos:
name: Run test suite for macOS systems
strategy:
matrix:
runner:
# x86_64-darwin
- macos-12
# aarch64-darwin
- macos-latest-xlarge
runs-on: ${{ matrix.runner }}
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: ./
with:
logger: pretty
log-directives: nix_installer=trace
backtrace: full
_internal-strict-mode: true
- name: echo $PATH
run: echo $PATH
- name: Test `nix` with `$GITHUB_PATH`
if: success() || failure()
run: |
nix run nixpkgs#hello
nix profile install nixpkgs#hello
hello
nix store gc
nix run nixpkgs#hello
- name: Test bash
run: nix-instantiate -E 'builtins.currentTime' --eval
if: success() || failure()
@@ -170,9 +84,8 @@ jobs:
if: success() || failure()
shell: sh -l {0}
- name: Test zsh
run: nix-instantiate -E 'builtins.currentTime' --eval
run: if (zsh --help > /dev/null); then zsh --login --interactive -c "nix-instantiate -E 'builtins.currentTime' --eval"; fi
if: success() || failure()
shell: zsh --login --interactive {0}
- name: Install Nix again (noop)
uses: ./
with:
@@ -180,6 +93,7 @@ jobs:
log-directives: nix_installer=trace
backtrace: full
_internal-strict-mode: true
determinate: ${{ matrix.determinate }}
- name: Test `nix` with `$GITHUB_PATH`
if: success() || failure()
run: |
@@ -198,6 +112,7 @@ jobs:
extra-conf: |
use-sqlite-wal = true
_internal-strict-mode: true
determinate: ${{ matrix.determinate }}
- name: Test `nix` with `$GITHUB_PATH`
if: success() || failure()
run: |
@@ -208,31 +123,7 @@ jobs:
nix run nixpkgs#hello
- name: Verify the generated nix.conf
run: |
cat /etc/nix/nix.conf
grep -E "^trusted-users = .*$USER" /etc/nix/nix.conf
grep -E "^use-sqlite-wal = true" /etc/nix/nix.conf
install-with-non-default-source-inputs:
name: Install Nix using non-default source-${{ matrix.inputs.key }}
runs-on: ubuntu-22.04
strategy:
matrix:
inputs:
# https://github.com/DeterminateSystems/nix-installer/blob/v0.18.0
- key: url
value: https://github.com/DeterminateSystems/nix-installer/releases/download/v0.18.0/nix-installer-x86_64-linux
nix-version: "2.21.2"
# https://github.com/DeterminateSystems/nix-installer/tree/7011c077ec491da410fbc39f68676b0908b9ce7e
- key: revision
value: 7011c077ec491da410fbc39f68676b0908b9ce7e
nix-version: "2.19.2"
steps:
- uses: actions/checkout@v4
- name: Install with alternative source-${{ matrix.inputs.key }}
uses: ./
with:
source-${{ matrix.inputs.key }}: ${{ matrix.inputs.value }}
_internal-strict-mode: true
- name: Ensure that the expected Nix version ${{ matrix.inputs.nix-version }} is installed via alternative source-${{ matrix.inputs.key }}
run: .github/verify-version.sh ${{ matrix.inputs.nix-version }}
nix config show
cat -n /etc/nix/nix.conf
nix config show | grep -E "^trusted-users = .*$USER"
nix config show | grep -E "^use-sqlite-wal = true"
+4 -3
View File
@@ -34,7 +34,7 @@ jobs:
### With FlakeHub
To fetch private flakes from FlakeHub, update the `permissions` block and pass `flakehub: true`:
To fetch private flakes from FlakeHub and Nix builds from FlakeHub Cache, update the `permissions` block and pass `determinate: true`:
```yaml
on:
@@ -53,7 +53,7 @@ jobs:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main
with:
flakehub: true
determinate: true
- run: nix build .
```
@@ -85,9 +85,10 @@ Differing from the upstream [Nix](https://github.com/NixOS/nix) installer script
| Parameter | Description | Type | Default |
| :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------- | :------------------------------------------------------------- |
| `backtrace` | The setting for [`RUST_BACKTRACE`][backtrace] | string | |
| `determinate` | Whether to install [Determinate Nix](https://determinate.systems/enterprise) and log in to FlakeHub for private Flakes and binary caches. | Boolean | `false` |
| `extra-args` | Extra arguments to pass to the planner (prefer using structured `with:` arguments unless using a custom [planner]!) | string | |
| `extra-conf` | Extra configuration lines for `/etc/nix/nix.conf` (includes `access-tokens` with `secrets.GITHUB_TOKEN` automatically if `github-token` is set) | string | |
| `flakehub` | Log in to FlakeHub to pull private flakes using the GitHub Actions [JSON Web Token](https://jwt.io) (JWT), which is bound to the `api.flakehub.com` audience. | Boolean | `false` |
| `flakehub` | Deprecated. Implies `determinate`. | Boolean | `false` |
| `force-docker-shim` | Force the use of Docker as a process supervisor. This setting is automatically enabled when necessary. | Boolean | `false` |
| `github-token` | A [GitHub token] for making authenticated requests (which have a higher rate-limit quota than unauthenticated requests) | string | `${{ github.token }}` |
| `github-server-url` | The URL for the GitHub server, to use with the `github-token` token. Defaults to the current GitHub server, supporting GitHub Enterprise Server automatically. Only change this value if the provided `github-token` is for a different GitHub server than the current server. | string | `${{ github.server }}` |
+8 -1
View File
@@ -7,6 +7,10 @@ inputs:
backtrace:
description: The setting for `RUST_BACKTRACE` (see https://doc.rust-lang.org/std/backtrace/index.html#environment-variables)
required: false
determinate:
description: |
Whether to install [Determinate Nix](https://determinate.systems/enterprise) and log in to FlakeHub for private Flakes and binary caches.
default: false
extra-args:
description: Extra args to pass to the planner (prefer using structured `with:` arguments unless using a custom planner!)
required: false
@@ -14,7 +18,7 @@ inputs:
description: Extra configuration lines for `/etc/nix/nix.conf` (includes `access-tokens` with `secrets.GITHUB_TOKEN` automatically if `github-token` is set)
required: false
flakehub:
description: Automatically log in to your [FlakeHub](https://flakehub.com) account, for accessing private flakes.
description: Deprecated. Implies `determinate`.
required: false
default: false
force-docker-shim:
@@ -30,6 +34,9 @@ inputs:
init:
description: "The init system to configure, requires `planner: linux-multi` (allowing the choice between `none` or `systemd`)"
required: false
job-status:
description: The overall status of the job. Set automatically, for aggregate analysis of Nix stability.
default: ${{ job.status }}
kvm:
description: Automatically configure the GitHub Actions Runner for NixOS test supports, if the host supports it.
required: false
Generated Vendored
+32087 -46202
View File
File diff suppressed because one or more lines are too long
+13 -13
View File
@@ -25,26 +25,26 @@
},
"homepage": "https://github.com/DeterminateSystems/nix-installer-action#readme",
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
"@actions/github": "^5.1.1",
"@actions/github": "^6.0.0",
"detsys-ts": "github:DeterminateSystems/detsys-ts",
"got": "^14.3.0",
"got": "^14.4.6",
"string-argv": "^0.3.2"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/node": "^20.14.0",
"@types/node": "^20.17.28",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^7.12.0",
"@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0",
"eslint-import-resolver-typescript": "^3.6.1",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@vercel/ncc": "^0.38.3",
"eslint": "^8.57.1",
"eslint-import-resolver-typescript": "^3.10.0",
"eslint-plugin-github": "^4.10.2",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"prettier": "^3.3.0",
"tsup": "^8.1.0",
"typescript": "^5.4.5"
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.5",
"prettier": "^3.5.3",
"tsup": "^8.4.0",
"typescript": "^5.8.2"
}
}
+1782 -1637
View File
File diff suppressed because it is too large Load Diff
+347 -119
View File
@@ -1,7 +1,6 @@
import * as actionsCore from "@actions/core";
import * as github from "@actions/github";
import * as actionsExec from "@actions/exec";
import { access, writeFile, readFile, mkdir } from "node:fs/promises";
import { access, open, readFile, stat } from "node:fs/promises";
import { join } from "node:path";
import fs from "node:fs";
import { userInfo } from "node:os";
@@ -10,6 +9,8 @@ import * as path from "path";
import { DetSysAction, inputs, platform, stringifyError } from "detsys-ts";
import { randomUUID } from "node:crypto";
import got from "got";
import { setTimeout } from "node:timers/promises";
import { SpawnOptions, spawn } from "node:child_process";
// Nix installation events
const EVENT_INSTALL_NIX_FAILURE = "install_nix_failure";
@@ -26,34 +27,36 @@ const EVENT_START_DOCKER_SHIM = "start_docker_shim";
const EVENT_LOGIN_TO_FLAKEHUB = "login_to_flakehub";
// Other events
const EVENT_CONCLUDE_WORKFLOW = "conclude_workflow";
const EVENT_CONCLUDE_JOB = "conclude_job";
// Facts
const FACT_DETERMINATE_NIX = "determinate_nix";
const FACT_HAS_DOCKER = "has_docker";
const FACT_HAS_SYSTEMD = "has_systemd";
const FACT_IN_ACT = "in_act";
const FACT_IN_NAMESPACE_SO = "in_namespace_so";
const FACT_NIX_INSTALLER_PLANNER = "nix_installer_planner";
type WorkflowConclusion =
| "success"
| "failure"
| "cancelled"
| "unavailable"
| "no-jobs";
// Flags
const FLAG_DETERMINATE = "--determinate";
// Pre/post state keys
const STATE_EVENT_LOG = "DETERMINATE_NIXD_EVENT_LOG";
const STATE_EVENT_PID = "DETERMINATE_NIXD_EVENT_PID";
class NixInstallerAction extends DetSysAction {
determinate: boolean;
platform: string;
nixPackageUrl: string | null;
backtrace: string | null;
extraArgs: string | null;
extraConf: string[] | null;
flakehub: boolean;
kvm: boolean;
githubServerUrl: string | null;
githubToken: string | null;
forceDockerShim: boolean;
init: string | null;
jobConclusion: string | null;
localRoot: string | null;
logDirectives: string | null;
logger: string | null;
@@ -84,17 +87,19 @@ class NixInstallerAction extends DetSysAction {
diagnosticsSuffix: "diagnostic",
});
this.determinate =
inputs.getBool("determinate") || inputs.getBool("flakehub");
this.platform = platform.getNixPlatform(platform.getArchOs());
this.nixPackageUrl = inputs.getStringOrNull("nix-package-url");
this.backtrace = inputs.getStringOrNull("backtrace");
this.extraArgs = inputs.getStringOrNull("extra-args");
this.extraConf = inputs.getMultilineStringOrNull("extra-conf");
this.flakehub = inputs.getBool("flakehub");
this.kvm = inputs.getBool("kvm");
this.forceDockerShim = inputs.getBool("force-docker-shim");
this.githubToken = inputs.getStringOrNull("github-token");
this.githubServerUrl = inputs.getStringOrNull("github-server-url");
this.init = inputs.getStringOrNull("init");
this.jobConclusion = inputs.getStringOrNull("job-status");
this.localRoot = inputs.getStringOrNull("local-root");
this.logDirectives = inputs.getStringOrNull("log-directives");
this.logger = inputs.getStringOrNull("logger");
@@ -121,11 +126,14 @@ class NixInstallerAction extends DetSysAction {
await this.scienceDebugFly();
await this.detectAndForceDockerShim();
await this.install();
await this.spewEventLog();
}
async post(): Promise<void> {
await this.cleanupDockerShim();
await this.reportOverall();
await this.slurpEventLog();
await this.cleanupLogger();
}
private get isMacOS(): boolean {
@@ -519,15 +527,6 @@ class NixInstallerAction extends DetSysAction {
}
extraConf += "\n";
}
if (this.flakehub) {
try {
const flakeHubNetrcFile = await this.flakehubLogin();
extraConf += `netrc-file = ${flakeHubNetrcFile}`;
extraConf += "\n";
} catch (e) {
actionsCore.warning(`Failed to set up FlakeHub: ${e}`);
}
}
if (this.extraConf !== null && this.extraConf.length !== 0) {
extraConf += this.extraConf.join("\n");
extraConf += "\n";
@@ -553,13 +552,9 @@ class NixInstallerAction extends DetSysAction {
return executionEnv;
}
private async executeInstall(binaryPath: string): Promise<number> {
const executionEnv = await this.executionEnvironment();
actionsCore.debug(
`Execution environment: ${JSON.stringify(executionEnv, null, 4)}`,
);
private get installerArgs(): string[] {
const args = ["install"];
if (this.planner) {
this.addFact(FACT_NIX_INSTALLER_PLANNER, this.planner);
args.push(this.planner);
@@ -573,8 +568,33 @@ class NixInstallerAction extends DetSysAction {
args.push(...extraArgs);
}
if (this.determinate) {
this.addFact(FACT_DETERMINATE_NIX, true);
actionsCore.info(
`Installing Determinate Nix using the ${FLAG_DETERMINATE} flag`,
);
if (!this.extraArgs) {
args.push(FLAG_DETERMINATE);
}
if (this.extraArgs && !this.extraArgs.includes(FLAG_DETERMINATE)) {
args.push(FLAG_DETERMINATE);
}
}
return args;
}
private async executeInstall(binaryPath: string): Promise<number> {
const executionEnv = await this.executionEnvironment();
actionsCore.debug(
`Execution environment: ${JSON.stringify(executionEnv, null, 4)}`,
);
this.recordEvent(EVENT_INSTALL_NIX_START);
const exitCode = await actionsExec.exec(binaryPath, args, {
const exitCode = await actionsExec.exec(binaryPath, this.installerArgs, {
env: {
...executionEnv,
...process.env, // To get $PATH, etc
@@ -603,8 +623,13 @@ class NixInstallerAction extends DetSysAction {
);
await this.executeUninstall();
} else {
// We're already installed, and not reinstalling, just set GITHUB_PATH and finish early
// We're already installed, and not reinstalling, just log in to FlakeHub, set GITHUB_PATH and finish early
await this.setGithubPath();
if (this.determinate) {
await this.flakehubLogin();
}
actionsCore.info("Nix was already installed, using existing install");
return;
}
@@ -623,7 +648,6 @@ class NixInstallerAction extends DetSysAction {
}
}
// Normal just doing of the install
actionsCore.startGroup("Installing Nix");
const binaryPath = await this.fetchBinary();
await this.executeInstall(binaryPath);
@@ -632,7 +656,12 @@ class NixInstallerAction extends DetSysAction {
if (this.forceDockerShim) {
await this.spawnDockerShim();
}
await this.setGithubPath();
if (this.determinate) {
await this.flakehubLogin();
}
}
async spawnDockerShim(): Promise<void> {
@@ -714,6 +743,10 @@ class NixInstallerAction extends DetSysAction {
dir: "/tmp",
readOnly: false,
},
{
dir: "/usr",
readOnly: true,
},
{
dir: "/nix",
readOnly: false,
@@ -737,6 +770,14 @@ class NixInstallerAction extends DetSysAction {
}
}
const plausibleDeterminateOptions = [];
const plausibleDeterminateArguments = [];
if (this.determinate) {
plausibleDeterminateOptions.push("--entrypoint");
plausibleDeterminateOptions.push("/usr/local/bin/determinate-nixd");
plausibleDeterminateArguments.push("daemon");
}
this.recordEvent(EVENT_START_DOCKER_SHIM);
const exitCode = await actionsExec.exec(
"docker",
@@ -754,8 +795,10 @@ class NixInstallerAction extends DetSysAction {
"--name",
`determinate-nix-shim-${this.getUniqueId()}-${randomUUID()}`,
]
.concat(plausibleDeterminateOptions)
.concat(mountArguments)
.concat(["determinate-nix-shim:latest"]),
.concat(["determinate-nix-shim:latest"])
.concat(plausibleDeterminateArguments),
{
silent: true,
listeners: {
@@ -785,10 +828,52 @@ class NixInstallerAction extends DetSysAction {
}
}
const maxDurationSeconds = 120;
const delayPerAttemptInMiliseconds = 50;
const maxAttempts =
(maxDurationSeconds * 1000) / delayPerAttemptInMiliseconds;
let didSucceed = false;
for (let attempt = 0; attempt <= maxAttempts; attempt += 1) {
if (await this.doesTheSocketExistYet()) {
didSucceed = true;
break;
}
await setTimeout(50);
}
if (!didSucceed) {
throw new Error("Timed out waiting for the Nix Daemon");
}
actionsCore.endGroup();
return;
}
async doesTheSocketExistYet(): Promise<boolean> {
const socketPath = "/nix/var/nix/daemon-socket/socket";
try {
await stat(socketPath);
return true;
} catch (error: unknown) {
// eslint-disable-next-line no-undef
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
actionsCore.debug(`Socket '${socketPath}' does not exist yet`);
return false;
}
actionsCore.warning(
`Error waiting for the Nix Daemon socket: ${stringifyError(error)}`,
);
this.recordEvent("docker-shim:wait-for-socket", {
exception: stringifyError(error),
});
throw error;
}
}
async cleanupDockerShim(): Promise<void> {
const containerId = actionsCore.getState("docker_shim_container_id");
@@ -830,13 +915,19 @@ class NixInstallerAction extends DetSysAction {
async setGithubPath(): Promise<void> {
// Interim versions of the `nix-installer` crate may have already manipulated `$GITHUB_PATH`, as root even! Accessing that will be an error.
try {
const nixVarNixProfilePath = "/nix/var/nix/profiles/default/bin";
const homeNixProfilePath = `${process.env["HOME"]}/.nix-profile/bin`;
actionsCore.addPath(nixVarNixProfilePath);
actionsCore.addPath(homeNixProfilePath);
actionsCore.info(
`Added \`${nixVarNixProfilePath}\` and \`${homeNixProfilePath}\` to \`$GITHUB_PATH\``,
);
const paths = [];
if (this.determinate) {
paths.push("/usr/local/bin");
}
paths.push("/nix/var/nix/profiles/default/bin");
paths.push(`${process.env["HOME"]}/.nix-profile/bin`);
for (const p of paths) {
actionsCore.addPath(p);
actionsCore.debug(`Added \`${p}\` to \`$GITHUB_PATH\``);
}
} catch {
actionsCore.info(
"Skipping setting $GITHUB_PATH in action, the `nix-installer` crate seems to have done this already. From `nix-installer` version 0.11.0 and up, this step is done in the action. Prior to 0.11.0, this was only done in the `nix-installer` binary.",
@@ -844,38 +935,23 @@ class NixInstallerAction extends DetSysAction {
}
}
async flakehubLogin(): Promise<string> {
this.recordEvent(EVENT_LOGIN_TO_FLAKEHUB);
const netrcPath = `${process.env["RUNNER_TEMP"]}/determinate-nix-installer-netrc`;
const jwt = await actionsCore.getIDToken("api.flakehub.com");
await writeFile(
netrcPath,
[
`machine api.flakehub.com login flakehub password ${jwt}`,
`machine cache.flakehub.com login flakehub password ${jwt}`,
`machine flakehub.com login flakehub password ${jwt}`,
].join("\n"),
);
const flakehubAuthDir = `${process.env["XDG_CONFIG_HOME"] || `${process.env["HOME"]}/.config`}/flakehub`;
await mkdir(flakehubAuthDir, { recursive: true });
const flakehubAuthPath = `${flakehubAuthDir}/auth`;
await writeFile(flakehubAuthPath, jwt);
actionsCore.info("Logging in to FlakeHub.");
// the join followed by a match on ^... looks silly, but extra_config
// could contain multi-line values
if (this.extraConf?.join("\n").match(/^netrc-file/m)) {
actionsCore.warning(
"Logging in to FlakeHub conflicts with the Nix option `netrc-file`.",
);
async flakehubLogin(): Promise<void> {
if (
process.env["ACTIONS_ID_TOKEN_REQUEST_URL"] &&
process.env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]
) {
actionsCore.startGroup("Logging in to FlakeHub");
this.recordEvent(EVENT_LOGIN_TO_FLAKEHUB);
try {
await actionsExec.exec(`determinate-nixd`, ["login", "github-action"]);
} catch (e: unknown) {
actionsCore.warning(`FlakeHub Login failure: ${stringifyError(e)}`);
this.recordEvent("flakehub-login:failure", {
exception: stringifyError(e),
});
}
actionsCore.endGroup();
}
return netrcPath;
}
async executeUninstall(): Promise<number> {
@@ -1017,63 +1093,14 @@ class NixInstallerAction extends DetSysAction {
async reportOverall(): Promise<void> {
try {
this.recordEvent(EVENT_CONCLUDE_WORKFLOW, {
conclusion: await this.getWorkflowConclusion(),
this.recordEvent(EVENT_CONCLUDE_JOB, {
conclusion: this.jobConclusion ?? "unknown",
});
} catch (e) {
actionsCore.debug(`Error submitting post-run diagnostics report: ${e}`);
}
}
private async getWorkflowConclusion(): Promise<
undefined | WorkflowConclusion
> {
if (this.githubToken == null) {
return undefined;
}
try {
const octokit = github.getOctokit(this.githubToken);
const jobs = await octokit.paginate(
octokit.rest.actions.listJobsForWorkflowRun,
{
owner: github.context.repo.owner,
repo: github.context.repo.repo,
/* eslint-disable camelcase */
run_id: github.context.runId,
},
);
actionsCore.debug(`awaited jobs: ${jobs}`);
const job = jobs
.filter((candidate) => candidate.name === github.context.job)
.at(0);
if (job === undefined) {
return "no-jobs";
}
const outcomes = (job.steps ?? []).map((j) => j.conclusion ?? "unknown");
// Possible values: success, failure, cancelled, or skipped
// from: https://docs.github.com/en/actions/learn-github-actions/contexts
if (outcomes.includes("failure")) {
// Any failures fails the job
return "failure";
}
if (outcomes.includes("cancelled")) {
// Any cancellations cancels the job
return "cancelled";
}
// Assume success if no jobs failed or were canceled
return "success";
} catch (e) {
actionsCore.debug(`Error determining final disposition: ${e}`);
return "unavailable";
}
}
private get defaultPlanner(): string {
if (this.isMacOS) {
return "macos";
@@ -1085,6 +1112,207 @@ class NixInstallerAction extends DetSysAction {
);
}
}
private async spewEventLog(): Promise<void> {
if (!this.determinate) {
return;
}
const logfile = this.getTemporaryName();
actionsCore.saveState(STATE_EVENT_LOG, logfile);
const stdout = await open(logfile, "a");
const stderr = await open(`${logfile}.stderr`, "a");
actionsCore.debug(`Event log: ${logfile}`);
const opts: SpawnOptions = {
stdio: ["ignore", stdout.fd, stderr.fd],
detached: true,
};
// Start the server. Once it is ready, it will notify us via the notification server.
const daemon = spawn(
"curl",
[
"--no-buffer",
"--unix-socket",
"/nix/var/determinate/determinate-nixd.socket",
"http://localhost/events",
],
opts,
);
actionsCore.saveState(STATE_EVENT_PID, daemon.pid);
daemon.unref();
// Wait a tick in the event loop in order for curl to actually be running
await Promise.resolve();
try {
await stdout.close();
} catch (error) {
actionsCore.info(`Could not close curl's stdout: ${error}`);
}
try {
await stderr.close();
} catch (error) {
actionsCore.info(`Could not close curl's stderr: ${error}`);
}
}
private async slurpEventLog(): Promise<void> {
if (!this.determinate) {
return;
}
try {
const logPath = actionsCore.getState(STATE_EVENT_LOG);
const events = await readMismatchEvents(logPath);
// No point doing any more work if there are no mismatch events
if (events.length === 0) {
actionsCore.debug("No hash mismatches found.");
return;
}
const listing = await getFileListing();
// For each file, search for potentially bad hashes
for (const file of listing) {
const text = await readFile(file, "utf-8");
const lines = text.split("\n");
for (const [index, line] of lines.entries()) {
const lineNumber = index + 1;
for (const event of events) {
const match = line.match(event.search);
if (!match) {
continue;
}
// Allegedly, match.index is optional, so default to 0
const column = (match.index ?? 0) + 1;
actionsCore.error(`This derivation's hash is \`${event.good}\``, {
title: "Determinate Nix detected an incorrect dependency hash.",
file,
startLine: lineNumber,
startColumn: column,
});
}
}
}
} 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 logs: ${error}`);
}
}
async cleanupLogger(): Promise<void> {
const rawPid = actionsCore.getState(STATE_EVENT_PID);
const pid = Number(rawPid);
try {
process.kill(pid);
} catch (error) {
actionsCore.info(`Could not kill pid ${rawPid}: ${error}`);
}
}
}
// Fields we're interested in from the source event
interface MismatchSourceEvent {
readonly drv: string;
readonly good: string;
readonly bad: readonly string[];
}
// Our augmented event with the RegExp to match against the bad hashes
interface MismatchEvent extends MismatchSourceEvent {
readonly search: RegExp;
}
async function readMismatchEvents(logPath: string): Promise<MismatchEvent[]> {
const prefix = "data: ";
// Used to deduplicate events (see below)
const memo = new Set<string>();
const events = (await readFile(logPath, "utf-8"))
.split(/\n/)
.filter((line) => line.startsWith(prefix))
.map((line) => {
// Note: this currently assumes that all events being ingested are mismatches
const json = line.slice(prefix.length);
const source = JSON.parse(json) as MismatchSourceEvent;
// Construct a regular expression to search for any of the hash patterns
// (do it here to avoid creating RegExp objects in a loop below)
const search = new RegExp(
source.bad.map((s) => s.replace(/[+]/g, (ch) => `\\${ch}`)).join("|"),
);
return {
...source,
search,
} satisfies MismatchEvent;
})
.filter((event) => {
// Deduplicate based on the derivation's store path and list of bad hashes.
const key = [event.drv, ...event.bad].join("\0");
if (memo.has(key)) {
false;
}
memo.add(key);
return true;
});
return events;
}
// Get the list of files with potential hash mismatches (limited currently to *.{nix,json,toml})
async function getFileListing(): Promise<readonly string[]> {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
let length = 0;
const child = spawn(
"git",
["ls-files", "-z", "*.nix", "*.json", "*.toml"],
{
stdio: ["ignore", "pipe", "inherit"],
},
);
child.stdout.on("data", (chunk: Buffer) => {
chunks.push(chunk);
length += chunk.length;
});
child.stdout.on("end", () => {
const lines = Buffer.concat(chunks, length)
.toString("utf-8")
.replace(/\0$/, "")
.split(/\0/);
resolve(lines);
});
child.stdout.on("error", reject);
child.on("error", reject);
child.on("exit", (code, signal) => {
// We should consider rejecting the promise here
if (code !== 0) {
actionsCore.warning(
`git ls-files exited suspiciously code=${code}; signal=${signal}`,
);
}
});
});
}
type ExecuteEnvironment = {