nix-installer-action/dist/main.js
Graham Christensen 5d2215c1e1
Rebase on top of detsys-ts for abstracting over install.determinate.systems (#74)
* Rebase on top of detsys-ts for abstracting over install.determinate.systems

* Support the legacy nix-installer-xxx source prefs

* Document source-* opts

* Update deps

* cut duration so it doesn't take forever

* Move the complete step into a finally block

* Test a busted run

* come on ...

* update to the main detsys-ts

* Switch to the delegated execution model

* throw an error to check behavior

* Fixup lint errors

* Drop the forced error
2024-04-11 11:58:56 -04:00

793 lines
33 KiB
JavaScript
Generated
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as actions_core from "@actions/core";
import * as github from "@actions/github";
import * as actions_exec from "@actions/exec";
import { access, writeFile, readFile } from "node:fs/promises";
import { join } from "node:path";
import fs from "node:fs";
import { userInfo } from "node:os";
import stringArgv from "string-argv";
import * as path from "path";
import { IdsToolbox } from "detsys-ts";
import { randomUUID } from "node:crypto";
class NixInstallerAction {
constructor() {
this.idslib = new IdsToolbox({
name: "nix-installer",
fetchStyle: "nix-style",
legacySourcePrefix: "nix-installer",
});
this.platform = get_nix_platform();
this.nix_package_url = action_input_string_or_null("nix-package-url");
this.backtrace = action_input_string_or_null("backtrace");
this.extra_args = action_input_string_or_null("extra-args");
this.extra_conf = action_input_multiline_string_or_null("extra-conf");
this.flakehub = action_input_bool("flakehub");
this.kvm = action_input_bool("kvm");
this.force_docker_shim = action_input_bool("force-docker-shim");
this.github_token = action_input_string_or_null("github-token");
this.github_server_url = action_input_string_or_null("github-server-url");
this.init = action_input_string_or_null("init");
this.local_root = action_input_string_or_null("local-root");
this.log_directives = action_input_string_or_null("log-directives");
this.logger = action_input_string_or_null("logger");
this.ssl_cert_file = action_input_string_or_null("ssl-cert-file");
this.proxy = action_input_string_or_null("proxy");
this.mac_case_sensitive = action_input_string_or_null("mac-case-sensitive");
this.mac_encrypt = action_input_string_or_null("mac-encrypt");
this.mac_root_disk = action_input_string_or_null("mac-root-disk");
this.mac_volume_label = action_input_string_or_null("mac-volume-label");
this.modify_profile = action_input_bool("modify-profile");
this.nix_build_group_id = action_input_number_or_null("nix-build-group-id");
this.nix_build_group_name = action_input_string_or_null("nix-build-group-name");
this.nix_build_user_base = action_input_number_or_null("nix_build-user-base");
this.nix_build_user_count = action_input_number_or_null("nix-build-user-count");
this.nix_build_user_prefix = action_input_string_or_null("nix-build-user-prefix");
this.planner = action_input_string_or_null("planner");
this.reinstall = action_input_bool("reinstall");
this.start_daemon = action_input_bool("start-daemon");
this.trust_runner_user = action_input_bool("trust-runner-user");
}
async detectAndForceDockerShim() {
// Detect if we're in a GHA runner which is Linux, doesn't have Systemd, and does have Docker.
// This is a common case in self-hosted runners, providers like [Namespace](https://namespace.so/),
// and especially GitHub Enterprise Server.
if (process.env.RUNNER_OS !== "Linux") {
if (this.force_docker_shim) {
actions_core.warning("Ignoring force-docker-shim which is set to true, as it is only supported on Linux.");
this.force_docker_shim = false;
}
return;
}
const systemdCheck = fs.statSync("/run/systemd/system", {
throwIfNoEntry: false,
});
if (systemdCheck?.isDirectory()) {
if (this.force_docker_shim) {
actions_core.warning("Systemd is detected, but ignoring it since force-docker-shim is enabled.");
}
else {
this.idslib.addFact("has_systemd", true);
return;
}
}
this.idslib.addFact("has_systemd", false);
actions_core.debug("Linux detected without systemd, testing for Docker with `docker info` as an alternative daemon supervisor.");
this.idslib.addFact("has_docker", false); // Set to false here, and only in the success case do we set it to true
let exit_code;
try {
exit_code = await actions_exec.exec("docker", ["info"], {
silent: true,
listeners: {
stdout: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
stderr: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
},
});
}
catch (e) {
actions_core.debug("Docker not detected, not enabling docker shim.");
return;
}
if (exit_code !== 0) {
if (this.force_docker_shim) {
actions_core.warning("docker info check failed, but trying anyway since force-docker-shim is enabled.");
}
else {
return;
}
}
this.idslib.addFact("has_docker", true);
if (!this.force_docker_shim &&
(await this.detectDockerWithMountedDockerSocket())) {
actions_core.debug("Detected a Docker container with a Docker socket mounted, not enabling docker shim.");
return;
}
actions_core.startGroup("Enabling the Docker shim for running Nix on Linux in CI without Systemd.");
if (this.init !== "none") {
actions_core.info(`Changing init from '${this.init}' to 'none'`);
this.init = "none";
}
if (this.planner !== "linux") {
actions_core.info(`Changing planner from '${this.planner}' to 'linux'`);
this.planner = "linux";
}
this.force_docker_shim = true;
actions_core.endGroup();
}
// Detect if we are running under `act` or some other system which is not using docker-in-docker,
// and instead using a mounted docker socket.
// In the case of the socket mount solution, the shim will cause issues since the given mount paths will
// equate to mount paths on the host, not mount paths to the docker container in question.
async detectDockerWithMountedDockerSocket() {
let cgroups_buffer;
try {
// If we are inside a docker container, the last line of `/proc/self/cgroup` should be
// 0::/docker/$SOME_ID
//
// If we are not, the line will likely be `0::/`
cgroups_buffer = await readFile("/proc/self/cgroup", {
encoding: "utf-8",
});
}
catch (e) {
actions_core.debug(`Did not detect \`/proc/self/cgroup\` existence, bailing on docker container ID detection:\n${e}`);
return false;
}
const cgroups = cgroups_buffer.trim().split("\n");
const last_cgroup = cgroups[cgroups.length - 1];
const last_cgroup_parts = last_cgroup.split(":");
const last_cgroup_path = last_cgroup_parts[last_cgroup_parts.length - 1];
if (!last_cgroup_path.includes("/docker/")) {
actions_core.debug("Did not detect a container ID, bailing on docker.sock detection");
return false;
}
// We are in a docker container, now to determine if this container is visible from
// the `docker` command, and if so, if there is a `docker.socket` mounted.
const last_cgroup_path_parts = last_cgroup_path.split("/");
const container_id = last_cgroup_path_parts[last_cgroup_path_parts.length - 1];
// If we cannot `docker inspect` this discovered container ID, we'll fall through to the `catch` below.
let stdout_buffer = "";
let stderr_buffer = "";
let exit_code;
try {
exit_code = await actions_exec.exec("docker", ["inspect", container_id], {
silent: true,
listeners: {
stdout: (data) => {
stdout_buffer += data.toString("utf-8");
},
stderr: (data) => {
stderr_buffer += data.toString("utf-8");
},
},
});
}
catch (e) {
actions_core.debug(`Could not execute \`docker inspect ${container_id}\`, bailing on docker container inspection:\n${e}`);
return false;
}
if (exit_code !== 0) {
actions_core.debug(`Unable to inspect detected docker container with id \`${container_id}\`, bailing on container inspection (exit ${exit_code}):\n${stderr_buffer}`);
return false;
}
const output = JSON.parse(stdout_buffer);
// `docker inspect $ID` prints an array containing objects.
// In our use case, we should only see 1 item in the array.
if (output.length !== 1) {
actions_core.debug(`Got \`docker inspect ${container_id}\` output which was not one item (was ${output.length}), bailing on docker.sock detection.`);
return false;
}
const item = output[0];
// On this array item we want the `Mounts` field, which is an array
// containing `{ Type, Source, Destination, Mode}`.
// We are looking for a `Destination` ending with `docker.sock`.
const mounts = item["Mounts"];
if (typeof mounts !== "object") {
actions_core.debug(`Got non-object in \`Mounts\` field of \`docker inspect ${container_id}\` output, bailing on docker.sock detection.`);
return false;
}
let found_docker_sock_mount = false;
for (const mount of mounts) {
const destination = mount["Destination"];
if (typeof destination === "string") {
if (destination.endsWith("docker.sock")) {
found_docker_sock_mount = true;
break;
}
}
}
return found_docker_sock_mount;
}
async executionEnvironment() {
const execution_env = {};
execution_env.NIX_INSTALLER_NO_CONFIRM = "true";
execution_env.NIX_INSTALLER_DIAGNOSTIC_ATTRIBUTION = JSON.stringify(this.idslib.getCorrelationHashes());
if (this.backtrace !== null) {
execution_env.RUST_BACKTRACE = this.backtrace;
}
if (this.modify_profile !== null) {
if (this.modify_profile) {
execution_env.NIX_INSTALLER_MODIFY_PROFILE = "true";
}
else {
execution_env.NIX_INSTALLER_MODIFY_PROFILE = "false";
}
}
if (this.nix_build_group_id !== null) {
execution_env.NIX_INSTALLER_NIX_BUILD_GROUP_ID = `${this.nix_build_group_id}`;
}
if (this.nix_build_group_name !== null) {
execution_env.NIX_INSTALLER_NIX_BUILD_GROUP_NAME =
this.nix_build_group_name;
}
if (this.nix_build_user_prefix !== null) {
execution_env.NIX_INSTALLER_NIX_BUILD_USER_PREFIX =
this.nix_build_user_prefix;
}
if (this.nix_build_user_count !== null) {
execution_env.NIX_INSTALLER_NIX_BUILD_USER_COUNT = `${this.nix_build_user_count}`;
}
if (this.nix_build_user_base !== null) {
execution_env.NIX_INSTALLER_NIX_BUILD_USER_ID_BASE = `${this.nix_build_user_count}`;
}
if (this.nix_package_url !== null) {
execution_env.NIX_INSTALLER_NIX_PACKAGE_URL = `${this.nix_package_url}`;
}
if (this.proxy !== null) {
execution_env.NIX_INSTALLER_PROXY = this.proxy;
}
if (this.ssl_cert_file !== null) {
execution_env.NIX_INSTALLER_SSL_CERT_FILE = this.ssl_cert_file;
}
execution_env.NIX_INSTALLER_DIAGNOSTIC_ENDPOINT =
this.idslib.getDiagnosticsUrl()?.toString() || "";
// TODO: Error if the user uses these on not-MacOS
if (this.mac_encrypt !== null) {
if (process.env.RUNNER_OS !== "macOS") {
throw new Error("`mac-encrypt` while `$RUNNER_OS` was not `macOS`");
}
execution_env.NIX_INSTALLER_ENCRYPT = this.mac_encrypt;
}
if (this.mac_case_sensitive !== null) {
if (process.env.RUNNER_OS !== "macOS") {
throw new Error("`mac-case-sensitive` while `$RUNNER_OS` was not `macOS`");
}
execution_env.NIX_INSTALLER_CASE_SENSITIVE = this.mac_case_sensitive;
}
if (this.mac_volume_label !== null) {
if (process.env.RUNNER_OS !== "macOS") {
throw new Error("`mac-volume-label` while `$RUNNER_OS` was not `macOS`");
}
execution_env.NIX_INSTALLER_VOLUME_LABEL = this.mac_volume_label;
}
if (this.mac_root_disk !== null) {
if (process.env.RUNNER_OS !== "macOS") {
throw new Error("`mac-root-disk` while `$RUNNER_OS` was not `macOS`");
}
execution_env.NIX_INSTALLER_ROOT_DISK = this.mac_root_disk;
}
if (this.logger !== null) {
execution_env.NIX_INSTALLER_LOGGER = this.logger;
}
if (this.log_directives !== null) {
execution_env.NIX_INSTALLER_LOG_DIRECTIVES = this.log_directives;
}
// TODO: Error if the user uses these on MacOS
if (this.init !== null) {
if (process.env.RUNNER_OS === "macOS") {
throw new Error("`init` is not a valid option when `$RUNNER_OS` is `macOS`");
}
execution_env.NIX_INSTALLER_INIT = this.init;
}
if (this.start_daemon !== null) {
if (this.start_daemon) {
execution_env.NIX_INSTALLER_START_DAEMON = "true";
}
else {
execution_env.NIX_INSTALLER_START_DAEMON = "false";
}
}
let extra_conf = "";
if (this.github_server_url !== null && this.github_token !== null) {
const server_url = this.github_server_url.replace("https://", "");
extra_conf += `access-tokens = ${server_url}=${this.github_token}`;
extra_conf += "\n";
}
if (this.trust_runner_user !== null) {
const user = userInfo().username;
if (user) {
extra_conf += `trusted-users = root ${user}`;
}
else {
extra_conf += `trusted-users = root`;
}
extra_conf += "\n";
}
if (this.flakehub) {
extra_conf += `netrc-file = ${await this.flakehub_login()}`;
extra_conf += "\n";
}
if (this.extra_conf !== null && this.extra_conf.length !== 0) {
extra_conf += this.extra_conf.join("\n");
extra_conf += "\n";
}
execution_env.NIX_INSTALLER_EXTRA_CONF = extra_conf;
if (process.env.ACT && !process.env.NOT_ACT) {
this.idslib.addFact("in_act", true);
actions_core.info("Detected `$ACT` environment, assuming this is a https://github.com/nektos/act created container, set `NOT_ACT=true` to override this. This will change the setting of the `init` to be compatible with `act`");
execution_env.NIX_INSTALLER_INIT = "none";
}
if (process.env.NSC_VM_ID && !process.env.NOT_NAMESPACE) {
this.idslib.addFact("in_namespace_so", true);
actions_core.info("Detected Namespace runner, assuming this is a https://namespace.so created container, set `NOT_NAMESPACE=true` to override this. This will change the setting of the `init` to be compatible with Namespace");
execution_env.NIX_INSTALLER_INIT = "none";
}
return execution_env;
}
async execute_install(binary_path) {
const execution_env = await this.executionEnvironment();
actions_core.debug(`Execution environment: ${JSON.stringify(execution_env, null, 4)}`);
const args = ["install"];
if (this.planner) {
this.idslib.addFact("nix_installer_planner", this.planner);
args.push(this.planner);
}
else {
this.idslib.addFact("nix_installer_planner", get_default_planner());
args.push(get_default_planner());
}
if (this.extra_args) {
const extra_args = stringArgv(this.extra_args);
args.concat(extra_args);
}
this.idslib.recordEvent("install_nix_start");
const exit_code = await actions_exec.exec(binary_path, args, {
env: {
...execution_env,
...process.env, // To get $PATH, etc
},
});
if (exit_code !== 0) {
this.idslib.recordEvent("install_nix_failure", {
exit_code,
});
throw new Error(`Non-zero exit code of \`${exit_code}\` detected`);
}
this.idslib.recordEvent("install_nix_success");
return exit_code;
}
async install() {
const existing_install = await this.detect_existing();
if (existing_install) {
if (this.reinstall) {
// We need to uninstall, then reinstall
actions_core.info("Nix was already installed, `reinstall` is set, uninstalling for a reinstall");
await this.execute_uninstall();
}
else {
// We're already installed, and not reinstalling, just set GITHUB_PATH and finish early
await this.set_github_path();
actions_core.info("Nix was already installed, using existing install");
return;
}
}
if (this.kvm) {
actions_core.startGroup("Configuring KVM");
if (await this.setup_kvm()) {
actions_core.endGroup();
actions_core.info("\u001b[32m Accelerated KVM is enabled \u001b[33m⚡");
actions_core.exportVariable("DETERMINATE_NIX_KVM", "1");
}
else {
actions_core.endGroup();
actions_core.info("KVM is not available.");
actions_core.exportVariable("DETERMINATE_NIX_KVM", "0");
}
}
// Normal just doing of the install
actions_core.startGroup("Installing Nix");
const binary_path = await this.fetch_binary();
await this.execute_install(binary_path);
actions_core.endGroup();
if (this.force_docker_shim) {
await this.spawnDockerShim();
}
await this.set_github_path();
}
async spawnDockerShim() {
actions_core.startGroup("Configuring the Docker shim as the Nix Daemon's process supervisor");
const images = {
X64: path.join(__dirname, "/../docker-shim/amd64.tar.gz"),
ARM64: path.join(__dirname, "/../docker-shim/arm64.tar.gz"),
};
let arch;
if (process.env.RUNNER_ARCH === "X64") {
arch = "X64";
}
else if (process.env.RUNNER_ARCH === "ARM64") {
arch = "ARM64";
}
else {
throw Error("Architecture not supported in Docker shim mode.");
}
actions_core.debug("Loading image: determinate-nix-shim:latest...");
{
const exit_code = await actions_exec.exec("docker", ["image", "load", "--input", images[arch]], {
silent: true,
listeners: {
stdout: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
stderr: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
},
});
if (exit_code !== 0) {
throw new Error(`Failed to build the shim image, exit code: \`${exit_code}\``);
}
}
{
actions_core.debug("Starting the Nix daemon through Docker...");
this.idslib.recordEvent("start_docker_shim");
const exit_code = await actions_exec.exec("docker", [
"--log-level=debug",
"run",
"--detach",
"--privileged",
"--network=host",
"--userns=host",
"--pid=host",
"--mount",
"type=bind,src=/bin,dst=/bin,readonly",
"--mount",
"type=bind,src=/lib,dst=/lib,readonly",
"--mount",
"type=bind,src=/home,dst=/home,readonly",
"--mount",
"type=bind,src=/tmp,dst=/tmp",
"--mount",
"type=bind,src=/nix,dst=/nix",
"--mount",
"type=bind,src=/etc,dst=/etc,readonly",
"--restart",
"always",
"--init",
"--name",
`determinate-nix-shim-${this.idslib.getUniqueId()}-${randomUUID()}`,
"determinate-nix-shim:latest",
], {
silent: true,
listeners: {
stdline: (data) => {
actions_core.saveState("docker_shim_container_id", data.trimEnd());
},
stdout: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
stderr: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
},
});
if (exit_code !== 0) {
throw new Error(`Failed to start the Nix daemon through Docker, exit code: \`${exit_code}\``);
}
}
actions_core.endGroup();
return;
}
async cleanupDockerShim() {
const container_id = actions_core.getState("docker_shim_container_id");
if (container_id !== "") {
actions_core.startGroup("Cleaning up the Nix daemon's Docker shim");
let cleaned = false;
try {
await actions_exec.exec("docker", ["rm", "--force", container_id]);
cleaned = true;
}
catch {
actions_core.warning("failed to cleanup nix daemon container");
}
if (!cleaned) {
actions_core.info("trying to pkill the container's shim process");
try {
await actions_exec.exec("pkill", [container_id]);
cleaned = true;
}
catch {
actions_core.warning("failed to forcibly kill the container's shim process");
}
}
if (cleaned) {
this.idslib.recordEvent("clean_up_docker_shim");
}
else {
actions_core.warning("Giving up on cleaning up the nix daemon container");
}
actions_core.endGroup();
}
}
async set_github_path() {
// Interim versions of the `nix-installer` crate may have already manipulated `$GITHUB_PATH`, as root even! Accessing that will be an error.
try {
const nix_var_nix_profile_path = "/nix/var/nix/profiles/default/bin";
const home_nix_profile_path = `${process.env.HOME}/.nix-profile/bin`;
actions_core.addPath(nix_var_nix_profile_path);
actions_core.addPath(home_nix_profile_path);
actions_core.info(`Added \`${nix_var_nix_profile_path}\` and \`${home_nix_profile_path}\` to \`$GITHUB_PATH\``);
}
catch (error) {
actions_core.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.");
}
}
async flakehub_login() {
this.idslib.recordEvent("login_to_flakehub");
const netrc_path = `${process.env["RUNNER_TEMP"]}/determinate-nix-installer-netrc`;
const jwt = await actions_core.getIDToken("api.flakehub.com");
await writeFile(netrc_path, [
`machine api.flakehub.com login flakehub password ${jwt}`,
`machine flakehub.com login flakehub password ${jwt}`,
].join("\n"));
actions_core.info("Logging in to FlakeHub.");
// the join followed by a match on ^... looks silly, but extra_config
// could contain multi-line values
if (this.extra_conf?.join("\n").match(/^netrc-file/m)) {
actions_core.warning("Logging in to FlakeHub conflicts with the Nix option `netrc-file`.");
}
return netrc_path;
}
async execute_uninstall() {
this.idslib.recordEvent("uninstall");
const exit_code = await actions_exec.exec(`/nix/nix-installer`, ["uninstall"], {
env: {
NIX_INSTALLER_NO_CONFIRM: "true",
...process.env, // To get $PATH, etc
},
});
if (exit_code !== 0) {
throw new Error(`Non-zero exit code of \`${exit_code}\` detected`);
}
return exit_code;
}
async detect_existing() {
const receipt_path = "/nix/receipt.json";
try {
await access(receipt_path);
// There is a /nix/receipt.json
return true;
}
catch {
// No /nix/receipt.json
return false;
}
}
async setup_kvm() {
this.idslib.recordEvent("setup_kvm");
const current_user = userInfo();
const is_root = current_user.uid === 0;
const maybe_sudo = is_root ? "" : "sudo";
const kvm_rules = "/etc/udev/rules.d/99-determinate-nix-installer-kvm.rules";
try {
const write_file_exit_code = await actions_exec.exec("sh", [
"-c",
`echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | ${maybe_sudo} tee ${kvm_rules} > /dev/null`,
], {
silent: true,
listeners: {
stdout: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
stderr: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
},
});
if (write_file_exit_code !== 0) {
throw new Error(`Non-zero exit code of \`${write_file_exit_code}\` detected while writing '${kvm_rules}'`);
}
const debug_root_run_throw = async (action, command, args) => {
if (!is_root) {
args = [command, ...args];
command = "sudo";
}
const reload_exit_code = await actions_exec.exec(command, args, {
silent: true,
listeners: {
stdout: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
stderr: (data) => {
const trimmed = data.toString("utf-8").trimEnd();
if (trimmed.length >= 0) {
actions_core.debug(trimmed);
}
},
},
});
if (reload_exit_code !== 0) {
throw new Error(`Non-zero exit code of \`${reload_exit_code}\` detected while ${action}.`);
}
};
await debug_root_run_throw("reloading udev rules", "udevadm", [
"control",
"--reload-rules",
]);
await debug_root_run_throw("triggering udev against kvm", "udevadm", [
"trigger",
"--name-match=kvm",
]);
return true;
}
catch (error) {
if (is_root) {
await actions_exec.exec("rm", ["-f", kvm_rules]);
}
else {
await actions_exec.exec("sudo", ["rm", "-f", kvm_rules]);
}
return false;
}
}
async fetch_binary() {
if (!this.local_root) {
return await this.idslib.fetchExecutable();
}
else {
const local_path = join(this.local_root, `nix-installer-${this.platform}`);
actions_core.info(`Using binary ${local_path}`);
return local_path;
}
}
async report_overall() {
try {
this.idslib.recordEvent("conclude_workflow", {
conclusion: await this.get_workflow_conclusion(),
});
}
catch (error) {
actions_core.debug(`Error submitting post-run diagnostics report: ${error}`);
}
}
async get_workflow_conclusion() {
if (this.github_token == null) {
return undefined;
}
try {
const octokit = github.getOctokit(this.github_token);
const jobs = await octokit.paginate(octokit.rest.actions.listJobsForWorkflowRun, {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
run_id: github.context.runId,
});
actions_core.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 (error) {
actions_core.debug(`Error determining final disposition: ${error}`);
return "unavailable";
}
}
}
function get_nix_platform() {
const env_os = process.env.RUNNER_OS;
const env_arch = process.env.RUNNER_ARCH;
if (env_os === "macOS" && env_arch === "X64") {
return "x86_64-darwin";
}
else if (env_os === "macOS" && env_arch === "ARM64") {
return "aarch64-darwin";
}
else if (env_os === "Linux" && env_arch === "X64") {
return "x86_64-linux";
}
else if (env_os === "Linux" && env_arch === "ARM64") {
return "aarch64-linux";
}
else {
throw new Error(`Unsupported \`RUNNER_OS\` (currently \`${env_os}\`) and \`RUNNER_ARCH\` (currently \`${env_arch}\`) combination`);
}
}
function get_default_planner() {
const env_os = process.env.RUNNER_OS;
if (env_os === "macOS") {
return "macos";
}
else if (env_os === "Linux") {
return "linux";
}
else {
throw new Error(`Unsupported \`RUNNER_OS\` (currently \`${env_os}\`)`);
}
}
function action_input_string_or_null(name) {
const value = actions_core.getInput(name);
if (value === "") {
return null;
}
else {
return value;
}
}
function action_input_multiline_string_or_null(name) {
const value = actions_core.getMultilineInput(name);
if (value.length === 0) {
return null;
}
else {
return value;
}
}
function action_input_number_or_null(name) {
const value = actions_core.getInput(name);
if (value === "") {
return null;
}
else {
return Number(value);
}
}
function action_input_bool(name) {
return actions_core.getBooleanInput(name);
}
function main() {
const installer = new NixInstallerAction();
installer.idslib.onMain(async () => {
await installer.detectAndForceDockerShim();
await installer.install();
});
installer.idslib.onPost(async () => {
await installer.cleanupDockerShim();
await installer.report_overall();
});
installer.idslib.execute();
}
main();