mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-07-06 17:13:24 -05:00
swtpm: 0.10.1-unstable-05-06-2026 -> 0.10.1-unstable-2026-05-21
This commit is contained in:
commit
a3c6651274
2813 changed files with 72084 additions and 28593 deletions
6
.github/ISSUE_TEMPLATE.md
vendored
6
.github/ISSUE_TEMPLATE.md
vendored
|
|
@ -1,6 +0,0 @@
|
|||
<!--
|
||||
Please note: This blank issue template is meant for extraordinary issues
|
||||
that do not fit the templates. Unless you know your issue is relevant to
|
||||
Nixpkgs and requires the free-form blank issue, please use the issue
|
||||
templates instead.
|
||||
-->
|
||||
3
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
|
|
@ -35,7 +35,8 @@ body:
|
|||
If you are using an older version, please update to the latest stable version and check if the issue persists before continuing this bug report.
|
||||
options:
|
||||
- "Please select a version."
|
||||
- "- Unstable (26.05)"
|
||||
- "- Unstable (26.11)"
|
||||
- "- Beta (26.05)"
|
||||
- "- Stable (25.11)"
|
||||
default: 0
|
||||
validations:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ body:
|
|||
If you are using an older version, please update to the latest stable version and check if the issue persists before continuing this bug report.
|
||||
options:
|
||||
- "Please select a version."
|
||||
- "- Unstable (26.05)"
|
||||
- "- Unstable (26.11)"
|
||||
- "- Beta (26.05)"
|
||||
- "- Stable (25.11)"
|
||||
default: 0
|
||||
validations:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ body:
|
|||
If you are using an older version, please update to the latest stable version and check if the issue persists before continuing this bug report.
|
||||
options:
|
||||
- "Please select a version."
|
||||
- "- Unstable (26.05)"
|
||||
- "- Unstable (26.11)"
|
||||
- "- Beta (26.05)"
|
||||
- "- Stable (25.11)"
|
||||
default: 0
|
||||
validations:
|
||||
|
|
|
|||
3
.github/ISSUE_TEMPLATE/04_build_failure.yml
vendored
3
.github/ISSUE_TEMPLATE/04_build_failure.yml
vendored
|
|
@ -37,7 +37,8 @@ body:
|
|||
If you are purposefully trying to build an ancient version of a package in an older Nixpkgs, please coordinate with the [NixOS Archivists](https://matrix.to/#/#archivists:nixos.org).
|
||||
options:
|
||||
- "Please select a version."
|
||||
- "- Unstable (26.05)"
|
||||
- "- Unstable (26.11)"
|
||||
- "- Beta (26.05)"
|
||||
- "- Stable (25.11)"
|
||||
default: 0
|
||||
validations:
|
||||
|
|
|
|||
3
.github/ISSUE_TEMPLATE/05_update_request.yml
vendored
3
.github/ISSUE_TEMPLATE/05_update_request.yml
vendored
|
|
@ -37,7 +37,8 @@ body:
|
|||
If the package has been updated in unstable, but you believe the update should be backported to the stable release of Nixpkgs, please file the '**Request: backport to stable**' form instead.
|
||||
options:
|
||||
- "Please select a version."
|
||||
- "- Unstable (26.05)"
|
||||
- "- Unstable (26.11)"
|
||||
- "- Beta (26.05)"
|
||||
- "- Stable (25.11)"
|
||||
default: 0
|
||||
validations:
|
||||
|
|
|
|||
3
.github/ISSUE_TEMPLATE/06_module_request.yml
vendored
3
.github/ISSUE_TEMPLATE/06_module_request.yml
vendored
|
|
@ -35,7 +35,8 @@ body:
|
|||
If you are using an older or stable version, please update to the latest **unstable** version and check if the module still does not exist before continuing this request.
|
||||
options:
|
||||
- "Please select a version."
|
||||
- "- Unstable (26.05)"
|
||||
- "- Unstable (26.11)"
|
||||
- "- Beta (26.05)"
|
||||
- "- Stable (25.11)"
|
||||
default: 0
|
||||
validations:
|
||||
|
|
|
|||
11
.github/labeler-no-sync.yml
vendored
11
.github/labeler-no-sync.yml
vendored
|
|
@ -33,4 +33,15 @@
|
|||
- maintainers/github-teams.json
|
||||
- base-branch: ['master']
|
||||
|
||||
"backport release-26.05":
|
||||
- all:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- .github/actions/**/*
|
||||
- .github/workflows/*
|
||||
- .github/labeler*.yml
|
||||
- ci/**/*.*
|
||||
- maintainers/github-teams.json
|
||||
- base-branch: ['master']
|
||||
|
||||
# keep-sorted end
|
||||
|
|
|
|||
4
.github/workflows/backport.yml
vendored
4
.github/workflows/backport.yml
vendored
|
|
@ -21,7 +21,7 @@ defaults:
|
|||
jobs:
|
||||
backport:
|
||||
name: Backport Pull Request
|
||||
if: vars.NIXPKGS_CI_APP_ID && github.event.pull_request.merged == true && (github.event.action != 'labeled' || startsWith(github.event.label.name, 'backport'))
|
||||
if: vars.NIXPKGS_CI_CLIENT_ID && github.event.pull_request.merged == true && (github.event.action != 'labeled' || startsWith(github.event.label.name, 'backport'))
|
||||
runs-on: ubuntu-slim
|
||||
timeout-minutes: 3
|
||||
steps:
|
||||
|
|
@ -30,7 +30,7 @@ jobs:
|
|||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
|
||||
client-id: ${{ vars.NIXPKGS_CI_CLIENT_ID }}
|
||||
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
|
|
|
|||
4
.github/workflows/bot.yml
vendored
4
.github/workflows/bot.yml
vendored
|
|
@ -57,10 +57,10 @@ jobs:
|
|||
|
||||
# Use a GitHub App, because it has much higher rate limits: 12,500 instead of 5,000 req / hour.
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_APP_ID
|
||||
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_CLIENT_ID
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
|
||||
client-id: ${{ vars.NIXPKGS_CI_CLIENT_ID }}
|
||||
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
permission-administration: read
|
||||
permission-contents: write
|
||||
|
|
|
|||
4
.github/workflows/comment.yml
vendored
4
.github/workflows/comment.yml
vendored
|
|
@ -31,10 +31,10 @@ jobs:
|
|||
|
||||
# Use the GitHub App to make sure the reaction happens with the same user who will later merge.
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_APP_ID
|
||||
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_CLIENT_ID
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
|
||||
client-id: ${{ vars.NIXPKGS_CI_CLIENT_ID }}
|
||||
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
permission-pull-requests: write
|
||||
|
||||
|
|
|
|||
2
.github/workflows/edited.yml
vendored
2
.github/workflows/edited.yml
vendored
|
|
@ -39,7 +39,7 @@ jobs:
|
|||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
|
||||
client-id: ${{ vars.NIXPKGS_CI_CLIENT_ID }}
|
||||
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
permission-pull-requests: write
|
||||
|
||||
|
|
|
|||
35
.github/workflows/periodic-merge-24h.yml
vendored
35
.github/workflows/periodic-merge-24h.yml
vendored
|
|
@ -22,7 +22,7 @@ defaults:
|
|||
|
||||
jobs:
|
||||
periodic-merge:
|
||||
if: github.repository_owner == 'NixOS'
|
||||
if: github.repository_owner == 'NixOS' || github.event_name == 'workflow_dispatch'
|
||||
strategy:
|
||||
# don't fail fast, so that all pairs are tried
|
||||
fail-fast: false
|
||||
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
into: staging-next-25.11
|
||||
- from: staging-next-25.11
|
||||
into: staging-25.11
|
||||
- from: master
|
||||
- from: release-26.05
|
||||
into: staging-next-26.05
|
||||
- from: staging-next-26.05
|
||||
into: staging-26.05
|
||||
|
|
@ -49,3 +49,34 @@ jobs:
|
|||
name: ${{ matrix.pairs.name || format('{0} → {1}', matrix.pairs.from, matrix.pairs.into) }}
|
||||
secrets:
|
||||
NIXPKGS_CI_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
|
||||
# Resets the target branch of the current haskell-updates PR.
|
||||
# This makes GitHub hide all the commits that are already part of staging and gives us a much clearer PR view.
|
||||
haskell-updates:
|
||||
needs: periodic-merge
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Find PR and update target branch
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
// There will at most be a single haskell-updates PR anyway, so no need to paginate.
|
||||
await Promise.all(
|
||||
(
|
||||
await github.rest.pulls.list({
|
||||
...context.repo,
|
||||
state: 'open',
|
||||
head: `${context.repo.owner}:haskell-updates`,
|
||||
})
|
||||
).data.map((pr) =>
|
||||
github.rest.pulls.update({
|
||||
...context.repo,
|
||||
pull_number: pr.number,
|
||||
// Just updating to the same branch to trigger a UI update.
|
||||
// This is staging most of the time, but could be staging-next in rare cases.
|
||||
base: pr.base.ref,
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
2
.github/workflows/periodic-merge-6h.yml
vendored
2
.github/workflows/periodic-merge-6h.yml
vendored
|
|
@ -22,7 +22,7 @@ defaults:
|
|||
|
||||
jobs:
|
||||
periodic-merge:
|
||||
if: github.repository_owner == 'NixOS'
|
||||
if: github.repository_owner == 'NixOS' || github.event_name == 'workflow_dispatch'
|
||||
strategy:
|
||||
# don't fail fast, so that all pairs are tried
|
||||
fail-fast: false
|
||||
|
|
|
|||
2
.github/workflows/periodic-merge.yml
vendored
2
.github/workflows/periodic-merge.yml
vendored
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
|
||||
client-id: ${{ vars.NIXPKGS_CI_CLIENT_ID }}
|
||||
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
|
|
|
|||
4
.github/workflows/review.yml
vendored
4
.github/workflows/review.yml
vendored
|
|
@ -28,10 +28,10 @@ jobs:
|
|||
|
||||
# Use the GitHub App to make sure the reaction happens with the same user who will later merge.
|
||||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_APP_ID
|
||||
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_CLIENT_ID
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
|
||||
client-id: ${{ vars.NIXPKGS_CI_CLIENT_ID }}
|
||||
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
permission-pull-requests: write
|
||||
|
||||
|
|
|
|||
2
.github/workflows/teams.yml
vendored
2
.github/workflows/teams.yml
vendored
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
|
||||
client-id: ${{ vars.NIXPKGS_CI_CLIENT_ID }}
|
||||
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
permission-administration: read
|
||||
permission-contents: write
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ nixos/modules/installer/tools/nix-fallback-paths.nix @Artturin @Ericson2314 @lo
|
|||
|
||||
# NixOS integration test driver
|
||||
/nixos/lib/test-driver @tfc
|
||||
/nixos/lib/testing @tfc
|
||||
|
||||
# NixOS QEMU virtualisation
|
||||
/nixos/modules/virtualisation/qemu-vm.nix @raitobezarius
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ let
|
|||
rebuildCountByKernel = lib.mapAttrs (
|
||||
kernel: kernelRebuilds: lib.length kernelRebuilds
|
||||
) rebuildsByKernel;
|
||||
rebuildNames = extractPackageNames diffAttrs.rebuilds;
|
||||
in
|
||||
writeText "changed-paths.json" (
|
||||
builtins.toJSON {
|
||||
|
|
@ -188,7 +189,8 @@ let
|
|||
kernel: rebuilds: lib.nameValuePair "10.rebuild-${kernel}-stdenv" (lib.elem "stdenv" rebuilds)
|
||||
) rebuildsByKernel
|
||||
// {
|
||||
"10.rebuild-nixos-tests" = lib.elem "nixosTests.simple" (extractPackageNames diffAttrs.rebuilds);
|
||||
"10.rebuild-nixos-tests" =
|
||||
lib.elem "nixosTests.simple-container" rebuildNames || lib.elem "nixosTests.simple-vm" rebuildNames;
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ in
|
|||
tweak (
|
||||
(removeAttrs nixpkgsJobs blacklist)
|
||||
// {
|
||||
nixosTests = lib.filterAttrs (name: _: name == "simple") nixosJobs.tests;
|
||||
nixosTests = lib.filterAttrs (
|
||||
name: _: name == "simple-container" || name == "simple-vm"
|
||||
) nixosJobs.tests;
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -99,7 +99,8 @@ async function checkTargetBranch({ github, context, core, dry }) {
|
|||
...Object.values(changed.rebuildCountByKernel),
|
||||
)
|
||||
const rebuildsAllTests =
|
||||
changed.attrdiff.changed.includes('nixosTests.simple')
|
||||
changed.attrdiff.changed.includes('nixosTests.simple-container') ||
|
||||
changed.attrdiff.changed.includes('nixosTests.simple-vm')
|
||||
|
||||
// https://github.com/NixOS/nixpkgs/pull/481205#issuecomment-3790123921
|
||||
// These should go to staging-nixos instead of master,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ unzip.section.md
|
|||
validatePkgConfig.section.md
|
||||
versionCheckHook.section.md
|
||||
waf.section.md
|
||||
writable-tmpdir-as-home-hook.section.md
|
||||
zig.section.md
|
||||
xcbuild.section.md
|
||||
xfce4-dev-tools.section.md
|
||||
|
|
|
|||
5
doc/hooks/writable-tmpdir-as-home-hook.section.md
Normal file
5
doc/hooks/writable-tmpdir-as-home-hook.section.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# writableTmpDirAsHomeHook {#writableTmpDirAsHomeHook}
|
||||
|
||||
This setup hook provides a writable home directory for packages that require it.
|
||||
|
||||
To use, just add the hook to the `nativeBuildInputs` of the package.
|
||||
|
|
@ -342,12 +342,54 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
pnpmDeps = fetchPnpmDeps {
|
||||
inherit (finalAttrs) pname version src;
|
||||
inherit pnpm;
|
||||
fetcherVersion = 3;
|
||||
fetcherVersion = 4;
|
||||
hash = "...";
|
||||
};
|
||||
})
|
||||
```
|
||||
|
||||
It is highly recommended to use a pinned version of pnpm (i.e., `pnpm_9` or `pnpm_10`), to increase future reproducibility. It might also be required to use an older version if the package needs support for a certain lock file version. To do so, you can pass the `pnpm` argument to `fetchPnpmDeps` and override the `pnpm` arg in `pnpmConfigHook`. Here are the changes in the example above to use a pinned pnpm version:
|
||||
|
||||
<!-- TODO: Does splicing still work when overriding in nativeBuildInputs here? -->
|
||||
|
||||
```diff
|
||||
{
|
||||
fetchPnpmDeps,
|
||||
nodejs,
|
||||
- pnpm,
|
||||
+ pnpm_10,
|
||||
pnpmConfigHook,
|
||||
stdenv,
|
||||
}:
|
||||
+let
|
||||
+ # Optionally override pnpm to use a custom nodejs version
|
||||
+ # Make sure that the same nodejs version is referenced in nativeBuildInputs
|
||||
+ # pnpm = pnpm_10.override { nodejs = nodejs-slim_22; };
|
||||
+in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "foo";
|
||||
version = "0-unstable-1980-01-01";
|
||||
|
||||
src = {
|
||||
#...
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
nodejs # in case scripts are run outside of a pnpm call
|
||||
pnpmConfigHook
|
||||
- pnpm # At least required by pnpmConfigHook, if not other (custom) phases
|
||||
+ pnpm_10 # At least required by pnpmConfigHook, if not other (custom) phases
|
||||
];
|
||||
|
||||
pnpmDeps = fetchPnpmDeps {
|
||||
inherit (finalAttrs) pname version src;
|
||||
+ pnpm = pnpm_10;
|
||||
fetcherVersion = 4;
|
||||
hash = "...";
|
||||
};
|
||||
})
|
||||
```
|
||||
|
||||
In case you are patching `package.json` or `pnpm-lock.yaml`, make sure to pass `finalAttrs.patches` to the function as well (i.e., `inherit (finalAttrs) patches`.
|
||||
|
||||
`pnpmConfigHook` supports adding additional `pnpm install` flags via `pnpmInstallFlags` which can be set to a Nix string array:
|
||||
|
|
@ -456,7 +498,7 @@ This is the version of the output of `fetchPnpmDeps`. New packages should use `3
|
|||
# ...
|
||||
pnpmDeps = fetchPnpmDeps {
|
||||
# ...
|
||||
fetcherVersion = 3;
|
||||
fetcherVersion = 4;
|
||||
hash = "..."; # clear this hash and generate a new one
|
||||
};
|
||||
}
|
||||
|
|
@ -474,6 +516,7 @@ Version 3 is the recommended value for new packages. Versions 1 and 2 are deprec
|
|||
- 1: Initial version, nothing special.
|
||||
- 2: [Ensure consistent permissions](https://github.com/NixOS/nixpkgs/pull/422975)
|
||||
- 3: [Build a reproducible tarball](https://github.com/NixOS/nixpkgs/pull/469950)
|
||||
- 4: [Dump SQLite database to an SQL file](https://github.com/NixOS/nixpkgs/pull/522703)
|
||||
|
||||
### Yarn {#javascript-yarn}
|
||||
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ The following is an example:
|
|||
vyp
|
||||
lblasc
|
||||
];
|
||||
license.fullName = "MIT/X11";
|
||||
license = lib.licenses.mit;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -871,6 +871,15 @@ general. A number of other parameters can be overridden:
|
|||
(hello { }).override { extraRustcOpts = "-Z debuginfo=2"; }
|
||||
```
|
||||
|
||||
- Extra arguments passed to `rustc` when the crate is a proc-macro,
|
||||
replacing `extraRustcOpts`. Useful to keep instrumentation flags
|
||||
(sanitizers, coverage) off host dylibs. Defaults to `null`, which
|
||||
inherits `extraRustcOpts`:
|
||||
|
||||
```nix
|
||||
(myProcMacro { }).override { extraRustcOptsForProcMacro = [ ]; }
|
||||
```
|
||||
|
||||
- The lint level cap passed to `rustc`. Defaults to `null`, which
|
||||
auto-resolves to `"allow"` (silences all lints) when `lints` is
|
||||
empty, or `"forbid"` (no cap) when `lints` is set. Because `rustc`
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
There is a TeX Live packaging that lives entirely under attribute `texlive`.
|
||||
|
||||
## User's guide (experimental new interface) {#sec-language-texlive-user-guide-experimental}
|
||||
|
||||
Release 23.11 ships with a new interface that will eventually replace `texlive.combine`.
|
||||
## User's guide {#sec-language-texlive-user-guide}
|
||||
|
||||
- For basic usage, use some of the prebuilt environments available at the top level, such as `texliveBasic`, `texliveSmall`. For the full list of prebuilt environments, inspect `texlive.schemes`.
|
||||
|
||||
|
|
@ -24,7 +22,7 @@ Release 23.11 ships with a new interface that will eventually replace `texlive.c
|
|||
|
||||
- `texlive.withPackages` uses the same logic as `buildEnv`. Only parts of a package are installed in an environment: its 'runtime' files (`tex` output), binaries (`out` output), and support files (`tlpkg` output). Moreover, man and info pages are assembled into separate `man` and `info` outputs. To add only the TeX files of a package, or its documentation (`texdoc` output), just specify the outputs:
|
||||
```nix
|
||||
texlive.withPackages (
|
||||
texliveBasic.withPackages (
|
||||
ps: with ps; [
|
||||
texdoc # recommended package to navigate the documentation
|
||||
perlPackages.LaTeXML.tex # tex files of LaTeXML, omit binaries
|
||||
|
|
@ -34,64 +32,19 @@ Release 23.11 ships with a new interface that will eventually replace `texlive.c
|
|||
)
|
||||
```
|
||||
|
||||
- To add the documentation for all packages in the environment, use
|
||||
```nix
|
||||
texliveSmall.overrideAttrs { withDocs = true; }
|
||||
```
|
||||
This can be applied before or after calling `withPackages`. The parameter `withSources` adds all source containers.
|
||||
|
||||
- All packages distributed by TeX Live, which contains most of CTAN, are available and can be found under `texlive.pkgs`:
|
||||
```ShellSession
|
||||
$ nix repl
|
||||
nix-repl> :l <nixpkgs>
|
||||
nix-repl> texlive.pkgs.[TAB]
|
||||
```
|
||||
Note that the packages in `texlive.pkgs` are only provided for search purposes and must not be used directly.
|
||||
|
||||
- **Experimental and subject to change without notice:** to add the documentation for all packages in the environment, use
|
||||
```nix
|
||||
texliveSmall.__overrideTeXConfig { withDocs = true; }
|
||||
```
|
||||
This can be applied before or after calling `withPackages`.
|
||||
|
||||
The function currently supports the parameters `withDocs`, `withSources`, and `requireTeXPackages`.
|
||||
|
||||
## User's guide {#sec-language-texlive-user-guide}
|
||||
|
||||
- For basic usage just pull `texlive.combined.scheme-basic` for an environment with basic LaTeX support.
|
||||
|
||||
- It typically won't work to use separately installed packages together. Instead, you can build a custom set of packages like this. Most CTAN packages should be available:
|
||||
|
||||
```nix
|
||||
texlive.combine {
|
||||
inherit (texlive)
|
||||
scheme-small
|
||||
collection-langkorean
|
||||
algorithms
|
||||
cm-super
|
||||
;
|
||||
}
|
||||
```
|
||||
|
||||
- There are all the schemes, collections and a few thousand packages, as defined upstream (perhaps with tiny differences).
|
||||
|
||||
- By default you only get executables and files needed during runtime, and a little documentation for the core packages. To change that, you need to add `pkgFilter` function to `combine`.
|
||||
|
||||
```nix
|
||||
texlive.combine {
|
||||
# inherit (texlive) whatever-you-want;
|
||||
pkgFilter =
|
||||
pkg: pkg.tlType == "run" || pkg.tlType == "bin" || pkg.hasManpages || pkg.pname == "cm-super";
|
||||
# elem tlType [ "run" "bin" "doc" "source" ]
|
||||
# there are also other attributes: version, name
|
||||
}
|
||||
```
|
||||
|
||||
- You can list packages e.g. by `nix repl`.
|
||||
|
||||
```ShellSession
|
||||
$ nix repl
|
||||
nix-repl> :l <nixpkgs>
|
||||
nix-repl> texlive.collection-[TAB]
|
||||
```
|
||||
|
||||
- Note that the wrapper assumes that the result has a chance to be useful. For example, the core executables should be present, as well as some core data files. The supported way of ensuring this is by including some scheme, for example, `scheme-basic`, into the combination.
|
||||
|
||||
- TeX Live packages are also available under `texlive.pkgs` as derivations with outputs `out`, `tex`, `texdoc`, `texsource`, `tlpkg`, `man`, `info`. They cannot be installed outside of `texlive.combine` but are available for other uses. To repackage a font, for instance, use
|
||||
These are derivations with outputs `out`, `tex`, `texdoc`, `texsource`, `tlpkg`, `man`, `info`. They cannot be installed outside of `texlive.withPackages` but are available for other uses. To repackage a font, for instance, use
|
||||
|
||||
```nix
|
||||
stdenvNoCC.mkDerivation (finalAttrs: {
|
||||
|
|
@ -112,9 +65,9 @@ Release 23.11 ships with a new interface that will eventually replace `texlive.c
|
|||
|
||||
## Custom packages {#sec-language-texlive-custom-packages}
|
||||
|
||||
You may find that you need to use an external TeX package. A derivation for such package has to provide the contents of the "texmf" directory in its `"tex"` output, according to the [TeX Directory Structure](https://tug.ctan.org/tds/tds.html). Dependencies on other TeX packages can be listed in the attribute `tlDeps`.
|
||||
You may find that you need to use an external TeX package. A derivation for such package has to provide the contents of the "texmf" directory in its `"tex"` output, according to the [TeX Directory Structure](https://tug.ctan.org/tds/tds.html). Dependencies on other TeX packages can be listed in the attribute `passthru.tlDeps`, which is a function taking a package set and returning a list of packages.
|
||||
|
||||
The functions `texlive.combine` and `texlive.withPackages` recognise the following outputs:
|
||||
The function `texlive.withPackages` recognise the following outputs:
|
||||
|
||||
- `"out"`: contents are linked in the TeX Live environment, and binaries in the `$out/bin` folder are wrapped;
|
||||
- `"tex"`: linked in `$TEXMFDIST`; files should follow the TDS (for instance `$tex/tex/latex/foiltex/foiltex.cls`);
|
||||
|
|
@ -122,8 +75,6 @@ The functions `texlive.combine` and `texlive.withPackages` recognise the followi
|
|||
- `"tlpkg"`: linked in `$TEXMFROOT/tlpkg`;
|
||||
- `"man"`, `"info"`, ...: the other outputs are combined into separate outputs.
|
||||
|
||||
When using `pkgFilter`, `texlive.combine` will assign `tlType` respectively `"bin"`, `"run"`, `"doc"`, `"source"`, `"tlpkg"` to the above outputs.
|
||||
|
||||
Here is a (very verbose) example. See also the packages `auctex`, `eukleides`, `mftrace` for more examples.
|
||||
|
||||
```nix
|
||||
|
|
@ -138,7 +89,7 @@ let
|
|||
"tex"
|
||||
"texdoc"
|
||||
];
|
||||
passthru.tlDeps = with texlive; [ latex ];
|
||||
passthru.tlDeps = ps: [ ps.latex ];
|
||||
|
||||
srcs = [
|
||||
(fetchurl {
|
||||
|
|
@ -169,13 +120,14 @@ let
|
|||
latexmk
|
||||
]
|
||||
))
|
||||
# multiple-outputs.sh fails if $out is not defined
|
||||
(writeShellScript "force-tex-output.sh" ''
|
||||
out="''${tex-}"
|
||||
'')
|
||||
writableTmpDirAsHomeHook # Need a writable $HOME for latexmk
|
||||
];
|
||||
|
||||
# multiple-outputs.sh fails if $out is not defined
|
||||
preHook = ''
|
||||
out="''${tex-}"
|
||||
'';
|
||||
|
||||
dontConfigure = true;
|
||||
|
||||
buildPhase = ''
|
||||
|
|
|
|||
|
|
@ -433,6 +433,30 @@
|
|||
"chap-overlays": [
|
||||
"index.html#chap-overlays"
|
||||
],
|
||||
"sec-nixpkgs-release-26.11": [
|
||||
"release-notes.html#sec-nixpkgs-release-26.11"
|
||||
],
|
||||
"sec-nixpkgs-release-26.11-highlights": [
|
||||
"release-notes.html#sec-nixpkgs-release-26.11-highlights"
|
||||
],
|
||||
"sec-nixpkgs-release-26.11-incompatibilities": [
|
||||
"release-notes.html#sec-nixpkgs-release-26.11-incompatibilities"
|
||||
],
|
||||
"sec-nixpkgs-release-26.11-lib": [
|
||||
"release-notes.html#sec-nixpkgs-release-26.11-lib"
|
||||
],
|
||||
"sec-nixpkgs-release-26.11-lib-breaking": [
|
||||
"release-notes.html#sec-nixpkgs-release-26.11-lib-breaking"
|
||||
],
|
||||
"sec-nixpkgs-release-26.11-lib-deprecations": [
|
||||
"release-notes.html#sec-nixpkgs-release-26.11-lib-deprecations"
|
||||
],
|
||||
"sec-nixpkgs-release-26.11-lib-additions-improvements": [
|
||||
"release-notes.html#sec-nixpkgs-release-26.11-lib-additions-improvements"
|
||||
],
|
||||
"sec-nixpkgs-release-26.11-notable-changes": [
|
||||
"release-notes.html#sec-nixpkgs-release-26.11-notable-changes"
|
||||
],
|
||||
"sec-nixpkgs-release-26.05": [
|
||||
"release-notes.html#sec-nixpkgs-release-26.05"
|
||||
],
|
||||
|
|
@ -1016,6 +1040,9 @@
|
|||
"tar-files": [
|
||||
"index.html#tar-files"
|
||||
],
|
||||
"writableTmpDirAsHomeHook": [
|
||||
"index.html#writableTmpDirAsHomeHook"
|
||||
],
|
||||
"x86_64-darwin-26.05": [
|
||||
"release-notes.html#x86_64-darwin-26.05"
|
||||
],
|
||||
|
|
@ -4428,11 +4455,9 @@
|
|||
"sec-language-texlive": [
|
||||
"index.html#sec-language-texlive"
|
||||
],
|
||||
"sec-language-texlive-user-guide-experimental": [
|
||||
"index.html#sec-language-texlive-user-guide-experimental"
|
||||
],
|
||||
"sec-language-texlive-user-guide": [
|
||||
"index.html#sec-language-texlive-user-guide"
|
||||
"index.html#sec-language-texlive-user-guide",
|
||||
"index.html#sec-language-texlive-user-guide-experimental"
|
||||
],
|
||||
"sec-language-texlive-custom-packages": [
|
||||
"index.html#sec-language-texlive-custom-packages"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
This section lists the release notes for each stable version of Nixpkgs and the current unstable revision.
|
||||
|
||||
```{=include=} sections
|
||||
rl-2611.section.md
|
||||
rl-2605.section.md
|
||||
rl-2511.section.md
|
||||
rl-2505.section.md
|
||||
|
|
|
|||
|
|
@ -253,6 +253,8 @@
|
|||
|
||||
- Ethercalc and its associated module have been removed, as the package is unmaintained and cannot be installed from source with npm now.
|
||||
|
||||
- The `services.avahi.wideArea` option now defaults to `false` as a mitigation against [`CVE-2024-52615`/`GHSA-x6vp-f33h-h32g`](https://github.com/avahi/avahi/security/advisories/GHSA-x6vp-f33h-h32g).
|
||||
|
||||
- `coreth` has been removed, as upstream has moved it into `avalanchego`.
|
||||
|
||||
- `nodePackages.prebuild-install` was removed because it appeared to be unmaintained upstream.
|
||||
|
|
@ -302,6 +304,8 @@
|
|||
|
||||
- The `pie` hardening flag has been removed and will now error, after being deprecated in 25.11. Compilers are expected to enable PIE by default, as has been common practice since 2016 outside of Nixpkgs. If a package needs `pie` disabled pass `-no-pie` in `CFLAGS`. It is unlikely this will be necessary in many cases; due to the prevalence of default PIE toolchains, most packages incompatible with PIE already pass `-no-pie`.
|
||||
|
||||
- `pqos-wrapper` was removed as it has been unmaintained since 2022 and not widely used.
|
||||
|
||||
## Other Notable Changes {#sec-nixpkgs-release-26.05-notable-changes}
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
|
@ -362,6 +366,8 @@
|
|||
|
||||
- We now use the upstream wrapper script for Gradle, supporting both the `JAVA_HOME` and `GRADLE_OPTS` environment variables.
|
||||
|
||||
- Updated `gonic` to 0.21.0. A full ("slow") scan is recommended after upgrading to v0.21.0 to pick up the newly scanned fields (contributors, ISRCs, record labels, per-track years, ARTIST_CREDIT).
|
||||
|
||||
- the `autossh-ng` NixOS module was introduced as a simpler alternative to the existing `autossh` module.
|
||||
|
||||
- Added `haskell.packages.microhs`, a set of Haskell packages built with MicroHs.
|
||||
|
|
@ -390,7 +396,7 @@ gnuradioMinimal.override {
|
|||
|
||||
### Breaking changes {#sec-nixpkgs-release-26.05-lib-breaking}
|
||||
|
||||
- The `nodejs_latest` alias now points to `nodejs_25` instead of `nodejs_24`.
|
||||
- The `nodejs_latest` alias now points to `nodejs_26` instead of `nodejs_24`.
|
||||
|
||||
- `nodejs-slim` no longer exposes a `corepack` executable, it has been moved to an ad-hoc output; to restore the previous behavior, `nodejs-slim.corepack` must be explicitly included.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Nixpkgs 26.11 (2026.11/??) {#sec-nixpkgs-release-26.11}
|
||||
# Nixpkgs 26.11 ("Zokor", 2026.11/??) {#sec-nixpkgs-release-26.11}
|
||||
|
||||
## Highlights {#sec-nixpkgs-release-26.11-highlights}
|
||||
|
||||
|
|
@ -10,9 +10,7 @@
|
|||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
- `pqos-wrapper` was removed
|
||||
as it has been unmaintained since 2022
|
||||
and not widely used.
|
||||
- `hurl` has been updated to `8.x.x` which has some breaking changes. See [upstream changelog](https://github.com/Orange-OpenSource/hurl/releases/tag/8.0.0) for details.
|
||||
|
||||
## Other Notable Changes {#sec-nixpkgs-release-26.11-notable-changes}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
26.05
|
||||
26.11
|
||||
|
|
@ -30,7 +30,6 @@ let
|
|||
flatten
|
||||
deepSeq
|
||||
extends
|
||||
toFunction
|
||||
id
|
||||
;
|
||||
inherit (lib.strings) levenshtein levenshteinAtMost;
|
||||
|
|
@ -842,14 +841,6 @@ rec {
|
|||
:::
|
||||
*/
|
||||
extendMkDerivation =
|
||||
let
|
||||
extendsWithExclusion =
|
||||
excludedNames: g: f: final:
|
||||
let
|
||||
previous = f final;
|
||||
in
|
||||
removeAttrs previous excludedNames // g final previous;
|
||||
in
|
||||
{
|
||||
constructDrv,
|
||||
excludeDrvArgNames ? [ ],
|
||||
|
|
@ -858,24 +849,27 @@ rec {
|
|||
inheritFunctionArgs ? true,
|
||||
transformDrv ? id,
|
||||
}:
|
||||
setFunctionArgs
|
||||
{
|
||||
# Adds the fixed-point style support
|
||||
(
|
||||
fpargs:
|
||||
__functor =
|
||||
self: fpargs:
|
||||
transformDrv (
|
||||
constructDrv (extendsWithExclusion excludeDrvArgNames extendDrvArgs (toFunction fpargs))
|
||||
)
|
||||
)
|
||||
# Add __functionArgs
|
||||
(
|
||||
removeAttrs (
|
||||
# Inherit the __functionArgs from the base build helper
|
||||
optionalAttrs inheritFunctionArgs (removeAttrs (functionArgs constructDrv) excludeDrvArgNames)
|
||||
# Recover the __functionArgs from the derived build helper
|
||||
// functionArgs (extendDrvArgs { })
|
||||
) excludeFunctionArgNames
|
||||
)
|
||||
// {
|
||||
constructDrv (
|
||||
final:
|
||||
let
|
||||
previous = if isFunction fpargs then fpargs final else fpargs;
|
||||
in
|
||||
removeAttrs previous excludeDrvArgNames // extendDrvArgs final previous
|
||||
)
|
||||
);
|
||||
|
||||
__functionArgs = removeAttrs (
|
||||
# Inherit the __functionArgs from the base build helper
|
||||
optionalAttrs inheritFunctionArgs (removeAttrs (functionArgs constructDrv) excludeDrvArgNames)
|
||||
# Recover the __functionArgs from the derived build helper
|
||||
// functionArgs (extendDrvArgs { })
|
||||
) excludeFunctionArgNames;
|
||||
|
||||
inherit
|
||||
# Expose to the result build helper.
|
||||
constructDrv
|
||||
|
|
|
|||
|
|
@ -1,16 +1,38 @@
|
|||
# snippets that can be shared by multiple fetchers (pkgs/build-support)
|
||||
{ lib }:
|
||||
let
|
||||
commonH = hashTypes: rec {
|
||||
hashNames = [ "hash" ] ++ hashTypes;
|
||||
hashSet = lib.genAttrs hashNames (lib.const { });
|
||||
};
|
||||
commonH =
|
||||
let
|
||||
defaultHashNames = [ "hash" ];
|
||||
in
|
||||
hashTypes: rec {
|
||||
hashNames = defaultHashNames ++ hashTypes;
|
||||
hashSet = genAttrs hashNames (const { });
|
||||
};
|
||||
|
||||
fakeH = {
|
||||
hash = lib.fakeHash;
|
||||
sha256 = lib.fakeSha256;
|
||||
sha512 = lib.fakeSha512;
|
||||
};
|
||||
|
||||
defaultHashTypes = [ "sha256" ];
|
||||
|
||||
inherit (lib)
|
||||
concatMapStringsSep
|
||||
head
|
||||
length
|
||||
throwIf
|
||||
;
|
||||
inherit (lib.attrsets)
|
||||
attrsToList
|
||||
intersectAttrs
|
||||
genAttrs
|
||||
removeAttrs
|
||||
optionalAttrs
|
||||
;
|
||||
|
||||
inherit (lib.trivial) const functionArgs setFunctionArgs;
|
||||
in
|
||||
rec {
|
||||
|
||||
|
|
@ -90,27 +112,14 @@ rec {
|
|||
*/
|
||||
normalizeHash =
|
||||
{
|
||||
hashTypes ? [ "sha256" ],
|
||||
hashTypes ? defaultHashTypes,
|
||||
required ? true,
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
concatMapStringsSep
|
||||
head
|
||||
tail
|
||||
throwIf
|
||||
;
|
||||
inherit (lib.attrsets)
|
||||
attrsToList
|
||||
intersectAttrs
|
||||
removeAttrs
|
||||
optionalAttrs
|
||||
;
|
||||
|
||||
inherit (commonH hashTypes) hashNames hashSet;
|
||||
in
|
||||
args:
|
||||
if args ? "outputHash" then
|
||||
if args ? outputHash then
|
||||
args
|
||||
else
|
||||
let
|
||||
|
|
@ -122,7 +131,7 @@ rec {
|
|||
in
|
||||
if hashesAsNVPairs == [ ] then
|
||||
throwIf required "fetcher called without `hash`" null
|
||||
else if tail hashesAsNVPairs != [ ] then
|
||||
else if length hashesAsNVPairs != 1 then
|
||||
throw "fetcher called with mutually-incompatible arguments: ${
|
||||
concatMapStringsSep ", " (a: a.name) hashesAsNVPairs
|
||||
}"
|
||||
|
|
@ -190,15 +199,20 @@ rec {
|
|||
and is implemented somewhat more efficiently.
|
||||
*/
|
||||
withNormalizedHash =
|
||||
let
|
||||
removedAttributes = [
|
||||
"outputHash"
|
||||
"outputHashAlgo"
|
||||
];
|
||||
in
|
||||
{
|
||||
hashTypes ? [ "sha256" ],
|
||||
hashTypes ? defaultHashTypes,
|
||||
}:
|
||||
let
|
||||
inherit (commonH hashTypes) hashSet;
|
||||
in
|
||||
fetcher:
|
||||
let
|
||||
inherit (lib.attrsets) intersectAttrs removeAttrs;
|
||||
inherit (lib.trivial) functionArgs setFunctionArgs;
|
||||
|
||||
inherit (commonH hashTypes) hashSet;
|
||||
fArgs = functionArgs fetcher;
|
||||
|
||||
normalize = normalizeHash {
|
||||
|
|
@ -211,10 +225,7 @@ rec {
|
|||
assert intersectAttrs fArgs hashSet == { };
|
||||
|
||||
setFunctionArgs (args: fetcher (normalize args)) (
|
||||
removeAttrs fArgs [
|
||||
"outputHash"
|
||||
"outputHashAlgo"
|
||||
]
|
||||
removeAttrs fArgs removedAttributes
|
||||
// {
|
||||
hash = fArgs.outputHash;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,23 @@
|
|||
{ lib }:
|
||||
|
||||
let
|
||||
inherit (lib) all any elem;
|
||||
handleComplexProperty =
|
||||
evaluateSubProperty: AND: OR: license:
|
||||
if license.licenseType == "compound" then
|
||||
if license.operator == "OR" then
|
||||
OR evaluateSubProperty license.licenses
|
||||
else if license.operator == "AND" then
|
||||
AND evaluateSubProperty license.licenses
|
||||
else
|
||||
throw "Unknown license operator"
|
||||
else if license.licenseType == "exception" then
|
||||
evaluateSubProperty license.license && evaluateSubProperty license.exception
|
||||
else if license.licenseType == "plus" then
|
||||
evaluateSubProperty license.license
|
||||
else
|
||||
throw "Unknown license type or legacy license";
|
||||
in
|
||||
rec {
|
||||
/**
|
||||
Evaluate a license expression for a given predicate.
|
||||
|
|
@ -21,29 +40,45 @@ rec {
|
|||
- [license] license expression to check
|
||||
*/
|
||||
evaluateProperty =
|
||||
predicate: permissive: license:
|
||||
predicate: permissive:
|
||||
let
|
||||
OR = if permissive then lib.any else lib.all;
|
||||
AND = if permissive then lib.all else lib.any;
|
||||
OR = if permissive then any else all;
|
||||
AND = if permissive then all else any;
|
||||
evaluateComplexProperty = handleComplexProperty (evaluateProperty predicate permissive) AND OR;
|
||||
in
|
||||
if license.licenseType == "simple" then
|
||||
predicate license
|
||||
else if license.licenseType == "compound" then
|
||||
if license.operator == "OR" then
|
||||
OR (x: evaluateProperty predicate permissive x) license.licenses
|
||||
else if license.operator == "AND" then
|
||||
AND (x: evaluateProperty predicate permissive x) license.licenses
|
||||
else
|
||||
throw "Unknown license operator"
|
||||
else if license.licenseType == "exception" then
|
||||
AND (x: evaluateProperty predicate permissive x) [
|
||||
license.license
|
||||
license.exception
|
||||
]
|
||||
else if license.licenseType == "plus" then
|
||||
evaluateProperty predicate permissive license.license
|
||||
else
|
||||
throw "Unknown license type or legacy license";
|
||||
license:
|
||||
if license.licenseType == "simple" then predicate license else evaluateComplexProperty license;
|
||||
|
||||
/**
|
||||
Evaluate a license expression for a given property name. The property must
|
||||
be defined as a boolean attribute of all licenses passed.
|
||||
|
||||
# Example
|
||||
|
||||
```nix
|
||||
evaluateNamedProperty "deprecated" true (with lib.licenses; AND [ ncsa (WITH asl20 llvm-exception) ])
|
||||
```
|
||||
# Type
|
||||
|
||||
```
|
||||
evaluateProperty :: String -> Bool -> AttrSet -> Bool
|
||||
```
|
||||
|
||||
# Arguments
|
||||
|
||||
- [name] name of the attribute to check
|
||||
- [permissive] whether to apply checks permissive or reciprocal
|
||||
- [license] license expression to check
|
||||
*/
|
||||
evaluateNamedProperty =
|
||||
name: permissive:
|
||||
let
|
||||
OR = if permissive then any else all;
|
||||
AND = if permissive then all else any;
|
||||
evaluateComplexProperty = handleComplexProperty (evaluateNamedProperty name permissive) AND OR;
|
||||
in
|
||||
license:
|
||||
if license.licenseType == "simple" then license.${name} else evaluateComplexProperty license;
|
||||
|
||||
/**
|
||||
Check whether a license expression is free.
|
||||
|
|
@ -65,7 +100,7 @@ rec {
|
|||
|
||||
- [license] License expression to check if free
|
||||
*/
|
||||
isFree = evaluateProperty (x: x.free) true;
|
||||
isFree = evaluateNamedProperty "free" true;
|
||||
|
||||
/**
|
||||
Check whether a license expression is redistributable.
|
||||
|
|
@ -87,7 +122,7 @@ rec {
|
|||
|
||||
- [license] License expression to check if redistributable
|
||||
*/
|
||||
isRedistributable = evaluateProperty (x: x.redistributable) true;
|
||||
isRedistributable = evaluateNamedProperty "redistributable" true;
|
||||
|
||||
/**
|
||||
Check whether any of the given licenses is required in the license expression.
|
||||
|
|
@ -110,7 +145,7 @@ rec {
|
|||
- [licenses] List of licenses to look
|
||||
- [license] License expression to check
|
||||
*/
|
||||
containsLicenses = licenses: evaluateProperty (x: lib.lists.elem x licenses) false;
|
||||
containsLicenses = licenses: evaluateProperty (x: elem x licenses) false;
|
||||
|
||||
/**
|
||||
Convert a license expression to an SPDX license expression string.
|
||||
|
|
|
|||
|
|
@ -1598,6 +1598,28 @@ let
|
|||
inherit priority content;
|
||||
};
|
||||
|
||||
/**
|
||||
Applies a function to the value inside a definition,
|
||||
preserving all surrounding properties (`mkForce`, `mkOrder`, `mkIf`, etc.).
|
||||
*/
|
||||
mapDefinitionValue =
|
||||
f: def:
|
||||
if def ? _type then
|
||||
if def._type == "merge" then
|
||||
def // { contents = map (mapDefinitionValue f) def.contents; }
|
||||
else if def._type == "if" then
|
||||
def // { content = mapDefinitionValue f def.content; }
|
||||
else if def._type == "override" then
|
||||
def // { content = mapDefinitionValue f def.content; }
|
||||
else if def._type == "order" then
|
||||
def // { content = mapDefinitionValue f def.content; }
|
||||
else if def._type == "definition" then
|
||||
def // { value = mapDefinitionValue f def.value; }
|
||||
else
|
||||
f def
|
||||
else
|
||||
f def;
|
||||
|
||||
mkBefore = mkOrder 500;
|
||||
defaultOrderPriority = 1000;
|
||||
mkAfter = mkOrder 1500;
|
||||
|
|
@ -2302,6 +2324,7 @@ private
|
|||
importApply
|
||||
importJSON
|
||||
importTOML
|
||||
mapDefinitionValue
|
||||
mergeDefinitions
|
||||
mergeAttrDefinitionsWithPrio
|
||||
mergeOptionDecls # should be private?
|
||||
|
|
|
|||
115
lib/sources.nix
115
lib/sources.nix
|
|
@ -7,12 +7,19 @@ let
|
|||
match
|
||||
split
|
||||
storeDir
|
||||
escapeRegex
|
||||
removePrefix
|
||||
;
|
||||
inherit (lib)
|
||||
boolToString
|
||||
filter
|
||||
isString
|
||||
readFile
|
||||
concatStrings
|
||||
length
|
||||
elemAt
|
||||
isList
|
||||
any
|
||||
;
|
||||
inherit (lib.filesystem)
|
||||
pathIsRegularFile
|
||||
|
|
@ -513,6 +520,113 @@ let
|
|||
else
|
||||
throw "repoRevToName: invalid kind";
|
||||
|
||||
/**
|
||||
Filter a source tree by a list of doublestar-style glob patterns,
|
||||
returning a source that only contains paths matching at least one
|
||||
pattern. `*` matches a single path component, and `**` matches any
|
||||
number of components.
|
||||
|
||||
# Inputs
|
||||
|
||||
`src`
|
||||
|
||||
: The source tree to filter.
|
||||
|
||||
`patterns`
|
||||
|
||||
: List of glob patterns to include, e.g. `[ "*.py" "src/**" ]`.
|
||||
A leading `**` (e.g. `**\/*.py` for all `.py` files at any depth)
|
||||
is also supported; the `\` here is just a Nix string escape used
|
||||
to avoid closing this comment.
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `sourceByGlobs` usage example
|
||||
|
||||
- Include everything under a subdirectory
|
||||
```nix
|
||||
src = sourceByGlobs ./. [ "src/**" "tests/**" ]
|
||||
```
|
||||
|
||||
- Include all .py files in root directory only
|
||||
```nix
|
||||
src = sourceByGlobs ./. [ "*.py" ]
|
||||
```
|
||||
|
||||
:::
|
||||
*/
|
||||
sourceByGlobs =
|
||||
let
|
||||
splitPath = path: filter isString (split "/" path);
|
||||
# Make component regex
|
||||
mkRe =
|
||||
s:
|
||||
if s == "**" then
|
||||
".*" # Has special handling below
|
||||
else
|
||||
concatStrings (map (tok: if isList tok then "[^/]*" else escapeRegex tok) (split "\\*+" s));
|
||||
|
||||
# Make a source filter function from pattern
|
||||
mkMatcher =
|
||||
pat:
|
||||
let
|
||||
globs = map mkRe (splitPath pat);
|
||||
glen = length globs;
|
||||
in
|
||||
path: type:
|
||||
let
|
||||
path' = splitPath path;
|
||||
plen = length path';
|
||||
|
||||
recurse =
|
||||
gi: pi:
|
||||
let
|
||||
g = elemAt globs gi;
|
||||
p = elemAt path' pi;
|
||||
m = match g p != null;
|
||||
in
|
||||
if pi >= plen then # Reached end of path
|
||||
gi >= glen || (type == "directory" || type == "symlink") # Only allow partial matches for directories
|
||||
else if gi >= glen then # Reached end of globs
|
||||
false
|
||||
else if g == ".*" then # Special handling for **
|
||||
(
|
||||
# Lookahead for next glob match
|
||||
if (gi + 1) == glen then
|
||||
true
|
||||
else if (match (elemAt globs (gi + 1)) p != null) then
|
||||
recurse (gi + 1) pi
|
||||
else if m then
|
||||
recurse gi (pi + 1)
|
||||
else
|
||||
false
|
||||
)
|
||||
else if m then
|
||||
recurse (gi + 1) (pi + 1)
|
||||
else
|
||||
false;
|
||||
|
||||
in
|
||||
recurse 0 0;
|
||||
|
||||
mkSourceFilter =
|
||||
root: patterns:
|
||||
let
|
||||
root' = "${toString root}/";
|
||||
matchers = map mkMatcher patterns;
|
||||
in
|
||||
name: type:
|
||||
let
|
||||
name' = removePrefix root' name;
|
||||
in
|
||||
any (m: m name' type) matchers;
|
||||
|
||||
in
|
||||
src: patterns:
|
||||
lib.cleanSourceWith {
|
||||
filter = mkSourceFilter src patterns;
|
||||
inherit src;
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit
|
||||
|
|
@ -532,6 +646,7 @@ in
|
|||
|
||||
sourceByRegex
|
||||
sourceFilesBySuffices
|
||||
sourceByGlobs
|
||||
|
||||
trace
|
||||
;
|
||||
|
|
|
|||
|
|
@ -5114,4 +5114,96 @@ runTests {
|
|||
);
|
||||
expected = false;
|
||||
};
|
||||
|
||||
# mapDefinitionValue
|
||||
|
||||
testMapDefinitionValuePlain = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) 5;
|
||||
expected = 6;
|
||||
};
|
||||
|
||||
testMapDefinitionValueMkForce = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) (lib.mkForce 5);
|
||||
expected = lib.mkForce 6;
|
||||
};
|
||||
|
||||
testMapDefinitionValueMkDefault = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) (lib.mkDefault 5);
|
||||
expected = lib.mkDefault 6;
|
||||
};
|
||||
|
||||
testMapDefinitionValueMkOrder = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) (lib.mkOrder 500 5);
|
||||
expected = lib.mkOrder 500 6;
|
||||
};
|
||||
|
||||
testMapDefinitionValueMkOverrideNested = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) (lib.mkForce (lib.mkOrder 500 5));
|
||||
expected = lib.mkForce (lib.mkOrder 500 6);
|
||||
};
|
||||
|
||||
testMapDefinitionValueMkIf = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) (lib.mkIf true 5);
|
||||
expected = lib.mkIf true 6;
|
||||
};
|
||||
|
||||
testMapDefinitionValueMkMerge = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) (
|
||||
lib.mkMerge [
|
||||
5
|
||||
10
|
||||
]
|
||||
);
|
||||
expected = lib.mkMerge [
|
||||
6
|
||||
11
|
||||
];
|
||||
};
|
||||
|
||||
testMapDefinitionValueMkDefinition = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) (
|
||||
lib.mkDefinition {
|
||||
file = "test";
|
||||
value = 5;
|
||||
}
|
||||
);
|
||||
expected = lib.mkDefinition {
|
||||
file = "test";
|
||||
value = 6;
|
||||
};
|
||||
};
|
||||
|
||||
testMapDefinitionValueDeep = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) (lib.mkIf true (lib.mkForce (lib.mkOrder 500 5)));
|
||||
expected = lib.mkIf true (lib.mkForce (lib.mkOrder 500 6));
|
||||
};
|
||||
|
||||
testMapDefinitionValueAllNested = {
|
||||
expr = lib.modules.mapDefinitionValue (x: x + 1) (
|
||||
lib.mkMerge [
|
||||
(lib.mkIf true (
|
||||
lib.mkForce (
|
||||
lib.mkOrder 500 (
|
||||
lib.mkDefinition {
|
||||
file = "test";
|
||||
value = lib.mkBefore 5;
|
||||
}
|
||||
)
|
||||
)
|
||||
))
|
||||
]
|
||||
);
|
||||
expected = lib.mkMerge [
|
||||
(lib.mkIf true (
|
||||
lib.mkForce (
|
||||
lib.mkOrder 500 (
|
||||
lib.mkDefinition {
|
||||
file = "test";
|
||||
value = lib.mkBefore 6;
|
||||
}
|
||||
)
|
||||
)
|
||||
))
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -899,6 +899,19 @@ checkConfigError 'Did you mean .enable., .ebe. or .enabled.\?' config ./error-ty
|
|||
checkConfigError 'Did you mean .services\.myservice\.port. or .services\.myservice\.enable.\?' config.services.myservice ./error-typo-submodule.nix
|
||||
checkConfigError 'Did you mean .services\.nginx\.virtualHosts\."example\.com"\.ssl\.certificate. or .services\.nginx\.virtualHosts\."example\.com"\.ssl\.certificateKey.\?' config.services.nginx.virtualHosts.\"example.com\" ./error-typo-deeply-nested.nix
|
||||
|
||||
# types.attrListOf
|
||||
checkConfigOutput '"ok"' config.assertions ./declare-attrList.nix
|
||||
checkConfigError 'A definition for option .attrListInt.badValue.a. is not of type .signed integer.. Definition values:' config.attrListIntStrict.badValue ./declare-attrList.nix
|
||||
checkConfigError 'A definition for option .attrList.badListElem. is not of type .attribute list of string.. Each list element must be a single-key attribute set, but got 2 keys' config.attrListStrict.badListElem ./declare-attrList.nix
|
||||
checkConfigError 'A definition for option .attrList.badString. is not of type .attribute list of string.. TypeError: Definition values:' config.attrListStrict.badString ./declare-attrList.nix
|
||||
checkConfigError 'A definition for option .attrList.badListString. is not of type .attribute list of string.. Each list element must be an attribute set, but got string' config.attrListStrict.badListString ./declare-attrList.nix
|
||||
|
||||
# attrListWith valueMeta.definitions: file propagation
|
||||
checkConfigError 'the-defs-file\.nix' config.argv ./attrList-valueMeta-definitions-file-diagnostic-forwarding.nix
|
||||
|
||||
# attrListOf does not support type merging
|
||||
checkConfigError 'The option .merged. in .*/declare-attrList-type-merge.nix. is already declared in .*/declare-attrList-type-merge.nix' config.merged ./declare-attrList-type-merge.nix
|
||||
|
||||
cat <<EOF
|
||||
====== module tests ======
|
||||
$pass Pass
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
{ lib, options, ... }:
|
||||
let
|
||||
inherit (lib) mkOption mkMerge types;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
{
|
||||
_file = "the-defs-file.nix";
|
||||
config.flags.my-flag = 3.14;
|
||||
}
|
||||
];
|
||||
|
||||
options.flags = mkOption {
|
||||
type = types.attrListWith {
|
||||
elemType = types.anything;
|
||||
asAttrs = true;
|
||||
mergeAttrValues = _name: vs: lib.head vs;
|
||||
};
|
||||
};
|
||||
options.argv = mkOption { type = types.listOf types.str; };
|
||||
|
||||
# Feed definitions into argv; the float from the-defs-file.nix should cause
|
||||
# a type error mentioning that file
|
||||
config.argv = mkMerge options.flags.valueMeta.definitions;
|
||||
}
|
||||
12
lib/tests/modules/declare-attrList-type-merge.nix
Normal file
12
lib/tests/modules/declare-attrList-type-merge.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Test that attrListOf does not support type merging:
|
||||
# two declarations of the same option should fail.
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
{ options.merged = mkOption { type = types.attrListOf types.str; }; }
|
||||
{ options.merged = mkOption { type = types.attrListOf types.str; }; }
|
||||
];
|
||||
}
|
||||
925
lib/tests/modules/declare-attrList.nix
Normal file
925
lib/tests/modules/declare-attrList.nix
Normal file
|
|
@ -0,0 +1,925 @@
|
|||
# Run with:
|
||||
# cd nixpkgs
|
||||
# ./lib/tests/modules.sh
|
||||
{ lib, config, ... }:
|
||||
let
|
||||
inherit (lib)
|
||||
mkOption
|
||||
mkOrder
|
||||
mkMerge
|
||||
mkBefore
|
||||
mkAfter
|
||||
mkIf
|
||||
mkOverride
|
||||
mkDefault
|
||||
mkForce
|
||||
types
|
||||
;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
attrList = mkOption {
|
||||
type = types.lazyAttrsOf (types.attrListOf types.str);
|
||||
};
|
||||
|
||||
attrListInt = mkOption {
|
||||
type = types.lazyAttrsOf (types.attrListOf types.int);
|
||||
};
|
||||
|
||||
attrListSubmodule = mkOption {
|
||||
type = types.attrListOf (
|
||||
types.submodule {
|
||||
options.port = mkOption {
|
||||
type = types.int;
|
||||
description = "Port number";
|
||||
};
|
||||
options.host = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = "Hostname";
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
# asAttrs: value is a merged attrset, ordered list in valueMeta
|
||||
asAttrs = mkOption {
|
||||
type = types.lazyAttrsOf (
|
||||
types.attrListWith {
|
||||
elemType = types.str;
|
||||
asAttrs = true;
|
||||
mergeAttrValues = _name: values: lib.last values;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
# asAttrs with default mergeAttrValues: duplicates collected into lists
|
||||
asAttrsDefault = mkOption {
|
||||
type = types.lazyAttrsOf (
|
||||
types.attrListWith {
|
||||
elemType = types.int;
|
||||
asAttrs = true;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
# Strict wrappers that force deep evaluation, for testing error cases
|
||||
attrListStrict = mkOption {
|
||||
type = types.lazyAttrsOf types.raw;
|
||||
};
|
||||
|
||||
attrListIntStrict = mkOption {
|
||||
type = types.lazyAttrsOf types.raw;
|
||||
};
|
||||
|
||||
# either picks attrList when input is list/attrset, int when input is int
|
||||
eitherAttrListOrInt = mkOption {
|
||||
type = types.either (types.attrListOf types.str) types.int;
|
||||
};
|
||||
|
||||
eitherAttrListOrIntFallback = mkOption {
|
||||
type = types.either (types.attrListOf types.str) types.int;
|
||||
};
|
||||
|
||||
eitherIntOrAttrList = mkOption {
|
||||
type = types.either types.int (types.attrListOf types.str);
|
||||
};
|
||||
|
||||
eitherIntOrAttrListFallback = mkOption {
|
||||
type = types.either types.int (types.attrListOf types.str);
|
||||
};
|
||||
|
||||
assertions = mkOption { };
|
||||
};
|
||||
|
||||
imports = [
|
||||
# Second module contributing to multiModule
|
||||
{
|
||||
attrListInt.multiModule = [
|
||||
{ b = 2; }
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
config = {
|
||||
|
||||
# List input: pass-through
|
||||
attrList.listInput = [
|
||||
{ a = "alpha"; }
|
||||
{ b = "beta"; }
|
||||
];
|
||||
|
||||
# Attrset input with explicit ordering
|
||||
attrList.attrsetOrdered = {
|
||||
x = mkOrder 200 "x-val";
|
||||
y = mkOrder 100 "y-val";
|
||||
};
|
||||
|
||||
# Mixed: list elements at default priority, attrset with mkOrder
|
||||
attrList.mixed = mkMerge [
|
||||
[
|
||||
{ m = "from-list"; }
|
||||
]
|
||||
{
|
||||
n = mkOrder 50 "from-attrset";
|
||||
}
|
||||
];
|
||||
|
||||
# Multiple list definitions from separate modules
|
||||
attrListInt.multiModule = [
|
||||
{ a = 1; }
|
||||
];
|
||||
|
||||
# Attrset without mkOrder uses default priority
|
||||
attrList.attrsetNoOrder = {
|
||||
foo = "bar";
|
||||
baz = "qux";
|
||||
};
|
||||
|
||||
# Empty list
|
||||
attrList.empty = [ ];
|
||||
|
||||
# Ordering test: lower priority first
|
||||
attrList.ordering = mkMerge [
|
||||
{
|
||||
last = mkOrder 1500 "last";
|
||||
}
|
||||
{
|
||||
first = mkOrder 500 "first";
|
||||
}
|
||||
[
|
||||
{ middle = "middle"; }
|
||||
]
|
||||
];
|
||||
|
||||
# List elements support mkOrder/mkBefore/mkAfter
|
||||
attrList.listOrdering = [
|
||||
(mkAfter { z = "after"; })
|
||||
{ m = "default"; }
|
||||
(mkBefore { a = "before"; })
|
||||
];
|
||||
|
||||
# Plain list entries land at default priority (1000):
|
||||
# they appear after mkOrder 999 and before mkOrder 1001.
|
||||
attrList.listDefaultPrio = mkMerge [
|
||||
{ after = mkOrder 1001 "after"; }
|
||||
[
|
||||
{ mid = "list-entry"; }
|
||||
]
|
||||
{ before = mkOrder 999 "before"; }
|
||||
];
|
||||
|
||||
# mkBefore and mkAfter
|
||||
attrList.beforeAfter = mkMerge [
|
||||
{
|
||||
z = mkAfter "after";
|
||||
}
|
||||
{
|
||||
a = mkBefore "before";
|
||||
}
|
||||
[
|
||||
{ m = "default"; }
|
||||
]
|
||||
];
|
||||
|
||||
# mkIf: conditional definition
|
||||
attrList.withMkIf = mkMerge [
|
||||
(mkIf true [
|
||||
{ yes = "included"; }
|
||||
])
|
||||
(mkIf false [
|
||||
{ no = "excluded"; }
|
||||
])
|
||||
];
|
||||
|
||||
# mkOverride: higher priority override wins
|
||||
attrList.withOverride = mkMerge [
|
||||
(mkOverride 100 [
|
||||
{ replaced = "gone"; }
|
||||
])
|
||||
(mkOverride 50 [
|
||||
{ winner = "wins"; }
|
||||
])
|
||||
];
|
||||
|
||||
# mkDefault: lower priority than normal
|
||||
attrList.withDefault = mkMerge [
|
||||
(mkDefault [
|
||||
{ default = "overridden"; }
|
||||
])
|
||||
[
|
||||
{ normal = "wins"; }
|
||||
]
|
||||
];
|
||||
|
||||
# mkForce on the whole option (should discard other defs)
|
||||
attrList.withForce = mkMerge [
|
||||
[
|
||||
{ discarded = "gone"; }
|
||||
]
|
||||
(mkForce [
|
||||
{ forced = "wins"; }
|
||||
])
|
||||
];
|
||||
|
||||
# mkForce with mkOrder inside
|
||||
attrList.forceWithOrder = mkForce [
|
||||
(mkAfter { second = "after"; })
|
||||
(mkBefore { first = "before"; })
|
||||
];
|
||||
|
||||
# mkForce on individual element values; non-forced entries are discarded.
|
||||
# Discarded values use a mix of: plain value, mkDefault(abort ...), mkOverride 100 (abort ...).
|
||||
# The abort variants verify laziness: peelProperties sees the wrapper without forcing the content.
|
||||
attrListInt.forceElementValue = [
|
||||
{ a = mkDefault (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated"); }
|
||||
{ a = mkForce 42; }
|
||||
{ a = mkOverride 100 (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated"); }
|
||||
{ b = 2; }
|
||||
];
|
||||
|
||||
# mkForce on attrset format
|
||||
attrList.forceAttrset = mkMerge [
|
||||
[
|
||||
{ discarded = "gone"; }
|
||||
]
|
||||
(mkForce {
|
||||
x = mkOrder 200 "x-val";
|
||||
y = mkOrder 100 "y-val";
|
||||
})
|
||||
];
|
||||
|
||||
# mkForce on repeated key: forced entries override non-forced
|
||||
attrList.forceRepeatedKey = [
|
||||
{ x = mkOverride 100 (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated"); }
|
||||
{ x = mkForce "wins"; }
|
||||
{ x = mkForce "wins 2"; }
|
||||
];
|
||||
|
||||
# mkForce on repeated key across mkMerge
|
||||
attrList.forceRepeatedKeyMerge = mkMerge [
|
||||
[
|
||||
{ x = "unused: overridden by mkForce"; }
|
||||
{ x = mkDefault (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated"); }
|
||||
]
|
||||
[
|
||||
{ x = mkOverride 100 (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated"); }
|
||||
]
|
||||
[
|
||||
{ x = mkForce "forced"; }
|
||||
]
|
||||
];
|
||||
|
||||
# mkForce on repeated key in attrset format across mkMerge
|
||||
attrList.forceRepeatedKeyAttrs = mkMerge [
|
||||
{
|
||||
x = mkDefault (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated");
|
||||
y = "kept";
|
||||
}
|
||||
{ x = mkForce "forced"; }
|
||||
];
|
||||
|
||||
# mkForce only affects the key it's on, other keys survive
|
||||
attrList.forcePartialAttrs = mkMerge [
|
||||
{
|
||||
x = "unused: overridden by mkForce";
|
||||
y = "normal y";
|
||||
}
|
||||
{ x = mkForce "forced x"; }
|
||||
];
|
||||
|
||||
# mkForce in attrset format overrides same key from list format
|
||||
attrList.forceMixedFormats = mkMerge [
|
||||
[
|
||||
{ x = mkOverride 100 (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated"); }
|
||||
{ y = "list y"; }
|
||||
]
|
||||
{ x = mkForce "attrset forced x"; }
|
||||
];
|
||||
|
||||
# Nesting: list format, mkOrder on element + mkForce on value
|
||||
attrList.nestListOrderForce = mkMerge [
|
||||
[
|
||||
{ x = mkDefault (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated"); }
|
||||
(mkOrder 500 { x = mkForce "forced-early"; })
|
||||
(mkOrder 1500 { y = "late"; })
|
||||
]
|
||||
[
|
||||
(mkOrder 100 { z = "earliest"; })
|
||||
]
|
||||
];
|
||||
|
||||
# Nesting: list format, mkOrder(mkForce(val)) on value
|
||||
attrList.nestListOrderOfForce = mkMerge [
|
||||
[
|
||||
{ x = mkOverride 100 (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated"); }
|
||||
{ y = "plain-early"; }
|
||||
]
|
||||
[
|
||||
{ x = mkOrder 1500 (mkForce "forced-late"); }
|
||||
{ z = mkOrder 500 "earliest"; }
|
||||
]
|
||||
[
|
||||
{ x = "unused: overridden by mkForce"; }
|
||||
{ w = mkOrder 1200 "mid"; }
|
||||
]
|
||||
];
|
||||
|
||||
# Nesting: list format, mkForce(mkOrder(val)) on value
|
||||
attrList.nestListForceOfOrder = mkMerge [
|
||||
[
|
||||
{ x = "unused: overridden by mkForce"; }
|
||||
{ y = "plain-early"; }
|
||||
]
|
||||
[
|
||||
{ x = mkForce (mkOrder 1500 "forced-late"); }
|
||||
{ z = mkOrder 500 "earliest"; }
|
||||
]
|
||||
[
|
||||
{ x = mkDefault (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated"); }
|
||||
{ w = mkOrder 1200 "mid"; }
|
||||
]
|
||||
];
|
||||
|
||||
# Nesting: attrset format, mkOrder wrapping mkForce
|
||||
attrList.nestAttrsOrderOfForce = mkMerge [
|
||||
{
|
||||
x = mkOverride 100 (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated");
|
||||
y = "plain-early";
|
||||
}
|
||||
{
|
||||
x = mkOrder 1500 (mkForce "forced-late");
|
||||
z = mkOrder 500 "earliest";
|
||||
}
|
||||
{
|
||||
x = "unused: overridden by mkForce";
|
||||
w = mkOrder 1200 "mid";
|
||||
}
|
||||
];
|
||||
|
||||
# Nesting: attrset format, mkForce wrapping mkOrder
|
||||
attrList.nestAttrsForceOfOrder = mkMerge [
|
||||
{
|
||||
x = "unused: overridden by mkForce";
|
||||
y = "plain-early";
|
||||
}
|
||||
{
|
||||
x = mkForce (mkOrder 1500 "forced-late");
|
||||
z = mkOrder 500 "earliest";
|
||||
}
|
||||
{
|
||||
x = mkDefault (abort "overridden by mkForce; laziness guarantee: MUST NOT be evaluated");
|
||||
w = mkOrder 1200 "mid";
|
||||
}
|
||||
];
|
||||
|
||||
# mkIf false on individual element value filters it out (list format)
|
||||
attrListInt.optionalValueList = [
|
||||
{ a = mkIf true 1; }
|
||||
{ b = mkIf false 2; }
|
||||
{ c = 3; }
|
||||
];
|
||||
|
||||
# mkIf false on individual element value filters it out (attrset format)
|
||||
attrListInt.optionalValueAttrs = {
|
||||
a = mkIf true 1;
|
||||
b = mkIf false 2;
|
||||
c = 3;
|
||||
};
|
||||
|
||||
# submodule elemType: produces real valueMeta
|
||||
attrListSubmodule = [
|
||||
{
|
||||
web = {
|
||||
port = 80;
|
||||
};
|
||||
}
|
||||
{
|
||||
db = {
|
||||
port = 5432;
|
||||
host = "dbhost";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
# asAttrs: unique keys — value is a plain attrset
|
||||
asAttrs.unique = [
|
||||
{ a = "alpha"; }
|
||||
{ b = "beta"; }
|
||||
];
|
||||
|
||||
# asAttrs: duplicate keys — last in order wins
|
||||
asAttrs.duplicateKeys = mkMerge [
|
||||
{ x = mkOrder 500 "first"; }
|
||||
{ x = mkOrder 1500 "last"; }
|
||||
{ y = "only"; }
|
||||
];
|
||||
|
||||
# asAttrs: with ordering — value is attrset, ordered list in valueMeta
|
||||
asAttrs.ordered = {
|
||||
z = mkOrder 200 "z-val";
|
||||
a = mkOrder 100 "a-val";
|
||||
};
|
||||
|
||||
# asAttrs: with mkForce — forced key overrides
|
||||
asAttrs.withForce = mkMerge [
|
||||
{ x = "unused: overridden by mkForce"; }
|
||||
{
|
||||
x = mkForce "forced";
|
||||
y = "kept";
|
||||
}
|
||||
];
|
||||
|
||||
# asAttrs: empty
|
||||
asAttrs.empty = [ ];
|
||||
|
||||
# asAttrsDefault: unique keys
|
||||
asAttrsDefault.unique = [
|
||||
{ a = 1; }
|
||||
{ b = 2; }
|
||||
];
|
||||
|
||||
# asAttrsDefault: duplicate keys — default collects into lists
|
||||
asAttrsDefault.duplicates = mkMerge [
|
||||
{ x = mkOrder 500 10; }
|
||||
{ x = mkOrder 1500 30; }
|
||||
{ y = 99; }
|
||||
[
|
||||
{ x = 20; }
|
||||
]
|
||||
];
|
||||
|
||||
# either: attrList branch matches for list input
|
||||
eitherAttrListOrInt = [
|
||||
{ a = "hello"; }
|
||||
{ b = "world"; }
|
||||
];
|
||||
|
||||
# either: int input falls through to int branch
|
||||
eitherAttrListOrIntFallback = 42;
|
||||
|
||||
# either (swapped): int first, attrList second — int input matches int
|
||||
eitherIntOrAttrList = 42;
|
||||
|
||||
# either (swapped): list input falls through to attrList branch
|
||||
eitherIntOrAttrListFallback = [
|
||||
{ a = "hello"; }
|
||||
];
|
||||
|
||||
# Bad: string where int expected
|
||||
attrListInt.badValue = [
|
||||
{ a = "not-an-int"; }
|
||||
];
|
||||
|
||||
# Bad: list element with multiple keys
|
||||
attrList.badListElem = [
|
||||
{
|
||||
a = "ok";
|
||||
b = "extra";
|
||||
}
|
||||
];
|
||||
|
||||
# Bad: plain string instead of list or attrset
|
||||
attrList.badString = "not-a-container";
|
||||
|
||||
# Bad: list element is a bare string, not a singleton attrset
|
||||
attrList.badListString = [
|
||||
"not a singleton attribute"
|
||||
];
|
||||
|
||||
attrListStrict = builtins.mapAttrs (k: v: builtins.deepSeq v v) config.attrList;
|
||||
attrListIntStrict = builtins.mapAttrs (k: v: builtins.deepSeq v v) config.attrListInt;
|
||||
|
||||
assertions =
|
||||
let
|
||||
c = lib.evalModules {
|
||||
modules = [ ./declare-attrList.nix ];
|
||||
};
|
||||
cfg = c.config;
|
||||
in
|
||||
|
||||
# List input preserves elements
|
||||
assert
|
||||
cfg.attrList.listInput == [
|
||||
{ a = "alpha"; }
|
||||
{ b = "beta"; }
|
||||
];
|
||||
|
||||
# Attrset input with mkOrder: lower priority comes first
|
||||
assert
|
||||
cfg.attrList.attrsetOrdered == [
|
||||
{ y = "y-val"; }
|
||||
{ x = "x-val"; }
|
||||
];
|
||||
|
||||
# Mixed input: mkOrder 50 < default 1000
|
||||
assert
|
||||
cfg.attrList.mixed == [
|
||||
{ n = "from-attrset"; }
|
||||
{ m = "from-list"; }
|
||||
];
|
||||
|
||||
# Multiple definitions from separate modules concatenate
|
||||
# (import module's definition comes before this module's)
|
||||
assert
|
||||
cfg.attrListInt.multiModule == [
|
||||
{ b = 2; }
|
||||
{ a = 1; }
|
||||
];
|
||||
|
||||
# Attrset without mkOrder: all at default priority
|
||||
assert builtins.length cfg.attrList.attrsetNoOrder == 2;
|
||||
|
||||
# Empty list stays empty
|
||||
assert cfg.attrList.empty == [ ];
|
||||
|
||||
# List elements support mkOrder/mkBefore/mkAfter
|
||||
assert
|
||||
cfg.attrList.listOrdering == [
|
||||
{ a = "before"; }
|
||||
{ m = "default"; }
|
||||
{ z = "after"; }
|
||||
];
|
||||
|
||||
# Plain list entries are at default priority (1000)
|
||||
assert
|
||||
cfg.attrList.listDefaultPrio == [
|
||||
{ before = "before"; }
|
||||
{ mid = "list-entry"; }
|
||||
{ after = "after"; }
|
||||
];
|
||||
|
||||
# Ordering: 500 < 1000 (default) < 1500
|
||||
assert
|
||||
cfg.attrList.ordering == [
|
||||
{ first = "first"; }
|
||||
{ middle = "middle"; }
|
||||
{ last = "last"; }
|
||||
];
|
||||
|
||||
# mkBefore (500) < default (1000) < mkAfter (1500)
|
||||
assert
|
||||
cfg.attrList.beforeAfter == [
|
||||
{ a = "before"; }
|
||||
{ m = "default"; }
|
||||
{ z = "after"; }
|
||||
];
|
||||
|
||||
# mkIf true includes, mkIf false excludes
|
||||
assert
|
||||
cfg.attrList.withMkIf == [
|
||||
{ yes = "included"; }
|
||||
];
|
||||
|
||||
# mkOverride: only lowest priority override survives
|
||||
assert
|
||||
cfg.attrList.withOverride == [
|
||||
{ winner = "wins"; }
|
||||
];
|
||||
|
||||
# mkDefault is overridden by normal definitions
|
||||
assert
|
||||
cfg.attrList.withDefault == [
|
||||
{ normal = "wins"; }
|
||||
];
|
||||
|
||||
# mkForce discards other definitions
|
||||
assert
|
||||
cfg.attrList.withForce == [
|
||||
{ forced = "wins"; }
|
||||
];
|
||||
|
||||
# mkForce with mkOrder inside: ordering still works
|
||||
assert
|
||||
cfg.attrList.forceWithOrder == [
|
||||
{ first = "before"; }
|
||||
{ second = "after"; }
|
||||
];
|
||||
|
||||
# mkForce on individual element values passes through
|
||||
assert
|
||||
cfg.attrListInt.forceElementValue == [
|
||||
{ a = 42; }
|
||||
{ b = 2; }
|
||||
];
|
||||
|
||||
# mkForce on attrset format: discards other defs, ordering preserved
|
||||
assert
|
||||
cfg.attrList.forceAttrset == [
|
||||
{ y = "y-val"; }
|
||||
{ x = "x-val"; }
|
||||
];
|
||||
|
||||
# mkForce on repeated key: forced entries override non-forced
|
||||
assert
|
||||
cfg.attrList.forceRepeatedKey == [
|
||||
{ x = "wins"; }
|
||||
{ x = "wins 2"; }
|
||||
];
|
||||
|
||||
# mkForce on repeated key across mkMerge: forced wins
|
||||
assert
|
||||
cfg.attrList.forceRepeatedKeyMerge == [
|
||||
{ x = "forced"; }
|
||||
];
|
||||
|
||||
# mkForce on repeated key in attrset format: discards other x, keeps y
|
||||
assert
|
||||
cfg.attrList.forceRepeatedKeyAttrs == [
|
||||
{ y = "kept"; }
|
||||
{ x = "forced"; }
|
||||
];
|
||||
|
||||
# mkForce only affects its own key
|
||||
assert
|
||||
cfg.attrList.forcePartialAttrs == [
|
||||
{ y = "normal y"; }
|
||||
{ x = "forced x"; }
|
||||
];
|
||||
|
||||
# mkForce in attrset format overrides same key from list format
|
||||
assert
|
||||
cfg.attrList.forceMixedFormats == [
|
||||
{ y = "list y"; }
|
||||
{ x = "attrset forced x"; }
|
||||
];
|
||||
|
||||
# Nesting: list format, mkOrder on element + mkForce on value
|
||||
# z(100) < x-forced(500) < y(1500); x-discarded filtered by mkForce
|
||||
assert
|
||||
cfg.attrList.nestListOrderForce == [
|
||||
{ z = "earliest"; }
|
||||
{ x = "forced-early"; }
|
||||
{ y = "late"; }
|
||||
];
|
||||
|
||||
# Nesting: list format, mkOrder(mkForce(val)) on value
|
||||
# z(500) < y(1000) < w(1200) < x-forced(1500); x-discarded entries filtered
|
||||
assert
|
||||
cfg.attrList.nestListOrderOfForce == [
|
||||
{ z = "earliest"; }
|
||||
{ y = "plain-early"; }
|
||||
{ w = "mid"; }
|
||||
{ x = "forced-late"; }
|
||||
];
|
||||
|
||||
# Nesting: list format, mkForce(mkOrder(val)) on value
|
||||
# z(500) < y(1000) < w(1200) < x-forced(1500); x-discarded entries filtered
|
||||
assert
|
||||
cfg.attrList.nestListForceOfOrder == [
|
||||
{ z = "earliest"; }
|
||||
{ y = "plain-early"; }
|
||||
{ w = "mid"; }
|
||||
{ x = "forced-late"; }
|
||||
];
|
||||
|
||||
# Nesting: attrset format, mkOrder(mkForce(val))
|
||||
# z(500) < y(1000) < w(1200) < x-forced(1500); x-discarded entries filtered
|
||||
assert
|
||||
cfg.attrList.nestAttrsOrderOfForce == [
|
||||
{ z = "earliest"; }
|
||||
{ y = "plain-early"; }
|
||||
{ w = "mid"; }
|
||||
{ x = "forced-late"; }
|
||||
];
|
||||
|
||||
# Nesting: attrset format, mkForce(mkOrder(val))
|
||||
# z(500) < y(1000) < w(1200) < x-forced(1500); x-discarded entries filtered
|
||||
assert
|
||||
cfg.attrList.nestAttrsForceOfOrder == [
|
||||
{ z = "earliest"; }
|
||||
{ y = "plain-early"; }
|
||||
{ w = "mid"; }
|
||||
{ x = "forced-late"; }
|
||||
];
|
||||
|
||||
# mkIf false on individual element value filters it out (list format)
|
||||
assert
|
||||
cfg.attrListInt.optionalValueList == [
|
||||
{ a = 1; }
|
||||
{ c = 3; }
|
||||
];
|
||||
|
||||
# mkIf false on individual element value filters it out (attrset format)
|
||||
assert
|
||||
cfg.attrListInt.optionalValueAttrs == [
|
||||
{ a = 1; }
|
||||
{ c = 3; }
|
||||
];
|
||||
|
||||
# submodule: value, option descriptions, and valueMeta with real configuration metadata
|
||||
assert
|
||||
cfg.attrListSubmodule == [
|
||||
{
|
||||
web = {
|
||||
host = "localhost";
|
||||
port = 80;
|
||||
};
|
||||
}
|
||||
{
|
||||
db = {
|
||||
host = "dbhost";
|
||||
port = 5432;
|
||||
};
|
||||
}
|
||||
];
|
||||
assert
|
||||
builtins.map (m: m.configuration.config) c.options.attrListSubmodule.valueMeta.attrList == [
|
||||
{
|
||||
host = "localhost";
|
||||
port = 80;
|
||||
}
|
||||
{
|
||||
host = "dbhost";
|
||||
port = 5432;
|
||||
}
|
||||
];
|
||||
assert
|
||||
builtins.map (
|
||||
m:
|
||||
builtins.mapAttrs (n: o: o.description) (builtins.removeAttrs m.configuration.options [ "_module" ])
|
||||
) c.options.attrListSubmodule.valueMeta.attrList == [
|
||||
{
|
||||
host = "Hostname";
|
||||
port = "Port number";
|
||||
}
|
||||
{
|
||||
host = "Hostname";
|
||||
port = "Port number";
|
||||
}
|
||||
];
|
||||
|
||||
# valueMeta.attrList has one entry per (non-filtered) element
|
||||
assert
|
||||
c.options.attrList.valueMeta.attrs.listInput.attrList == [
|
||||
{ }
|
||||
{ }
|
||||
];
|
||||
assert
|
||||
c.options.attrList.valueMeta.attrs.attrsetOrdered.attrList == [
|
||||
{ }
|
||||
{ }
|
||||
];
|
||||
assert
|
||||
c.options.attrList.valueMeta.attrs.mixed.attrList == [
|
||||
{ }
|
||||
{ }
|
||||
];
|
||||
assert c.options.attrList.valueMeta.attrs.empty.attrList == [ ];
|
||||
assert
|
||||
c.options.attrListInt.valueMeta.attrs.optionalValueList.attrList == [
|
||||
{ }
|
||||
{ }
|
||||
];
|
||||
|
||||
# either: headError is null for valid attrList input, so attrList branch is picked
|
||||
assert
|
||||
cfg.eitherAttrListOrInt == [
|
||||
{ a = "hello"; }
|
||||
{ b = "world"; }
|
||||
];
|
||||
|
||||
# either: headError is non-null for int input, so int branch is picked
|
||||
assert cfg.eitherAttrListOrIntFallback == 42;
|
||||
|
||||
# either (swapped): int first — int input matches
|
||||
assert cfg.eitherIntOrAttrList == 42;
|
||||
|
||||
# either (swapped): list input falls through to attrList branch
|
||||
assert
|
||||
cfg.eitherIntOrAttrListFallback == [
|
||||
{ a = "hello"; }
|
||||
];
|
||||
|
||||
# asAttrs: unique keys — value is a plain attrset
|
||||
assert
|
||||
cfg.asAttrs.unique == {
|
||||
a = "alpha";
|
||||
b = "beta";
|
||||
};
|
||||
# ordered list preserved in valueMeta
|
||||
assert
|
||||
c.options.asAttrs.valueMeta.attrs.unique.attrListValue == [
|
||||
{ a = "alpha"; }
|
||||
{ b = "beta"; }
|
||||
];
|
||||
|
||||
# asAttrs: duplicate keys — last in order wins
|
||||
assert
|
||||
cfg.asAttrs.duplicateKeys == {
|
||||
x = "last";
|
||||
y = "only";
|
||||
};
|
||||
assert
|
||||
c.options.asAttrs.valueMeta.attrs.duplicateKeys.attrListValue == [
|
||||
{ x = "first"; }
|
||||
{ y = "only"; }
|
||||
{ x = "last"; }
|
||||
];
|
||||
|
||||
# asAttrs: ordered — value is attrset (unordered), list in valueMeta preserves order
|
||||
assert
|
||||
cfg.asAttrs.ordered == {
|
||||
a = "a-val";
|
||||
z = "z-val";
|
||||
};
|
||||
assert
|
||||
c.options.asAttrs.valueMeta.attrs.ordered.attrListValue == [
|
||||
{ a = "a-val"; }
|
||||
{ z = "z-val"; }
|
||||
];
|
||||
|
||||
# asAttrs: mkForce — forced key overrides, value is attrset
|
||||
assert
|
||||
cfg.asAttrs.withForce == {
|
||||
x = "forced";
|
||||
y = "kept";
|
||||
};
|
||||
|
||||
# asAttrs: empty — value is empty attrset
|
||||
assert cfg.asAttrs.empty == { };
|
||||
|
||||
# asAttrsDefault: unique keys — each value wrapped in singleton list
|
||||
assert
|
||||
cfg.asAttrsDefault.unique == {
|
||||
a = [ 1 ];
|
||||
b = [ 2 ];
|
||||
};
|
||||
|
||||
# asAttrsDefault: duplicate keys — values collected into list in order
|
||||
assert
|
||||
cfg.asAttrsDefault.duplicates == {
|
||||
x = [
|
||||
10
|
||||
20
|
||||
30
|
||||
];
|
||||
y = [ 99 ];
|
||||
};
|
||||
assert
|
||||
c.options.asAttrsDefault.valueMeta.attrs.duplicates.attrListValue == [
|
||||
{ x = 10; }
|
||||
{ y = 99; }
|
||||
{ x = 20; }
|
||||
{ x = 30; }
|
||||
];
|
||||
|
||||
# valueMeta.definitions: mkDefinition records with mkOrder-wrapped single-key attrsets
|
||||
# Use duplicateKeys which has mixed priorities and repeated keys
|
||||
assert
|
||||
let
|
||||
defs = c.options.asAttrs.valueMeta.attrs.duplicateKeys.definitions;
|
||||
extract = d: {
|
||||
prio = d.value.priority;
|
||||
value = d.value.content;
|
||||
};
|
||||
in
|
||||
map extract defs == [
|
||||
{
|
||||
prio = 500;
|
||||
value = {
|
||||
x = "first";
|
||||
};
|
||||
}
|
||||
{
|
||||
prio = 1000;
|
||||
value = {
|
||||
y = "only";
|
||||
};
|
||||
}
|
||||
{
|
||||
prio = 1500;
|
||||
value = {
|
||||
x = "last";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
# Round-trip: feed definitions through mapDefinitionValue + mkMerge into a listOf option
|
||||
assert
|
||||
let
|
||||
rendered = lib.modules.mapDefinitionValue (attr: lib.cli.toCommandLineGNU { } attr) (
|
||||
mkMerge c.options.asAttrs.valueMeta.attrs.duplicateKeys.definitions
|
||||
);
|
||||
result =
|
||||
(lib.evalModules {
|
||||
modules = [
|
||||
{ options.out = mkOption { type = types.listOf types.str; }; }
|
||||
{ config.out = rendered; }
|
||||
# Interleave: mkOrder 800 lands between x(500) and y(1000)
|
||||
{ config.out = mkOrder 800 [ "--interleaved" ]; }
|
||||
];
|
||||
}).config.out;
|
||||
in
|
||||
result == [
|
||||
"-xfirst"
|
||||
"--interleaved"
|
||||
"-yonly"
|
||||
"-xlast"
|
||||
];
|
||||
|
||||
# Error cases are tested via checkConfigError in modules.sh
|
||||
|
||||
"ok";
|
||||
};
|
||||
}
|
||||
|
|
@ -167,6 +167,28 @@ in
|
|||
elemType = str;
|
||||
lazy = false;
|
||||
}).description == "attribute set of string";
|
||||
assert (attrListOf str).description == "attribute list of string";
|
||||
assert (attrListOf int).description == "attribute list of signed integer";
|
||||
assert (attrListOf bool).description == "attribute list of boolean";
|
||||
assert (attrListOf (either int str)).description == "attribute list of (signed integer or string)";
|
||||
assert (attrListOf (nullOr str)).description == "attribute list of (null or string)";
|
||||
assert (attrListOf (listOf str)).description == "attribute list of list of string";
|
||||
assert
|
||||
(attrListOf (attrsOf int)).description == "attribute list of attribute set of signed integer";
|
||||
assert (attrListOf (attrListOf str)).description == "attribute list of attribute list of string";
|
||||
assert (attrListOf ints.positive).description == "attribute list of (positive integer, meaning >0)";
|
||||
assert
|
||||
(attrListOf (enum [
|
||||
"a"
|
||||
"b"
|
||||
])).description == "attribute list of (one of \"a\", \"b\")";
|
||||
assert
|
||||
(attrListOf (strMatching "[0-9]+")).description
|
||||
== "attribute list of string matching the pattern [0-9]+";
|
||||
assert
|
||||
(attrListOf (nonEmptyListOf str)).description == "attribute list of non-empty (list of string)";
|
||||
assert (attrListOf (submodule { })).description == "attribute list of (submodule)";
|
||||
|
||||
assert (coercedTo str abort int).description == "signed integer or string convertible to it";
|
||||
assert (coercedTo int abort str).description == "string or signed integer convertible to it";
|
||||
assert (coercedTo bool abort str).description == "string or boolean convertible to it";
|
||||
|
|
|
|||
|
|
@ -70,4 +70,16 @@ dir="$(nix-instantiate --eval --strict --read-write-mode --json --expr '(with im
|
|||
EOF
|
||||
) || die "cleanSourceWith + cleanSource"
|
||||
|
||||
|
||||
dir="$(nix-instantiate --eval --strict --read-write-mode --json --expr '(with import <nixpkgs/lib>; "${
|
||||
sources.sourceByGlobs '"$work"' [ "*.md" "**/*.o" ]
|
||||
}")' | crudeUnquoteJSON)"
|
||||
(cd "$dir"; find) | sort -f | diff -U10 - <(cat <<EOF
|
||||
.
|
||||
./module.o
|
||||
./README.md
|
||||
EOF
|
||||
) || die "sourceByGlobs 1"
|
||||
|
||||
|
||||
echo >&2 tests ok
|
||||
|
|
|
|||
|
|
@ -7,14 +7,16 @@ let
|
|||
functionArgs
|
||||
pathExists
|
||||
release
|
||||
setFunctionArgs
|
||||
toBaseDigits
|
||||
version
|
||||
versionSuffix
|
||||
warn
|
||||
;
|
||||
inherit (lib)
|
||||
foldr
|
||||
fromJSON
|
||||
isString
|
||||
readFile
|
||||
;
|
||||
in
|
||||
{
|
||||
|
|
@ -502,7 +504,7 @@ in
|
|||
On each release the first letter is bumped and a new animal is chosen
|
||||
starting with that new letter.
|
||||
*/
|
||||
codeName = "Yarara";
|
||||
codeName = "Zokor";
|
||||
|
||||
/**
|
||||
Returns the current nixpkgs version suffix as string.
|
||||
|
|
@ -789,7 +791,7 @@ in
|
|||
importJSON :: Path -> Any
|
||||
```
|
||||
*/
|
||||
importJSON = path: builtins.fromJSON (builtins.readFile path);
|
||||
importJSON = path: fromJSON (readFile path);
|
||||
|
||||
/**
|
||||
Reads a TOML file.
|
||||
|
|
@ -836,7 +838,7 @@ in
|
|||
importTOML :: Path -> Any
|
||||
```
|
||||
*/
|
||||
importTOML = path: fromTOML (builtins.readFile path);
|
||||
importTOML = path: fromTOML (readFile path);
|
||||
|
||||
/**
|
||||
`warn` *`message`* *`value`*
|
||||
|
|
@ -1046,7 +1048,7 @@ in
|
|||
|
||||
info = msg: builtins.trace "INFO: ${msg}";
|
||||
|
||||
showWarnings = warnings: res: lib.foldr (w: x: warn w x) res warnings;
|
||||
showWarnings = warnings: res: foldr warn res warnings;
|
||||
|
||||
## Function annotations
|
||||
|
||||
|
|
@ -1177,7 +1179,10 @@ in
|
|||
let
|
||||
fArgs = functionArgs f;
|
||||
in
|
||||
g: setFunctionArgs g fArgs;
|
||||
g: {
|
||||
__functor = self: g;
|
||||
__functionArgs = fArgs;
|
||||
};
|
||||
|
||||
/**
|
||||
Turns any non-callable values into constant functions.
|
||||
|
|
@ -1325,11 +1330,11 @@ in
|
|||
r = i - ((i / base) * base);
|
||||
q = (i - r) / base;
|
||||
in
|
||||
[ r ] ++ go q;
|
||||
go q ++ [ r ];
|
||||
in
|
||||
assert (isInt base);
|
||||
assert (isInt i);
|
||||
assert (base >= 2);
|
||||
assert (i >= 0);
|
||||
lib.reverseList (go i);
|
||||
go i;
|
||||
}
|
||||
|
|
|
|||
180
lib/types.nix
180
lib/types.nix
|
|
@ -20,6 +20,7 @@ let
|
|||
isStorePath
|
||||
isString
|
||||
substring
|
||||
sort
|
||||
throwIf
|
||||
toDerivation
|
||||
toList
|
||||
|
|
@ -27,6 +28,7 @@ let
|
|||
;
|
||||
inherit (lib.lists)
|
||||
concatLists
|
||||
concatMap
|
||||
elemAt
|
||||
filter
|
||||
foldl'
|
||||
|
|
@ -70,6 +72,11 @@ let
|
|||
mergeDefinitions
|
||||
fixupOptionType
|
||||
mergeOptionDecls
|
||||
defaultOrderPriority
|
||||
defaultOverridePriority
|
||||
mkDefinition
|
||||
mkOrder
|
||||
mkOverride
|
||||
;
|
||||
inherit (lib.fileset)
|
||||
isFileset
|
||||
|
|
@ -805,6 +812,179 @@ rec {
|
|||
substSubModules = m: nonEmptyListOf (elemType.substSubModules m);
|
||||
};
|
||||
|
||||
attrListOf = elemType: attrListWith { inherit elemType; };
|
||||
|
||||
attrListWith =
|
||||
{
|
||||
elemType,
|
||||
asAttrs ? false,
|
||||
mergeAttrValues ? _name: values: values,
|
||||
}:
|
||||
mkOptionType rec {
|
||||
name = "attrListOf";
|
||||
description = "attribute list of ${
|
||||
optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType
|
||||
}";
|
||||
descriptionClass = "composite";
|
||||
check = {
|
||||
__functor = _self: x: isList x || isAttrs x;
|
||||
isV2MergeCoherent = true;
|
||||
};
|
||||
merge = {
|
||||
__functor =
|
||||
self: loc: defs:
|
||||
(self.v2 { inherit loc defs; }).value;
|
||||
v2 =
|
||||
{ loc, defs }:
|
||||
let
|
||||
# Peel order and override properties from a value in any nesting order.
|
||||
# Returns { value, prio, overridePrio }.
|
||||
# mkOrder is stripped (we consume it for sorting).
|
||||
# mkOverride is preserved in value (mergeDefinitions strips it).
|
||||
peelProperties =
|
||||
value:
|
||||
let
|
||||
type = value._type or null;
|
||||
in
|
||||
if type == "order" then
|
||||
let
|
||||
inner = peelProperties value.content;
|
||||
in
|
||||
{
|
||||
inherit (inner) value overridePrio;
|
||||
prio = value.priority;
|
||||
}
|
||||
else if type == "override" then
|
||||
let
|
||||
inner = peelProperties value.content;
|
||||
in
|
||||
{
|
||||
inherit (inner) prio;
|
||||
overridePrio = value.priority;
|
||||
# Re-wrap mkOverride around the inner value (with mkOrder stripped)
|
||||
value = mkOverride value.priority inner.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
inherit value;
|
||||
prio = defaultOrderPriority;
|
||||
overridePrio = defaultOverridePriority;
|
||||
};
|
||||
|
||||
# Extract { file, key, value, prio, overridePrio } from a single-key attrset,
|
||||
# optionally wrapped in mkOrder at the element level (list format).
|
||||
extractItem =
|
||||
file: raw:
|
||||
let
|
||||
hasOrder = isType "order" raw;
|
||||
item = if hasOrder then raw.content else raw;
|
||||
key = head (attrNames item);
|
||||
peeled = peelProperties item.${key};
|
||||
in
|
||||
if isAttrs item && length (attrNames item) == 1 then
|
||||
peeled
|
||||
// {
|
||||
inherit file key;
|
||||
prio = if hasOrder then raw.priority else peeled.prio;
|
||||
}
|
||||
else
|
||||
throw "A definition for option `${showOption loc}' is not of type `${description}'. ${
|
||||
if !isAttrs item then
|
||||
"Each list element must be an attribute set, but got ${builtins.typeOf item}"
|
||||
else
|
||||
"Each list element must be a single-key attribute set, but got ${toString (length (attrNames item))} keys"
|
||||
}.${
|
||||
showDefs [
|
||||
{
|
||||
inherit file;
|
||||
value = raw;
|
||||
}
|
||||
]
|
||||
}";
|
||||
|
||||
# Convert a definition to a flat list of { file, key, value, prio, overridePrio }
|
||||
defToItems =
|
||||
def:
|
||||
if isList def.value then
|
||||
map (extractItem def.file) def.value
|
||||
else
|
||||
# isAttrs: properties are on the values directly
|
||||
map (
|
||||
key:
|
||||
peelProperties def.value.${key}
|
||||
// {
|
||||
inherit (def) file;
|
||||
inherit key;
|
||||
}
|
||||
) (attrNames def.value);
|
||||
|
||||
allItems = concatMap defToItems defs;
|
||||
|
||||
# Per key, find the highest override priority (lowest number)
|
||||
winningOverridePrio = foldl' (
|
||||
acc: item:
|
||||
let
|
||||
prev = acc.${item.key} or defaultOverridePriority;
|
||||
in
|
||||
if item.overridePrio < prev then
|
||||
acc // { ${item.key} = item.overridePrio; }
|
||||
else
|
||||
# minimize `//` operations
|
||||
acc
|
||||
) { } allItems;
|
||||
|
||||
# Keep only items at the winning override priority for their key
|
||||
items = sort (a: b: a.prio < b.prio) (
|
||||
filter (
|
||||
item: item.overridePrio == winningOverridePrio.${item.key} or defaultOverridePriority
|
||||
) allItems
|
||||
);
|
||||
|
||||
evals = filter (e: e.eval.optionalValue ? value) (
|
||||
map (item: {
|
||||
inherit (item) key file prio;
|
||||
eval = mergeDefinitions (loc ++ [ item.key ]) elemType [
|
||||
{
|
||||
inherit (item) file value;
|
||||
}
|
||||
];
|
||||
}) items
|
||||
);
|
||||
|
||||
attrListValue = map (e: { ${e.key} = e.eval.optionalValue.value or e.eval.mergedValue; }) evals;
|
||||
in
|
||||
{
|
||||
headError = checkDefsForError check loc defs;
|
||||
value = if asAttrs then zipAttrsWith mergeAttrValues attrListValue else attrListValue;
|
||||
valueMeta.attrList = map (e: e.eval.checkedAndMerged.valueMeta) evals;
|
||||
/**
|
||||
The ordered list representation, especially useful when asAttrs is set.
|
||||
*/
|
||||
valueMeta.attrListValue = attrListValue;
|
||||
valueMeta.definitions = map (
|
||||
e:
|
||||
mkDefinition {
|
||||
inherit (e) file;
|
||||
value = mkOrder e.prio { ${e.key} = e.eval.optionalValue.value or e.eval.mergedValue; };
|
||||
}
|
||||
) evals;
|
||||
};
|
||||
};
|
||||
emptyValue = {
|
||||
value = if asAttrs then { } else [ ];
|
||||
};
|
||||
getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "*" ]);
|
||||
getSubModules = elemType.getSubModules;
|
||||
substSubModules =
|
||||
m:
|
||||
attrListWith {
|
||||
inherit asAttrs mergeAttrValues;
|
||||
elemType = elemType.substSubModules m;
|
||||
};
|
||||
typeMerge = t: null; # Disable type merging
|
||||
nestedTypes.elemType = elemType;
|
||||
};
|
||||
|
||||
attrsOf = elemType: attrsWith { inherit elemType; };
|
||||
|
||||
# A version of attrsOf that's lazy in its values at the expense of
|
||||
|
|
|
|||
|
|
@ -862,6 +862,13 @@
|
|||
githubId = 209175;
|
||||
name = "Alesya Huzik";
|
||||
};
|
||||
Ai-Ya-Ya = {
|
||||
email = "spg2500@gmail.com";
|
||||
github = "Ai-Ya-Ya";
|
||||
githubId = 72513839;
|
||||
matrix = "aiya:catgirl.cloud";
|
||||
name = "aiya";
|
||||
};
|
||||
aij = {
|
||||
email = "aij+git@mrph.org";
|
||||
github = "aij";
|
||||
|
|
@ -4275,6 +4282,12 @@
|
|||
{ fingerprint = "D088 A5AF C45B 78D1 CD4F 457C 6957 B3B6 46F2 BB4E"; }
|
||||
];
|
||||
};
|
||||
c6rg0 = {
|
||||
email = "c6rg0@protonmail.com";
|
||||
github = "c6rg0";
|
||||
githubId = 64259221;
|
||||
name = "c6rg0";
|
||||
};
|
||||
caarlos0 = {
|
||||
name = "Carlos A Becker";
|
||||
email = "carlos@becker.software";
|
||||
|
|
@ -4448,12 +4461,6 @@
|
|||
githubId = 289492;
|
||||
name = "Philip Horger";
|
||||
};
|
||||
candeira = {
|
||||
email = "javier@candeira.com";
|
||||
github = "candeira";
|
||||
githubId = 91694;
|
||||
name = "Javier Candeira";
|
||||
};
|
||||
caniko = {
|
||||
email = "gpg@rotas.mozmail.com";
|
||||
github = "caniko";
|
||||
|
|
@ -4845,11 +4852,6 @@
|
|||
githubId = 18648043;
|
||||
name = "Daniel Cartwright";
|
||||
};
|
||||
chewblacka = {
|
||||
github = "chewblacka";
|
||||
githubId = 18430320;
|
||||
name = "John Garcia";
|
||||
};
|
||||
Chili-Man = {
|
||||
email = "dr.elhombrechile@gmail.com";
|
||||
name = "Diego Rodriguez";
|
||||
|
|
@ -5062,12 +5064,6 @@
|
|||
github = "cigrainger";
|
||||
githubId = 3984794;
|
||||
};
|
||||
ciil = {
|
||||
email = "simon@lackerbauer.com";
|
||||
github = "ciil";
|
||||
githubId = 3956062;
|
||||
name = "Simon Lackerbauer";
|
||||
};
|
||||
cilki = {
|
||||
github = "cilki";
|
||||
githubId = 10459406;
|
||||
|
|
@ -6326,12 +6322,6 @@
|
|||
githubId = 4347318;
|
||||
email = "davinci42.cn@gmail.com";
|
||||
};
|
||||
davisrichard437 = {
|
||||
email = "davisrichard437@gmail.com";
|
||||
github = "davisrichard437";
|
||||
githubId = 85075437;
|
||||
name = "Richard Davis";
|
||||
};
|
||||
davorb = {
|
||||
email = "davor@davor.se";
|
||||
github = "davorb";
|
||||
|
|
@ -7370,12 +7360,6 @@
|
|||
github = "DSeeLP";
|
||||
githubId = 46624152;
|
||||
};
|
||||
dsferruzza = {
|
||||
email = "david.sferruzza@gmail.com";
|
||||
github = "dsferruzza";
|
||||
githubId = 1931963;
|
||||
name = "David Sferruzza";
|
||||
};
|
||||
dsluijk = {
|
||||
name = "Dany Sluijk";
|
||||
email = "nix@dany.dev";
|
||||
|
|
@ -7626,6 +7610,11 @@
|
|||
githubId = 54573;
|
||||
name = "Edward Amsden";
|
||||
};
|
||||
eana = {
|
||||
github = "eana";
|
||||
githubId = 18534280;
|
||||
name = "Jonas Eana";
|
||||
};
|
||||
earldouglas = {
|
||||
email = "james@earldouglas.com";
|
||||
github = "earldouglas";
|
||||
|
|
@ -8674,6 +8663,13 @@
|
|||
githubId = 88741530;
|
||||
name = "Fabian Rigoll";
|
||||
};
|
||||
fabiob = {
|
||||
email = "fabio@atelie.dev.br";
|
||||
github = "fabiob";
|
||||
githubId = 140875;
|
||||
name = "Fábio Batista";
|
||||
keys = [ { fingerprint = "D2D8 69D8 5EEC 30AD D327 B4A5 6CD5 5257 DB01 8B72"; } ];
|
||||
};
|
||||
fallenbagel = {
|
||||
name = "fallenbagel";
|
||||
github = "fallenbagel";
|
||||
|
|
@ -10146,6 +10142,12 @@
|
|||
githubId = 6893840;
|
||||
name = "Yacine Hmito";
|
||||
};
|
||||
gquetel = {
|
||||
email = "gregor.quetel@telecom-paris.fr";
|
||||
github = "gquetel";
|
||||
githubId = 48437427;
|
||||
name = "Grégor Quetel";
|
||||
};
|
||||
gracicot = {
|
||||
email = "dev@gracicot.com";
|
||||
matrix = "@gracicot-59e8f173d73408ce4f7ac803:gitter.im";
|
||||
|
|
@ -15605,12 +15607,6 @@
|
|||
githubId = 4312404;
|
||||
name = "Chris Rendle-Short";
|
||||
};
|
||||
lightdiscord = {
|
||||
email = "root@arnaud.sh";
|
||||
github = "lightdiscord";
|
||||
githubId = 24509182;
|
||||
name = "Arnaud Pascal";
|
||||
};
|
||||
lightquantum = {
|
||||
email = "self@lightquantum.me";
|
||||
github = "PhotonQuantum";
|
||||
|
|
@ -15817,12 +15813,6 @@
|
|||
githubId = 23727619;
|
||||
name = "Luca Ruperto";
|
||||
};
|
||||
lnl7 = {
|
||||
email = "daiderd@gmail.com";
|
||||
github = "LnL7";
|
||||
githubId = 689294;
|
||||
name = "Daiderd Jordan";
|
||||
};
|
||||
lo1tuma = {
|
||||
email = "schreck.mathias@gmail.com";
|
||||
github = "lo1tuma";
|
||||
|
|
@ -16075,6 +16065,12 @@
|
|||
githubId = 83420438;
|
||||
name = "Lewis";
|
||||
};
|
||||
luc65r = {
|
||||
email = "lucas@ransan.fr";
|
||||
github = "luc65r";
|
||||
githubId = 59375051;
|
||||
name = "Lucas Ransan";
|
||||
};
|
||||
LucaGuerra = {
|
||||
email = "luca@guerra.sh";
|
||||
github = "LucaGuerra";
|
||||
|
|
@ -17397,12 +17393,6 @@
|
|||
githubId = 613740;
|
||||
name = "Martin Baillie";
|
||||
};
|
||||
mbbx6spp = {
|
||||
email = "me@susanpotter.net";
|
||||
github = "mbbx6spp";
|
||||
githubId = 564;
|
||||
name = "Susan Potter";
|
||||
};
|
||||
mbe = {
|
||||
email = "brandonedens@gmail.com";
|
||||
github = "brandonedens";
|
||||
|
|
@ -19254,6 +19244,12 @@
|
|||
name = "Naufal Fikri";
|
||||
keys = [ { fingerprint = "1575 D651 E31EC 6117A CF0AA C1A3B 8BBC A515 8835"; } ];
|
||||
};
|
||||
naurissteins = {
|
||||
name = "Nauris Steins";
|
||||
email = "me@naurissteins.com";
|
||||
github = "naurissteins";
|
||||
githubId = 5653746;
|
||||
};
|
||||
naxdy = {
|
||||
name = "Naxdy";
|
||||
email = "naxdy@naxdy.org";
|
||||
|
|
@ -19549,6 +19545,11 @@
|
|||
githubId = 10180857;
|
||||
name = "Anmol Sethi";
|
||||
};
|
||||
nhshah15 = {
|
||||
github = "nhshah15";
|
||||
githubId = 31825306;
|
||||
name = "Neil Shah";
|
||||
};
|
||||
ni5arga = {
|
||||
email = "hello@ni5arga.com";
|
||||
github = "ni5arga";
|
||||
|
|
@ -26592,6 +26593,13 @@
|
|||
github = "supermarin";
|
||||
githubId = 627285;
|
||||
};
|
||||
Superredstone = {
|
||||
email = "patrickcanal3@gmail.com";
|
||||
matrix = "@superredstone:matrix.org";
|
||||
github = "Superredstone";
|
||||
githubId = 56410215;
|
||||
name = "Patrick Canal";
|
||||
};
|
||||
SuperSandro2000 = {
|
||||
email = "sandro.jaeckel@gmail.com";
|
||||
matrix = "@sandro:supersandro.de";
|
||||
|
|
@ -26688,12 +26696,6 @@
|
|||
githubId = 120188;
|
||||
name = "Scott W. Dunlop";
|
||||
};
|
||||
sweber = {
|
||||
email = "sweber2342+nixpkgs@gmail.com";
|
||||
github = "sweber83";
|
||||
githubId = 19905904;
|
||||
name = "Simon Weber";
|
||||
};
|
||||
sweenu = {
|
||||
name = "sweenu";
|
||||
email = "contact@sweenu.xyz";
|
||||
|
|
@ -28115,12 +28117,6 @@
|
|||
githubId = 50843046;
|
||||
name = "tornax";
|
||||
};
|
||||
toschmidt = {
|
||||
email = "tobias.schmidt@in.tum.de";
|
||||
github = "toschmidt";
|
||||
githubId = 27586264;
|
||||
name = "Tobias Schmidt";
|
||||
};
|
||||
totalchaos = {
|
||||
email = "basil.keeler@outlook.com";
|
||||
github = "totalchaos05";
|
||||
|
|
@ -28532,6 +28528,12 @@
|
|||
githubId = 30677291;
|
||||
name = "u2x1";
|
||||
};
|
||||
u3kkasha = {
|
||||
email = "fida.waseque@gmail.com";
|
||||
github = "u3kkasha";
|
||||
githubId = 146055002;
|
||||
name = "Fida Waseque Choudhury";
|
||||
};
|
||||
uakci = {
|
||||
name = "uakci";
|
||||
email = "git@uakci.space";
|
||||
|
|
@ -29376,6 +29378,14 @@
|
|||
github = "wamserma";
|
||||
githubId = 60148;
|
||||
};
|
||||
wanderer = {
|
||||
name = "Martin Becze";
|
||||
email = "martin@becze.org";
|
||||
matrix = "@null_radix:matrix.org";
|
||||
github = "wanderer";
|
||||
githubId = 158211;
|
||||
keys = [ { fingerprint = "82CB 91F4 F3E4 04A5 DAB2 78E8 8B99 1293 8FF2 1020"; } ];
|
||||
};
|
||||
wariuccio = {
|
||||
name = "Wariuccio";
|
||||
github = "wariuccio";
|
||||
|
|
@ -30482,6 +30492,12 @@
|
|||
githubId = 7040031;
|
||||
name = "Yannik Sander";
|
||||
};
|
||||
ysnt-yes = {
|
||||
name = "Yes";
|
||||
github = "ysnt-yes";
|
||||
githubId = 85800291;
|
||||
email = "yes@ysnt.live";
|
||||
};
|
||||
yuannan = {
|
||||
email = "brandon@emergence.ltd";
|
||||
github = "yuannan";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
and builds all derivations (or at least a reasonable subset) affected by
|
||||
these overrides.
|
||||
|
||||
By default, it checks `configuration-{common,nix,ghc-9.8.x}.nix`. You can
|
||||
By default, it checks `configuration-{common,nix,ghc-9.10.x}.nix`. You can
|
||||
invoke it like this:
|
||||
|
||||
nix-build maintainers/scripts/haskell/test-configurations.nix --keep-going
|
||||
|
|
@ -55,6 +55,8 @@
|
|||
nixpkgsPath ? ../../..,
|
||||
config ? {
|
||||
allowBroken = true;
|
||||
# TODO(@sternenseemann): better way to workaround throw-ing aliases?
|
||||
allowAliases = false;
|
||||
},
|
||||
skipEvalErrors ? true,
|
||||
skipBinaryGHCs ? true,
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ sed -r \
|
|||
-e '/ hledger-lib /d' \
|
||||
-e '/ hledger-ui /d' \
|
||||
-e '/ hledger-web /d' \
|
||||
-e '/ spacecookie /d' \
|
||||
< "${tmpfile_new}" >> $stackage_config
|
||||
# Explanations:
|
||||
# cabal2nix, distribution-nixpkgs, jailbreak-cabal, language-nix: These are our packages and we know what we are doing.
|
||||
|
|
@ -111,9 +112,4 @@ sed -r \
|
|||
# ShellCheck: latest version of command-line dev tool.
|
||||
# Agda: The Agda community is fast-moving; we strive to always include the newest versions of Agda and the Agda packages in nixpkgs.
|
||||
|
||||
# Work around Stackage LTS including a bogus version of cassava which has been deprecated on Hackage.
|
||||
# See <https://github.com/haskell-hvr/cassava/issues/248>.
|
||||
# TODO(@sternenseemann): drop this once the situation has been resolved in Stackage LTS
|
||||
sed -e 's/cassava ==0.5.5.0/cassava >= 0.5.4.0 && (> 0.5.5.0 || < 0.5.5.0) && < 0.6.0.0/' -i "$stackage_config"
|
||||
|
||||
echo "$old_version -> $version"
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ lpeglabel,,,,1.6.0,,
|
|||
lrexlib-gnu,,,,,,
|
||||
lrexlib-oniguruma,,,,,,junestepp
|
||||
lrexlib-pcre,,,,,,
|
||||
lrexlib-pcre2,,,,,,wishstudio
|
||||
lrexlib-posix,,,,,,
|
||||
lsp-progress.nvim,,,,,5.1,gepbird
|
||||
lsqlite3,,,,,,
|
||||
|
|
@ -167,6 +168,7 @@ telescope.nvim,,,,,5.1,
|
|||
tiktoken_core,,,,,,natsukium
|
||||
tl,,,,,,mephistophiles
|
||||
toml-edit,,,,,5.1,mrcjkb
|
||||
tomlua,,,,,,birdee
|
||||
tree-sitter-cli,,,,,,
|
||||
tree-sitter-http,,,,0.0.33-1,,
|
||||
tree-sitter-norg,,,,,5.1,mrcjkb
|
||||
|
|
|
|||
|
|
|
@ -494,6 +494,47 @@ Composed types are types that take a type as parameter. `listOf
|
|||
|
||||
Displays the option as `foo.<id>` in the manual.
|
||||
|
||||
`types.attrListOf` *`t`*
|
||||
|
||||
: An ordered list of single-attribute attribute sets, where each value is of *`t`* type.
|
||||
The output is always `[ { name1 = value1; } { name2 = value2; } ... ]`.
|
||||
|
||||
Definitions can be provided in two formats, which may be mixed via `lib.mkMerge`, `imports`, etc:
|
||||
|
||||
- **List format**: `[ { a = 1; } { b = 2; } ]` — each element must be a single-attribute attribute set.
|
||||
Elements may be wrapped in `lib.mkOrder` (or `lib.mkBefore`/`lib.mkAfter`) to control ordering;
|
||||
unwrapped elements use the default order priority.
|
||||
|
||||
- **Attribute set format**: `{ a = lib.mkOrder 100 1; b = 2; }` — each name-value pair becomes a single-attribute attribute set in the output.
|
||||
Values may be wrapped in `lib.mkOrder` (or `lib.mkBefore`/`lib.mkAfter`) to control ordering.
|
||||
Values without `lib.mkOrder` use the default priority.
|
||||
|
||||
Multiple definitions of the same option are concatenated and then sorted by priority.
|
||||
Entries at the same priority level preserve their definition order.
|
||||
|
||||
`types.attrListWith` { *`elemType`*, *`asAttrs`* ? false, *`mergeAttrValues`* ? _name: values: values }
|
||||
|
||||
: An ordered list of single-attribute attribute sets, where each value is of *`elemType`* type.
|
||||
|
||||
**Parameters**
|
||||
|
||||
`elemType` (Required)
|
||||
: Specifies the type of each value in the attribute list.
|
||||
|
||||
`asAttrs`
|
||||
: When `true`, the option value is an attribute set instead of a list.
|
||||
Duplicate keys are merged using `mergeAttrValues`.
|
||||
The ordered list is always available via `valueMeta.attrListValue`.
|
||||
|
||||
`mergeAttrValues`
|
||||
: A function `name: values: mergedValue` that controls how duplicate keys
|
||||
are combined when `asAttrs = true`. This is passed as the callback to
|
||||
`lib.zipAttrsWith`. The `values` list is in order of priority.
|
||||
By default, all values are collected into a list.
|
||||
|
||||
**Behavior**
|
||||
|
||||
- `attrListWith { elemType = t; }` is equivalent to `attrListOf t`
|
||||
|
||||
`types.uniq` *`t`*
|
||||
|
||||
|
|
|
|||
|
|
@ -2363,6 +2363,21 @@
|
|||
"ch-release-notes": [
|
||||
"release-notes.html#ch-release-notes"
|
||||
],
|
||||
"sec-release-26.11": [
|
||||
"release-notes.html#sec-release-26.11"
|
||||
],
|
||||
"sec-release-26.11-highlights": [
|
||||
"release-notes.html#sec-release-26.11-highlights"
|
||||
],
|
||||
"sec-release-26.11-new-modules": [
|
||||
"release-notes.html#sec-release-26.11-new-modules"
|
||||
],
|
||||
"sec-release-26.11-incompatibilities": [
|
||||
"release-notes.html#sec-release-26.11-incompatibilities"
|
||||
],
|
||||
"sec-release-26.11-notable-changes": [
|
||||
"release-notes.html#sec-release-26.11-notable-changes"
|
||||
],
|
||||
"sec-release-26.05": [
|
||||
"release-notes.html#sec-release-26.05"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
This section lists the release notes for each stable version of NixOS and current unstable revision.
|
||||
|
||||
```{=include=} sections
|
||||
rl-2611.section.md
|
||||
rl-2605.section.md
|
||||
rl-2511.section.md
|
||||
rl-2505.section.md
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@
|
|||
# Pinned Nixpkgs archive
|
||||
#
|
||||
# Use `curl -I https://channels.nixos.org/nixos-26.05` to get the
|
||||
# latest commit of the stable channel and `nix-prefetch-url --unpack`
|
||||
# to compute its sha256 hash.
|
||||
# latest commit of the stable channel and `nix --extra-experimental-features nix-command store prefetch-file --unpack`
|
||||
# to compute its hash.
|
||||
nixpkgs = builtins.fetchTarball {
|
||||
url = "https://github.com/NixOS/nixpkgs/archive/c217913993d6.tar.gz";
|
||||
sha256 = "026mprs324330pfazlgbw987qmsa8ligglarvqbcxzig2kgw0lqg";
|
||||
hash = "sha256-D1PA3xQv/s4W3lnR9yJFSld8UOLr0a/cBWMQMXS+1Qg=";
|
||||
};
|
||||
in
|
||||
import "${nixpkgs}/nixos" {
|
||||
|
|
@ -132,6 +132,8 @@
|
|||
|
||||
- [Howdy](https://github.com/boltgolt/howdy), a Windows Hello™ style facial authentication program for Linux.
|
||||
|
||||
- [SuiteNumérique Drive](https://github.com/suitenumerique/drive), a collaborative file sharing and document management platform that scales. Built with Django and React. Open source alternative to Sharepoint or Google Drive.
|
||||
|
||||
- [linux-enable-ir-emitter](https://github.com/EmixamPP/linux-enable-ir-emitter), a tool used to set up IR cameras, used with Howdy.
|
||||
|
||||
- [udp-over-tcp](https://github.com/mullvad/udp-over-tcp), a tunnel for proxying UDP traffic over a TCP stream. Available as `services.udp-over-tcp`.
|
||||
|
|
@ -191,6 +193,17 @@
|
|||
|
||||
- `services.pyload` has been removed because the package it relies on does not exist anymore in nixpkgs due to vulnerabilities and being unmaintained.
|
||||
|
||||
- Removed the ability from systemd to start units installed via `nix-env -i`.
|
||||
This feature is hardly used and most units do not work without NixOS specific
|
||||
changes anyways. Removing this allows us to drop a custom systemd patch
|
||||
improving systemd maintainability and update speed.
|
||||
|
||||
- Removed the ability from systemd to depend on open-yet-unformatted dm-crypt
|
||||
devices as systemd device units. For example you cannot call systemd-makefs
|
||||
on such devices. This was never implemented upstream but patched into
|
||||
Nixpkgs's systemd. Removing this allows us to drop a custom systemd patch
|
||||
improving systemd maintainability and update speed.
|
||||
|
||||
- `linux_hardened` kernel has been removed due to a lack of maintenance.
|
||||
|
||||
- `services.tandoor-recipes` now uses a sub-directory for media files by default starting with `26.05`. Existing setups should move media files out of the data directory and adjust `services.tandoor-recipes.extraConfig.MEDIA_ROOT` accordingly. See [Migrating media files for pre 26.05 installations](#module-services-tandoor-recipes-migrating-media).
|
||||
|
|
@ -207,9 +220,22 @@
|
|||
|
||||
- `services.mattermost` now defaults to version 11, which has dropped support for MySQL in favor of Postgres. As a result, all support for MySQL has been removed from the module.
|
||||
See the [migration steps](https://docs.mattermost.com/deployment-guide/manual-postgres-migration.html) if you were not running Postgres.
|
||||
Note that version 11 also restricts the user limit to 250 [by default](https://forum.mattermost.com/t/clarification-request-on-user-limits-max-250-user-server-v-11/25309);
|
||||
see the `pkgs.mattermost` removeUserLimit and removeFreeBadge options combined with `services.mattermost.package` to change this behavior. For example:
|
||||
|
||||
```nix
|
||||
{
|
||||
services.mattermost.package = pkgs.mattermost.override {
|
||||
removeUserLimit = true;
|
||||
removeFreeBadge = true;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
- `post-resume.target` has been removed. See {manpage}`systemd.special(7)` about `sleep.target` for instructions on ordering a process after resume with `ExecStop=`.
|
||||
|
||||
- `services.vsftpd` no longer automatically configures a PAM module. This means configurations using `services.vsftpd.localUsers` will no longer work unless `services.vsftpd.enableVirtualUsers` and `services.vsftpd.userDbPath` are also configured. The old behaviour can be restored by setting `security.pam.services.vsftpd.enable = true`, although this only ever worked by accident and may not be secure.
|
||||
|
||||
- `services.kubernetes.addons.dns.coredns` has been renamed to `services.kubernetes.addons.dns.corednsImage` and now expects a
|
||||
package instead of attrs. Now, by default, nixpkgs.coredns in conjunction with dockerTools.buildImage is used, instead
|
||||
of pulling the upstream container image from Docker Hub. If you want the old behavior, you can set:
|
||||
|
|
@ -373,6 +399,10 @@ See <https://github.com/NixOS/nixpkgs/issues/481673>.
|
|||
|
||||
- `services.esphome` no longer uses `DynamicUser`. The service now runs as a static `esphome` system user. systemd handles the migration from `/var/lib/private/esphome` automatically, but users with [impermanence](https://github.com/nix-community/impermanence) setups should ensure `/var/lib/esphome` is persisted.
|
||||
|
||||
- `programs.pqos-wrapper` module has been deleted as the corresponding package has been dropped from nixpkgs.
|
||||
|
||||
- `programs.benchexec.enable = true` no longer sets `programs.pqos-wrapper.enable = true` as the corresponding module has been deleted.
|
||||
|
||||
## Other Notable Changes {#sec-release-26.05-notable-changes}
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Release 26.11 (2026.11/??) {#sec-release-26.11}
|
||||
# Release 26.11 ("Zokor", 2026.11/??) {#sec-release-26.11}
|
||||
|
||||
## Highlights {#sec-release-26.11-highlights}
|
||||
|
||||
|
|
@ -16,12 +16,7 @@
|
|||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
- `programs.pqos-wrapper` module has been deleted
|
||||
as the corresponding package has been dropped from nixpkgs.
|
||||
|
||||
- `programs.benchexec.enable = true`
|
||||
no longer sets `programs.pqos-wrapper.enable = true`
|
||||
as the corresponding module has been deleted.
|
||||
- Create the first release note entry in this section!
|
||||
|
||||
## Other Notable Changes {#sec-release-26.11-notable-changes}
|
||||
|
||||
|
|
|
|||
|
|
@ -1475,7 +1475,6 @@ class NspawnMachine(BaseMachine):
|
|||
|
||||
self.start_command = start_command
|
||||
self.process = None
|
||||
self.pid = None
|
||||
|
||||
self.machine_sock_path = self.tmp_dir / f"{self.name}-nspawn.sock"
|
||||
|
||||
|
|
@ -1486,14 +1485,13 @@ class NspawnMachine(BaseMachine):
|
|||
return f'ssh -o User=root -o ProxyCommand="{proxy_cmd}" bash'
|
||||
|
||||
def release(self) -> None:
|
||||
if self.pid is None:
|
||||
if self.process is None:
|
||||
return
|
||||
|
||||
if self.machine_sock:
|
||||
self.machine_sock.close()
|
||||
|
||||
self.logger.info(f"kill NspawnMachine (pid {self.pid})")
|
||||
assert self.process is not None
|
||||
self.logger.info(f"kill NspawnMachine (pid {self.process.pid})")
|
||||
self.process.terminate()
|
||||
# Wait for the wrapper to finish its context-manager cleanups
|
||||
# (veth/bridge/netns teardown) before returning, so the driver's
|
||||
|
|
@ -1502,7 +1500,7 @@ class NspawnMachine(BaseMachine):
|
|||
self.process.wait(timeout=30)
|
||||
except subprocess.TimeoutExpired:
|
||||
self.logger.error(
|
||||
f"NspawnMachine {self.name} (pid {self.pid}) did not exit after SIGTERM; sending SIGKILL"
|
||||
f"NspawnMachine {self.name} (pid {self.process.pid}) did not exit after SIGTERM; sending SIGKILL"
|
||||
)
|
||||
self.process.kill()
|
||||
self.process.wait()
|
||||
|
|
@ -1694,9 +1692,7 @@ class NspawnMachine(BaseMachine):
|
|||
stdout=subprocess.PIPE,
|
||||
)
|
||||
|
||||
self.pid = self.process.pid
|
||||
|
||||
self.log(f"systemd-nspawn running (pid {self.pid})")
|
||||
self.log(f"systemd-nspawn running (pid {self.process.pid})")
|
||||
|
||||
journal_thread = threading.Thread(target=self._stream_journal, daemon=True)
|
||||
journal_thread.start()
|
||||
|
|
|
|||
|
|
@ -150,7 +150,14 @@ in
|
|||
config.enableDebugHook -> isLinux
|
||||
) "The debugging hook is not supported for macOS host systems!";
|
||||
{
|
||||
name = "vm-test-run-${config.name}";
|
||||
name =
|
||||
let
|
||||
inherit (config.driverConfiguration) containers vms;
|
||||
kind = lib.concatStringsSep "-and-" (
|
||||
(lib.optional (containers != { }) "container") ++ (lib.optional (vms != { }) "vm")
|
||||
);
|
||||
in
|
||||
"${kind}-test-run-${config.name}";
|
||||
|
||||
requiredSystemFeatures = lib.attrNames (lib.filterAttrs (_: v: v) config.requiredFeatures);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
}:
|
||||
let
|
||||
cfg = config.console;
|
||||
i18nCfg = config.i18n;
|
||||
|
||||
makeColor = i: lib.concatMapStringsSep "," (x: "0x" + lib.substring (2 * i) 2 x);
|
||||
|
||||
|
|
@ -164,8 +165,10 @@ in
|
|||
environment.systemPackages = [ pkgs.kbd ];
|
||||
|
||||
# Let systemd-vconsole-setup.service do the work of setting up the
|
||||
# virtual consoles.
|
||||
environment.etc."vconsole.conf".source = vconsoleConf true;
|
||||
# virtual consoles. Skip when imperative so localectl can manage it.
|
||||
environment.etc."vconsole.conf" = lib.mkIf (!i18nCfg.imperativeLocale) {
|
||||
source = vconsoleConf true;
|
||||
};
|
||||
# Provide kbd with additional packages.
|
||||
environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
|
||||
|
||||
|
|
@ -211,13 +214,22 @@ in
|
|||
"systemd-vconsole-setup.service"
|
||||
];
|
||||
|
||||
# When imperative, seed /etc/vconsole.conf on first boot from declared
|
||||
# defaults so the keymap isn't lost before localectl is ever used
|
||||
systemd.tmpfiles.rules = lib.mkIf i18nCfg.imperativeLocale [
|
||||
"C /etc/vconsole.conf - - - - ${vconsoleConf true}"
|
||||
];
|
||||
|
||||
systemd.services.reload-systemd-vconsole-setup = {
|
||||
description = "Reset console on configuration changes";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
restartTriggers = [
|
||||
(config.environment.etc."vconsole.conf".source)
|
||||
(consoleEnv pkgs.kbd)
|
||||
];
|
||||
restartTriggers =
|
||||
lib.optionals (!i18nCfg.imperativeLocale) [
|
||||
(config.environment.etc."vconsole.conf".source)
|
||||
]
|
||||
++ [
|
||||
(consoleEnv pkgs.kbd)
|
||||
];
|
||||
reloadIfChanged = true;
|
||||
serviceConfig = {
|
||||
RemainAfterExit = true;
|
||||
|
|
|
|||
|
|
@ -5,24 +5,29 @@
|
|||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.i18n;
|
||||
localeConf = pkgs.writeText "locale.conf" ''
|
||||
LANG=${cfg.defaultLocale}
|
||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "${n}=${v}") cfg.extraLocaleSettings)}
|
||||
'';
|
||||
sanitizeUTF8Capitalization =
|
||||
lang: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] lang);
|
||||
aggregatedLocales =
|
||||
lib.optionals (config.i18n.defaultLocale != "C") [
|
||||
"${config.i18n.defaultLocale}/${config.i18n.defaultCharset}"
|
||||
lib.optionals (cfg.defaultLocale != "C") [
|
||||
"${cfg.defaultLocale}/${cfg.defaultCharset}"
|
||||
]
|
||||
++ lib.pipe config.i18n.extraLocaleSettings [
|
||||
++ lib.pipe cfg.extraLocaleSettings [
|
||||
# See description of extraLocaleSettings for why is this ignored here.
|
||||
(x: lib.removeAttrs x [ "LANGUAGE" ])
|
||||
(lib.mapAttrs (n: v: (sanitizeUTF8Capitalization v)))
|
||||
# C locales are always installed
|
||||
(lib.filterAttrs (n: v: v != "C"))
|
||||
(lib.mapAttrsToList (LCRole: lang: lang + "/" + (config.i18n.localeCharsets.${LCRole} or "UTF-8")))
|
||||
(lib.mapAttrsToList (LCRole: lang: lang + "/" + (cfg.localeCharsets.${LCRole} or "UTF-8")))
|
||||
]
|
||||
++ (map sanitizeUTF8Capitalization (
|
||||
lib.optionals (builtins.isList config.i18n.extraLocales) config.i18n.extraLocales
|
||||
lib.optionals (builtins.isList cfg.extraLocales) cfg.extraLocales
|
||||
))
|
||||
++ (lib.optional (builtins.isString config.i18n.extraLocales) config.i18n.extraLocales);
|
||||
++ (lib.optional (builtins.isString cfg.extraLocales) cfg.extraLocales);
|
||||
in
|
||||
{
|
||||
###### interface
|
||||
|
|
@ -35,8 +40,8 @@ in
|
|||
default =
|
||||
if pkgs.glibcLocales != null then
|
||||
pkgs.glibcLocales.override {
|
||||
allLocales = lib.elem "all" config.i18n.supportedLocales;
|
||||
locales = config.i18n.supportedLocales;
|
||||
allLocales = lib.elem "all" cfg.supportedLocales;
|
||||
locales = cfg.supportedLocales;
|
||||
}
|
||||
else
|
||||
null;
|
||||
|
|
@ -150,6 +155,16 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
imperativeLocale = lib.mkEnableOption ''
|
||||
imperative locale and keyboard management via localectl.
|
||||
|
||||
When enabled, locale and keyboard settings can be changed at runtime
|
||||
using `localectl set-locale` and `localectl set-keymap`.
|
||||
When disabled (the default), these settings are managed declaratively
|
||||
through {option}`i18n.defaultLocale`, {option}`i18n.extraLocaleSettings`,
|
||||
and {option}`console.keyMap`.
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
|
@ -161,8 +176,8 @@ in
|
|||
lib.optional
|
||||
(
|
||||
!(
|
||||
(lib.subtractLists config.i18n.supportedLocales aggregatedLocales) == [ ]
|
||||
|| lib.elem "all" config.i18n.supportedLocales
|
||||
(lib.subtractLists cfg.supportedLocales aggregatedLocales) == [ ]
|
||||
|| lib.elem "all" cfg.supportedLocales
|
||||
)
|
||||
)
|
||||
''
|
||||
|
|
@ -178,29 +193,36 @@ in
|
|||
|
||||
environment.systemPackages =
|
||||
# We increase the priority a little, so that plain glibc in systemPackages can't win.
|
||||
lib.optional (config.i18n.glibcLocales != null && config.i18n.supportedLocales != [ ]) (
|
||||
lib.setPrio (-1) config.i18n.glibcLocales
|
||||
lib.optional (cfg.glibcLocales != null && cfg.supportedLocales != [ ]) (
|
||||
lib.setPrio (-1) cfg.glibcLocales
|
||||
);
|
||||
|
||||
environment.sessionVariables = {
|
||||
LANG = config.i18n.defaultLocale;
|
||||
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
|
||||
}
|
||||
// config.i18n.extraLocaleSettings;
|
||||
# When imperative, leave LANG/LC_* to pam_systemd so /etc/set-environment
|
||||
# does not override what localectl wrote to /etc/locale.conf.
|
||||
// lib.optionalAttrs (!cfg.imperativeLocale) (
|
||||
{
|
||||
LANG = cfg.defaultLocale;
|
||||
}
|
||||
// cfg.extraLocaleSettings
|
||||
);
|
||||
|
||||
systemd.globalEnvironment =
|
||||
lib.mkIf (config.i18n.glibcLocales != null && config.i18n.supportedLocales != [ ])
|
||||
{
|
||||
LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
|
||||
};
|
||||
systemd.globalEnvironment = lib.mkIf (cfg.glibcLocales != null && cfg.supportedLocales != [ ]) {
|
||||
LOCALE_ARCHIVE = "${cfg.glibcLocales}/lib/locale/locale-archive";
|
||||
};
|
||||
|
||||
# ‘/etc/locale.conf’ is used by systemd.
|
||||
environment.etc."locale.conf".source = pkgs.writeText "locale.conf" ''
|
||||
LANG=${config.i18n.defaultLocale}
|
||||
${lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings
|
||||
)}
|
||||
'';
|
||||
# If imperative, see below
|
||||
environment.etc."locale.conf" = lib.mkIf (!cfg.imperativeLocale) {
|
||||
source = localeConf;
|
||||
};
|
||||
|
||||
# When imperative, seed /etc/locale.conf on first boot from declared defaults
|
||||
# so the system doesn’t fall back to C.UTF-8
|
||||
systemd.tmpfiles.rules = lib.mkIf cfg.imperativeLocale [
|
||||
"C /etc/locale.conf - - - - ${localeConf}"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,10 +86,6 @@ in
|
|||
# This way services are restarted when tzdata changes.
|
||||
systemd.globalEnvironment.TZDIR = tzdir;
|
||||
|
||||
systemd.services.systemd-timedated.environment = lib.optionalAttrs (config.time.timeZone != null) {
|
||||
NIXOS_STATIC_TIMEZONE = "1";
|
||||
};
|
||||
|
||||
environment.etc = {
|
||||
zoneinfo.source = tzdir;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,23 @@ let
|
|||
|
||||
cfg = config.environment;
|
||||
|
||||
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeSessionVariables (
|
||||
envVar: suffixes:
|
||||
lib.flip lib.concatMap cfg.profiles (profile: map (suffix: "${profile}${suffix}") suffixes)
|
||||
);
|
||||
|
||||
combinedSessionVars = lib.zipAttrsWith (n: lib.concatLists) [
|
||||
# Make sure security wrappers are prioritized without polluting
|
||||
# shell environments with an extra entry. Sessions which depend on
|
||||
# pam for its environment will otherwise have eg. broken sudo. In
|
||||
# particular Gnome Shell sometimes fails to source a proper
|
||||
# environment from a shell.
|
||||
{ PATH = [ config.security.wrapperDir ]; }
|
||||
|
||||
(lib.mapAttrs (n: lib.toList) cfg.sessionVariables)
|
||||
suffixedVariables
|
||||
];
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
|
@ -73,13 +90,11 @@ in
|
|||
};
|
||||
|
||||
config = {
|
||||
environment.etc."environment.d/50-systemd-path.conf".text = ''
|
||||
PATH="${lib.concatStringsSep ":" combinedSessionVars.PATH}"
|
||||
'';
|
||||
environment.etc."pam/environment".text =
|
||||
let
|
||||
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeSessionVariables (
|
||||
envVar: suffixes:
|
||||
lib.flip lib.concatMap cfg.profiles (profile: map (suffix: "${profile}${suffix}") suffixes)
|
||||
);
|
||||
|
||||
# We're trying to use the same syntax for PAM variables and env variables.
|
||||
# That means we need to map the env variables that people might use to their
|
||||
# equivalent PAM variable.
|
||||
|
|
@ -88,21 +103,7 @@ in
|
|||
pamVariable =
|
||||
n: v: ''${n} DEFAULT="${lib.concatStringsSep ":" (map replaceEnvVars (lib.toList v))}"'';
|
||||
|
||||
pamVariables = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList pamVariable (
|
||||
lib.zipAttrsWith (n: lib.concatLists) [
|
||||
# Make sure security wrappers are prioritized without polluting
|
||||
# shell environments with an extra entry. Sessions which depend on
|
||||
# pam for its environment will otherwise have eg. broken sudo. In
|
||||
# particular Gnome Shell sometimes fails to source a proper
|
||||
# environment from a shell.
|
||||
{ PATH = [ config.security.wrapperDir ]; }
|
||||
|
||||
(lib.mapAttrs (n: lib.toList) cfg.sessionVariables)
|
||||
suffixedVariables
|
||||
]
|
||||
)
|
||||
);
|
||||
pamVariables = lib.concatStringsSep "\n" (lib.mapAttrsToList pamVariable combinedSessionVars);
|
||||
in
|
||||
''
|
||||
${pamVariables}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@
|
|||
rxvt-unicode-unwrapped
|
||||
rxvt-unicode-unwrapped-emoji
|
||||
st
|
||||
termite
|
||||
tmux
|
||||
wezterm
|
||||
yaft
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ EOF
|
|||
# This should work for single and multi-device systems.
|
||||
# still needs subvolume support
|
||||
if ($fsType eq "bcachefs") {
|
||||
my ($status, @info) = runCommand("bcachefs fs usage $rootDir$mountPoint");
|
||||
my ($status, @info) = runCommand("@bcachefs@ fs usage $rootDir$mountPoint");
|
||||
my $UUID = $info[0];
|
||||
|
||||
if ($status == 0 && $UUID =~ /^Filesystem:[ \t\n]*([0-9a-z-]+)/) {
|
||||
|
|
|
|||
|
|
@ -30,15 +30,20 @@ let
|
|||
name = "nixos-generate-config";
|
||||
src = ./nixos-generate-config.pl;
|
||||
replacements = {
|
||||
perl = "${
|
||||
perl = lib.getExe (
|
||||
pkgs.perl.withPackages (p: [
|
||||
p.FileSlurp
|
||||
p.ConfigIniFiles
|
||||
])
|
||||
}/bin/perl";
|
||||
);
|
||||
hostPlatformSystem = pkgs.stdenv.hostPlatform.system;
|
||||
detectvirt = "${config.systemd.package}/bin/systemd-detect-virt";
|
||||
btrfs = "${pkgs.btrfs-progs}/bin/btrfs";
|
||||
detectvirt = lib.getExe' config.systemd.package "systemd-detect-virt";
|
||||
bcachefs =
|
||||
if pkgs.bcachefs-tools.meta.broken then
|
||||
lib.getExe' pkgs.coreutils "false"
|
||||
else
|
||||
lib.getExe pkgs.bcachefs-tools;
|
||||
btrfs = lib.getExe pkgs.btrfs-progs;
|
||||
inherit (config.system.nixos-generate-config) configuration desktopConfiguration flake;
|
||||
xserverEnabled = config.services.xserver.enable;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1697,6 +1697,7 @@
|
|||
./services/web-apps/komga.nix
|
||||
./services/web-apps/lanraragi.nix
|
||||
./services/web-apps/lasuite-docs.nix
|
||||
./services/web-apps/lasuite-drive.nix
|
||||
./services/web-apps/lasuite-meet.nix
|
||||
./services/web-apps/lauti.nix
|
||||
./services/web-apps/lemmy.nix
|
||||
|
|
|
|||
|
|
@ -33,7 +33,10 @@ in
|
|||
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
default = builtins.pathExists config.programs.command-not-found.dbPath;
|
||||
defaultText = lib.literalExpression ''
|
||||
builtins.pathExists config.programs.command-not-found.dbPath
|
||||
'';
|
||||
description = ''
|
||||
Whether interactive shells should show which Nix package (if
|
||||
any) provides a missing command.
|
||||
|
|
@ -45,6 +48,11 @@ in
|
|||
};
|
||||
|
||||
dbPath = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = pkgs.path + "/programs.sqlite";
|
||||
defaultText = lib.literalExpression ''
|
||||
pkgs.path + "/programs.sqlite"
|
||||
'';
|
||||
description = ''
|
||||
Absolute path to `programs.sqlite`, which contains mappings from binary names to package names.
|
||||
|
||||
|
|
@ -54,39 +62,29 @@ in
|
|||
`/nix/var/nix/profiles/per-user/root/channels/nixos/programs.sqlite`.
|
||||
If you do so, you can update it with `sudo nix-channels --update`.
|
||||
'';
|
||||
type = lib.types.path;
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
programs.command-not-found = {
|
||||
enable = lib.mkDefault (builtins.pathExists cfg.dbPath);
|
||||
dbPath = pkgs.path + "/programs.sqlite";
|
||||
};
|
||||
}
|
||||
config = lib.mkIf cfg.enable {
|
||||
programs.bash.interactiveShellInit = ''
|
||||
command_not_found_handle() {
|
||||
'${commandNotFound}/bin/command-not-found' "$@"
|
||||
}
|
||||
'';
|
||||
|
||||
(lib.mkIf cfg.enable {
|
||||
programs.bash.interactiveShellInit = ''
|
||||
command_not_found_handle() {
|
||||
'${commandNotFound}/bin/command-not-found' "$@"
|
||||
}
|
||||
'';
|
||||
programs.zsh.interactiveShellInit = ''
|
||||
command_not_found_handler() {
|
||||
'${commandNotFound}/bin/command-not-found' "$@"
|
||||
}
|
||||
'';
|
||||
|
||||
programs.zsh.interactiveShellInit = ''
|
||||
command_not_found_handler() {
|
||||
'${commandNotFound}/bin/command-not-found' "$@"
|
||||
}
|
||||
'';
|
||||
# NOTE: Fish by itself checks for nixos command-not-found, let's instead makes it explicit.
|
||||
programs.fish.interactiveShellInit = ''
|
||||
function fish_command_not_found
|
||||
"${commandNotFound}/bin/command-not-found" $argv
|
||||
end
|
||||
'';
|
||||
|
||||
# NOTE: Fish by itself checks for nixos command-not-found, let's instead makes it explicit.
|
||||
programs.fish.interactiveShellInit = ''
|
||||
function fish_command_not_found
|
||||
"${commandNotFound}/bin/command-not-found" $argv
|
||||
end
|
||||
'';
|
||||
|
||||
environment.systemPackages = [ commandNotFound ];
|
||||
})
|
||||
];
|
||||
environment.systemPackages = [ commandNotFound ];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ in
|
|||
programs.gamemode = {
|
||||
enable = lib.mkEnableOption "GameMode to optimise system performance on demand";
|
||||
|
||||
package = lib.mkPackageOption pkgs "gamemode" { };
|
||||
|
||||
enableRenice =
|
||||
lib.mkEnableOption "CAP_SYS_NICE on gamemoded to support lowering process niceness"
|
||||
// {
|
||||
|
|
@ -53,7 +55,7 @@ in
|
|||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment = {
|
||||
systemPackages = [ pkgs.gamemode ];
|
||||
systemPackages = [ cfg.package ];
|
||||
etc."gamemode.ini".source = configFile;
|
||||
};
|
||||
|
||||
|
|
@ -63,14 +65,14 @@ in
|
|||
gamemoded = {
|
||||
owner = "root";
|
||||
group = "root";
|
||||
source = "${pkgs.gamemode}/bin/gamemoded";
|
||||
source = "${cfg.package}/bin/gamemoded";
|
||||
capabilities = "cap_sys_nice+ep";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd = {
|
||||
packages = [ pkgs.gamemode ];
|
||||
packages = [ cfg.package ];
|
||||
user.services.gamemoded = {
|
||||
# Use pkexec from the security wrappers to allow users to
|
||||
# run libexec/cpugovctl & libexec/gpuclockctl as root with
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ in
|
|||
|
||||
{
|
||||
config = lib.mkIf (cfg.enable && nixPackage.pname == "lix") {
|
||||
# Require the tun kernel module for pasta, can be disabled if pasta is not used.
|
||||
boot.kernelModules.tun = lib.mkDefault true;
|
||||
|
||||
environment.systemPackages = [
|
||||
nixPackage
|
||||
pkgs.nix-info
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ in
|
|||
# config file. starship appears to use a hardcoded config location
|
||||
# rather than one inside an XDG folder:
|
||||
# https://github.com/starship/starship/blob/686bda1706e5b409129e6694639477a0f8a3f01b/src/configure.rs#L651
|
||||
if [[ ! -f "$${STARSHIP_CONFIG:-$HOME/.config/starship.toml}" ]]; then
|
||||
if [[ ! -f "''${STARSHIP_CONFIG:-$HOME/.config/starship.toml}" ]]; then
|
||||
export STARSHIP_CONFIG=${settingsFile}
|
||||
fi
|
||||
eval "$(${cfg.package}/bin/starship init bash --print-full-init)"
|
||||
|
|
@ -154,7 +154,7 @@ in
|
|||
# config file. starship appears to use a hardcoded config location
|
||||
# rather than one inside an XDG folder:
|
||||
# https://github.com/starship/starship/blob/686bda1706e5b409129e6694639477a0f8a3f01b/src/configure.rs#L651
|
||||
if [[ ! -f "$${STARSHIP_CONFIG:-$HOME/.config/starship.toml}" ]]; then
|
||||
if [[ ! -f "''${STARSHIP_CONFIG:-$HOME/.config/starship.toml}" ]]; then
|
||||
export STARSHIP_CONFIG=${settingsFile}
|
||||
fi
|
||||
eval "$(${cfg.package}/bin/starship init zsh)"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ in
|
|||
enable = lib.mkEnableOption "TUN mode of Throne";
|
||||
|
||||
setuid = lib.mkEnableOption ''
|
||||
setting suid bit for throne-core to run as root, which is less
|
||||
setting suid bit for ThroneCore to run as root, which is less
|
||||
secure than default setcap method but closer to upstream assumptions.
|
||||
Enable this if you find the default setcap method configured in
|
||||
this module doesn't work for you
|
||||
|
|
@ -36,8 +36,8 @@ in
|
|||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
security.wrappers.throne-core = lib.mkIf cfg.tunMode.enable {
|
||||
source = "${cfg.package}/share/throne/Core";
|
||||
security.wrappers."ThroneCore" = lib.mkIf cfg.tunMode.enable {
|
||||
source = "${cfg.package}/share/throne/ThroneCore";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
setuid = lib.mkIf cfg.tunMode.setuid true;
|
||||
|
|
@ -49,7 +49,7 @@ in
|
|||
|
||||
# avoid resolvectl password prompt popping up three times
|
||||
# https://github.com/SagerNet/sing-tun/blob/0686f8c4f210f4e7039c352d42d762252f9d9cf5/tun_linux.go#L1062
|
||||
# We use a hack here to determine whether the requested process is throne-core
|
||||
# We use a hack here to determine whether the requested process is ThroneCore
|
||||
# Detect whether its capabilities contain at least `net_admin` and `net_raw`.
|
||||
# This does not reduce security, as we can already bypass `resolved` with them.
|
||||
# Alternatives to consider:
|
||||
|
|
@ -61,7 +61,7 @@ in
|
|||
# change its own cmdline. `/proc/<pid>/exe` is reliable but kernel forbids
|
||||
# checking that entry of process from different users, and polkit runs `spawn`
|
||||
# as an unprivileged user.
|
||||
# 3. Put throne-core into a systemd service, and let polkit check service name.
|
||||
# 3. Put ThroneCore into a systemd service, and let polkit check service name.
|
||||
# This is the most secure and convenient way but requires heavy modification
|
||||
# to Throne source code. Would be good to let upstream support that eventually.
|
||||
security.polkit.extraConfig =
|
||||
|
|
@ -69,6 +69,7 @@ in
|
|||
''
|
||||
polkit.addRule(function(action, subject) {
|
||||
const allowedActionIds = [
|
||||
"org.freedesktop.resolve1.revert",
|
||||
"org.freedesktop.resolve1.set-domains",
|
||||
"org.freedesktop.resolve1.set-default-route",
|
||||
"org.freedesktop.resolve1.set-dns-servers"
|
||||
|
|
|
|||
|
|
@ -81,7 +81,12 @@ in
|
|||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
environment = {
|
||||
systemPackages = [ cfg.package ];
|
||||
|
||||
# Allows lua stub file to be accessed from /run/current-system/sw/share/hypr
|
||||
pathsToLink = [ "/share/hypr" ];
|
||||
};
|
||||
|
||||
# Hyprland needs permissions to give itself SCHED_RR on startup:
|
||||
# https://github.com/hyprwm/Hyprland/blob/main/src/init/initHelpers.cpp
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ in
|
|||
with pkgs; [ swaylock foot dmenu ];
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
with pkgs; [ termite rofi light ]
|
||||
with pkgs; [ alacritty rofi light ]
|
||||
'';
|
||||
description = ''
|
||||
Extra packages to be installed system wide. See
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ in
|
|||
with pkgs; [ brightnessctl foot grim pulseaudio swayidle swaylock wmenu ];
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
with pkgs; [ i3status i3status-rust termite rofi light ]
|
||||
with pkgs; [ i3status i3status-rust alacritty rofi light ]
|
||||
'';
|
||||
description = ''
|
||||
Extra packages to be installed system wide. See
|
||||
|
|
|
|||
|
|
@ -249,8 +249,8 @@ in
|
|||
setopt ${builtins.concatStringsSep " " cfg.setOptions}
|
||||
''}
|
||||
|
||||
# Alternative method of determining short and full hostname.
|
||||
HOST=${config.networking.fqdnOrHostName}
|
||||
# Determine current fqdn hostname
|
||||
HOST=$(hostname --fqdn)
|
||||
|
||||
# Setup command line history.
|
||||
# Don't export these, otherwise other shells (bash) will try to use same HISTFILE.
|
||||
|
|
|
|||
|
|
@ -155,7 +155,8 @@ in
|
|||
CapabilityBoundingSet = [ "" ];
|
||||
DevicePolicy = "closed";
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = !useYTMusic;
|
||||
# breaks pyopenssl's cffi calls, used in remote access feature
|
||||
MemoryDenyWriteExecute = false;
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
|
|
|
|||
|
|
@ -90,12 +90,9 @@ in
|
|||
|
||||
Plugins = {
|
||||
Enabled = mkOption {
|
||||
default = (builtins.length cfg.plugins) != 0;
|
||||
defaultText = literalExpression "builtins.length \"\${config.services.navidrome.plugins != 0}\"";
|
||||
default = true;
|
||||
description = ''
|
||||
Enable plugin support in navidrome.
|
||||
|
||||
This is automatically enabled if {option}`services.navidrome.plugins` is used.
|
||||
'';
|
||||
};
|
||||
Folder = mkOption {
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ in
|
|||
LoadCredential = config.systemd.services.radicle-node.serviceConfig.LoadCredential or [ ];
|
||||
|
||||
BindReadOnlyPaths = config.systemd.services.radicle-node.serviceConfig.BindReadOnlyPaths ++ [
|
||||
"/run/credentials/radicle-ci-broker.service/xyz.radicle.node.secret:/var/lib/radicle/keys/radicle"
|
||||
"/run/credentials/radicle-ci-broker.service/dev.radicle.node.secret:/var/lib/radicle/keys/radicle"
|
||||
];
|
||||
ReadWritePaths = [ RAD_HOME ];
|
||||
|
||||
|
|
|
|||
|
|
@ -469,6 +469,7 @@ in
|
|||
pkgs.gnome-maps
|
||||
pkgs.gnome-music
|
||||
pkgs.gnome-system-monitor
|
||||
pkgs.gnome-tecla
|
||||
pkgs.gnome-weather
|
||||
pkgs.loupe
|
||||
pkgs.nautilus
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ in
|
|||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = with pkgs; [
|
||||
seatd
|
||||
sdnotify-wrapper
|
||||
];
|
||||
users.groups.seat = lib.mkIf (cfg.group == "seat") { };
|
||||
|
||||
|
|
@ -55,7 +54,7 @@ in
|
|||
Type = "notify";
|
||||
NotifyAccess = "all";
|
||||
SyslogIdentifier = "seatd";
|
||||
ExecStart = "${pkgs.sdnotify-wrapper}/bin/sdnotify-wrapper ${pkgs.seatd.bin}/bin/seatd -n 1 -u ${cfg.user} -g ${cfg.group} -l ${cfg.logLevel}";
|
||||
ExecStart = "${lib.getExe' pkgs.s6 "s6-notify-socket-from-fd"} ${pkgs.seatd.bin}/bin/seatd -n 1 -u ${cfg.user} -g ${cfg.group} -l ${cfg.logLevel}";
|
||||
RestartSec = 1;
|
||||
Restart = "always";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ in
|
|||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = [
|
||||
pkgs.cosmic-comp
|
||||
pkgs.cosmic-icons
|
||||
cfg.package
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,20 @@ let
|
|||
defaultSessionName = config.services.displayManager.defaultSession;
|
||||
|
||||
setSessionScript = pkgs.callPackage ../x11/display-managers/account-service-util.nix { };
|
||||
|
||||
greeterUsers = lib.genAttrs' [ null 1 2 3 4 ] (
|
||||
i:
|
||||
let
|
||||
# adding 1 to create `gdm-greeter{-2,-3,-4,-5}`
|
||||
suffix = lib.optionalString (i != null) "-${toString (i + 1)}";
|
||||
in
|
||||
lib.nameValuePair "gdm-greeter${suffix}" {
|
||||
isSystemUser = true;
|
||||
uid = 60578 + (if i == null then 0 else i);
|
||||
group = "gdm";
|
||||
home = "/run/gdm/home/gdm-greeter${suffix}";
|
||||
}
|
||||
);
|
||||
in
|
||||
|
||||
{
|
||||
|
|
@ -103,6 +117,12 @@ in
|
|||
[ "services" "xserver" "displayManager" "gdm" "autoLogin" "delay" ]
|
||||
[ "services" "displayManager" "gdm" "autoLogin" "delay" ]
|
||||
)
|
||||
(lib.mkRemovedOptionModule [
|
||||
"services"
|
||||
"displayManager"
|
||||
"gdm"
|
||||
"wayland"
|
||||
] "Disabling this option is no longer supported with GNOME 50.")
|
||||
];
|
||||
|
||||
meta = {
|
||||
|
|
@ -128,14 +148,6 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
wayland = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Allow GDM to run on Wayland instead of Xserver.
|
||||
'';
|
||||
};
|
||||
|
||||
autoSuspend = lib.mkOption {
|
||||
default = true;
|
||||
description = ''
|
||||
|
|
@ -188,6 +200,8 @@ in
|
|||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
warnings = lib.optional config.services.pulseaudio.enable "Support for Pulseaudio + gdm will be removed in NixOS 26.11";
|
||||
|
||||
services.xserver.displayManager.lightdm.enable = false;
|
||||
|
||||
users.users = lib.mkMerge [
|
||||
|
|
@ -198,24 +212,8 @@ in
|
|||
group = "gdm";
|
||||
description = "GDM user";
|
||||
};
|
||||
|
||||
gdm-greeter = {
|
||||
isSystemUser = true;
|
||||
uid = 60578;
|
||||
group = "gdm";
|
||||
home = "/run/gdm";
|
||||
};
|
||||
}
|
||||
|
||||
(lib.genAttrs' [ 1 2 3 4 ] (
|
||||
i:
|
||||
lib.nameValuePair "gdm-greeter-${toString i}" {
|
||||
isSystemUser = true;
|
||||
uid = 60578 + i;
|
||||
group = "gdm";
|
||||
home = "/run/gdm-${toString i}";
|
||||
}
|
||||
))
|
||||
greeterUsers
|
||||
];
|
||||
|
||||
users.groups.gdm.gid = config.ids.gids.gdm;
|
||||
|
|
@ -257,17 +255,20 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /run/gdm/.config 0711 gdm gdm"
|
||||
]
|
||||
++ lib.optionals config.services.pulseaudio.enable [
|
||||
"d /run/gdm/.config/pulse 0711 gdm gdm"
|
||||
"L+ /run/gdm/.config/pulse/${pulseConfig.name} - - - - ${pulseConfig}"
|
||||
]
|
||||
++ lib.optionals config.services.gnome.gnome-initial-setup.enable [
|
||||
# Create stamp file for gnome-initial-setup to prevent it starting in GDM.
|
||||
"f /run/gdm/.config/gnome-initial-setup-done 0711 gdm gdm - yes"
|
||||
];
|
||||
systemd.tmpfiles.rules =
|
||||
lib.optionals config.services.pulseaudio.enable (
|
||||
lib.concatLists (
|
||||
lib.mapAttrsToList (name: user: [
|
||||
"d ${user.home}/.config 0711 ${name} gdm"
|
||||
"d ${user.home}/.config/pulse 0711 ${name} gdm"
|
||||
"L+ ${user.home}/.config/pulse/${pulseConfig.name} - - - - ${pulseConfig}"
|
||||
]) greeterUsers
|
||||
)
|
||||
)
|
||||
++ lib.optionals config.services.gnome.gnome-initial-setup.enable [
|
||||
# Create stamp file for gnome-initial-setup to prevent it starting in GDM.
|
||||
"f /run/gdm/gdm.ran-initial-setup 0711 gdm gdm - yes"
|
||||
];
|
||||
|
||||
# Otherwise GDM will not be able to start correctly and display Wayland sessions
|
||||
systemd.packages = [
|
||||
|
|
@ -361,7 +362,6 @@ in
|
|||
# presented and there's a little delay.
|
||||
services.displayManager.gdm.settings = {
|
||||
daemon = lib.mkMerge [
|
||||
{ WaylandEnable = cfg.wayland; }
|
||||
# nested if else didn't work
|
||||
(lib.mkIf (config.services.displayManager.autoLogin.enable && cfg.autoLogin.delay != 0) {
|
||||
TimedLoginEnable = true;
|
||||
|
|
|
|||
|
|
@ -113,12 +113,12 @@ in
|
|||
{
|
||||
name = "nologin";
|
||||
control = "requisite";
|
||||
modulePath = "pam_nologin.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_nologin.so";
|
||||
}
|
||||
{
|
||||
name = "ly-normal-user";
|
||||
control = "required";
|
||||
modulePath = "pam_succeed_if.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_succeed_if.so";
|
||||
settings.quiet = true;
|
||||
args = lib.mkBefore [
|
||||
"uid"
|
||||
|
|
@ -129,7 +129,7 @@ in
|
|||
{
|
||||
name = "permit";
|
||||
control = "required";
|
||||
modulePath = "pam_permit.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_permit.so";
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -125,12 +125,12 @@ in
|
|||
{
|
||||
name = "nologin";
|
||||
control = "requisite";
|
||||
modulePath = "pam_nologin.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_nologin.so";
|
||||
}
|
||||
{
|
||||
name = "permit";
|
||||
control = "required";
|
||||
modulePath = "pam_permit.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_permit.so";
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ in
|
|||
# Load environment from /etc/environment and ~/.pam_environment
|
||||
name = "env";
|
||||
control = "required";
|
||||
modulePath = "pam_env.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_env.so";
|
||||
settings.conffile = "/etc/pam/environment";
|
||||
settings.readenv = 0;
|
||||
}
|
||||
|
|
@ -174,7 +174,7 @@ in
|
|||
# Always let the greeter start without authentication
|
||||
name = "permit";
|
||||
control = "required";
|
||||
modulePath = "pam_permit.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_permit.so";
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ in
|
|||
# No action required for account management
|
||||
name = "permit";
|
||||
control = "required";
|
||||
modulePath = "pam_permit.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_permit.so";
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ in
|
|||
# Can't change password
|
||||
name = "deny";
|
||||
control = "required";
|
||||
modulePath = "pam_deny.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_deny.so";
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ in
|
|||
# Setup session
|
||||
name = "unix";
|
||||
control = "required";
|
||||
modulePath = "pam_unix.so";
|
||||
modulePath = "${config.security.pam.package}/lib/security/pam_unix.so";
|
||||
}
|
||||
{
|
||||
name = "systemd";
|
||||
|
|
|
|||
|
|
@ -12,10 +12,7 @@ let
|
|||
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
sweber
|
||||
hexa
|
||||
];
|
||||
meta.maintainers = with lib.maintainers; [ hexa ];
|
||||
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ let
|
|||
};
|
||||
|
||||
credentials = {
|
||||
privateKey = "xyz.radicle.node.secret";
|
||||
privateKeyPassphrase = "xyz.radicle.node.passphrase";
|
||||
privateKey = "dev.radicle.node.secret";
|
||||
privateKeyPassphrase = "dev.radicle.node.passphrase";
|
||||
};
|
||||
|
||||
# Convenient wrapper to run `rad` in the namespaces of `radicle-node.service`
|
||||
|
|
|
|||
|
|
@ -155,8 +155,12 @@ in
|
|||
|
||||
wideArea = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Whether to enable wide-area service discovery.";
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable wide-area service discovery.
|
||||
|
||||
It is recommended to keep this options disabled as it exposes the system to `CVE-2024-52615`/`GHSA-x6vp-f33h-h32g`.
|
||||
'';
|
||||
};
|
||||
|
||||
reflector = lib.mkOption {
|
||||
|
|
@ -279,6 +283,10 @@ in
|
|||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
warnings = [
|
||||
(lib.mkIf cfg.wideArea "Enabling `services.avahi.wideArea` exposes this system to `CVE-2024-52615`.")
|
||||
];
|
||||
|
||||
users.users.avahi = {
|
||||
description = "avahi-daemon privilege separation user";
|
||||
home = "/var/empty";
|
||||
|
|
|
|||
|
|
@ -591,7 +591,7 @@ in
|
|||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
path = optionals (!config.services.resolved.enable) [ pkgs.openresolv ];
|
||||
path = [ pkgs.shadow ] ++ optionals (!config.services.resolved.enable) [ pkgs.openresolv ];
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${getExe client.wrapper} service run";
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ let
|
|||
in
|
||||
''
|
||||
# Can't use -u (unset) because api.sh uses API_URL before it is set
|
||||
set +u
|
||||
set -eo pipefail
|
||||
pihole="${lib.getExe pihole}"
|
||||
jq="${lib.getExe pkgs.jq}"
|
||||
|
|
@ -29,21 +30,24 @@ in
|
|||
if [ ! -f '${cfg.settings.files.gravity}' ]; then
|
||||
$pihole -g
|
||||
# Send SIGRTMIN to FTL, which makes it reload the database, opening the newly created one
|
||||
${lib.getExe' pkgs.procps "kill"} -s SIGRTMIN $(systemctl show --property MainPID --value ${config.systemd.services.pihole-ftl.name})
|
||||
${lib.getExe' pkgs.procps "kill"} -s SIGRTMIN "$(systemctl show --property MainPID --value ${config.systemd.services.pihole-ftl.name})"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source ${pihole}/share/pihole/advanced/Scripts/api.sh
|
||||
# shellcheck disable=SC1091
|
||||
source ${pihole}/share/pihole/advanced/Scripts/utils.sh
|
||||
|
||||
any_failed=0
|
||||
|
||||
addList() {
|
||||
local payload="$1"
|
||||
local payload="$1" result type error
|
||||
|
||||
echo "Adding list: $payload"
|
||||
local result=$(PostFTLData "lists" "$payload")
|
||||
type=$($jq -r '.type' <<< "$payload")
|
||||
result=$(PostFTLData "lists?type=$type" "$payload")
|
||||
|
||||
local error="$($jq '.error' <<< "$result")"
|
||||
error="$($jq '.error' <<< "$result")"
|
||||
if [[ "$error" != "null" ]]; then
|
||||
echo "Error: $error"
|
||||
any_failed=1
|
||||
|
|
@ -61,7 +65,7 @@ in
|
|||
echo "Added list ID $id: $result"
|
||||
}
|
||||
|
||||
for i in 1 2 3; do
|
||||
for _ in 1 2 3; do
|
||||
(TestAPIAvailability) && break
|
||||
echo "Retrying API shortly..."
|
||||
${lib.getExe' pkgs.coreutils "sleep"} .5s
|
||||
|
|
|
|||
|
|
@ -491,11 +491,18 @@ in
|
|||
|
||||
users.groups.${cfg.group} = { };
|
||||
|
||||
environment.etc."pihole/pihole.toml" = {
|
||||
source = settingsFile;
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
mode = "400";
|
||||
environment.etc = {
|
||||
"pihole/pihole.toml" = {
|
||||
source = settingsFile;
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
mode = "400";
|
||||
};
|
||||
|
||||
"pihole/versions".text = ''
|
||||
CORE_VERSION=${cfg.piholePackage.src.src.tag}
|
||||
FTL_VERSION=${cfg.package.src.tag}
|
||||
'';
|
||||
};
|
||||
|
||||
environment.systemPackages = [ cfg.pihole ];
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ let
|
|||
builtins.storeDir
|
||||
"/etc/"
|
||||
]
|
||||
++ cfg.extraConfigFiles
|
||||
++ lib.optional (cfg.secretsFile != null) cfg.secretsFile;
|
||||
DeviceAllow = "/dev/rfkill rw";
|
||||
LockPersonality = true;
|
||||
|
|
|
|||
542
nixos/modules/services/web-apps/lasuite-drive.nix
Normal file
542
nixos/modules/services/web-apps/lasuite-drive.nix
Normal file
|
|
@ -0,0 +1,542 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
utils,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
concatMapStringsSep
|
||||
concatStringsSep
|
||||
escapeShellArg
|
||||
getExe
|
||||
hasSuffix
|
||||
mapAttrs
|
||||
match
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkPackageOption
|
||||
mkOption
|
||||
types
|
||||
optional
|
||||
optionalString
|
||||
;
|
||||
|
||||
cfg = config.services.lasuite-drive;
|
||||
|
||||
pythonEnvironment = mapAttrs (
|
||||
_: value:
|
||||
if value == null then
|
||||
"None"
|
||||
else if value == true then
|
||||
"True"
|
||||
else if value == false then
|
||||
"False"
|
||||
else
|
||||
toString value
|
||||
) cfg.settings;
|
||||
|
||||
commonServiceConfig = {
|
||||
RuntimeDirectory = "lasuite-drive";
|
||||
StateDirectory = "lasuite-drive";
|
||||
WorkingDirectory = "/var/lib/lasuite-drive";
|
||||
|
||||
User = "lasuite-drive";
|
||||
DynamicUser = true;
|
||||
SupplementaryGroups = mkIf cfg.redis.createLocally [
|
||||
config.services.redis.servers.lasuite-drive.group
|
||||
];
|
||||
# hardening
|
||||
AmbientCapabilities = "";
|
||||
CapabilityBoundingSet = [ "" ];
|
||||
DevicePolicy = "closed";
|
||||
LockPersonality = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProtectSystem = "strict";
|
||||
RemoveIPC = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_UNIX"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
UMask = "0077";
|
||||
EnvironmentFile = cfg.environmentFiles;
|
||||
};
|
||||
|
||||
proxySuffix = if match "unix:.*" cfg.bind != null then ":" else "";
|
||||
|
||||
# Convert environment variables to be used as systemd-run arguments
|
||||
envArgs = lib.concatStringsSep " " (
|
||||
lib.mapAttrsToList (name: value: "-E ${escapeShellArg "${name}=${value}"}") pythonEnvironment
|
||||
);
|
||||
|
||||
# Easier usage of django manage.py stuff
|
||||
manage = pkgs.writeShellScriptBin "lasuite-drive-manage" ''
|
||||
exec ${lib.getExe' config.systemd.package "systemd-run"} \
|
||||
-p User=${commonServiceConfig.User} \
|
||||
-p DynamicUser=yes \
|
||||
-p StateDirectory=${commonServiceConfig.StateDirectory} \
|
||||
${optionalString cfg.redis.createLocally "-p SupplementaryGroups=${config.services.redis.servers.lasuite-drive.group} \\"}
|
||||
${concatMapStringsSep "\n" (envFile: "-p EnvironmentFile=${envFile} \\") cfg.environmentFiles}
|
||||
--working-directory=${commonServiceConfig.WorkingDirectory} \
|
||||
--quiet --collect --pipe --pty \
|
||||
${envArgs} ${lib.getExe cfg.package} "$@"
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.services.lasuite-drive = {
|
||||
enable = mkEnableOption "SuiteNumérique Drive";
|
||||
|
||||
package = mkPackageOption pkgs "lasuite-drive" { };
|
||||
|
||||
bind = mkOption {
|
||||
type = types.str;
|
||||
default = "unix:/run/lasuite-drive/gunicorn.sock";
|
||||
example = "127.0.0.1:8000";
|
||||
description = ''
|
||||
The path, host/port or file descriptior to bind the gunicorn socket to.
|
||||
|
||||
See <https://docs.gunicorn.org/en/stable/settings.html#bind> for possible options.
|
||||
'';
|
||||
};
|
||||
|
||||
enableNginx = mkEnableOption "enable and configure Nginx for reverse proxying" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
secretKeyPath = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = ''
|
||||
Path to the Django secret key.
|
||||
|
||||
The key can be generated using:
|
||||
```
|
||||
python3 -c 'import secrets; print(secrets.token_hex())'
|
||||
```
|
||||
|
||||
:::{.note}
|
||||
If not specified, a secret key is automatically generated and stored in the state directory.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
s3Url = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
URL of the S3 bucket.
|
||||
'';
|
||||
};
|
||||
|
||||
postgresql = {
|
||||
createLocally = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Configure local PostgreSQL database server for drive.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
redis = {
|
||||
createLocally = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Configure local Redis cache server for drive.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
gunicorn = {
|
||||
extraArgs = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"--name=drive"
|
||||
"--workers=3"
|
||||
];
|
||||
description = ''
|
||||
Extra arguments to pass to the gunicorn process.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
celery = {
|
||||
extraArgs = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Extra arguments to pass to the celery process.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Domain name of the drive instance.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = types.attrsOf (
|
||||
types.nullOr (
|
||||
types.oneOf [
|
||||
types.str
|
||||
types.bool
|
||||
types.path
|
||||
types.int
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
options = {
|
||||
DJANGO_CONFIGURATION = mkOption {
|
||||
type = types.str;
|
||||
internal = true;
|
||||
default = "Production";
|
||||
description = "The configuration that Django will use";
|
||||
};
|
||||
|
||||
DJANGO_SETTINGS_MODULE = mkOption {
|
||||
type = types.str;
|
||||
internal = true;
|
||||
default = "drive.settings";
|
||||
description = "The configuration module that Django will use";
|
||||
};
|
||||
|
||||
DJANGO_SECRET_KEY_FILE = mkOption {
|
||||
type = types.path;
|
||||
default =
|
||||
if cfg.secretKeyPath == null then "/var/lib/lasuite-drive/django_secret_key" else cfg.secretKeyPath;
|
||||
description = "The path to the file containing Django's secret key";
|
||||
};
|
||||
|
||||
DATA_DIR = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/lasuite-drive";
|
||||
description = "Path to the data directory";
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
DJANGO_ALLOWED_HOSTS = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default =
|
||||
if cfg.enableNginx then
|
||||
[
|
||||
"localhost"
|
||||
"127.0.0.1"
|
||||
cfg.domain
|
||||
]
|
||||
else
|
||||
[ ];
|
||||
defaultText = lib.literalExpression ''
|
||||
if cfg.enableNginx then [ "localhost" "127.0.0.1" cfg.domain ] else [ ]
|
||||
'';
|
||||
apply = list: concatStringsSep "," list;
|
||||
description = "Comma-separated list of hosts that are able to connect to the server";
|
||||
};
|
||||
|
||||
DB_NAME = mkOption {
|
||||
type = types.str;
|
||||
default = "lasuite-drive";
|
||||
description = "Name of the database";
|
||||
};
|
||||
|
||||
DB_USER = mkOption {
|
||||
type = types.str;
|
||||
default = "lasuite-drive";
|
||||
description = "User of the database";
|
||||
};
|
||||
|
||||
DB_HOST = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = if cfg.postgresql.createLocally then "/run/postgresql" else null;
|
||||
description = "Host of the database";
|
||||
};
|
||||
|
||||
REDIS_URL = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default =
|
||||
if cfg.redis.createLocally then
|
||||
"unix://${config.services.redis.servers.lasuite-drive.unixSocket}?db=1"
|
||||
else
|
||||
null;
|
||||
description = "URL of the redis backend";
|
||||
};
|
||||
|
||||
CELERY_BROKER_URL = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default =
|
||||
if cfg.redis.createLocally then
|
||||
"redis+socket://${config.services.redis.servers.lasuite-drive.unixSocket}?db=2"
|
||||
else
|
||||
null;
|
||||
description = "URL of the redis backend for celery";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
example = ''
|
||||
{
|
||||
AWS_S3_ENDPOINT_URL = "https://s3.us-west.amazonaws.com";
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Configuration options of drive.
|
||||
|
||||
See <https://github.com/suitenumerique/drive/blob/v${cfg.package.version}/docs/env.md>
|
||||
|
||||
`REDIS_URL` and `CELERY_BROKER_URL` are set if `services.lasuite-drive.redis.createLocally` is true.
|
||||
`DB_HOST` is set if `services.lasuite-drive.postgresql.createLocally` is true.
|
||||
'';
|
||||
};
|
||||
|
||||
environmentFiles = mkOption {
|
||||
type = types.listOf types.path;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Path to environment files.
|
||||
|
||||
This can be useful to pass secrets to drive via tools like `agenix` or `sops`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
warnings = mkIf (cfg.enableNginx && !(hasSuffix "/" cfg.s3Url)) [
|
||||
''
|
||||
services.lasuite-drive.s3Url should end with a trailing slash (/).
|
||||
This could break the HTTP requests by nginx to the S3 backend.
|
||||
''
|
||||
];
|
||||
|
||||
environment.systemPackages = [ manage ];
|
||||
|
||||
systemd.services.lasuite-drive = {
|
||||
description = "Drive from SuiteNumérique";
|
||||
after = [
|
||||
"network-online.target"
|
||||
]
|
||||
++ (optional cfg.postgresql.createLocally "postgresql.service")
|
||||
++ (optional cfg.redis.createLocally "redis-lasuite-drive.service");
|
||||
wants =
|
||||
(optional cfg.postgresql.createLocally "postgresql.service")
|
||||
++ (optional cfg.redis.createLocally "redis-lasuite-drive.service");
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
preStart = ''
|
||||
if [ ! -f .version ]; then
|
||||
touch .version
|
||||
fi
|
||||
|
||||
${optionalString (cfg.secretKeyPath == null) ''
|
||||
if [[ ! -f /var/lib/lasuite-drive/django_secret_key ]]; then
|
||||
(
|
||||
umask 0377
|
||||
tr -dc A-Za-z0-9 < /dev/urandom | head -c64 | ${pkgs.moreutils}/bin/sponge /var/lib/lasuite-drive/django_secret_key
|
||||
)
|
||||
fi
|
||||
''}
|
||||
if [ "${cfg.package.version}" != "$(cat .version)" ]; then
|
||||
${getExe cfg.package} migrate
|
||||
echo -n "${cfg.package.version}" > .version
|
||||
fi
|
||||
'';
|
||||
|
||||
environment = pythonEnvironment;
|
||||
|
||||
serviceConfig = {
|
||||
BindReadOnlyPaths = "${cfg.package}/share/static:/var/lib/lasuite-drive/static";
|
||||
|
||||
ExecStart = utils.escapeSystemdExecArgs (
|
||||
[
|
||||
(lib.getExe' cfg.package "gunicorn")
|
||||
"--bind=${cfg.bind}"
|
||||
]
|
||||
++ cfg.gunicorn.extraArgs
|
||||
++ [ "drive.wsgi:application" ]
|
||||
);
|
||||
}
|
||||
// commonServiceConfig;
|
||||
};
|
||||
|
||||
systemd.services.lasuite-drive-celery = {
|
||||
description = "Docs Celery broker from SuiteNumérique";
|
||||
after = [
|
||||
"network-online.target"
|
||||
]
|
||||
++ (optional cfg.postgresql.createLocally "postgresql.service")
|
||||
++ (optional cfg.redis.createLocally "redis-lasuite-drive.service");
|
||||
wants =
|
||||
(optional cfg.postgresql.createLocally "postgresql.service")
|
||||
++ (optional cfg.redis.createLocally "redis-lasuite-drive.service");
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
environment = pythonEnvironment;
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = utils.escapeSystemdExecArgs (
|
||||
[ (lib.getExe' cfg.package "celery") ]
|
||||
++ cfg.celery.extraArgs
|
||||
++ [
|
||||
"--app=drive.celery_app"
|
||||
"worker"
|
||||
]
|
||||
);
|
||||
}
|
||||
// commonServiceConfig;
|
||||
};
|
||||
|
||||
systemd.services.lasuite-drive-beat = {
|
||||
description = "Docs Celery beat from SuiteNumérique";
|
||||
after = [
|
||||
"network-online.target"
|
||||
]
|
||||
++ (optional cfg.postgresql.createLocally "postgresql.service")
|
||||
++ (optional cfg.redis.createLocally "redis-lasuite-drive.service");
|
||||
wants =
|
||||
(optional cfg.postgresql.createLocally "postgresql.service")
|
||||
++ (optional cfg.redis.createLocally "redis-lasuite-drive.service");
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
environment = pythonEnvironment;
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = utils.escapeSystemdExecArgs (
|
||||
[ (lib.getExe' cfg.package "celery") ]
|
||||
++ cfg.celery.extraArgs
|
||||
++ [
|
||||
"--app=drive.celery_app"
|
||||
"beat"
|
||||
]
|
||||
);
|
||||
}
|
||||
// commonServiceConfig;
|
||||
};
|
||||
|
||||
services.postgresql = mkIf cfg.postgresql.createLocally {
|
||||
enable = true;
|
||||
ensureDatabases = [ "lasuite-drive" ];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "lasuite-drive";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
services.redis.servers.lasuite-drive = mkIf cfg.redis.createLocally { enable = true; };
|
||||
|
||||
services.nginx = mkIf cfg.enableNginx {
|
||||
enable = true;
|
||||
|
||||
virtualHosts.${cfg.domain} = {
|
||||
extraConfig = ''
|
||||
error_page 401 /401.html;
|
||||
error_page 403 /403.html;
|
||||
error_page 404 /404.html;
|
||||
'';
|
||||
|
||||
root = cfg.package.frontend;
|
||||
|
||||
locations."/" = {
|
||||
tryFiles = "$uri $uri.html index.html $uri/ =404";
|
||||
};
|
||||
|
||||
locations."~ '^/explorer/items/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/?$'" =
|
||||
{
|
||||
tryFiles = "$uri /explorer/items/[id].html";
|
||||
};
|
||||
|
||||
locations."~ '^/explorer/items/files/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/?$'" =
|
||||
{
|
||||
tryFiles = "$uri /explorer/items/files/[id].html";
|
||||
};
|
||||
|
||||
locations."~ '^/wopi/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/?$'" = {
|
||||
tryFiles = "$uri /wopi/[id].html";
|
||||
};
|
||||
|
||||
locations."/static/" = {
|
||||
alias = "${cfg.package}/share/static/";
|
||||
};
|
||||
|
||||
locations."/api" = {
|
||||
proxyPass = "http://${cfg.bind}";
|
||||
recommendedProxySettings = true;
|
||||
};
|
||||
|
||||
locations."/admin" = {
|
||||
proxyPass = "http://${cfg.bind}";
|
||||
recommendedProxySettings = true;
|
||||
};
|
||||
|
||||
locations."/media-auth" = {
|
||||
proxyPass = "http://${cfg.bind}${proxySuffix}/api/v1.0/items/media-auth/";
|
||||
recommendedProxySettings = true;
|
||||
extraConfig = ''
|
||||
proxy_set_header X-Original-URL $request_uri;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Original-Method $request_method;
|
||||
'';
|
||||
};
|
||||
|
||||
locations."/media/" = {
|
||||
proxyPass = cfg.s3Url;
|
||||
extraConfig = ''
|
||||
auth_request /media-auth;
|
||||
auth_request_set $authHeader $upstream_http_authorization;
|
||||
auth_request_set $authDate $upstream_http_x_amz_date;
|
||||
auth_request_set $authContentSha256 $upstream_http_x_amz_content_sha256;
|
||||
|
||||
proxy_set_header Authorization $authHeader;
|
||||
proxy_set_header X-Amz-Date $authDate;
|
||||
proxy_set_header X-Amz-Content-SHA256 $authContentSha256;
|
||||
|
||||
add_header Content-Disposition "attachment";
|
||||
'';
|
||||
};
|
||||
|
||||
locations."/media/preview/" = {
|
||||
proxyPass = cfg.s3Url;
|
||||
extraConfig = ''
|
||||
auth_request /media-auth;
|
||||
auth_request_set $authHeader $upstream_http_authorization;
|
||||
auth_request_set $authDate $upstream_http_x_amz_date;
|
||||
auth_request_set $authContentSha256 $upstream_http_x_amz_content_sha256;
|
||||
|
||||
proxy_set_header Authorization $authHeader;
|
||||
proxy_set_header X-Amz-Date $authDate;
|
||||
proxy_set_header X-Amz-Content-SHA256 $authContentSha256;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
meta = {
|
||||
buildDocsInSandbox = false;
|
||||
maintainers = [ lib.maintainers.soyouzpanda ];
|
||||
};
|
||||
}
|
||||
|
|
@ -145,9 +145,11 @@ let
|
|||
pkgs.writeShellScriptBin "mastodon-tootctl" ''
|
||||
set -a
|
||||
export RAILS_ROOT="${cfg.package}"
|
||||
export HOME="/var/lib/mastodon"
|
||||
source "${envFile}"
|
||||
source /var/lib/mastodon/.secrets_env
|
||||
${sourceExtraEnv}
|
||||
cd /var/lib/mastodon
|
||||
|
||||
sudo=exec
|
||||
if [[ "$USER" != ${cfg.user} ]]; then
|
||||
|
|
|
|||
|
|
@ -149,7 +149,10 @@ in
|
|||
systemd.services.miniflux = {
|
||||
description = "Miniflux service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
requires = lib.optional cfg.createDatabaseLocally "miniflux-dbsetup.service";
|
||||
requires = lib.optionals cfg.createDatabaseLocally [
|
||||
"miniflux-dbsetup.service"
|
||||
"postgresql.target"
|
||||
];
|
||||
after = [
|
||||
"network.target"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -68,6 +68,11 @@ It requires elevated permissions to become the `nextcloud` user. Given the way t
|
|||
escalation is implemented, parameters passed via the environment to Nextcloud are
|
||||
currently ignored, except for `OC_PASS` and `NC_PASS`.
|
||||
|
||||
::: {.warning}
|
||||
When Polkit is enabled, the command being executed by `nextcloud-occ` might be logged
|
||||
into the system's journal. Be careful to not leak secrets that way!
|
||||
:::
|
||||
|
||||
Custom service units that need to run `nextcloud-occ` either need elevated privileges
|
||||
or the systemd configuration from `nextcloud-setup.service` (recommended):
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@ in
|
|||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.etc."pihole/versions".text = "WEB_VERSION=${cfg.package.src.tag}";
|
||||
|
||||
services.pihole-ftl.settings.webserver = {
|
||||
domain = cfg.hostName;
|
||||
port = cfg.ports;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
lib.types.enum [
|
||||
"modern"
|
||||
"intermediate"
|
||||
"old"
|
||||
]
|
||||
);
|
||||
default = null;
|
||||
|
|
@ -28,10 +27,6 @@
|
|||
: General-purpose servers with a variety of clients, recommended for
|
||||
almost all systems
|
||||
|
||||
old
|
||||
: Compatible with a number of very old clients, & should be used only as
|
||||
a last resort
|
||||
|
||||
The default for all virtual hosts can be set with
|
||||
services.h2o.defaultTLSRecommendations, but this value can be overridden
|
||||
on a per-host basis using services.h2o.hosts.<name>.tls.recommmendations.
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@ let
|
|||
# other settings with the tests @
|
||||
# `nixos/tests/web-servers/h2o/tls-recommendations.nix`
|
||||
# & run with `nix-build -A nixosTests.h2o.tls-recommendations`
|
||||
version = "5.7";
|
||||
git_tag = "v5.7.1";
|
||||
version = "6.0";
|
||||
git_tag = "v6.0";
|
||||
guidelinesJSON =
|
||||
lib.pipe
|
||||
{
|
||||
|
|
@ -93,7 +93,7 @@ let
|
|||
"https://ssl-config.mozilla.org/guidelines/${version}.json"
|
||||
"https://raw.githubusercontent.com/mozilla/ssl-config-generator/refs/tags/${git_tag}/src/static/guidelines/${version}.json"
|
||||
];
|
||||
sha256 = "sha256:1mj2pcb1hg7q2wpgdq3ac8pc2q64wvwvwlkb9xjmdd9jm4hiyny7";
|
||||
sha256 = "sha256-aHdzLNPo4c6jlbS+Fg3R0X5VcdPKtUky0oX5Q7Y94SQ=";
|
||||
}
|
||||
[
|
||||
pkgs.fetchurl
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ in
|
|||
|
||||
proxy_pass http://unix:${cfgAuth.socketPath};
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
|
||||
# Upstream uses $http_host here, but we are using gixy to check nginx configurations
|
||||
# gixy wants us to use $host: https://github.com/yandex/gixy/blob/master/docs/en/plugins/hostspoofing.md
|
||||
|
|
|
|||
|
|
@ -18,12 +18,9 @@ let
|
|||
cfg = config.services.vinyl-cache;
|
||||
|
||||
# Vinyl Cache has very strong opinions and very complicated code around handling
|
||||
# the stateDir. After a lot of back and forth, we decided that we a)
|
||||
# do not want a configurable option here, as most of the handling depends
|
||||
# on the version and the compile time options. Putting everything into
|
||||
# /var/run (RAM backed) is absolutely recommended by Vinyl Cache anyways.
|
||||
# We do need to pay attention to the version-dependend variations, though!
|
||||
stateDir = "/var/run/vinyld";
|
||||
# the stateDir. After a lot of back and forth, we decided to set the stateDir
|
||||
# at compile time and let the package expose the particular path as passthru.
|
||||
stateDir = cfg.package.stateDir;
|
||||
|
||||
# from --help:
|
||||
# -a [<name>=]address[:port][,proto] # HTTP listen address and port
|
||||
|
|
@ -183,13 +180,13 @@ in
|
|||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = "${cfg.package}/bin/vinyld ${commandLineAddresses} -n ${stateDir} -F ${cfg.extraCommandLine} ${commandLine}";
|
||||
ExecStart = "${cfg.package}/bin/vinyld ${commandLineAddresses} -F ${cfg.extraCommandLine} ${commandLine}";
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
User = "vinyl-cache";
|
||||
Group = "vinyl-cache";
|
||||
DynamicUser = true;
|
||||
RuntimeDirectory = lib.removePrefix "/var/run/" stateDir;
|
||||
RuntimeDirectory = lib.removePrefix "/run/" stateDir;
|
||||
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
||||
NoNewPrivileges = true;
|
||||
LimitNOFILE = 131072;
|
||||
|
|
@ -221,6 +218,10 @@ in
|
|||
assertion = cfg.package.pname == "vinyl-cache";
|
||||
message = "services.vinyl-cache only supports Vinyl Cache. Please use services.varnish.";
|
||||
}
|
||||
{
|
||||
assertion = lib.strings.hasPrefix "/run/" stateDir;
|
||||
message = "The vinyl-cache NixOS mosule only supports statedirs in /run/, but vinyl-cache package was compiled with ${stateDir}.";
|
||||
}
|
||||
];
|
||||
})
|
||||
(lib.mkIf (cfg.enable && cfg.enableFileLogging) {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ let
|
|||
|
||||
${config.system.systemBuilderCommands}
|
||||
|
||||
cp "$extraDependenciesPath" "$out/extra-dependencies"
|
||||
printf "%s " "''${extraDependencies[@]}" > "$out/extra-dependencies"
|
||||
|
||||
${optionalString (!config.boot.isContainer && config.boot.bootspec.enable) ''
|
||||
${config.boot.bootspec.writer}
|
||||
|
|
@ -60,7 +60,6 @@ let
|
|||
name = "nixos-system-${config.system.name}-${config.system.nixos.label}";
|
||||
preferLocalBuild = true;
|
||||
allowSubstitutes = false;
|
||||
passAsFile = [ "extraDependencies" ];
|
||||
buildCommand = systemBuilder;
|
||||
|
||||
systemd = config.systemd.package;
|
||||
|
|
@ -70,6 +69,9 @@ let
|
|||
inherit (config.system) extraDependencies;
|
||||
}
|
||||
// config.system.systemBuilderArgs
|
||||
// {
|
||||
__structuredAttrs = true;
|
||||
}
|
||||
);
|
||||
|
||||
# Handle assertions and warnings
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue