Compare commits

...

9 commits

Author SHA1 Message Date
Artemis Tosini 570cc523a6
Update test plans for substituters 2024-07-13 16:43:42 +00:00
Artemis Tosini 3bf2630935
don't duplicate substituter list 2024-07-13 15:32:33 +00:00
Artemis Tosini eb253d097d
start plumbing in fetch_and_unpack_nix_substituter 2024-07-13 03:03:37 +00:00
Artemis Tosini 4711dd7167
More substituter stuff compiles 2024-07-08 22:51:47 +00:00
Artemis Tosini 6512b8d2cc
Initial work on substituter 2024-07-08 21:43:17 +00:00
jade 8d04ca8d3d Fix the contributing documentation for Lix, some
Some. There's still a bunch of stuff that's busted and we need to fix it
up.

Change-Id: Id9fe1fc054616df34045340ec3b02dda723f1d9c
2024-06-15 20:07:34 -07:00
jade 0256e915e7 Fix various bugs in the tests such that Lix works now
- We were looking for a top-level tarball path that looked like nix-*.
  This was invalid, since our tarballs have lix-*.
- We were looking for a store path that looked like nix-*.*.*. This was
  invalid, since ours is lix-*.*.*.

  This change accepts both.
- We also added a symlink for nix-installer and nix-installer.sh, which
  deals with the test suite being pretty tightly coupled to that path.
- We fixed a bug exposed in the tests where --no-substitute is not
  passed while self-testing builds. This seems to have been relying on
  offline detection and while it eventually passed, it was busted and
  took ages.

Change-Id: I2f497bd647ecf1db5963a4bb245279db582d2af3
2024-06-15 20:03:03 -07:00
jade 4a4f16676d Update version to 2.90.0-rc1
Change-Id: I05f3efd6663bec418ce93fb2dca53977d3523f7d
2024-06-14 21:12:44 -07:00
Artemis Tosini f1a45806c7
flake: test using lix
Previously we used a nix install tarball built from nix 2.20.5,
download with flakehub.

That does not make sense for a lix installer, use Lix 2.90 beta 1
instead.

Also use this opportunity to remove the unused flake-compat input.

Change-Id: I622bb9fedb45f3a03c1e5f43757afbd0222c6b90
2024-06-02 01:41:14 +00:00
24 changed files with 1175 additions and 193 deletions

View file

