diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78054af..3b4ff03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,9 +11,9 @@ jobs: runs-on: ubuntu-22.04 needs: - check-dist-up-to-date - - install-nix-linux - - install-nix-macos + - install-nix - install-with-non-default-source-inputs + - install-no-id-token # 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. @@ -44,14 +44,21 @@ 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: matrix: runner: - ubuntu-latest - nscloud-ubuntu-22.04-amd64-4x16 - namespace-profile-default-arm64 + # - macos-12-large # determinate-nixd is broken on macos-12 + - macos-13-large + - macos-14-large + - macos-14-xlarge # arm64 + determinate: + - true + - false runs-on: ${{ matrix.runner }} permissions: contents: read @@ -65,6 +72,7 @@ jobs: log-directives: nix_installer=trace backtrace: full _internal-strict-mode: true + determinate: ${{ matrix.determinate }} - name: echo $PATH run: echo $PATH @@ -82,85 +90,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 +99,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 +108,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 +127,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,9 +138,10 @@ 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 + 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" install-with-non-default-source-inputs: name: Install Nix using non-default source-${{ matrix.inputs.key }} @@ -236,3 +167,13 @@ jobs: _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 }} + + install-no-id-token: + name: Install Nix without an ID token + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: ./ + with: + _internal-strict-mode: true + determinate: true diff --git a/README.md b/README.md index 68d085a..1b833ae 100644 --- a/README.md +++ b/README.md @@ -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 }}` | diff --git a/action.yml b/action.yml index f1b4d03..aa5609e 100644 --- a/action.yml +++ b/action.yml @@ -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: diff --git a/dist/index.js b/dist/index.js index 1f64c38..7428339 100644 --- a/dist/index.js +++ b/dist/index.js @@ -103177,11 +103177,13 @@ var EVENT_CLEAN_UP_DOCKER_SHIM = "clean_up_docker_shim"; var EVENT_START_DOCKER_SHIM = "start_docker_shim"; var EVENT_LOGIN_TO_FLAKEHUB = "login_to_flakehub"; var EVENT_CONCLUDE_WORKFLOW = "conclude_workflow"; +var FACT_DETERMINATE_NIX = "determinate_nix"; var FACT_HAS_DOCKER = "has_docker"; var FACT_HAS_SYSTEMD = "has_systemd"; var FACT_IN_ACT = "in_act"; var FACT_IN_NAMESPACE_SO = "in_namespace_so"; var FACT_NIX_INSTALLER_PLANNER = "nix_installer_planner"; +var FLAG_DETERMINATE = "--determinate"; var NixInstallerAction = class extends DetSysAction { constructor() { super({ @@ -103191,12 +103193,12 @@ var NixInstallerAction = class extends DetSysAction { requireNix: "ignore", diagnosticsSuffix: "diagnostic" }); + this.determinate = inputs_exports.getBool("determinate") || inputs_exports.getBool("flakehub"); this.platform = platform_exports.getNixPlatform(platform_exports.getArchOs()); this.nixPackageUrl = inputs_exports.getStringOrNull("nix-package-url"); this.backtrace = inputs_exports.getStringOrNull("backtrace"); this.extraArgs = inputs_exports.getStringOrNull("extra-args"); this.extraConf = inputs_exports.getMultilineStringOrNull("extra-conf"); - this.flakehub = inputs_exports.getBool("flakehub"); this.kvm = inputs_exports.getBool("kvm"); this.forceDockerShim = inputs_exports.getBool("force-docker-shim"); this.githubToken = inputs_exports.getStringOrNull("github-token"); @@ -103563,15 +103565,6 @@ ${stderrBuffer}` } extraConf += "\n"; } - if (this.flakehub) { - try { - const flakeHubNetrcFile = await this.flakehubLogin(); - extraConf += `netrc-file = ${flakeHubNetrcFile}`; - extraConf += "\n"; - } catch (e) { - core.warning(`Failed to set up FlakeHub: ${e}`); - } - } if (this.extraConf !== null && this.extraConf.length !== 0) { extraConf += this.extraConf.join("\n"); extraConf += "\n"; @@ -103593,11 +103586,7 @@ ${stderrBuffer}` } return executionEnv; } - async executeInstall(binaryPath) { - const executionEnv = await this.executionEnvironment(); - core.debug( - `Execution environment: ${JSON.stringify(executionEnv, null, 4)}` - ); + get installerArgs() { const args = ["install"]; if (this.planner) { this.addFact(FACT_NIX_INSTALLER_PLANNER, this.planner); @@ -103610,8 +103599,27 @@ ${stderrBuffer}` const extraArgs = parseArgsStringToArgv(this.extraArgs); args.push(...extraArgs); } + if (this.determinate) { + this.addFact(FACT_DETERMINATE_NIX, true); + core.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; + } + async executeInstall(binaryPath) { + const executionEnv = await this.executionEnvironment(); + core.debug( + `Execution environment: ${JSON.stringify(executionEnv, null, 4)}` + ); this.recordEvent(EVENT_INSTALL_NIX_START); - const exitCode = await exec.exec(binaryPath, args, { + const exitCode = await exec.exec(binaryPath, this.installerArgs, { env: { ...executionEnv, ...process.env @@ -103660,6 +103668,9 @@ ${stderrBuffer}` if (this.forceDockerShim) { await this.spawnDockerShim(); } + if (this.determinate) { + await this.flakehubLogin(); + } await this.setGithubPath(); } async spawnDockerShim() { @@ -103735,6 +103746,10 @@ ${stderrBuffer}` dir: "/tmp", readOnly: false }, + { + dir: "/usr", + readOnly: true + }, { dir: "/nix", readOnly: false @@ -103755,6 +103770,13 @@ ${stderrBuffer}` ); } } + 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 exec.exec( "docker", @@ -103771,7 +103793,7 @@ ${stderrBuffer}` "--init", "--name", `determinate-nix-shim-${this.getUniqueId()}-${(0,external_node_crypto_namespaceObject.randomUUID)()}` - ].concat(mountArguments).concat(["determinate-nix-shim:latest"]), + ].concat(plausibleDeterminateOptions).concat(mountArguments).concat(["determinate-nix-shim:latest"]).concat(plausibleDeterminateArguments), { silent: true, listeners: { @@ -103850,28 +103872,18 @@ ${stderrBuffer}` } } async flakehubLogin() { - this.recordEvent(EVENT_LOGIN_TO_FLAKEHUB); - const netrcPath = `${process.env["RUNNER_TEMP"]}/determinate-nix-installer-netrc`; - const jwt = await core.getIDToken("api.flakehub.com"); - await (0,promises_namespaceObject.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 (0,promises_namespaceObject.mkdir)(flakehubAuthDir, { recursive: true }); - const flakehubAuthPath = `${flakehubAuthDir}/auth`; - await (0,promises_namespaceObject.writeFile)(flakehubAuthPath, jwt); - core.info("Logging in to FlakeHub."); - if (this.extraConf?.join("\n").match(/^netrc-file/m)) { - core.warning( - "Logging in to FlakeHub conflicts with the Nix option `netrc-file`." - ); + if (process.env["ACTIONS_ID_TOKEN_REQUEST_URL"] && process.env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"]) { + core.startGroup("Logging in to FlakeHub"); + this.recordEvent(EVENT_LOGIN_TO_FLAKEHUB); + try { + await exec.exec(`determinate-nixd`, ["login", "github-action"]); + } catch (e) { + this.recordEvent("flakehub-login:failure", { + exception: stringifyError(e) + }); + } + core.endGroup(); } - return netrcPath; } async executeUninstall() { this.recordEvent(EVENT_UNINSTALL_NIX); diff --git a/src/index.ts b/src/index.ts index 5f09ab0..fe8f55f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ 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, readFile } from "node:fs/promises"; import { join } from "node:path"; import fs from "node:fs"; import { userInfo } from "node:os"; @@ -29,12 +29,16 @@ const EVENT_LOGIN_TO_FLAKEHUB = "login_to_flakehub"; const EVENT_CONCLUDE_WORKFLOW = "conclude_workflow"; // 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"; +// Flags +const FLAG_DETERMINATE = "--determinate"; + type WorkflowConclusion = | "success" | "failure" @@ -43,12 +47,12 @@ type WorkflowConclusion = | "no-jobs"; 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; @@ -84,12 +88,13 @@ 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"); @@ -519,15 +524,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 +549,9 @@ class NixInstallerAction extends DetSysAction { return executionEnv; } - private async executeInstall(binaryPath: string): Promise { - 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 +565,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 { + 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 @@ -623,7 +640,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,6 +648,11 @@ class NixInstallerAction extends DetSysAction { if (this.forceDockerShim) { await this.spawnDockerShim(); } + + if (this.determinate) { + await this.flakehubLogin(); + } + await this.setGithubPath(); } @@ -714,6 +735,10 @@ class NixInstallerAction extends DetSysAction { dir: "/tmp", readOnly: false, }, + { + dir: "/usr", + readOnly: true, + }, { dir: "/nix", readOnly: false, @@ -737,6 +762,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 +787,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: { @@ -844,38 +879,22 @@ class NixInstallerAction extends DetSysAction { } } - async flakehubLogin(): Promise { - 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 { + 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) { + this.recordEvent("flakehub-login:failure", { + exception: stringifyError(e), + }); + } + actionsCore.endGroup(); } - - return netrcPath; } async executeUninstall(): Promise {