Use Result-based version of detsys-ts #81

Closed
lucperkins wants to merge 4 commits from detsys-ts-results into main
4 changed files with 730 additions and 181 deletions

765
dist/index.js generated vendored

File diff suppressed because it is too large Load diff

View file

@ -28,7 +28,7 @@
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/github": "^5.1.1", "@actions/github": "^5.1.1",
"detsys-ts": "github:DeterminateSystems/detsys-ts", "detsys-ts": "github:DeterminateSystems/detsys-ts#ts-results",
"string-argv": "^0.3.2" "string-argv": "^0.3.2"
}, },
"devDependencies": { "devDependencies": {

View file

@ -15,8 +15,8 @@ dependencies:
specifier: ^5.1.1 specifier: ^5.1.1
version: 5.1.1 version: 5.1.1
detsys-ts: detsys-ts:
specifier: github:DeterminateSystems/detsys-ts specifier: github:DeterminateSystems/detsys-ts#ts-results
version: github.com/DeterminateSystems/detsys-ts/cd38b227c4d6faca10aed591b1f8863ef7b93dce version: github.com/DeterminateSystems/detsys-ts/abd5b78c8e24857812017ab2da0f962095811f10
string-argv: string-argv:
specifier: ^0.3.2 specifier: ^0.3.2
version: 0.3.2 version: 0.3.2
@ -5019,6 +5019,13 @@ packages:
} }
dev: true dev: true
/ts-results@3.3.0:
resolution:
{
integrity: sha512-FWqxGX2NHp5oCyaMd96o2y2uMQmSu8Dey6kvyuFdRJ2AzfmWo3kWa4UsPlCGlfQ/qu03m09ZZtppMoY8EMHuiA==,
}
dev: false
/tsconfig-paths@3.15.0: /tsconfig-paths@3.15.0:
resolution: resolution:
{ {
@ -5440,10 +5447,10 @@ packages:
engines: { node: ">=10" } engines: { node: ">=10" }
dev: true dev: true
github.com/DeterminateSystems/detsys-ts/cd38b227c4d6faca10aed591b1f8863ef7b93dce: github.com/DeterminateSystems/detsys-ts/abd5b78c8e24857812017ab2da0f962095811f10:
resolution: resolution:
{ {
tarball: https://codeload.github.com/DeterminateSystems/detsys-ts/tar.gz/cd38b227c4d6faca10aed591b1f8863ef7b93dce, tarball: https://codeload.github.com/DeterminateSystems/detsys-ts/tar.gz/abd5b78c8e24857812017ab2da0f962095811f10,
} }
name: detsys-ts name: detsys-ts
version: 1.0.0 version: 1.0.0
@ -5452,6 +5459,7 @@ packages:
"@actions/core": 1.10.1 "@actions/core": 1.10.1
"@actions/exec": 1.1.1 "@actions/exec": 1.1.1
got: 14.2.1 got: 14.2.1
ts-results: 3.3.0
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
dev: false dev: false

View file

@ -7,7 +7,7 @@ import fs from "node:fs";
import { userInfo } from "node:os"; import { userInfo } from "node:os";
import stringArgv from "string-argv"; import stringArgv from "string-argv";
import * as path from "path"; import * as path from "path";
import { IdsToolbox, inputs, platform } from "detsys-ts"; import { IdsToolbox, inputs, platform, result } from "detsys-ts";
import { randomUUID } from "node:crypto"; import { randomUUID } from "node:crypto";
// Nix installation events // Nix installation events
@ -73,9 +73,22 @@ class NixInstallerAction {
fetchStyle: "nix-style", fetchStyle: "nix-style",
legacySourcePrefix: "nix-installer", legacySourcePrefix: "nix-installer",
requireNix: "ignore", requireNix: "ignore",
hookMain: async () => {
await this.detectAndForceDockerShim();
await this.install();
return result.SUCCESS;
},
hookPost: async () => {
await this.cleanupDockerShim();
await this.reportOverall();
return result.SUCCESS;
},
}); });
this.platform = platform.getNixPlatform(platform.getArchOs()); const archOs = result.valueOrFail(platform.getArchOs());
const nixPlatform = result.valueOrFail(platform.getNixPlatform(archOs));
this.platform = nixPlatform;
this.nixPackageUrl = inputs.getStringOrNull("nix-package-url"); this.nixPackageUrl = inputs.getStringOrNull("nix-package-url");
this.backtrace = inputs.getStringOrNull("backtrace"); this.backtrace = inputs.getStringOrNull("backtrace");
this.extraArgs = inputs.getStringOrNull("extra-args"); this.extraArgs = inputs.getStringOrNull("extra-args");
@ -307,7 +320,9 @@ class NixInstallerAction {
return foundDockerSockMount; return foundDockerSockMount;
} }
private async executionEnvironment(): Promise<ExecuteEnvironment> { private async executionEnvironment(): Promise<
result.Result<ExecuteEnvironment>
> {
const executionEnv: ExecuteEnvironment = {}; const executionEnv: ExecuteEnvironment = {};
const runnerOs = process.env["RUNNER_OS"]; const runnerOs = process.env["RUNNER_OS"];
@ -366,14 +381,14 @@ class NixInstallerAction {
// TODO: Error if the user uses these on not-MacOS // TODO: Error if the user uses these on not-MacOS
if (this.macEncrypt !== null) { if (this.macEncrypt !== null) {
if (runnerOs !== "macOS") { if (runnerOs !== "macOS") {
throw new Error("`mac-encrypt` while `$RUNNER_OS` was not `macOS`"); return result.Err("`mac-encrypt` while `$RUNNER_OS` was not `macOS`");
} }
executionEnv.NIX_INSTALLER_ENCRYPT = this.macEncrypt; executionEnv.NIX_INSTALLER_ENCRYPT = this.macEncrypt;
} }
if (this.macCaseSensitive !== null) { if (this.macCaseSensitive !== null) {
if (runnerOs !== "macOS") { if (runnerOs !== "macOS") {
throw new Error( return result.Err(
"`mac-case-sensitive` while `$RUNNER_OS` was not `macOS`", "`mac-case-sensitive` while `$RUNNER_OS` was not `macOS`",
); );
} }
@ -382,7 +397,7 @@ class NixInstallerAction {
if (this.macVolumeLabel !== null) { if (this.macVolumeLabel !== null) {
if (runnerOs !== "macOS") { if (runnerOs !== "macOS") {
throw new Error( return result.Err(
"`mac-volume-label` while `$RUNNER_OS` was not `macOS`", "`mac-volume-label` while `$RUNNER_OS` was not `macOS`",
); );
} }
@ -391,7 +406,7 @@ class NixInstallerAction {
if (this.macRootDisk !== null) { if (this.macRootDisk !== null) {
if (runnerOs !== "macOS") { if (runnerOs !== "macOS") {
throw new Error("`mac-root-disk` while `$RUNNER_OS` was not `macOS`"); return result.Err("`mac-root-disk` while `$RUNNER_OS` was not `macOS`");
} }
executionEnv.NIX_INSTALLER_ROOT_DISK = this.macRootDisk; executionEnv.NIX_INSTALLER_ROOT_DISK = this.macRootDisk;
} }
@ -407,7 +422,7 @@ class NixInstallerAction {
// TODO: Error if the user uses these on MacOS // TODO: Error if the user uses these on MacOS
if (this.init !== null) { if (this.init !== null) {
if (runnerOs === "macOS") { if (runnerOs === "macOS") {
throw new Error( return result.Err(
"`init` is not a valid option when `$RUNNER_OS` is `macOS`", "`init` is not a valid option when `$RUNNER_OS` is `macOS`",
); );
} }
@ -468,11 +483,13 @@ class NixInstallerAction {
executionEnv.NIX_INSTALLER_INIT = "none"; executionEnv.NIX_INSTALLER_INIT = "none";
} }
return executionEnv; return result.Ok(executionEnv);
} }
private async executeInstall(binaryPath: string): Promise<number> { private async executeInstall(
const executionEnv = await this.executionEnvironment(); binaryPath: string,
): Promise<result.Result<number>> {
const executionEnv = result.valueOrFail(await this.executionEnvironment());
actionsCore.debug( actionsCore.debug(
`Execution environment: ${JSON.stringify(executionEnv, null, 4)}`, `Execution environment: ${JSON.stringify(executionEnv, null, 4)}`,
); );
@ -482,8 +499,9 @@ class NixInstallerAction {
this.idslib.addFact(FACT_NIX_INSTALLER_PLANNER, this.planner); this.idslib.addFact(FACT_NIX_INSTALLER_PLANNER, this.planner);
args.push(this.planner); args.push(this.planner);
} else { } else {
this.idslib.addFact(FACT_NIX_INSTALLER_PLANNER, getDefaultPlanner()); const defaultPlanner = result.valueOrFail(getDefaultPlanner());
args.push(getDefaultPlanner()); this.idslib.addFact(FACT_NIX_INSTALLER_PLANNER, defaultPlanner);
args.push(defaultPlanner);
} }
if (this.extraArgs) { if (this.extraArgs) {
@ -503,12 +521,12 @@ class NixInstallerAction {
this.idslib.recordEvent(EVENT_INSTALL_NIX_FAILURE, { this.idslib.recordEvent(EVENT_INSTALL_NIX_FAILURE, {
exitCode, exitCode,
}); });
throw new Error(`Non-zero exit code of \`${exitCode}\` detected`); return result.Err(`Non-zero exit code of \`${exitCode}\` detected`);
} }
this.idslib.recordEvent(EVENT_INSTALL_NIX_SUCCESS); this.idslib.recordEvent(EVENT_INSTALL_NIX_SUCCESS);
return exitCode; return result.Ok(exitCode);
} }
async install(): Promise<void> { async install(): Promise<void> {
@ -553,7 +571,7 @@ class NixInstallerAction {
await this.setGithubPath(); await this.setGithubPath();
} }
async spawnDockerShim(): Promise<void> { async spawnDockerShim(): Promise<result.Result<void>> {
actionsCore.startGroup( actionsCore.startGroup(
"Configuring the Docker shim as the Nix Daemon's process supervisor", "Configuring the Docker shim as the Nix Daemon's process supervisor",
); );
@ -571,7 +589,7 @@ class NixInstallerAction {
} else if (runnerArch === "ARM64") { } else if (runnerArch === "ARM64") {
arch = "ARM64"; arch = "ARM64";
} else { } else {
throw Error("Architecture not supported in Docker shim mode."); return result.Err("Architecture not supported in Docker shim mode.");
} }
actionsCore.debug("Loading image: determinate-nix-shim:latest..."); actionsCore.debug("Loading image: determinate-nix-shim:latest...");
{ {
@ -598,7 +616,7 @@ class NixInstallerAction {
); );
if (exitCode !== 0) { if (exitCode !== 0) {
throw new Error( return result.Err(
`Failed to build the shim image, exit code: \`${exitCode}\``, `Failed to build the shim image, exit code: \`${exitCode}\``,
); );
} }
@ -659,7 +677,7 @@ class NixInstallerAction {
); );
if (exitCode !== 0) { if (exitCode !== 0) {
throw new Error( return result.Err(
`Failed to start the Nix daemon through Docker, exit code: \`${exitCode}\``, `Failed to start the Nix daemon through Docker, exit code: \`${exitCode}\``,
); );
} }
@ -667,8 +685,9 @@ class NixInstallerAction {
actionsCore.endGroup(); actionsCore.endGroup();
return; return result.Ok(undefined);
} }
async cleanupDockerShim(): Promise<void> { async cleanupDockerShim(): Promise<void> {
const containerId = actionsCore.getState("docker_shim_container_id"); const containerId = actionsCore.getState("docker_shim_container_id");
@ -751,7 +770,7 @@ class NixInstallerAction {
return netrcPath; return netrcPath;
} }
async executeUninstall(): Promise<number> { async executeUninstall(): Promise<result.Result<number>> {
this.idslib.recordEvent(EVENT_UNINSTALL_NIX); this.idslib.recordEvent(EVENT_UNINSTALL_NIX);
const exitCode = await actionsExec.exec( const exitCode = await actionsExec.exec(
`/nix/nix-installer`, `/nix/nix-installer`,
@ -765,10 +784,10 @@ class NixInstallerAction {
); );
if (exitCode !== 0) { if (exitCode !== 0) {
throw new Error(`Non-zero exit code of \`${exitCode}\` detected`); return result.Err(`Non-zero exit code of \`${exitCode}\` detected`);
} }
return exitCode; return result.Ok(exitCode);
} }
async detectExisting(): Promise<boolean> { async detectExisting(): Promise<boolean> {
@ -783,7 +802,7 @@ class NixInstallerAction {
} }
} }
private async setupKvm(): Promise<boolean> { private async setupKvm(): Promise<result.Result<boolean>> {
this.idslib.recordEvent(EVENT_SETUP_KVM); this.idslib.recordEvent(EVENT_SETUP_KVM);
const currentUser = userInfo(); const currentUser = userInfo();
const isRoot = currentUser.uid === 0; const isRoot = currentUser.uid === 0;
@ -817,7 +836,7 @@ class NixInstallerAction {
); );
if (writeFileExitCode !== 0) { if (writeFileExitCode !== 0) {
throw new Error( return result.Err(
`Non-zero exit code of \`${writeFileExitCode}\` detected while writing '${kvmRules}'`, `Non-zero exit code of \`${writeFileExitCode}\` detected while writing '${kvmRules}'`,
); );
} }
@ -826,7 +845,7 @@ class NixInstallerAction {
action: string, action: string,
command: string, command: string,
args: string[], args: string[],
): Promise<void> => { ): Promise<result.Result<void>> => {
if (!isRoot) { if (!isRoot) {
args = [command, ...args]; args = [command, ...args];
command = "sudo"; command = "sudo";
@ -850,23 +869,29 @@ class NixInstallerAction {
}); });
if (reloadExitCode !== 0) { if (reloadExitCode !== 0) {
throw new Error( return result.Err(
`Non-zero exit code of \`${reloadExitCode}\` detected while ${action}.`, `Non-zero exit code of \`${reloadExitCode}\` detected while ${action}.`,
); );
} }
return result.Ok(undefined);
}; };
await debugRootRunThrow("reloading udev rules", "udevadm", [ await result.failOnError(
debugRootRunThrow("reloading udev rules", "udevadm", [
"control", "control",
"--reload-rules", "--reload-rules",
]); ]),
);
await debugRootRunThrow("triggering udev against kvm", "udevadm", [ await result.failOnError(
debugRootRunThrow("triggering udev against kvm", "udevadm", [
"trigger", "trigger",
"--name-match=kvm", "--name-match=kvm",
]); ]),
);
return true; return result.Ok(true);
} catch { } catch {
if (isRoot) { if (isRoot) {
await actionsExec.exec("rm", ["-f", kvmRules]); await actionsExec.exec("rm", ["-f", kvmRules]);
@ -874,7 +899,7 @@ class NixInstallerAction {
await actionsExec.exec("sudo", ["rm", "-f", kvmRules]); await actionsExec.exec("sudo", ["rm", "-f", kvmRules]);
} }
return false; return result.Ok(false);
} }
} }
@ -974,31 +999,20 @@ type ExecuteEnvironment = {
NIX_INSTALLER_LOGGER?: string; NIX_INSTALLER_LOGGER?: string;
}; };
function getDefaultPlanner(): string { function getDefaultPlanner(): result.Result<string> {
const envOs = process.env["RUNNER_OS"]; const envOs = process.env["RUNNER_OS"];
if (envOs === "macOS") { if (envOs === "macOS") {
return "macos"; return result.Ok("macos");
} else if (envOs === "Linux") { } else if (envOs === "Linux") {
return "linux"; return result.Ok("linux");
} else { } else {
throw new Error(`Unsupported \`RUNNER_OS\` (currently \`${envOs}\`)`); return result.Err(`Unsupported \`RUNNER_OS\` (currently \`${envOs}\`)`);
} }
} }
function main(): void { function main(): void {
const installer = new NixInstallerAction(); const installer = new NixInstallerAction();
installer.idslib.onMain(async () => {
await installer.detectAndForceDockerShim();
await installer.install();
});
installer.idslib.onPost(async () => {
await installer.cleanupDockerShim();
await installer.reportOverall();
});
installer.idslib.execute(); installer.idslib.execute();
} }