@ -1,14 +1,14 @@
# Contributing to `nix-installer`
# Contributing to `lix-installer`
We're excited to see what you'd like to contribute to `nix-installer`!
We're excited to see what you'd like to contribute to `lix-installer`!
Regardless of what (or how much) you contribute to `nix-installer`, we value your time
Regardless of what (or how much) you contribute to `lix-installer`, we value your time
and energy trying to improve the tool.
In order to ensure we all have a good experience, please review this document
if you have any questions about the process.
**Regular Rust committer?** Contributing to `nix-installer` should feel similar to
**Regular Rust committer?** Contributing to `lix-installer` should feel similar to
contributing to other serious Rust ecosystem projects. You may already know
the process and expectations of you, this document shouldn't contain any
surprises.
@ -16,7 +16,7 @@ surprises.
# What kinds of contributions are needed?
`nix-installer` can benefit from all kinds of contributions:
`lix-installer` can benefit from all kinds of contributions:
* Bug reports
* Code improvements
@ -27,33 +27,29 @@ surprises.
* Graphical/visual asset improvement
* Kind words or recommendation on your own site, repo, stream, or social media
account
* Onboarding others to using `nix-installer`
* Onboarding others to using `lix-installer`
# What are the expectations you can have of the maintainers?
You can expect us to:
* Follow the [Contributor Covenant](CODE_OF_CONDUCT.md), just like you
* Follow the [Lix community standards], just like you
* Help diagnose bug reports (for supported platforms using supported
languages)
* Give constructive feedback on pull requests
* Merge pull requests which:
* Give constructive feedback on changes
* Merge changes which:
+ Have been approved of by at least 1 maintainer
+ Pass all tests
+ Have no complex conflicts with in-flight high priority work
The maintainers of this project use a separate issue tracker for some internal
tasks. Unfortunately, the contents of this tracker is not publicly visible as
it may contain sensitive or confidential data. Our maintainers will endeavor to
ensure you are not 'left out' of the discussion about your contributions.
[Lix community standards]: https://lix.systems/community-standards/
# What kind of expectations do the maintainers have from you?
We expect you to:
* Follow the [Contributor Covenant](CODE_OF_CONDUCT.md), just like them
* Follow the [Lix community standards], just like them
* Make an earnest attempt to follow the contribution process described in this
document
* Update bug reports with a solution, if you find one before we do
@ -74,8 +70,8 @@ Create an issue on [the issue page](https://git.lix.systems/lix-project/lix-inst
It should contain:
1. Your OS (Linux, Mac) and architecture (x86_64, aarch64)
2. Your `nix-installer` version (`/nix/nix-installer --version`)
3. The thing you tried to run (eg `nix-installer`)
2. Your `lix-installer` version (`/nix/lix-installer --version`)
3. The thing you tried to run (eg `lix-installer`)
4. What happened (the output of the command, please)
5. What you expected to happen
6. If you tried to fix it, what did you try?
@ -364,6 +360,9 @@ To cut a release:
+ `nix flake check -L`
+ `nix build .#hydraJobs.container-test.all.x86_64-linux.all -L -j 6`
+ `nix build .#hydraJobs.vm-test.all.x86_64-linux.all -L -j 6`
FIXME: the following is outdated for Lix and needs rewriting:
* Push the branch, create a PR ("Release v0.0.1")
* Once the PR tests pass and it has been reviewed, merge it
* `git pull` on the `main` branch

281
Cargo.lock generated
View file

@ -134,6 +134,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -146,6 +152,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
@ -177,11 +192,22 @@ dependencies = [
"serde",
]
[[package]]
name = "camino"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
[[package]]
name = "cc"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd"
dependencies = [
"jobserver",
"libc",
"once_cell",
]
[[package]]
name = "cfg-if"
@ -282,6 +308,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -298,6 +330,52 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "curve25519-dalek"
version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
"rustc_version",
"subtle",
"zeroize",
]
[[package]]
name = "curve25519-dalek-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "darling"
version = "0.20.8"
@ -333,6 +411,16 @@ dependencies = [
"syn",
]
[[package]]
name = "der"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
dependencies = [
"const-oid",
"zeroize",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -343,6 +431,16 @@ dependencies = [
"serde",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "dirs"
version = "5.0.1"
@ -391,6 +489,31 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]]
name = "ed25519"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
"pkcs8",
"serde",
"signature",
]
[[package]]
name = "ed25519-dalek"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
dependencies = [
"curve25519-dalek",
"ed25519",
"serde",
"sha2",
"subtle",
"zeroize",
]
[[package]]
name = "either"
version = "1.11.0"
@ -459,6 +582,12 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "fiat-crypto"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "filetime"
version = "0.2.23"
@ -553,6 +682,16 @@ dependencies = [
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.14"
@ -817,6 +956,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
[[package]]
name = "is_executable"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8"
dependencies = [
"winapi",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
@ -829,6 +977,15 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jobserver"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.69"
@ -877,17 +1034,20 @@ name = "lix-installer"
version = "0.17.1"
dependencies = [
"async-trait",
"base64 0.22.1",
"bytes 1.6.0",
"clap",
"color-eyre",
"dirs",
"dyn-clone",
"ed25519-dalek",
"eyre",
"glob",
"indexmap 2.2.6",
"is_ci",
"nix",
"nix-config-parser",
"nix-nar",
"os-release",
"owo-colors 4.0.0",
"plist",
@ -897,6 +1057,7 @@ dependencies = [
"serde",
"serde_json",
"serde_with",
"sha2",
"strum",
"sysctl",
"tar",
@ -914,6 +1075,7 @@ dependencies = [
"walkdir",
"which",
"xz2",
"zstd",
]
[[package]]
@ -1007,6 +1169,18 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nix-nar"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5549158a8b179c4fcd06a19f4bcc557db60c9cbd6771add9563f46c8d0325b5"
dependencies = [
"camino",
"is_executable",
"symlink",
"thiserror",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@ -1140,6 +1314,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"spki",
]
[[package]]
name = "pkg-config"
version = "0.3.30"
@ -1367,6 +1551,15 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.34"
@ -1574,6 +1767,17 @@ dependencies = [
"syn",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@ -1592,6 +1796,15 @@ dependencies = [
"libc",
]
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"rand_core",
]
[[package]]
name = "slab"
version = "0.4.9"
@ -1623,6 +1836,16 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "spki"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "strsim"
version = "0.10.0"
@ -1657,6 +1880,12 @@ dependencies = [
"syn",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "supports-color"
version = "2.1.0"
@ -1667,6 +1896,12 @@ dependencies = [
"is_ci",
]
[[package]]
name = "symlink"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a"
[[package]]
name = "syn"
version = "2.0.60"
@ -1995,6 +2230,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "typetag"
version = "0.2.16"
@ -2079,6 +2320,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.5.0"
@ -2421,3 +2668,37 @@ dependencies = [
"lzma-sys",
"tokio-io",
]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
[[package]]
name = "zstd"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.12+zstd.1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13"
dependencies = [
"cc",
"pkg-config",
]

View file

@ -60,6 +60,11 @@ which = "6.0.0"
sysctl = "0.5.4"
walkdir = "2.3.3"
indexmap = { version = "2.0.2", features = ["serde"] }
nix-nar = "0.3.0"
zstd = { version = "0.13.2", default-features = false }
sha2 = "0.10.8"
ed25519-dalek = { version = "2.1.1", features = ["serde"] }
base64 = "0.22.1"
[dev-dependencies]
eyre = { version = "0.6.8", default-features = false, features = [ "track-caller" ] }

View file

@ -338,13 +338,14 @@ Here are some example `nix` package URLs including nix version, OS and architect
## Installation Differences
Differing from the upstream [Nix](https://github.com/NixOS/nix) installer scripts:
Differing from the [CppNix](https://github.com/NixOS/nix) installer scripts:
* In `nix.conf`:
+ the `nix-command` and `flakes` features are enabled
+ the `nix-command` and `flakes` features are optionally enabled
+ `bash-prompt-prefix` is set
+ `auto-optimise-store` is set to `true` (On Linux only)
* `extra-nix-path` is set to `nixpkgs=flake:nixpkgs`
* `extra-nix-path` is set to `nixpkgs=flake:nixpkgs` if flakes are enabled
when installing
* `max-jobs` is set to `auto`
* an installation receipt (for uninstalling) is stored at `/nix/receipt.json` as well as a copy of the install binary at `/nix/lix-installer`
* `nix-channel --update` is not run, `~/.nix-channels` is not provisioned

View file

@ -22,27 +22,13 @@
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"revCount": 57,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/edolstra/flake-compat/1.0.0.tar.gz"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
@ -51,20 +37,24 @@
"type": "github"
}
},
"libgit2": {
"flake": false,
"lix": {
"inputs": {
"flake-compat": "flake-compat",
"nix2container": "nix2container",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression",
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
"lastModified": 1718419213,
"narHash": "sha256-WY7BGnu5PnbK4O8cKKv9kvxwzZIGbIQUQLGPHFXitI0=",
"rev": "253546d5fbf8a5aa60ac8164c1b4f5794dc4e9d1",
"type": "tarball",
"url": "https://git.lix.systems/api/v1/repos/lix-project/lix/archive/253546d5fbf8a5aa60ac8164c1b4f5794dc4e9d1.tar.gz"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
"type": "tarball",
"url": "https://git.lix.systems/lix-project/lix/archive/2.90.0-rc1.tar.gz"
}
},
"naersk": {
@ -87,38 +77,34 @@
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": "flake-compat_2",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
},
"nix2container": {
"flake": false,
"locked": {
"lastModified": 1709808984,
"narHash": "sha256-bfFe38BkoQws7om4gBtBWoNTLkt9piMXdLLoHYl+vBQ=",
"rev": "f8170ce9f119e5e6724eb81ff1b5a2d4c0024000",
"revCount": 16143,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/NixOS/nix/2.20.5/018e199b-ae2c-703d-ab99-4c648be473b2/source.tar.gz"
"lastModified": 1712990762,
"narHash": "sha256-hO9W3w7NcnYeX8u8cleHiSpK2YJo7ecarFTUlbybl7k=",
"owner": "nlewo",
"repo": "nix2container",
"rev": "20aad300c925639d5d6cbe30013c8357ce9f2a2e",
"type": "github"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/NixOS/nix/%3D2.20.5.tar.gz"
"owner": "nlewo",
"repo": "nix2container",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1705033721,
"narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=",
"lastModified": 1718379166,
"narHash": "sha256-B/Q/Pf4kD+yWk3fGh5H0fUpwxmLgEKt9KBon+bZ3d9U=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea",
"rev": "93fbfcd45e966ea1cff043d48bd45d1285082770",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.05-small",
"ref": "nixos-24.05-small",
"repo": "nixpkgs",
"type": "github"
}
@ -141,26 +127,41 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1714763106,
"narHash": "sha256-DrDHo74uTycfpAF+/qxZAMlP/Cpe04BVioJb6fdI0YY=",
"lastModified": 1718379166,
"narHash": "sha256-B/Q/Pf4kD+yWk3fGh5H0fUpwxmLgEKt9KBon+bZ3d9U=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e9be42459999a253a9f92559b1f5b72e1b44c13d",
"rev": "93fbfcd45e966ea1cff043d48bd45d1285082770",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"ref": "nixos-24.05-small",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"flake": false,
"locked": {
"lastModified": 1712055707,
"narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "e35aed5fda3cc79f88ed7f1795021e559582093a",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"fenix": "fenix",
"flake-compat": "flake-compat",
"lix": "lix",
"naersk": "naersk",
"nix": "nix",
"nixpkgs": "nixpkgs_2"
}
},

View file

@ -2,7 +2,7 @@
description = "The Lix Installer";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05-small";
fenix = {
url = "github:nix-community/fenix";
@ -14,12 +14,13 @@
inputs.nixpkgs.follows = "nixpkgs";
};
nix = {
url = "https://flakehub.com/f/NixOS/nix/=2.20.5.tar.gz";
lix = {
# See set_version.py
# BEGIN GENERATE-URLS
url = "https://git.lix.systems/lix-project/lix/archive/2.90.0-rc1.tar.gz";
# END GENERATE-URLS
# Omitting `inputs.nixpkgs.follows = "nixpkgs";` on purpose
};
flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.0.0.tar.gz";
};
outputs =
@ -27,7 +28,7 @@
, nixpkgs
, fenix
, naersk
, nix
, lix
, ...
} @ inputs:
let
@ -94,6 +95,8 @@
};
postInstall = ''
cp lix-installer.sh $out/bin/lix-installer.sh
ln -s lix-installer $out/bin/nix-installer
ln -s lix-installer.sh $out/bin/nix-installer.sh
'';
};
in
@ -124,6 +127,13 @@
let
toolchain = fenixToolchain system;
check = import ./nix/check.nix { inherit pkgs toolchain; };
inherit (pkgs) lib;
pythonEnv = pkgs.python3.withPackages (p: [
(p.toPythonModule pkgs.xonsh-unwrapped)
p.requests
]);
in
{
default = pkgs.mkShell {
@ -131,10 +141,21 @@
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
nativeBuildInputs = with pkgs; [ ];
buildInputs = with pkgs; [
buildInputs = lib.optionals (pkgs.stdenv.isDarwin)
(with pkgs; [
libiconv
darwin.apple_sdk.frameworks.Security
darwin.apple_sdk.frameworks.SystemConfiguration
])
++ lib.optionals (pkgs.stdenv.isLinux) (with pkgs; [
checkpolicy
semodule-utils
/* users are expected to have a system docker, too */
]);
nativeBuildInputs = with pkgs; [
zig
xonsh
pythonEnv
awscli2
toolchain
rust-analyzer
@ -150,17 +171,7 @@
check.check-editorconfig
check.check-semver
check.check-clippy
]
++ lib.optionals (pkgs.stdenv.isDarwin) (with pkgs; [
libiconv
darwin.apple_sdk.frameworks.Security
darwin.apple_sdk.frameworks.SystemConfiguration
])
++ lib.optionals (pkgs.stdenv.isLinux) (with pkgs; [
checkpolicy
semodule-utils
/* users are expected to have a system docker, too */
]);
];
};
});
@ -211,12 +222,12 @@
hydraJobs = {
vm-test = import ./nix/tests/vm-test {
inherit forSystem;
inherit (nix.hydraJobs) binaryTarball;
inherit (lix.hydraJobs) binaryTarball;
inherit (nixpkgs) lib;
};
container-test = import ./nix/tests/container-test {
inherit forSystem;
inherit (nix.hydraJobs) binaryTarball;
inherit (lix.hydraJobs) binaryTarball;
};
};
};

View file

@ -2,7 +2,7 @@ FROM default
COPY lix-installer /lix-installer
RUN chmod +x /lix-installer
COPY binary-tarball /binary-tarball
RUN mv /binary-tarball/nix-*.tar.xz nix.tar.xz
RUN mv /binary-tarball/[nl]ix-*.tar.xz nix.tar.xz
RUN /lix-installer/bin/lix-installer install linux --logger pretty --log-directive lix_installer=debug --nix-package-url file:///nix.tar.xz --init none --extra-conf "sandbox = false" --no-confirm -vvv
ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin"
RUN nix-build --no-substitute -E 'derivation { name = "foo"; system = "x86_64-linux"; builder = "/bin/sh"; args = ["-c" "echo foobar > $out"]; }'

View file

@ -576,7 +576,7 @@ let
scp -P 20022 $ssh_opts $installer/bin/lix-installer vagrant@localhost:nix-installer
echo "Copying nix tarball..."
scp -P 20022 $ssh_opts $binaryTarball/nix-*.tar.xz vagrant@localhost:nix.tar.xz
scp -P 20022 $ssh_opts $binaryTarball/lix-*.tar.xz vagrant@localhost:nix.tar.xz
echo "Running preinstall..."
$ssh "set -eux; $preinstallScript"

83
set_version.py Executable file
View file

@ -0,0 +1,83 @@
#!/usr/bin/env python
from pathlib import Path
import textwrap
import dataclasses
import requests
SYSTEMS = ['x86_64-linux', 'x86_64-darwin', 'aarch64-linux', 'aarch64-darwin']
@dataclasses.dataclass
class Package:
system: str
version: str
def url(self):
return f"https://releases.lix.systems/lix/lix-{self.version}/lix-{self.version}-{self.system}.tar.xz";
def variable_name(self):
system = self.system.replace('-', '_').upper()
return f'LIX_{system}_URL'
def make_urls_section(packages: list[Package]):
def one_item(package: Package):
return textwrap.dedent("""\
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for {system}.
pub const {variable_name}: &str =
"{url}";
""").format(system=package.system, variable_name=package.variable_name(), url=package.url())
return '\n'.join(one_item(package) for package in packages)
def replace_section(old: str, section: str) -> str:
lines = []
eat = False
for line in old.splitlines():
next_eat = eat
if 'BEGIN GENERATE-URLS' in line.strip():
next_eat = True
elif 'END GENERATE-URLS' in line.strip():
lines.append(section)
eat = False
next_eat = False
if not eat:
lines.append(line)
eat = next_eat
return '\n'.join(lines)
def replace_in_file(file: Path, section: str):
new_file = replace_section(file.read_text(), section)
file.write_text(new_file)
def make_flake_url_section(version: str) -> str:
return f' url = "https://git.lix.systems/lix-project/lix/archive/{version}.tar.gz";'
def main():
import argparse
ap = argparse.ArgumentParser(description='Update the version of lix-installer')
ap.add_argument('new_version', help='The new version')
args = ap.parse_args()
settings_rs = Path('src/settings.rs')
packages = [Package(system, args.new_version) for system in SYSTEMS]
for package in packages:
resp = requests.head(package.url())
if resp.status_code != 200:
print(f'Warning: broken URL {package.url()} returns HTTP {resp.status_code}')
replace_in_file(settings_rs, make_urls_section(packages))
flake_nix = Path('flake.nix')
replace_in_file(flake_nix, make_flake_url_section(args.new_version))
if __name__ == '__main__':
main()

View file

@ -0,0 +1,458 @@
use std::{
collections::HashMap,
io::BufRead,
path::{Path, PathBuf},
};
use base64::Engine;
use ed25519_dalek::{SignatureError, VerifyingKey};
use reqwest::Url;
use tracing::{span, Span};
use crate::{
action::{Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction},
parse_ssl_cert,
settings::UrlOrPath,
};
/// Fetch an output and its dependencies from a set of substituters,
/// given an output path, subsititer URLs, and trusted keys.
/// Also generates a ".reginfo" compatible with `nix-store --load-db`
/// Only implements a subset of nix substitution features:
/// * Substituter priorites are highest to lowest as given to [`plan`],
/// instead of priority from nix-cache-info
/// * narinfo signatures are always required
/// * ca-derivations are not supported
/// * NarHash must be sha256
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct FetchAndUnpackNixSubstituter {
/// Map from key name (e.g. cache.nixos.org-1) to parsed ed25519 key
trusted_keys: HashMap<String, VerifyingKey>,
/// Base URLs for substituters, e.g. https://cache.nixos.org/
substituters: Vec<Url>,
/// Desired derivation output, e.g.
/// `/nix/store/n50jk09x9hshwx1lh6k3qaiygc7yxbv9-lix-2.90.0-rc1`
target: StorePath,
/// Destination directory, normally temporary.
/// For compatibility with tarballs, files will be placed in
/// the nix/store subdirectory of the destination
dest: PathBuf,
/// Proxy used for all requests from substituters
proxy: Option<Url>,
/// Extra SSL certificates trusted for all requests
ssl_cert_file: Option<PathBuf>,
}
/// Root directory of the nix store.
/// Technically this could be something other than /nix/store,
/// but that is rarely done in production
const STORE_DIR: &str = "/nix/store/";
impl FetchAndUnpackNixSubstituter {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(
target: UrlOrPath,
dest: PathBuf,
trusted_keys: Vec<String>,
substituters: Vec<Url>,
proxy: Option<Url>,
ssl_cert_file: Option<PathBuf>,
) -> Result<StatefulAction<Self>, ActionError> {
let UrlOrPath::Path(target_path) = target else {
return Err(Self::error(SubstitutionError::InvalidStorePath));
};
let trusted_keys_parsed = trusted_keys
.iter()
.map(|key| parse_key(key))
.collect::<Result<HashMap<_, _>, _>>()
.map_err(Self::error)?;
if let Some(proxy) = &proxy {
match proxy.scheme() {
"https" | "http" | "socks5" => (),
_ => return Err(Self::error(SubstitutionError::UnknownProxyScheme)),
};
}
if let Some(ssl_cert_file) = &ssl_cert_file {
parse_ssl_cert(ssl_cert_file).await.map_err(Self::error)?;
}
Ok(Self {
target: StorePath::from_path(&target_path)
.ok_or_else(|| Self::error(SubstitutionError::InvalidStorePath))?,
trusted_keys: trusted_keys_parsed,
dest,
proxy,
substituters,
ssl_cert_file,
}
.into())
}
}
#[async_trait::async_trait]
#[typetag::serde(name = "fetch_and_unpack_nix")]
impl Action for FetchAndUnpackNixSubstituter {
fn action_tag() -> ActionTag {
ActionTag("fetch_and_unpack_nix_substituter")
}
fn tracing_synopsis(&self) -> String {
format!(
"Fetch `{}` from substituters to `{}`",
self.target.full_path,
self.dest.display()
)
}
fn tracing_span(&self) -> Span {
let span = span!(
tracing::Level::DEBUG,
"fetch_and_unpack_nix_substituter",
target = tracing::field::debug(&self.target.full_path),
proxy = tracing::field::Empty,
ssl_cert_file = tracing::field::Empty,
dest = tracing::field::display(self.dest.display()),
);
if let Some(proxy) = &self.proxy {
span.record("proxy", tracing::field::display(&proxy));
}
if let Some(ssl_cert_file) = &self.ssl_cert_file {
span.record(
"ssl_cert_file",
tracing::field::display(&ssl_cert_file.display()),
);
}
span
}
fn execute_description(&self) -> Vec<ActionDescription> {
vec![ActionDescription::new(self.tracing_synopsis(), vec![])]
}
#[tracing::instrument(level = "debug", skip_all)]
async fn execute(&mut self) -> Result<(), ActionError> {
Ok(())
}
fn revert_description(&self) -> Vec<ActionDescription> {
vec![/* Deliberately empty -- this is a noop */]
}
#[tracing::instrument(level = "debug", skip_all)]
async fn revert(&mut self) -> Result<(), ActionError> {
Ok(())
}
}
/// Parse a nix trusted key into name and ed25519
fn parse_key(key: &str) -> Result<(String, VerifyingKey), SubstitutionError> {
let (name, key_base64) = key
.split_once(':')
.ok_or_else(|| SubstitutionError::PublicKey)?;
// seems to be the best way to handle keys both with and without padding
let key_bytes = base64::engine::general_purpose::STANDARD_NO_PAD
.decode(key_base64.trim_end_matches("=").as_bytes())
.map_err(|_| SubstitutionError::PublicKey)?;
let key_array: [u8; ed25519_dalek::PUBLIC_KEY_LENGTH] = key_bytes
.try_into()
.map_err(|_| SubstitutionError::PublicKey)?;
let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(&key_array)
.map_err(|_| SubstitutionError::PublicKey)?;
Ok((name.to_string(), verifying_key))
}
/// Utility struct representing a store path
#[derive(Clone, Eq, Debug)]
struct StorePath {
/// Full path of the output including STORE_DIR,
/// as seen in StorePath in narinfo
/// e.g. `/nix/store/n50jk09x9hshwx1lh6k3qaiygc7yxbv9-lix-2.90.0-rc1`
pub full_path: String,
/// The base32 hash part of a store path,
/// e.g. `n50jk09x9hshwx1lh6k3qaiygc7yxbv9`
pub digest: String,
/// The full name of a path, not including STORE_DIR,
/// e.g. `n50jk09x9hshwx1lh6k3qaiygc7yxbv9-lix-2.90.0-rc1`
pub full_name: String,
}
impl StorePath {
pub fn from_full_path(full_path: &str) -> Option<Self> {
if !full_path.starts_with(STORE_DIR) {
return None;
}
let (_, full_name) = full_path.split_at(STORE_DIR.len());
let (digest, name) = full_name.split_once('-')?;
if digest.len() != 32
|| digest.contains(|c: char| !c.is_ascii_lowercase() && !c.is_ascii_digit())
{
return None;
}
if name.contains(|c: char| {
!c.is_ascii_alphanumeric()
&& c != '+'
&& c != '-'
&& c != '.'
&& c != '_'
&& c != '?'
&& c != '='
}) {
return None;
}
Some(Self {
full_path: full_path.to_string(),
digest: digest.to_string(),
full_name: full_name.to_string(),
})
}
pub fn from_path(path: &Path) -> Option<Self> {
Self::from_full_path(path.to_str()?)
}
pub fn from_full_name(full_name: &str) -> Option<Self> {
Self::from_full_path(
&Path::new(STORE_DIR)
.join(&full_name)
.into_os_string()
.into_string()
.ok()?,
)
}
}
impl PartialEq for StorePath {
fn eq(&self, other: &Self) -> bool {
self.full_path == other.full_path
}
}
impl serde::Serialize for StorePath {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.full_path.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for StorePath {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_string(StorePathVisitor)
}
}
struct StorePathVisitor;
impl serde::de::Visitor<'_> for StorePathVisitor {
type Value = StorePath;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a valid nix store path starting with /nix/store")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
StorePath::from_full_path(value).ok_or_else(|| E::custom("invalid store path"))
}
}
/// Compression types for nar files.
/// Not exhaustive, this is just what
/// I've seen in the real world
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum NarCompression {
Zstd,
Xz,
}
impl NarCompression {
pub fn from_name(name: &str) -> Option<Self> {
match name {
"zstd" => Some(Self::Zstd),
"xz" => Some(Self::Zstd),
_ => None,
}
}
}
/// Extracted data from a narinfo.
/// May only be constructed by verifying signature
/// Missing some fields, like Deriver and FileHash because they aren't signed
#[derive(Debug, Clone, PartialEq, Eq)]
struct NarInfo {
/// Store path represented by the nar
pub store_path: StorePath,
/// Full URL to download the nar
pub url: Url,
/// Method used to compress the nar
pub compression: NarCompression,
/// sha256 hash of the nar after decompression
/// encoded in nix base32 format
pub nar_hash: String,
/// Size of the decompressed nar in bytes
pub nar_size: u64,
/// Other store paths referenced by the nar
pub references: Vec<StorePath>,
/// Signature of the nar, used to sign other items
pub sig: (String, Vec<u8>),
}
impl NarInfo {
fn parse(substituter_url: &Url, contents: &bytes::Bytes) -> Result<Self, SubstitutionError> {
let mut store_path = None;
let mut url = None;
let mut compression = None;
let mut nar_hash = None;
let mut nar_size = None;
let mut references = None;
let mut sig = None;
for maybe_line in contents.lines() {
let line = maybe_line.map_err(SubstitutionError::UndecodableNarInfo)?;
let (tag, rest) = line
.split_once(':')
.ok_or_else(|| SubstitutionError::BadNarInfo)?;
let value = rest.trim_start_matches(' ');
match tag {
"StorePath" => store_path = StorePath::from_full_path(value),
"URL" => url = substituter_url.join(value).ok(),
"Compression" => compression = NarCompression::from_name(value),
"NarHash" => {
nar_hash = {
let (algorithm, digest) = value
.split_once(':')
.ok_or_else(|| SubstitutionError::BadNarInfo)?;
if algorithm == "sha256" {
Some(digest.to_string())
} else {
None
}
}
},
"NarSize" => nar_size = u64::from_str_radix(value, 10).ok(),
"References" => {
references = value
.split(' ')
.map(StorePath::from_full_name)
.collect::<Option<Vec<_>>>()
},
"Sig" => {
let (signer, base64_signature) = value
.split_once(':')
.ok_or_else(|| SubstitutionError::BadNarInfo)?;
let signature = base64::engine::general_purpose::STANDARD
.decode(base64_signature)
.map_err(|_| SubstitutionError::BadNarInfo)?;
sig = Some((signer.to_string(), signature))
},
_ => {},
}
}
Ok(Self {
store_path: store_path.ok_or_else(|| SubstitutionError::BadNarInfo)?,
url: url.ok_or_else(|| SubstitutionError::BadNarInfo)?,
compression: compression.ok_or_else(|| SubstitutionError::BadNarInfo)?,
nar_hash: nar_hash.ok_or_else(|| SubstitutionError::BadNarInfo)?,
nar_size: nar_size.ok_or_else(|| SubstitutionError::BadNarInfo)?,
references: references.ok_or_else(|| SubstitutionError::BadNarInfo)?,
sig: sig.ok_or_else(|| SubstitutionError::BadNarInfo)?,
})
}
fn verify(
&self,
trusted_keys: &HashMap<String, VerifyingKey>,
) -> Result<(), SubstitutionError> {
let Some(key) = trusted_keys.get(&self.sig.0) else {
return Err(SubstitutionError::BadSignature);
};
// Fingerprint format not documented, but implemented in lix:
// https://git.lix.systems/lix-project/lix/src/commit/d461cc1d7b2f489c3886f147166ba5b5e0e37541/src/libstore/path-info.cc#L25
let fingerprint = format!(
"1;{};{};{};{}",
self.store_path.full_path,
self.nar_hash,
self.nar_size,
self.references
.iter()
.map(|reference| reference.full_path.as_ref())
.collect::<Vec<&str>>()
.join(",")
);
key.verify_strict(
fingerprint.as_bytes(),
&ed25519_dalek::Signature::from_bytes(
self.sig
.1
.as_slice()
.try_into()
.map_err(|_| SubstitutionError::BadSignature)?,
),
)
.map_err(|_| SubstitutionError::BadSignature)
}
pub fn parse_and_verify(
trusted_keys: &HashMap<String, VerifyingKey>,
substituter_url: &Url,
expected_store_path: &StorePath,
contents: &bytes::Bytes,
) -> Result<Self, SubstitutionError> {
let parsed = Self::parse(substituter_url, contents)?;
if &parsed.store_path != expected_store_path {
return Err(SubstitutionError::BadSignature);
}
parsed.verify(trusted_keys)?;
Ok(parsed)
}
}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum SubstitutionError {
#[error("Unarchiving error")]
Unarchive(#[source] std::io::Error),
#[error("Unknown proxy scheme, `https://`, `socks5://`, and `http://` supported")]
UnknownProxyScheme,
#[error("Invalid public key")]
/// Normally an ed25519_dalek::SignatureError,
/// but that comes with no extra information so no need to include it
PublicKey,
#[error("Undecodable narinfo")]
UndecodableNarInfo(#[source] std::io::Error),
#[error("Bad narinfo contents")]
BadNarInfo,
#[error("Bad narinfo signature")]
BadSignature,
#[error("Invalid nix store path")]
InvalidStorePath,
}
impl From<SubstitutionError> for ActionErrorKind {
fn from(val: SubstitutionError) -> Self {
ActionErrorKind::Custom(Box::new(val))
}
}

View file

@ -9,6 +9,7 @@ pub(crate) mod create_or_merge_nix_config;
pub(crate) mod create_user;
pub(crate) mod delete_user;
pub(crate) mod fetch_and_unpack_nix;
pub(crate) mod fetch_and_unpack_nix_substituter;
pub(crate) mod move_unpacked_nix;
pub(crate) mod remove_directory;
pub(crate) mod setup_default_profile;
@ -22,6 +23,7 @@ pub use create_or_merge_nix_config::CreateOrMergeNixConfig;
pub use create_user::CreateUser;
pub use delete_user::DeleteUser;
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};
pub use fetch_and_unpack_nix_substituter::{FetchAndUnpackNixSubstituter, SubstitutionError};
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
pub use remove_directory::RemoveDirectory;
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};

View file

@ -6,8 +6,9 @@ use std::{
use tracing::{span, Span};
use walkdir::WalkDir;
use crate::action::{
Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction,
use crate::{
action::{Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction},
release_tarball,
};
pub(crate) const DEST: &str = "/nix/";
@ -62,15 +63,9 @@ impl Action for MoveUnpackedNix {
let Self { unpacked_path } = self;
// This is the `nix-$VERSION` folder which unpacks from the tarball, not a nix derivation
let found_nix_paths = glob::glob(&format!("{}/nix-*", unpacked_path.display()))
.map_err(|e| Self::error(MoveUnpackedNixError::from(e)))?
.collect::<Result<Vec<_>, _>>()
.map_err(|e| Self::error(MoveUnpackedNixError::from(e)))?;
if found_nix_paths.len() != 1 {
return Err(Self::error(ActionErrorKind::MalformedBinaryTarball));
}
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
let src_store = found_nix_path.join("store");
let toplevel_path =
release_tarball::find_toplevel_path(&unpacked_path).map_err(Self::error)?;
let src_store = toplevel_path.join("store");
let mut src_store_listing = tokio::fs::read_dir(src_store.clone())
.await
.map_err(|e| ActionErrorKind::ReadDir(src_store.clone(), e))

View file

@ -2,7 +2,7 @@ use std::path::PathBuf;
use crate::{
action::{ActionError, ActionErrorKind, ActionTag, StatefulAction},
execute_command, set_env,
execute_command, release_tarball, set_env,
};
use glob::glob;
@ -51,8 +51,10 @@ impl Action for SetupDefaultProfile {
#[tracing::instrument(level = "debug", skip_all)]
async fn execute(&mut self) -> Result<(), ActionError> {
// Find an `nix` package
let nix_pkg_glob = format!("{}/nix-*/store/*-nix-*.*.*", self.unpacked_path.display());
let toplevel_path =
release_tarball::find_toplevel_path(&self.unpacked_path).map_err(Self::error)?;
// Find a `nix` package
let nix_pkg_glob = format!("{}/store/*-[nl]ix-*.*.*", toplevel_path.display());
let mut found_nix_pkg = None;
for entry in glob(&nix_pkg_glob).map_err(Self::error)? {
match entry {
@ -78,10 +80,7 @@ impl Action for SetupDefaultProfile {
};
// Find an `nss-cacert` package, add it too.
let nss_ca_cert_pkg_glob = format!(
"{}/nix-*/store/*-nss-cacert-*.*",
self.unpacked_path.display()
);
let nss_ca_cert_pkg_glob = format!("{}/store/*-nss-cacert-*.*", toplevel_path.display());
let mut found_nss_ca_cert_pkg = None;
for entry in glob(&nss_ca_cert_pkg_glob).map_err(Self::error)? {
match entry {
@ -108,14 +107,8 @@ impl Action for SetupDefaultProfile {
return Err(Self::error(SetupDefaultProfileError::NoNssCacert));
};
let found_nix_paths = glob::glob(&format!("{}/nix-*", self.unpacked_path.display()))
.map_err(Self::error)?
.collect::<Result<Vec<_>, _>>()
.map_err(Self::error)?;
if found_nix_paths.len() != 1 {
return Err(Self::error(ActionErrorKind::MalformedBinaryTarball));
}
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
let found_nix_path =
release_tarball::find_toplevel_path(&self.unpacked_path).map_err(Self::error)?;
let reginfo_path = found_nix_path.join(".reginfo");
let reginfo = tokio::fs::read(&reginfo_path)
.await

View file

@ -7,7 +7,7 @@ use crate::action::{
Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction,
};
use crate::parse_ssl_cert;
use crate::settings::UrlOrPathOrString;
use crate::settings::{UrlOrPathOrString, LIX_DEFAULT_SUBSTITUTERS, LIX_DEFAULT_SUBSTITUTER_KEYS};
use indexmap::map::Entry;
use std::path::PathBuf;
@ -136,11 +136,11 @@ impl PlaceNixConfiguration {
// Set up our substituters.
settings.insert(
"substituters".to_string(),
"https://cache.nixos.org https://cache.lix.systems".to_string(),
LIX_DEFAULT_SUBSTITUTERS.join(" "),
);
settings.insert(
"trusted-public-keys".to_string(),
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=".to_string()
LIX_DEFAULT_SUBSTITUTER_KEYS.join(" "),
);
if enable_flakes {

View file

@ -3,19 +3,52 @@ use tracing::{span, Span};
use super::CreateNixTree;
use crate::{
action::{
base::{FetchAndUnpackNix, MoveUnpackedNix},
base::{FetchAndUnpackNix, FetchAndUnpackNixSubstituter, MoveUnpackedNix},
Action, ActionDescription, ActionError, ActionErrorKind, ActionTag, StatefulAction,
},
settings::{CommonSettings, SCRATCH_DIR},
};
use std::path::PathBuf;
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub enum FetchNix {
FromTarball(StatefulAction<FetchAndUnpackNix>),
FromSubstituter(StatefulAction<FetchAndUnpackNixSubstituter>),
}
impl FetchNix {
pub async fn try_execute(&mut self) -> Result<(), ActionError> {
match self {
FetchNix::FromTarball(action) => action.try_execute().await,
FetchNix::FromSubstituter(action) => action.try_execute().await,
}
}
pub fn describe_execute(&self) -> Vec<ActionDescription> {
match self {
FetchNix::FromTarball(action) => action.describe_execute(),
FetchNix::FromSubstituter(action) => action.describe_execute(),
}
}
pub async fn try_revert(&mut self) -> Result<(), ActionError> {
match self {
FetchNix::FromTarball(action) => action.try_revert().await,
FetchNix::FromSubstituter(action) => action.try_revert().await,
}
}
pub fn describe_revert(&self) -> Vec<ActionDescription> {
match self {
FetchNix::FromTarball(action) => action.describe_revert(),
FetchNix::FromSubstituter(action) => action.describe_revert(),
}
}
}
/**
Place Nix and it's requirements onto the target
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ProvisionNix {
fetch_nix: StatefulAction<FetchAndUnpackNix>,
fetch_nix: FetchNix,
create_nix_tree: StatefulAction<CreateNixTree>,
move_unpacked_nix: StatefulAction<MoveUnpackedNix>,
}
@ -23,13 +56,29 @@ pub struct ProvisionNix {
impl ProvisionNix {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
let fetch_nix = FetchAndUnpackNix::plan(
settings.nix_package_url.clone(),
PathBuf::from(SCRATCH_DIR),
settings.proxy.clone(),
settings.ssl_cert_file.clone(),
)
.await?;
let fetch_nix = if settings.use_substituters {
FetchNix::FromSubstituter(
FetchAndUnpackNixSubstituter::plan(
settings.nix_package_url.clone(),
PathBuf::from(SCRATCH_DIR),
settings.substituter_trusted_keys.clone(),
settings.substituters.clone(),
settings.proxy.clone(),
settings.ssl_cert_file.clone(),
)
.await?,
)
} else {
FetchNix::FromTarball(
FetchAndUnpackNix::plan(
settings.nix_package_url.clone(),
PathBuf::from(SCRATCH_DIR),
settings.proxy.clone(),
settings.ssl_cert_file.clone(),
)
.await?,
)
};
let create_nix_tree = CreateNixTree::plan().await.map_err(Self::error)?;
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from(SCRATCH_DIR))

View file

@ -488,10 +488,7 @@ pub enum ActionErrorKind {
"".to_string()
}
)]
CommandOutput {
command: String,
output: Output,
},
CommandOutput { command: String, output: Output },
#[error("Joining spawned async task")]
Join(
#[source]
@ -509,7 +506,7 @@ pub enum ActionErrorKind {
/// A MacOS (Darwin) plist related error
#[error(transparent)]
Plist(#[from] plist::Error),
#[error("Unexpected binary tarball contents found, the build result from `https://releases.nixos.org/?prefix=nix/` or `nix build nix#hydraJobs.binaryTarball.$SYSTEM` is expected")]
#[error("Unexpected binary tarball contents found, the build result from `https://releases.lix.systems/?prefix=nix/` or `nix build lix#hydraJobs.binaryTarball.$SYSTEM` is expected")]
MalformedBinaryTarball,
#[error("Could not find `{0}` in PATH; This action only works on SteamOS, which should have this present in PATH.")]
MissingSteamosBinary(String),

View file

@ -20,9 +20,9 @@ pub trait CommandExecute {
}
/**
The Determinate Nix installer (lix variant)
The Lix installer.
A fast, friendly, and reliable tool to help you use Nix with Flakes everywhere.
A fast, friendly, and reliable tool to help you install CppNix or Lix on your system.
*/
#[derive(Debug, Parser)]
#[clap(version)]

View file

@ -76,6 +76,7 @@ mod error;
mod os;
mod plan;
pub mod planner;
mod release_tarball;
pub mod self_test;
pub mod settings;

17
src/release_tarball.rs Normal file
View file

@ -0,0 +1,17 @@
use std::path::{Path, PathBuf};
use crate::action::ActionErrorKind;
/// Finds the top-level path in the tarball, e.g. lix-2.90.0-rc1-x86_64-linux
pub fn find_toplevel_path(unpacked_path: &Path) -> Result<PathBuf, ActionErrorKind> {
let found_nix_paths = glob::glob(&format!("{}/[nl]ix-*", unpacked_path.display()))
.map_err(ActionErrorKind::from)?
.collect::<Result<Vec<_>, _>>()
.map_err(ActionErrorKind::from)?;
if found_nix_paths.len() != 1 {
return Err(ActionErrorKind::MalformedBinaryTarball);
}
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
Ok(found_nix_path)
}

View file

@ -86,7 +86,7 @@ impl Shell {
.as_millis();
command.arg(format!(
r#"nix build --no-link --expr 'derivation {{ name = "self-test-{executable}-{timestamp_millis}"; system = "{SYSTEM}"; builder = "/bin/sh"; args = ["-c" "echo hello > \$out"]; }}'"#
r#"nix build --no-substitute --no-link --expr 'derivation {{ name = "self-test-{executable}-{timestamp_millis}"; system = "{SYSTEM}"; builder = "/bin/sh"; args = ["-c" "echo hello > \$out"]; }}'"#
));
let command_str = format!("{:?}", command.as_std());

View file

@ -11,21 +11,33 @@ use url::Url;
pub const SCRATCH_DIR: &str = "/nix/temp-install-dir";
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for Linux x86_64
pub const NIX_X64_64_LINUX_URL: &str =
"https://releases.lix.systems/lix/lix-2.90-beta.1/nix-2.90.0-beta.1-x86_64-linux.tar.xz";
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for Linux x86 (32 bit)
pub const NIX_I686_LINUX_URL: &str =
"https://releases.lix.systems/lix/lix-2.90-beta.1/nix-2.90.0-beta.1-i686-linux.tar.xz";
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for Linux aarch64
pub const NIX_AARCH64_LINUX_URL: &str =
"https://releases.lix.systems/lix/lix-2.90-beta.1/nix-2.90.0-beta.1-aarch64-linux.tar.xz";
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for Darwin x86_64
pub const NIX_X64_64_DARWIN_URL: &str =
"https://releases.lix.systems/lix/lix-2.90-beta.1/nix-2.90.0-beta.1-x86_64-darwin.tar.xz";
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for Darwin aarch64
pub const NIX_AARCH64_DARWIN_URL: &str =
"https://releases.lix.systems/lix/lix-2.90-beta.1/nix-2.90.0-beta.1-aarch64-darwin.tar.xz";
// See set_version.py
// BEGIN GENERATE-URLS
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for x86_64-linux.
pub const LIX_X86_64_LINUX_URL: &str =
"https://releases.lix.systems/lix/lix-2.90.0-rc1/lix-2.90.0-rc1-x86_64-linux.tar.xz";
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for x86_64-darwin.
pub const LIX_X86_64_DARWIN_URL: &str =
"https://releases.lix.systems/lix/lix-2.90.0-rc1/lix-2.90.0-rc1-x86_64-darwin.tar.xz";
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for aarch64-linux.
pub const LIX_AARCH64_LINUX_URL: &str =
"https://releases.lix.systems/lix/lix-2.90.0-rc1/lix-2.90.0-rc1-aarch64-linux.tar.xz";
/// Default [`nix_package_url`](CommonSettings::nix_package_url) for aarch64-darwin.
pub const LIX_AARCH64_DARWIN_URL: &str =
"https://releases.lix.systems/lix/lix-2.90.0-rc1/lix-2.90.0-rc1-aarch64-darwin.tar.xz";
// END GENERATE-URLS
pub const LIX_DEFAULT_SUBSTITUTERS: &[&str; 2] =
&["https://cache.nixos.org", "https://cache.lix.systems"];
pub const LIX_DEFAULT_SUBSTITUTER_KEYS: &[&str; 2] = &[
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=",
"cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=",
];
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
@ -142,6 +154,24 @@ pub struct CommonSettings {
)]
pub nix_build_user_id_base: u32,
/// Substituters used to download Lix, when enabled
#[cfg_attr(
feature = "cli",
clap(long, env = "NIX_INSTALLER_SUBSTITUTERS", default_values = LIX_DEFAULT_SUBSTITUTERS)
)]
pub substituters: Vec<Url>,
/// Trusted signing keys for substituters
#[cfg_attr(
feature = "cli",
clap(long, env = "NIX_INSTALLER_TRUSTED_KEYS", default_values = LIX_DEFAULT_SUBSTITUTER_KEYS)
)]
pub substituter_trusted_keys: Vec<String>,
/// Download Lix from a substituter instead of an install tarball
#[cfg_attr(feature = "cli", clap(long, env = "NIX_INSTALLER_USE_SUBSTITUTER"))]
pub use_substituters: bool,
/// The Nix package URL
#[cfg_attr(
feature = "cli",
@ -150,31 +180,34 @@ pub struct CommonSettings {
#[cfg_attr(
all(target_os = "macos", target_arch = "x86_64", feature = "cli"),
clap(
default_value = NIX_X64_64_DARWIN_URL,
default_value = LIX_X86_64_DARWIN_URL,
)
)]
#[cfg_attr(
all(target_os = "macos", target_arch = "aarch64", feature = "cli"),
clap(
default_value = NIX_AARCH64_DARWIN_URL,
default_value = LIX_AARCH64_DARWIN_URL,
)
)]
#[cfg_attr(
all(target_os = "linux", target_arch = "x86_64", feature = "cli"),
clap(
default_value = NIX_X64_64_LINUX_URL,
default_value = LIX_X86_64_LINUX_URL,
)
)]
// FIXME(i686): release i686 binaries again
/*
#[cfg_attr(
all(target_os = "linux", target_arch = "x86", feature = "cli"),
clap(
default_value = NIX_I686_LINUX_URL,
default_value = LIX_I686_LINUX_URL,
)
)]
*/
#[cfg_attr(
all(target_os = "linux", target_arch = "aarch64", feature = "cli"),
clap(
default_value = NIX_AARCH64_LINUX_URL,
default_value = LIX_AARCH64_LINUX_URL,
)
)]
pub nix_package_url: UrlOrPath,
@ -216,7 +249,6 @@ pub struct CommonSettings {
)
)]
pub enable_flakes: bool,
}
impl CommonSettings {
@ -231,21 +263,24 @@ impl CommonSettings {
match (Architecture::host(), OperatingSystem::host()) {
#[cfg(target_os = "linux")]
(Architecture::X86_64, OperatingSystem::Linux) => {
url = NIX_X64_64_LINUX_URL;
url = LIX_X86_64_LINUX_URL;
nix_build_user_prefix = "nixbld";
nix_build_user_id_base = 30000;
nix_build_user_count = 32;
},
// FIXME(i686): support i686-linux again
/*
#[cfg(target_os = "linux")]
(Architecture::X86_32(_), OperatingSystem::Linux) => {
url = NIX_I686_LINUX_URL;
url = LIX_I686_LINUX_URL;
nix_build_user_prefix = "nixbld";
nix_build_user_id_base = 30000;
nix_build_user_count = 32;
},
*/
#[cfg(target_os = "linux")]
(Architecture::Aarch64(_), OperatingSystem::Linux) => {
url = NIX_AARCH64_LINUX_URL;
url = LIX_AARCH64_LINUX_URL;
nix_build_user_prefix = "nixbld";
nix_build_user_id_base = 30000;
nix_build_user_count = 32;
@ -253,7 +288,7 @@ impl CommonSettings {
#[cfg(target_os = "macos")]
(Architecture::X86_64, OperatingSystem::MacOSX { .. })
| (Architecture::X86_64, OperatingSystem::Darwin) => {
url = NIX_X64_64_DARWIN_URL;
url = LIX_X86_64_DARWIN_URL;
nix_build_user_prefix = "_nixbld";
nix_build_user_id_base = 300;
nix_build_user_count = 32;
@ -261,7 +296,7 @@ impl CommonSettings {
#[cfg(target_os = "macos")]
(Architecture::Aarch64(_), OperatingSystem::MacOSX { .. })
| (Architecture::Aarch64(_), OperatingSystem::Darwin) => {
url = NIX_AARCH64_DARWIN_URL;
url = LIX_AARCH64_DARWIN_URL;
nix_build_user_prefix = "_nixbld";
nix_build_user_id_base = 300;
nix_build_user_count = 32;
@ -280,6 +315,15 @@ impl CommonSettings {
nix_build_user_id_base,
nix_build_user_count,
nix_build_user_prefix: nix_build_user_prefix.to_string(),
substituters: LIX_DEFAULT_SUBSTITUTERS
.iter()
.map(|s| s.parse())
.collect::<Result<Vec<_>, _>>()?,
substituter_trusted_keys: LIX_DEFAULT_SUBSTITUTER_KEYS
.iter()
.map(|s| s.to_string())
.collect(),
use_substituters: false,
nix_package_url: url.parse()?,
proxy: Default::default(),
extra_conf: Default::default(),
@ -298,6 +342,9 @@ impl CommonSettings {
nix_build_user_prefix,
nix_build_user_id_base,
nix_build_user_count,
substituters,
substituter_trusted_keys,
use_substituters,
nix_package_url,
proxy,
extra_conf,
@ -331,6 +378,15 @@ impl CommonSettings {
"nix_build_user_count".into(),
serde_json::to_value(nix_build_user_count)?,
);
map.insert("substituters".into(), serde_json::to_value(substituters)?);
map.insert(
"substituter_trusted_keys".into(),
serde_json::to_value(substituter_trusted_keys)?,
);
map.insert(
"use_substituters".into(),
serde_json::to_value(use_substituters)?,
);
map.insert(
"nix_package_url".into(),
serde_json::to_value(nix_package_url)?,

View file

@ -17,15 +17,17 @@
"action": {
"action": "provision_nix",
"fetch_nix": {
"action": {
"url_or_path": {
"Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz"
"FromTarball": {
"action": {
"url_or_path": {
"Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz"
},
"dest": "/nix/temp-install-dir",
"proxy": null,
"ssl_cert_file": null
},
"dest": "/nix/temp-install-dir",
"proxy": null,
"ssl_cert_file": null
},
"state": "Uncompleted"
"state": "Uncompleted"
}
},
"delete_users": [],
"create_group": {
@ -409,6 +411,15 @@
"nix_build_user_count": 0,
"nix_build_user_prefix": "nixbld",
"nix_build_user_id_base": 30000,
"substituters": [
"https://cache.nixos.org/",
"https://cache.lix.systems/"
],
"substituter_trusted_keys": [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=",
"cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o="
],
"use_substituters": false,
"nix_package_url": {
"Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz"
},

View file

@ -61,15 +61,17 @@
"action": {
"action": "provision_nix",
"fetch_nix": {
"action": {
"url_or_path": {
"Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz"
"FromTarball": {
"action": {
"url_or_path": {
"Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz"
},
"dest": "/nix/temp-install-dir",
"proxy": null,
"ssl_cert_file": null
},
"dest": "/nix/temp-install-dir",
"proxy": null,
"ssl_cert_file": null
},
"state": "Uncompleted"
"state": "Uncompleted"
}
},
"delete_users": [],
"create_group": {
@ -393,6 +395,15 @@
"nix_build_user_count": 0,
"nix_build_user_prefix": "nixbld",
"nix_build_user_id_base": 30000,
"substituters": [
"https://cache.nixos.org/",
"https://cache.lix.systems/"
],
"substituter_trusted_keys": [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=",
"cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o="
],
"use_substituters": false,
"nix_package_url": {
"Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-linux.tar.xz"
},

View file

@ -87,15 +87,17 @@
"action": {
"action": "provision_nix",
"fetch_nix": {
"action": {
"url_or_path": {
"Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-darwin.tar.xz"
"FromTarball": {
"action": {
"url_or_path": {
"Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-darwin.tar.xz"
},
"dest": "/nix/temp-install-dir",
"proxy": null,
"ssl_cert_file": null
},
"dest": "/nix/temp-install-dir",
"proxy": null,
"ssl_cert_file": null
},
"state": "Uncompleted"
"state": "Uncompleted"
}
},
"delete_users_in_group": null,
"create_group": {
@ -420,6 +422,15 @@
"nix_build_user_count": 32,
"nix_build_user_prefix": "_nixbld",
"nix_build_user_id_base": 300,
"substituters": [
"https://cache.nixos.org/",
"https://cache.lix.systems/"
],
"substituter_trusted_keys": [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=",
"cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o="
],
"use_substituters": false,
"nix_package_url": {
"Url": "https://releases.nixos.org/nix/nix-2.17.0/nix-2.17.0-x86_64-darwin.tar.xz"
},