Compare commits

...

45 Commits
v3 ... v6

Author SHA1 Message Date
Graham Christensen bc7b192574 Update the action to use node20 (#41)
* Update nixpkgs to unstable, and nodejs to latest

* Upgrade the action to use node20
2023-10-12 11:19:09 -04:00
Graham Christensen 07ebb8d274 Support private flakes on FlakeHub (#38)
Use the GitHub Actions-issued JWT to authenticate with FlakeHub.
The repository will be granted its due permissions on FlakeHub,
and be able to pull the user's private flakes.
2023-10-04 17:35:16 -04:00
Graham Christensen d654f7b93a Post run diagnostics (#39) 2023-10-04 15:31:05 -04:00
Ana Hobden 3ebd1aebb4 Merge pull request #34 from DeterminateSystems/hoverbear/ds-893-make-installer-action-use-typescript
Typescript rewrite
2023-07-17 11:13:55 -07:00
Ana Hobden 15d47b15b5 Use released installer, not main 2023-07-17 11:00:58 -07:00
Ana Hobden c683402ad5 Tweak messaging when nix-installer makes GITHUB_PATH not action writable 2023-07-17 10:56:49 -07:00
Ana Hobden 5103a0df22 test against main 2023-07-14 12:29:59 -07:00
Ana Hobden d29c939172 Better messaging when we set GITHUB_PATH 2023-07-14 09:07:56 -07:00
Ana Hobden bdec4311cf Fixup wording, test again against fixed pr 2023-07-14 08:57:09 -07:00
Ana Hobden a5bdb5e70c Tweak the warning 2023-07-14 08:27:14 -07:00
Ana Hobden 15d6a6a814 More handling around old nix-installer versions 2023-07-14 08:03:38 -07:00
Ana Hobden b6d507aa32 Handle main branch 2023-07-14 07:58:53 -07:00
Ana Hobden 09be496fdd Add guardrail against old nix-installer versions that set GITHUB_PATH 2023-07-14 07:58:00 -07:00
Ana Hobden 017103f261 Use fixed nix-installer branch 2023-07-13 13:18:57 -07:00
Ana Hobden d7dba96ff9 Run npm build... 2023-07-13 11:33:33 -07:00
Ana Hobden 47de24d14a Set path on happy install path 2023-07-13 11:29:35 -07:00
Ana Hobden 3d54c0c487 More review feedback 2023-07-13 11:10:08 -07:00
Ana Hobden ea2f18de2f Review feedback 2023-07-13 11:03:55 -07:00
Ana Hobden 2b06132852 Fixup descriptions 2023-07-13 10:23:02 -07:00
Ana Hobden 20c8a30d1b Merge branch 'main' into hoverbear/ds-893-make-installer-action-use-typescript 2023-07-13 10:12:33 -07:00
Ana Hobden 5eda0a4d27 Use less esoteric formatting settings 2023-07-13 10:11:00 -07:00
Ana Hobden 37c0d0291e Merge pull request #35 from DeterminateSystems/fix-custom-planner
Fix specifying a custom planner
2023-07-13 10:07:27 -07:00
Ana Hobden 42bc6b2199 Remove steam-deck test 2023-07-13 09:58:04 -07:00
Ana Hobden 6479c3bcee Specify steam-deck 2023-07-13 09:26:06 -07:00
Ana Hobden 546f09255a Add output 2023-07-13 09:18:31 -07:00
Ana Hobden 27df44f1c8 Add custom planner test 2023-07-13 08:40:43 -07:00
Ana Hobden 13153f8246 Fix specifying a custom planner 2023-07-13 08:38:42 -07:00
Ana Hobden 195f007ef0 Add logging directives 2023-07-12 13:17:46 -07:00
Ana Hobden 8910100981 Refine trimming a bit 2023-07-12 11:21:13 -07:00
Ana Hobden 35759c8458 Trim logging and encode stdout/stderr 2023-07-12 11:09:28 -07:00
Ana Hobden 3bae1cc12f Tidy up logging 2023-07-12 10:52:09 -07:00
Ana Hobden 339fb7942a Info log 2023-07-12 10:28:15 -07:00
Ana Hobden 8e89511534 Add extra_conf, reinstall 2023-07-12 10:24:35 -07:00
Ana Hobden 74554e45ae Merge branch 'main' into hoverbear/ds-893-make-installer-action-use-typescript 2023-07-11 10:40:02 -07:00
Ana Hobden 79baecff38 Typescript rewrite 2023-07-11 10:36:41 -07:00
Ana Hobden 22b6142a17 Merge pull request #32 from DeterminateSystems/fix-build-user-prefix-and-base-logic
--nix-build-user-base and --nix-build-user-prefix logic no longer confused
2023-07-06 11:13:46 -07:00
Ana Hobden 5f22ee1377 --nix-build-user-base and --nix-build-user-prefix logic no longer confused 2023-07-06 10:39:45 -07:00
Ana Hobden 3df486a535 Add act support (#30)
* Add act support

* Unwhoops

* Better format bash

* Relocate some code

* Mac doesn't want to get fancy with mktemp

* Change branch

* Change to a pr instead of branch

* Use the pr all over

* Use main

* Don't cat the conf

* Improve act support

* Bash eval fix

* use single quotes instead

* Fix mac planner name

* Fix quotes again
2023-07-01 22:59:30 +02:00
Graham Christensen 677cbc8aa1 Merge pull request #27 from DeterminateSystems/bump-readme
Update README.md to say v4
2023-06-06 13:05:06 -04:00
Graham Christensen ca688cebfe Update README.md to say v4 2023-06-06 10:25:36 -04:00
Ana Hobden 65d7c888b2 Merge pull request #26 from DeterminateSystems/different-url-for-ci
Add a ?ci=github to GHA requests
2023-06-05 10:53:09 -07:00
Ana Hobden 322d409404 Add quotes 2023-06-01 11:32:35 -07:00
Ana Hobden 11e71cc767 Add a ?ci=github to GHA requests 2023-05-31 13:44:05 -07:00
Ana Hobden 8c00b3afd2 Merge pull request #25 from ajaxbits/patch-1
Update example in README to use v3
2023-05-30 09:50:55 -07:00
Alex Jackson 9c5d3b3cb7 Update README.md with latest v3 version
Updates readme to reflect latest `v3` release of the action.
2023-05-30 10:28:21 -05:00
20 changed files with 25319 additions and 216 deletions
+1
View File
@@ -0,0 +1 @@
use flake
+74
View File
@@ -0,0 +1,74 @@
{
"plugins": [
"@typescript-eslint"
],
"extends": [
"plugin:github/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"i18n-text/no-en": "off",
"eslint-comments/no-use": "off",
"import/no-namespace": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_"
}
],
"@typescript-eslint/explicit-member-accessibility": [
"error",
{
"accessibility": "no-public"
}
],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-comment": "error",
"camelcase": "off",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/explicit-function-return-type": [
"error",
{
"allowExpressions": true
}
],
"@typescript-eslint/func-call-spacing": [
"error",
"never"
],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error"
},
"env": {
"node": true,
"es6": true
}
}
+1 -1
View File
@@ -138,6 +138,6 @@ jobs:
nix run nixpkgs#hello
- name: Verify the generated nix.conf
run: |
cat -n /etc/nix/nix.conf
cat /etc/nix/nix.conf
grep -E "^trusted-users = .*$USER" /etc/nix/nix.conf
grep -E "^use-sqlite-wal = true" /etc/nix/nix.conf
+101
View File
@@ -0,0 +1,101 @@
# Dependency directory
node_modules
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# OS metadata
.DS_Store
Thumbs.db
# Ignore built ts files
__tests__/runner/*
lib/**/*
.direnv
+3
View File
@@ -0,0 +1,3 @@
dist/
lib/
node_modules/
+1
View File
@@ -0,0 +1 @@
{}
+27 -1
View File
@@ -15,13 +15,38 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v2
uses: DeterminateSystems/nix-installer-action@main
- name: Run `nix build`
run: nix build .
```
See [`.github/workflows/ci.yml`](.github/workflows/ci.yml) for a full example.
To use private flakes from FlakeHub, use a configuration like this:
```yaml
on:
pull_request:
push:
branches: [main]
jobs:
lints:
name: Build
runs-on: ubuntu-22.04
permissions:
id-token: "write"
contents: "read"
steps:
- uses: actions/checkout@v3
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
with:
flakehub: true
- name: Run `nix build`
run: nix build .
```
## Configuration
| Parameter | Description | Type | Default |
@@ -29,6 +54,7 @@ See [`.github/workflows/ci.yml`](.github/workflows/ci.yml) for a full example.
| `backtrace` | The setting for [`RUST_BACKTRACE`][backtrace] | string | |
| `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` |
| `github-token` | A [GitHub token] for making authenticated requests (which have a higher rate-limit quota than unauthenticated requests) | string | `${{ github.token }}` |
| `init` | The init system to configure (requires `planner: linux-multi`) | enum (`none` or `systemd`) | |
| `local-root` | A local `nix-installer` binary root. Overrides the `nix-installer-url` setting (a `nix-installer.sh` should exist, binaries should be named `nix-installer-$ARCH`, eg. `nix-installer-x86_64-linux`). | Boolean | `false` |
+13 -214
View File
@@ -13,6 +13,10 @@ inputs:
extra-conf:
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.
required: false
default: false
github-token:
description: A GitHub token for making authenticated requests (which have a higher rate-limit quota than unauthenticated requests)
default: ${{ github.token }}
@@ -20,7 +24,7 @@ inputs:
description: "The init system to configure, requires `planner: linux-multi` (allowing the choice between `none` or `systemd`)"
required: false
local-root:
description: A local `nix-installer` binary root, overrides the `nix-installer-url` setting (a `nix-installer.sh` script should exist, binaries should be named `nix-installer-$ARCH`, eg. `nix-installer-x86_64-linux`)
description: A local `nix-installer` binary root, overrides any settings which change the `nix-installer` used (binaries should be named `nix-installer-$ARCH-$OS`, eg. `nix-installer-x86_64-linux`)
required: false
log-directives:
description: A list of Tracing directives, comma separated, `-`s replaced with `_` (eg. `nix_installer=trace`, see https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives)
@@ -31,7 +35,7 @@ inputs:
ssl-cert-file:
description: "An SSL cert to use (if any), used for fetching Nix and sets `NIX_SSL_CERT_FILE` for Nix"
required: false
pproxy:
proxy:
description: "The proxy to use (if any), valid proxy bases are `https://$URL`, `http://$URL` and `socks5://$URL`"
required: false
mac-case-sensitive:
@@ -49,6 +53,7 @@ inputs:
modify-profile:
description: Modify the user profile to automatically load nix
required: false
default: true
nix-build-group-id:
description: The Nix build group GID
required: false
@@ -88,9 +93,11 @@ inputs:
reinstall:
description: Force a reinstall if an existing installation is detected (consider backing up `/nix/store`)
required: false
default: false
start-daemon:
description: "If the daemon should be started, requires `planner: linux-multi`"
description: "If the daemon should be started, requires `planner: linux`"
required: false
default: true
diagnostic-endpoint:
description: "Diagnostic endpoint url where the installer sends data to. To disable set this to an empty string."
default: "https://install.determinate.systems/nix/diagnostic"
@@ -99,214 +106,6 @@ inputs:
default: "true"
runs:
using: composite
steps:
- name: Install Nix
shell: bash
run: |
if [ -f "/nix/receipt.json" ] && [ -f "/nix/nix-installer" ]; then
if ([ -n "${{ inputs.nix-installer-revision }}" ] || [ -n "${{ inputs.nix-installer-branch }}" ]) && [ "${{ inputs.reinstall }}" != "true" ]; then
echo "Detection of existing installation versions isn't supported when `nix-installer-revision` or `nix-installer-branch` are set, `reinstall: true` should also be set"
fi
if [ -n "${{ inputs.reinstall }}" ] && [ "${{ inputs.reinstall }}" == "true" ]; then
/nix/nix-installer uninstall --no-confirm
else
EXISTING_VERSION=$(/nix/nix-installer --version | awk '{ print $2 }')
if [ -n "${{ inputs.nix-installer-tag }}" ] && [ "${{ inputs.nix-installer-tag }}" != "$EXISTING_VERSION" ]; then
echo "`nix-installer` version mismatch, use `reinstall: true` to reinstall Nix using the new `nix-installer` version (consider backing up `/nix/store`)"
exit 1
fi
echo "Nix was already installed, using existing install"
echo "/nix/var/nix/profiles/default/bin" >> $GITHUB_PATH
echo "$HOME/.nix-profile/bin" >> $GITHUB_PATH
exit 0
fi
fi
if [ -n "${{ inputs.modify-profile }}" ]; then
export NIX_INSTALLER_MODIFY_PROFILE=${{ inputs.modify-profile }}
echo "Set NIX_INSTALLER_MODIFY_PROFILE=$NIX_INSTALLER_MODIFY_PROFILE"
fi
if [ -n "${{ inputs.nix-build-user-count }}" ]; then
export NIX_INSTALLER_NIX_BUILD_USER_COUNT=${{ inputs.nix-build-user-count }}
echo "Set NIX_INSTALLER_NIX_BUILD_USER_COUNT=$NIX_INSTALLER_NIX_BUILD_USER_COUNT"
fi
if [ -n "${{ inputs.nix-build-group-name }}" ]; then
export NIX_INSTALLER_NIX_BUILD_GROUP_NAME=${{ inputs.nix-build-group-name }}
echo "Set NIX_INSTALLER_NIX_BUILD_GROUP_NAME=$NIX_INSTALLER_NIX_BUILD_GROUP_NAME"
fi
if [ -n "${{ inputs.nix-build-group-id }}" ]; then
export NIX_INSTALLER_NIX_BUILD_GROUP_ID=${{ inputs.nix-build-group-id }}
echo "Set NIX_INSTALLER_NIX_BUILD_GROUP_ID=$NIX_INSTALLER_NIX_BUILD_GROUP_ID"
fi
if [ -n "${{ inputs.nix-build-user-prefix }}" ]; then
export NIX_INSTALLER_NIX_BUILD_USER_ID_BASE=${{ inputs.nix-build-user-prefix }}
echo "Set NIX_INSTALLER_NIX_BUILD_USER_ID_BASE=$NIX_INSTALLER_NIX_BUILD_USER_ID_BASE"
fi
if [ -n "${{ inputs.nix-build-user-base }}" ]; then
export NIX_INSTALLER_NIX_BUILD_USER_PREFIX=${{ inputs.nix-build-user-base }}
echo "Set NIX_INSTALLER_NIX_BUILD_USER_PREFIX=$NIX_INSTALLER_NIX_BUILD_USER_PREFIX"
fi
if [ -n "${{ inputs.nix-package-url }}" ]; then
export NIX_INSTALLER_NIX_PACKAGE_URL=${{ inputs.nix-package-url }}
echo "Set NIX_INSTALLER_NIX_PACKAGE_URL=$NIX_INSTALLER_NIX_PACKAGE_URL"
fi
NIX_EXTRA_CONF=""
NEWLINE='
'
if [ -n "${{ inputs.extra-conf }}" ]; then
NIX_EXTRA_CONF="${{ inputs.extra-conf }}"
fi
if [ -n "${{ inputs.github-token }}" ]; then
NIX_EXTRA_CONF="${NIX_EXTRA_CONF:+$NIX_EXTRA_CONF$NEWLINE}access-tokens = github.com=${{ inputs.github-token }}"
fi
if [ "${{ inputs.trust-runner-user }}" == "true" ]; then
NIX_EXTRA_CONF="${NIX_EXTRA_CONF:+$NIX_EXTRA_CONF$NEWLINE}trusted-users = root $USER"
fi
if [ -n "$NIX_EXTRA_CONF" ]; then
export NIX_INSTALLER_EXTRA_CONF="$NIX_EXTRA_CONF"
echo "Set NIX_INSTALLER_EXTRA_CONF=$NIX_INSTALLER_EXTRA_CONF"
fi
if [ -n "${{ inputs.mac-encrypt }}" ]; then
export NIX_INSTALLER_ENCRYPT=${{ inputs.mac-encrypt }}
echo "Set NIX_INSTALLER_ENCRYPT=$NIX_INSTALLER_ENCRYPT"
fi
if [ -n "${{ inputs.mac-case-sensitive }}" ]; then
export NIX_INSTALLER_CASE_SENSITIVE=${{ inputs.mac-case-sensitive }}
echo "Set NIX_INSTALLER_CASE_SENSITIVE=$NIX_INSTALLER_CASE_SENSITIVE"
fi
if [ -n "${{ inputs.mac-volume-label }}" ]; then
export NIX_INSTALLER_VOLUME_LABEL=${{ inputs.mac-volume-label }}
echo "Set NIX_INSTALLER_VOLUME_LABEL=$NIX_INSTALLER_VOLUME_LABEL"
fi
if [ -n "${{ inputs.mac-root-disk }}" ]; then
export NIX_INSTALLER_ROOT_DISK=${{ inputs.mac-root-disk }}
echo "Set NIX_INSTALLER_ROOT_DISK=$NIX_INSTALLER_ROOT_DISK"
fi
if [ -n "${{ inputs.nix-installer-pr }}" ] && [ -n "${{ inputs.nix-installer-tag }}" ]; then
echo "The nix-installer-pr and nix-installer-tag options conflict, please choose one"
exit 1
fi
if [ -n "${{ inputs.nix-installer-pr }}" ] && [ -n "${{ inputs.nix-installer-revision }}" ]; then
echo "The nix-installer-pr and nix-installer-revision options conflict, please choose one"
exit 1
fi
if [ -n "${{ inputs.nix-installer-pr }}" ] && [ -n "${{ inputs.nix-installer-branch }}" ]; then
echo "The nix-installer-pr and nix-installer-branch options conflict, please choose one"
exit 1
fi
if [ -n "${{ inputs.nix-installer-tag }}" ] && [ -n "${{ inputs.nix-installer-revision }}" ]; then
echo "The nix-installer-tag and nix-installer-revision options conflict, please choose one"
exit 1
fi
if [ -n "${{ inputs.nix-installer-branch }}" ] && [ -n "${{ inputs.nix-installer-revision }}" ]; then
echo "The nix-installer-branch and nix-installer-revision options conflict, please choose one"
exit 1
fi
if [ -n "${{ inputs.nix-installer-tag }}" ] && [ -n "${{ inputs.nix-installer-branch }}" ]; then
echo "The nix-installer-tag and nix-installer-branch options conflict, please choose one"
exit 1
fi
if [ -n "${{ inputs.nix-installer-url }}" ] && [ -n "${{ inputs.nix-installer-pr }}" ]; then
echo "The nix-installer-url and nix-installer-pr options conflict, please choose one"
exit 1
fi
if [ -n "${{ inputs.nix-installer-url }}" ] && [ -n "${{ inputs.nix-installer-tag }}" ]; then
echo "The nix-installer-url and nix-installer-tag options conflict, please choose one"
exit 1
fi
if [ -n "${{ inputs.nix-installer-url }}" ] && [ -n "${{ inputs.nix-installer-branch }}" ]; then
echo "The nix-installer-url and nix-installer-branch options conflict, please choose one"
exit 1
fi
if [ -n "${{ inputs.local-root }}" ]; then
if [ "$RUNNER_OS" == "macOS" ]; then
export PYTHON="python3"
else
export PYTHON="python"
fi
$PYTHON -m http.server --directory ${{ inputs.local-root }} --bind 0.0.0.0 8000 &
export HTTP_PID=$!
echo "Started simple http server for ${{ inputs.local-root }} on 0.0.0.0:8000"
while (! (: </dev/tcp/localhost/8000) &> /dev/null); do
sleep 1
done
export NIX_INSTALLER_FORCE_ALLOW_HTTP="1"
echo "Set NIX_INSTALLER_FORCE_ALLOW_HTTP=$NIX_INSTALLER_FORCE_ALLOW_HTTP"
export NIX_INSTALLER_URL=0.0.0.0:8000/nix-installer.sh
echo "Set NIX_INSTALLER_URL=$NIX_INSTALLER_URL"
export NIX_INSTALLER_BINARY_ROOT=http://0.0.0.0:8000/
echo "Set NIX_INSTALLER_BINARY_ROOT=$NIX_INSTALLER_BINARY_ROOT"
export NIX_INSTALLER_FORCE_ALLOW_HTTP=1
echo "Set NIX_INSTALLER_FORCE_ALLOW_HTTP=$NIX_INSTALLER_FORCE_ALLOW_HTTP"
else
if [ -n "${{ inputs.nix-installer-url }}" ]; then
export NIX_INSTALLER_URL=${{ inputs.nix-installer-url }}
else
if [ -n "${{ inputs.nix-installer-pr }}" ]; then
export NIX_INSTALLER_URL=https://install.determinate.systems/nix/pr/${{ inputs.nix-installer-pr }}
elif [ -n "${{ inputs.nix-installer-tag }}" ]; then
export NIX_INSTALLER_URL=https://install.determinate.systems/nix/tag/${{ inputs.nix-installer-tag }}
elif [ -n "${{ inputs.nix-installer-revision }}" ]; then
export NIX_INSTALLER_URL=https://install.determinate.systems/nix/rev/${{ inputs.nix-installer-revision }}
elif [ -n "${{ inputs.nix-installer-branch }}" ]; then
export NIX_INSTALLER_URL=https://install.determinate.systems/nix/branch/${{ inputs.nix-installer-branch }}
else
export NIX_INSTALLER_URL=https://install.determinate.systems/nix
fi
fi
echo "Set NIX_INSTALLER_URL=$NIX_INSTALLER_URL"
fi
if [ -n "${{ inputs.logger }}" ]; then
export NIX_INSTALLER_LOGGER=${{ inputs.logger }}
echo "Set NIX_INSTALLER_LOGGER=$NIX_INSTALLER_LOGGER"
fi
if [ -n "${{ inputs.init }}" ]; then
export NIX_INSTALLER_INIT=${{ inputs.init }}
echo "Set NIX_INSTALLER_INIT=$NIX_INSTALLER_INIT"
fi
if [ -n "${{ inputs.start-daemon }}" ]; then
export NIX_INSTALLER_START_DAEMON=${{ inputs.start-daemon }}
echo "Set NIX_INSTALLER_START_DAEMON=$NIX_INSTALLER_START_DAEMON"
fi
if [ -n "${{ inputs.log-directives }}" ]; then
export NIX_INSTALLER_LOG_DIRECTIVES=${{ inputs.log-directives }}
echo "Set NIX_INSTALLER_LOG_DIRECTIVES=$NIX_INSTALLER_LOG_DIRECTIVES"
fi
if [ -n "${{ inputs.backtrace }}" ]; then
export RUST_BACKTRACE=${{ inputs.backtrace }}
echo "Set RUST_BACKTRACE=$RUST_BACKTRACE"
fi
if [ -n "${{ inputs.diagnostic-endpoint }}" ]; then
export NIX_INSTALLER_DIAGNOSTIC_ENDPOINT="${{ inputs.diagnostic-endpoint }}"
echo "Set NIX_INSTALLER_DIAGNOSTIC_ENDPOINT=$NIX_INSTALLER_DIAGNOSTIC_ENDPOINT"
fi
export NIX_INSTALLER_NO_CONFIRM=true
echo "Set NIX_INSTALLER_NO_CONFIRM=$NIX_INSTALLER_NO_CONFIRM"
curl --retry 20 -L $NIX_INSTALLER_URL | sh -s -- install ${{ inputs.planner }} ${{ inputs.extra-args }}
if [ -n "$HTTP_PID" ]; then
kill $HTTP_PID
fi
using: "node20"
main: 'dist/index.js'
post: 'dist/index.js'
+453
View File
@@ -0,0 +1,453 @@
"use strict";
exports.id = 37;
exports.ids = [37];
exports.modules = {
/***/ 4037:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "toFormData": () => (/* binding */ toFormData)
/* harmony export */ });
/* harmony import */ var fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2185);
/* harmony import */ var formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8010);
let s = 0;
const S = {
START_BOUNDARY: s++,
HEADER_FIELD_START: s++,
HEADER_FIELD: s++,
HEADER_VALUE_START: s++,
HEADER_VALUE: s++,
HEADER_VALUE_ALMOST_DONE: s++,
HEADERS_ALMOST_DONE: s++,
PART_DATA_START: s++,
PART_DATA: s++,
END: s++
};
let f = 1;
const F = {
PART_BOUNDARY: f,
LAST_BOUNDARY: f *= 2
};
const LF = 10;
const CR = 13;
const SPACE = 32;
const HYPHEN = 45;
const COLON = 58;
const A = 97;
const Z = 122;
const lower = c => c | 0x20;
const noop = () => {};
class MultipartParser {
/**
* @param {string} boundary
*/
constructor(boundary) {
this.index = 0;
this.flags = 0;
this.onHeaderEnd = noop;
this.onHeaderField = noop;
this.onHeadersEnd = noop;
this.onHeaderValue = noop;
this.onPartBegin = noop;
this.onPartData = noop;
this.onPartEnd = noop;
this.boundaryChars = {};
boundary = '\r\n--' + boundary;
const ui8a = new Uint8Array(boundary.length);
for (let i = 0; i < boundary.length; i++) {
ui8a[i] = boundary.charCodeAt(i);
this.boundaryChars[ui8a[i]] = true;
}
this.boundary = ui8a;
this.lookbehind = new Uint8Array(this.boundary.length + 8);
this.state = S.START_BOUNDARY;
}
/**
* @param {Uint8Array} data
*/
write(data) {
let i = 0;
const length_ = data.length;
let previousIndex = this.index;
let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
const boundaryLength = this.boundary.length;
const boundaryEnd = boundaryLength - 1;
const bufferLength = data.length;
let c;
let cl;
const mark = name => {
this[name + 'Mark'] = i;
};
const clear = name => {
delete this[name + 'Mark'];
};
const callback = (callbackSymbol, start, end, ui8a) => {
if (start === undefined || start !== end) {
this[callbackSymbol](ui8a && ui8a.subarray(start, end));
}
};
const dataCallback = (name, clear) => {
const markSymbol = name + 'Mark';
if (!(markSymbol in this)) {
return;
}
if (clear) {
callback(name, this[markSymbol], i, data);
delete this[markSymbol];
} else {
callback(name, this[markSymbol], data.length, data);
this[markSymbol] = 0;
}
};
for (i = 0; i < length_; i++) {
c = data[i];
switch (state) {
case S.START_BOUNDARY:
if (index === boundary.length - 2) {
if (c === HYPHEN) {
flags |= F.LAST_BOUNDARY;
} else if (c !== CR) {
return;
}
index++;
break;
} else if (index - 1 === boundary.length - 2) {
if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
state = S.END;
flags = 0;
} else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
index = 0;
callback('onPartBegin');
state = S.HEADER_FIELD_START;
} else {
return;
}
break;
}
if (c !== boundary[index + 2]) {
index = -2;
}
if (c === boundary[index + 2]) {
index++;
}
break;
case S.HEADER_FIELD_START:
state = S.HEADER_FIELD;
mark('onHeaderField');
index = 0;
// falls through
case S.HEADER_FIELD:
if (c === CR) {
clear('onHeaderField');
state = S.HEADERS_ALMOST_DONE;
break;
}
index++;
if (c === HYPHEN) {
break;
}
if (c === COLON) {
if (index === 1) {
// empty header field
return;
}
dataCallback('onHeaderField', true);
state = S.HEADER_VALUE_START;
break;
}
cl = lower(c);
if (cl < A || cl > Z) {
return;
}
break;
case S.HEADER_VALUE_START:
if (c === SPACE) {
break;
}
mark('onHeaderValue');
state = S.HEADER_VALUE;
// falls through
case S.HEADER_VALUE:
if (c === CR) {
dataCallback('onHeaderValue', true);
callback('onHeaderEnd');
state = S.HEADER_VALUE_ALMOST_DONE;
}
break;
case S.HEADER_VALUE_ALMOST_DONE:
if (c !== LF) {
return;
}
state = S.HEADER_FIELD_START;
break;
case S.HEADERS_ALMOST_DONE:
if (c !== LF) {
return;
}
callback('onHeadersEnd');
state = S.PART_DATA_START;
break;
case S.PART_DATA_START:
state = S.PART_DATA;
mark('onPartData');
// falls through
case S.PART_DATA:
previousIndex = index;
if (index === 0) {
// boyer-moore derrived algorithm to safely skip non-boundary data
i += boundaryEnd;
while (i < bufferLength && !(data[i] in boundaryChars)) {
i += boundaryLength;
}
i -= boundaryEnd;
c = data[i];
}
if (index < boundary.length) {
if (boundary[index] === c) {
if (index === 0) {
dataCallback('onPartData', true);
}
index++;
} else {
index = 0;
}
} else if (index === boundary.length) {
index++;
if (c === CR) {
// CR = part boundary
flags |= F.PART_BOUNDARY;
} else if (c === HYPHEN) {
// HYPHEN = end boundary
flags |= F.LAST_BOUNDARY;
} else {
index = 0;
}
} else if (index - 1 === boundary.length) {
if (flags & F.PART_BOUNDARY) {
index = 0;
if (c === LF) {
// unset the PART_BOUNDARY flag
flags &= ~F.PART_BOUNDARY;
callback('onPartEnd');
callback('onPartBegin');
state = S.HEADER_FIELD_START;
break;
}
} else if (flags & F.LAST_BOUNDARY) {
if (c === HYPHEN) {
callback('onPartEnd');
state = S.END;
flags = 0;
} else {
index = 0;
}
} else {
index = 0;
}
}
if (index > 0) {
// when matching a possible boundary, keep a lookbehind reference
// in case it turns out to be a false lead
lookbehind[index - 1] = c;
} else if (previousIndex > 0) {
// if our boundary turned out to be rubbish, the captured lookbehind
// belongs to partData
const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
callback('onPartData', 0, previousIndex, _lookbehind);
previousIndex = 0;
mark('onPartData');
// reconsider the current character even so it interrupted the sequence
// it could be the beginning of a new sequence
i--;
}
break;
case S.END:
break;
default:
throw new Error(`Unexpected state entered: ${state}`);
}
}
dataCallback('onHeaderField');
dataCallback('onHeaderValue');
dataCallback('onPartData');
// Update properties for the next call
this.index = index;
this.state = state;
this.flags = flags;
}
end() {
if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
(this.state === S.PART_DATA && this.index === this.boundary.length)) {
this.onPartEnd();
} else if (this.state !== S.END) {
throw new Error('MultipartParser.end(): stream ended unexpectedly');
}
}
}
function _fileName(headerValue) {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
if (!m) {
return;
}
const match = m[2] || m[3] || '';
let filename = match.slice(match.lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
filename = filename.replace(/&#(\d{4});/g, (m, code) => {
return String.fromCharCode(code);
});
return filename;
}
async function toFormData(Body, ct) {
if (!/multipart/i.test(ct)) {
throw new TypeError('Failed to fetch');
}
const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
if (!m) {
throw new TypeError('no or bad content-type header, no multipart boundary');
}
const parser = new MultipartParser(m[1] || m[2]);
let headerField;
let headerValue;
let entryValue;
let entryName;
let contentType;
let filename;
const entryChunks = [];
const formData = new formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__/* .FormData */ .Ct();
const onPartData = ui8a => {
entryValue += decoder.decode(ui8a, {stream: true});
};
const appendToFile = ui8a => {
entryChunks.push(ui8a);
};
const appendFileToFormData = () => {
const file = new fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__/* .File */ .$B(entryChunks, filename, {type: contentType});
formData.append(entryName, file);
};
const appendEntryToFormData = () => {
formData.append(entryName, entryValue);
};
const decoder = new TextDecoder('utf-8');
decoder.decode();
parser.onPartBegin = function () {
parser.onPartData = onPartData;
parser.onPartEnd = appendEntryToFormData;
headerField = '';
headerValue = '';
entryValue = '';
entryName = '';
contentType = '';
filename = null;
entryChunks.length = 0;
};
parser.onHeaderField = function (ui8a) {
headerField += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderValue = function (ui8a) {
headerValue += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderEnd = function () {
headerValue += decoder.decode();
headerField = headerField.toLowerCase();
if (headerField === 'content-disposition') {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
if (m) {
entryName = m[2] || m[3] || '';
}
filename = _fileName(headerValue);
if (filename) {
parser.onPartData = appendToFile;
parser.onPartEnd = appendFileToFormData;
}
} else if (headerField === 'content-type') {
contentType = headerValue;
}
headerValue = '';
headerField = '';
};
for await (const chunk of Body) {
parser.write(chunk);
}
parser.end();
return formData;
}
/***/ })
};
;
//# sourceMappingURL=37.index.js.map
+1
View File
File diff suppressed because one or more lines are too long
+17588
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+23
View File
@@ -0,0 +1,23 @@
node-fetch
MIT
The MIT License (MIT)
Copyright (c) 2016 David Frank
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1
View File
File diff suppressed because one or more lines are too long
Generated
+40
View File
@@ -0,0 +1,40 @@
{
"nodes": {
"flake-schemas": {
"locked": {
"lastModified": 1693491534,
"narHash": "sha256-ifw8Td8kD08J8DxFbYjeIx5naHcDLz7s2IFP3X42I/U=",
"rev": "c702cbb663d6d70bbb716584a2ee3aeb35017279",
"revCount": 21,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/flake-schemas/0.1.1/018a4c59-80e1-708a-bb4d-854930c20f72/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/DeterminateSystems/flake-schemas/%2A.tar.gz"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1696879762,
"narHash": "sha256-Ud6bH4DMcYHUDKavNMxAhcIpDGgHMyL/yaDEAVSImQY=",
"rev": "f99e5f03cc0aa231ab5950a15ed02afec45ed51a",
"revCount": 534224,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.534224%2Brev-f99e5f03cc0aa231ab5950a15ed02afec45ed51a/018b1d3c-12f0-76a5-b796-7668d7633f08/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.1.0.tar.gz"
}
},
"root": {
"inputs": {
"flake-schemas": "flake-schemas",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+29
View File
@@ -0,0 +1,29 @@
# This flake was initially generated by fh, the CLI for FlakeHub (version 0.1.5)
{
description = "Development environment for the Nix Installer action for GitHub.";
inputs = {
flake-schemas.url = "https://flakehub.com/f/DeterminateSystems/flake-schemas/*.tar.gz";
nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1.0.tar.gz";
};
outputs = { self, flake-schemas, nixpkgs }:
let
supportedSystems = [ "x86_64-linux" "aarch64-darwin" ];
forEachSupportedSystem = f: nixpkgs.lib.genAttrs supportedSystems (system: f {
pkgs = import nixpkgs { inherit system; };
});
in
{
schemas = flake-schemas.schemas;
devShells = forEachSupportedSystem ({ pkgs }: {
default = pkgs.mkShell {
packages = with pkgs; [
nodejs_latest
nixpkgs-fmt
];
};
});
};
}
+6222
View File
File diff suppressed because it is too large Load Diff
+41
View File
@@ -0,0 +1,41 @@
{
"name": "nix-installer-action",
"version": "1.0.0",
"description": "You can use [`nix-installer`](https://github.com/DeterminateSystems/nix-installer) as a Github action:",
"main": "lib/main.js",
"scripts": {
"build": "tsc",
"format": "prettier --write '**/*.ts'",
"lint": "eslint src/**/*.ts",
"package": "ncc build --source-map --license licenses.txt",
"all": "npm run build && npm run format && npm run lint && npm run package"
},
"repository": {
"type": "git",
"url": "git+https://github.com/DeterminateSystems/nix-installer-action.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/DeterminateSystems/nix-installer-action/issues"
},
"homepage": "https://github.com/DeterminateSystems/nix-installer-action#readme",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"node-fetch": "^3.3.1",
"string-argv": "^0.3.2"
},
"devDependencies": {
"@types/node": "^18.16.3",
"@typescript-eslint/parser": "^5.59.2",
"@vercel/ncc": "^0.36.1",
"eslint": "^8.44.0",
"eslint-plugin-github": "^4.3.6",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^5.0.0-alpha.1",
"prettier": "^3.0.0",
"typescript": "^5.1.6"
}
}
+684
View File
@@ -0,0 +1,684 @@
import * as actions_core from "@actions/core";
import * as github from "@actions/github";
import { mkdtemp, chmod, access, writeFile } from "node:fs/promises";
import { spawn } from "node:child_process";
import { randomUUID } from "node:crypto";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { pipeline } from "node:stream";
import fetch from "node-fetch";
import { promisify } from "node:util";
import fs from "node:fs";
import stringArgv from "string-argv";
class NixInstallerAction {
platform: string;
nix_package_url: string | null;
backtrace: string | null;
extra_args: string | null;
extra_conf: string[] | null;
flakehub: boolean;
github_token: string | null;
// TODO: linux_init
init: string | null;
local_root: string | null;
log_directives: string | null;
logger: string | null;
ssl_cert_file: string | null;
proxy: string | null;
mac_case_sensitive: string | null;
mac_encrypt: string | null;
mac_root_disk: string | null;
mac_volume_label: string | null;
modify_profile: boolean;
nix_build_group_id: number | null;
nix_build_group_name: string | null;
nix_build_user_base: number | null;
nix_build_user_count: number | null;
nix_build_user_prefix: string | null;
planner: string | null;
reinstall: boolean;
start_daemon: boolean;
diagnostic_endpoint: string | null;
trust_runner_user: boolean | null;
nix_installer_url: URL;
// Connects the installation diagnostic report to the post-run diagnostic report.
// This is for monitoring the real impact of Nix updates, to avoid breaking large
// swaths of users at once with botched Nix releases. For example:
// https://github.com/NixOS/nix/issues/9052.
correlation: string | undefined;
constructor() {
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.github_token = action_input_string_or_null("github-token");
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.diagnostic_endpoint = action_input_string_or_null(
"diagnostic-endpoint",
);
this.trust_runner_user = action_input_bool("trust-runner-user");
this.correlation = process.env["STATE_correlation"];
this.nix_installer_url = resolve_nix_installer_url(
this.platform,
this.correlation,
);
}
private async executionEnvironment(): Promise<ExecuteEnvironment> {
const execution_env: ExecuteEnvironment = {};
execution_env.NIX_INSTALLER_NO_CONFIRM = "true";
execution_env.NIX_INSTALLER_DIAGNOSTIC_ATTRIBUTION = this.correlation;
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;
}
if (this.diagnostic_endpoint !== null) {
execution_env.NIX_INSTALLER_DIAGNOSTIC_ENDPOINT =
this.diagnostic_endpoint;
}
// 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_token !== null) {
extra_conf += `access-tokens = github.com=${this.github_token}`;
extra_conf += "\n";
}
if (this.trust_runner_user !== null) {
extra_conf += `trusted-users = root ${process.env.USER}`;
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) {
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";
}
return execution_env;
}
private async execute_install(binary_path: string): Promise<number> {
const execution_env = await this.executionEnvironment();
actions_core.info(
`Execution environment: ${JSON.stringify(execution_env, null, 4)}`,
);
const args = ["install"];
if (this.planner) {
args.push(this.planner);
} else {
args.push(get_default_planner());
}
if (this.extra_args) {
const extra_args = stringArgv(this.extra_args);
args.concat(extra_args);
}
const merged_env = {
...process.env, // To get $PATH, etc
...execution_env,
};
const spawned = spawn(`${binary_path}`, args, {
env: merged_env,
});
spawned.stdout.setEncoding("utf-8");
spawned.stdout.on("data", (data) => {
const trimmed = data.trimEnd();
if (trimmed.length >= 0) {
actions_core.info(trimmed);
}
});
spawned.stderr.setEncoding("utf-8");
spawned.stderr.on("data", (data) => {
const trimmed = data.trimEnd();
if (trimmed.length >= 0) {
actions_core.info(trimmed);
}
});
const exit_code: number = await new Promise((resolve, _reject) => {
spawned.on("close", resolve);
});
if (exit_code !== 0) {
throw new Error(`Non-zero exit code of \`${exit_code}\` detected`);
}
return exit_code;
}
async install(): Promise<void> {
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;
}
}
// Normal just doing of the install
const binary_path = await this.fetch_binary();
await this.execute_install(binary_path);
await this.set_github_path();
}
async set_github_path(): 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 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(): Promise<string> {
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(): Promise<number> {
const spawned = spawn(`/nix/nix-installer`, ["uninstall"], {
env: {
NIX_INSTALLER_NO_CONFIRM: "true",
...process.env, // To get $PATH, etc
},
});
spawned.stdout.setEncoding("utf-8");
spawned.stdout.on("data", (data) => {
const trimmed = data.trimEnd();
if (trimmed.length >= 0) {
actions_core.info(trimmed);
}
});
spawned.stderr.setEncoding("utf-8");
spawned.stderr.on("data", (data) => {
const trimmed = data.trimEnd();
if (trimmed.length >= 0) {
actions_core.info(trimmed);
}
});
const exit_code: number = await new Promise((resolve, _reject) => {
spawned.on("close", resolve);
});
if (exit_code !== 0) {
throw new Error(`Non-zero exit code of \`${exit_code}\` detected`);
}
return exit_code;
}
async detect_existing(): Promise<boolean> {
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;
}
}
private async fetch_binary(): Promise<string> {
if (!this.local_root) {
actions_core.info(`Fetching binary from ${this.nix_installer_url}`);
const response = await fetch(this.nix_installer_url);
if (!response.ok) {
throw new Error(
`Got a status of ${response.status} from \`${this.nix_installer_url}\`, expected a 200`,
);
}
const tempdir = await mkdtemp(join(tmpdir(), "nix-installer-"));
const tempfile = join(tempdir, `nix-installer-${this.platform}`);
if (!response.ok) {
throw new Error(`unexpected response ${response.statusText}`);
}
if (response.body !== null) {
const streamPipeline = promisify(pipeline);
await streamPipeline(response.body, fs.createWriteStream(tempfile));
actions_core.info(`Downloaded \`nix-installer\` to \`${tempfile}\``);
} else {
throw new Error("No response body recieved");
}
// Make executable
await chmod(tempfile, fs.constants.S_IXUSR | fs.constants.S_IXGRP);
return tempfile;
} 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(): Promise<void> {
if (this.diagnostic_endpoint == null) {
return;
}
try {
await fetch(this.diagnostic_endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
"post-github-workflow-run-report": true,
correlation: this.correlation,
conclusion: await this.get_workflow_conclusion(),
}),
});
} catch (error) {
actions_core.debug(
`Error submitting post-run diagnostics report: ${error}`,
);
}
}
private async get_workflow_conclusion(): Promise<
undefined | "success" | "failure" | "cancelled" | "unavailable" | "no-jobs"
> {
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";
}
}
}
type ExecuteEnvironment = {
// All env vars are strings, no fanciness here.
RUST_BACKTRACE?: string;
NIX_INSTALLER_MODIFY_PROFILE?: string;
NIX_INSTALLER_NIX_BUILD_GROUP_NAME?: string;
NIX_INSTALLER_NIX_BUILD_GROUP_ID?: string;
NIX_INSTALLER_NIX_BUILD_USER_PREFIX?: string;
NIX_INSTALLER_NIX_BUILD_USER_COUNT?: string;
NIX_INSTALLER_NIX_BUILD_USER_ID_BASE?: string;
NIX_INSTALLER_NIX_PACKAGE_URL?: string;
NIX_INSTALLER_PROXY?: string;
NIX_INSTALLER_SSL_CERT_FILE?: string;
NIX_INSTALLER_DIAGNOSTIC_ENDPOINT?: string;
NIX_INSTALLER_DIAGNOSTIC_ATTRIBUTION?: string;
NIX_INSTALLER_ENCRYPT?: string;
NIX_INSTALLER_CASE_SENSITIVE?: string;
NIX_INSTALLER_VOLUME_LABEL?: string;
NIX_INSTALLER_ROOT_DISK?: string;
NIX_INSTALLER_INIT?: string;
NIX_INSTALLER_START_DAEMON?: string;
NIX_INSTALLER_NO_CONFIRM?: string;
NIX_INSTALLER_EXTRA_CONF?: string;
NIX_INSTALLER_LOG_DIRECTIVES?: string;
NIX_INSTALLER_LOGGER?: string;
};
function get_nix_platform(): string {
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(): string {
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 resolve_nix_installer_url(
platform: string,
correlation?: string,
): URL {
// Only one of these are allowed.
const nix_installer_branch = action_input_string_or_null(
"nix-installer-branch",
);
const nix_installer_pr = action_input_number_or_null("nix-installer-pr");
const nix_installer_revision = action_input_string_or_null(
"nix-installer-revision",
);
const nix_installer_tag = action_input_string_or_null("nix-installer-tag");
const nix_installer_url = action_input_string_or_null("nix-installer-url");
const url_suffix = `ci=github&correlation=${correlation}`;
let resolved_nix_installer_url = null;
let num_set = 0;
if (nix_installer_branch !== null) {
num_set += 1;
resolved_nix_installer_url = new URL(
`https://install.determinate.systems/nix/branch/${nix_installer_branch}/nix-installer-${platform}?${url_suffix}`,
);
} else if (nix_installer_pr !== null) {
num_set += 1;
resolved_nix_installer_url = new URL(
`https://install.determinate.systems/nix/pr/${nix_installer_pr}/nix-installer-${platform}?${url_suffix}`,
);
} else if (nix_installer_revision !== null) {
num_set += 1;
resolved_nix_installer_url = new URL(
`https://install.determinate.systems/nix/rev/${nix_installer_revision}/nix-installer-${platform}?${url_suffix}`,
);
} else if (nix_installer_tag !== null) {
num_set += 1;
resolved_nix_installer_url = new URL(
`https://install.determinate.systems/nix/tag/${nix_installer_tag}/nix-installer-${platform}?${url_suffix}`,
);
} else if (nix_installer_url !== null) {
num_set += 1;
resolved_nix_installer_url = new URL(nix_installer_url);
} else {
resolved_nix_installer_url = new URL(
`https://install.determinate.systems/nix/nix-installer-${platform}?${url_suffix}`,
);
}
if (num_set > 1) {
throw new Error(
`The following options are mututally exclusive, but ${num_set} were set: \`nix_installer_branch\`, \`nix_installer_pr\`, \`nix_installer_revision\`, \`nix_installer_tag\`, and \`nix_installer_url\``,
);
}
return resolved_nix_installer_url;
}
function action_input_string_or_null(name: string): string | null {
const value = actions_core.getInput(name);
if (value === "") {
return null;
} else {
return value;
}
}
function action_input_multiline_string_or_null(name: string): string[] | null {
const value = actions_core.getMultilineInput(name);
if (value.length === 0) {
return null;
} else {
return value;
}
}
function action_input_number_or_null(name: string): number | null {
const value = actions_core.getInput(name);
if (value === "") {
return null;
} else {
return Number(value);
}
}
function action_input_bool(name: string): boolean {
return actions_core.getBooleanInput(name);
}
async function main(): Promise<void> {
try {
if (!process.env["STATE_correlation"]) {
const correlation = `GH-${randomUUID()}`;
actions_core.saveState("correlation", correlation);
process.env["STATE_correlation"] = correlation;
}
const installer = new NixInstallerAction();
const isPost = !!process.env["STATE_isPost"];
if (!isPost) {
actions_core.saveState("isPost", "true");
await installer.install();
} else {
installer.report_overall();
}
} catch (error) {
if (error instanceof Error) actions_core.setFailed(error);
}
}
main();
+15
View File
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"outDir": "./lib", /* Redirect output structure to the directory. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
},
"exclude": [
"node_modules",
"**/*.test.ts"
]
}