Merge branch 'master' into sqlfluff-bump
This commit is contained in:
@@ -19,6 +19,8 @@ name: Codeowners v2
|
||||
#
|
||||
# This split is done because checking code owners requires handling untrusted PR input,
|
||||
# while requesting code owners requires PR write access, and those shouldn't be mixed.
|
||||
#
|
||||
# Note that the latter is also used for ./eval.yml requesting reviewers.
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
@@ -104,6 +106,6 @@ jobs:
|
||||
run: nix-build ci -A requestReviews
|
||||
|
||||
- name: Request reviews
|
||||
run: result/bin/request-reviews.sh ${{ github.repository }} ${{ github.event.number }} "$OWNERS_FILE"
|
||||
run: result/bin/request-code-owner-reviews.sh ${{ github.repository }} ${{ github.event.number }} "$OWNERS_FILE"
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
@@ -133,6 +133,7 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ needs.get-merge-commit.outputs.mergedSha }}
|
||||
fetch-depth: 2
|
||||
path: nixpkgs
|
||||
|
||||
- name: Install Nix
|
||||
@@ -194,12 +195,18 @@ jobs:
|
||||
- name: Compare against the base branch
|
||||
if: steps.baseRunId.outputs.baseRunId
|
||||
run: |
|
||||
nix-build nixpkgs/ci -A eval.compare \
|
||||
git -C nixpkgs worktree add ../base ${{ needs.attrs.outputs.baseSha }}
|
||||
git -C nixpkgs diff --name-only ${{ needs.attrs.outputs.baseSha }} ${{ needs.attrs.outputs.mergedSha }} \
|
||||
| jq --raw-input --slurp 'split("\n")[:-1]' > touched-files.json
|
||||
|
||||
# Use the base branch to get accurate maintainer info
|
||||
nix-build base/ci -A eval.compare \
|
||||
--arg beforeResultDir ./baseResult \
|
||||
--arg afterResultDir ./prResult \
|
||||
--arg touchedFilesJson ./touched-files.json \
|
||||
-o comparison
|
||||
|
||||
cat comparison/step-summary.md >> "$GITHUB_STEP_SUMMARY"
|
||||
# TODO: Request reviews from maintainers for packages whose files are modified in the PR
|
||||
|
||||
- name: Upload the combined results
|
||||
if: steps.baseRunId.outputs.baseRunId
|
||||
@@ -212,19 +219,42 @@ jobs:
|
||||
tag:
|
||||
name: Tag
|
||||
runs-on: ubuntu-latest
|
||||
needs: process
|
||||
needs: [ attrs, process ]
|
||||
if: needs.process.outputs.baseRunId
|
||||
permissions:
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
steps:
|
||||
# See ./codeowners-v2.yml, reuse the same App because we need the same permissions
|
||||
# Can't use the token received from permissions above, because it can't get enough permissions
|
||||
- uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.OWNER_APP_ID }}
|
||||
private-key: ${{ secrets.OWNER_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Download process result
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: comparison
|
||||
path: comparison
|
||||
|
||||
- name: Tagging pull request
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30
|
||||
|
||||
# Important: This workflow job runs with extra permissions,
|
||||
# so we need to make sure to not run untrusted code from PRs
|
||||
- name: Check out Nixpkgs at the base commit
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ needs.attrs.outputs.baseSha }}
|
||||
path: base
|
||||
sparse-checkout: ci
|
||||
|
||||
- name: Build the requestReviews derivation
|
||||
run: nix-build base/ci -A requestReviews
|
||||
|
||||
- name: Labelling pull request
|
||||
run: |
|
||||
# Get all currently set rebuild labels
|
||||
gh api \
|
||||
@@ -252,11 +282,26 @@ jobs:
|
||||
/repos/"$REPOSITORY"/issues/"$NUMBER"/labels \
|
||||
-f "labels[]=$toAdd"
|
||||
done < <(comm -13 before after)
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
NUMBER: ${{ github.event.number }}
|
||||
|
||||
- name: Requesting maintainer reviews
|
||||
run: |
|
||||
# maintainers.json contains GitHub IDs. Look up handles to request reviews from.
|
||||
# There appears to be no API to request reviews based on GitHub IDs
|
||||
jq -r 'keys[]' comparison/maintainers.json \
|
||||
| while read -r id; do gh api /user/"$id" --jq .login; done \
|
||||
| GH_TOKEN=${{ steps.app-token.outputs.token }} result/bin/request-reviewers.sh "$REPOSITORY" "$NUMBER" "$AUTHOR"
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
NUMBER: ${{ github.event.number }}
|
||||
AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
|
||||
- name: Add eval summary to commit statuses
|
||||
if: ${{ github.event_name == 'pull_request_target' }}
|
||||
run: |
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
name: "No channel PR"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
# Re-run should be triggered when the base branch is updated, instead of silently failing
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
branches:
|
||||
- 'nixos-**'
|
||||
- 'nixpkgs-**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
fail:
|
||||
permissions:
|
||||
contents: none
|
||||
name: "This PR is is targeting a channel branch"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
name: "Set pending OfBorg status"
|
||||
on:
|
||||
pull_request_target:
|
||||
|
||||
# Sets the ofborg-eval status to "pending" to signal that we are waiting for
|
||||
# OfBorg even if it is running late. The status will be overwritten by OfBorg
|
||||
# once it starts evaluation.
|
||||
|
||||
# WARNING:
|
||||
# When extending this action, be aware that $GITHUB_TOKEN allows (restricted) write access to
|
||||
# the GitHub repository. This means that it should not evaluate user input in a
|
||||
# way that allows code injection.
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
action:
|
||||
name: set-ofborg-pending
|
||||
if: github.repository_owner == 'NixOS'
|
||||
permissions:
|
||||
statuses: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Set pending OfBorg status"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
curl \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
||||
-d '{"context": "ofborg-eval", "state": "pending", "description": "Waiting for OfBorg..."}' \
|
||||
"https://api.github.com/repos/NixOS/nixpkgs/commits/${{ github.event.pull_request.head.sha }}/statuses"
|
||||
@@ -33,8 +33,6 @@ jobs:
|
||||
max-parallel: 1
|
||||
matrix:
|
||||
pairs:
|
||||
- from: master
|
||||
into: haskell-updates
|
||||
- from: release-24.11
|
||||
into: staging-next-24.11
|
||||
- from: staging-next-24.11
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# This action periodically merges a merge base of master and staging into haskell-updates.
|
||||
#
|
||||
# haskell-updates is based on master (so there are little unrelated failures and the cache
|
||||
# is already prepopulated), but needs to target staging due to the high amount of rebuilds
|
||||
# it typically causes. To prevent unrelated commits clattering the GitHub UI, we need to
|
||||
# take care to only merge the merge-base of master and staging into haskell-updates.
|
||||
#
|
||||
# See also https://github.com/NixOS/nixpkgs/issues/361143.
|
||||
|
||||
name: "Periodic Merges (haskell-updates)"
|
||||
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# * is a special character in YAML so you have to quote this string
|
||||
# Merge every 24 hours
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
periodic-merge:
|
||||
permissions:
|
||||
contents: write # for devmasx/merge-branch to merge branches
|
||||
pull-requests: write # for peter-evans/create-or-update-comment to create or update comment
|
||||
if: github.repository_owner == 'NixOS'
|
||||
runs-on: ubuntu-latest
|
||||
name: git merge-base master staging → haskell-updates
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Note: If we want to do something similar for more branches, we can move this into a
|
||||
# separate job, so we can use the matrix strategy again.
|
||||
- name: Find merge base of master and staging
|
||||
id: find_merge_base_step
|
||||
run: |
|
||||
merge_base="$(git merge-base refs/remotes/origin/master refs/remotes/origin/staging)"
|
||||
echo "Found merge base: $merge_base" >&2
|
||||
echo "merge_base=$merge_base" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: git merge-base master staging → haskell-updates
|
||||
uses: devmasx/merge-branch@854d3ac71ed1e9deb668e0074781b81fdd6e771f # 1.4.0
|
||||
with:
|
||||
type: now
|
||||
head_to_merge: ${{ steps.find_merge_base_step.outputs.merge_base }}
|
||||
target_branch: haskell-updates
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Comment on failure
|
||||
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
issue-number: 367709
|
||||
body: |
|
||||
Periodic merge from `${{ steps.find_merge_base_step.outputs.merge_base }}` into `haskell-updates` has [failed](https://github.com/NixOS/nixpkgs/actions/runs/${{ github.run_id }}).
|
||||
@@ -10,6 +10,9 @@ Fiona Behrens <me@kloenk.dev>
|
||||
Fiona Behrens <me@kloenk.dev> <me@kloenk.de>
|
||||
goatastronaut0212 <goatastronaut0212@outlook.com> <goatastronaut0212@proton.me>
|
||||
Janne Heß <janne@hess.ooo> <dasJ@users.noreply.github.com>
|
||||
jopejoe1 <nixpkgs@missing.ninja>
|
||||
jopejoe1 <nixpkgs@missing.ninja> <johannes@joens.email>
|
||||
jopejoe1 <nixpkgs@missing.ninja> <34899572+jopejoe1@users.noreply.github.com>
|
||||
Jörg Thalheim <joerg@thalheim.io> <Mic92@users.noreply.github.com>
|
||||
Lin Jian <me@linj.tech> <linj.dev@outlook.com>
|
||||
Lin Jian <me@linj.tech> <75130626+jian-lin@users.noreply.github.com>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2003-2024 Eelco Dolstra and the Nixpkgs/NixOS contributors
|
||||
Copyright (c) 2003-2025 Eelco Dolstra and the Nixpkgs/NixOS contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
writeText,
|
||||
...
|
||||
}:
|
||||
{ beforeResultDir, afterResultDir }:
|
||||
{
|
||||
beforeResultDir,
|
||||
afterResultDir,
|
||||
touchedFilesJson,
|
||||
}:
|
||||
let
|
||||
/*
|
||||
Derivation that computes which packages are affected (added, changed or removed) between two revisions of nixpkgs.
|
||||
@@ -77,11 +81,11 @@ let
|
||||
# - values: lists of `packagePlatformPath`s
|
||||
diffAttrs = diff beforeAttrs afterAttrs;
|
||||
|
||||
rebuilds = uniqueStrings (diffAttrs.added ++ diffAttrs.changed);
|
||||
rebuildsPackagePlatformAttrs = convertToPackagePlatformAttrs rebuilds;
|
||||
|
||||
changed-paths =
|
||||
let
|
||||
rebuilds = uniqueStrings (diffAttrs.added ++ diffAttrs.changed);
|
||||
rebuildsPackagePlatformAttrs = convertToPackagePlatformAttrs rebuilds;
|
||||
|
||||
rebuildsByPlatform = groupByPlatform rebuildsPackagePlatformAttrs;
|
||||
rebuildsByKernel = groupByKernel rebuildsPackagePlatformAttrs;
|
||||
rebuildCountByKernel = lib.mapAttrs (
|
||||
@@ -104,10 +108,17 @@ let
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
maintainers = import ./maintainers.nix {
|
||||
changedattrs = lib.unique (map (a: a.packagePath) rebuildsPackagePlatformAttrs);
|
||||
changedpathsjson = touchedFilesJson;
|
||||
};
|
||||
in
|
||||
runCommand "compare"
|
||||
{
|
||||
nativeBuildInputs = [ jq ];
|
||||
maintainers = builtins.toJSON maintainers;
|
||||
passAsFile = [ "maintainers" ];
|
||||
}
|
||||
''
|
||||
mkdir $out
|
||||
@@ -115,5 +126,8 @@ runCommand "compare"
|
||||
cp ${changed-paths} $out/changed-paths.json
|
||||
|
||||
jq -r -f ${./generate-step-summary.jq} < ${changed-paths} > $out/step-summary.md
|
||||
|
||||
cp "$maintainersPath" "$out/maintainers.json"
|
||||
|
||||
# TODO: Compare eval stats
|
||||
''
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
# Almost directly vendored from https://github.com/NixOS/ofborg/blob/5a4e743f192fb151915fcbe8789922fa401ecf48/ofborg/src/maintainers.nix
|
||||
{ changedattrs, changedpathsjson }:
|
||||
let
|
||||
pkgs = import ../../.. {
|
||||
system = "x86_64-linux";
|
||||
config = { };
|
||||
overlays = [ ];
|
||||
};
|
||||
inherit (pkgs) lib;
|
||||
|
||||
changedpaths = builtins.fromJSON (builtins.readFile changedpathsjson);
|
||||
|
||||
anyMatchingFile =
|
||||
filename:
|
||||
let
|
||||
matching = builtins.filter (changed: lib.strings.hasSuffix changed filename) changedpaths;
|
||||
in
|
||||
(builtins.length matching) > 0;
|
||||
|
||||
anyMatchingFiles = files: (builtins.length (builtins.filter anyMatchingFile files)) > 0;
|
||||
|
||||
enrichedAttrs = builtins.map (path: {
|
||||
path = path;
|
||||
name = builtins.concatStringsSep "." path;
|
||||
}) changedattrs;
|
||||
|
||||
validPackageAttributes = builtins.filter (
|
||||
pkg:
|
||||
if (lib.attrsets.hasAttrByPath pkg.path pkgs) then
|
||||
(
|
||||
if (builtins.tryEval (lib.attrsets.attrByPath pkg.path null pkgs)).success then
|
||||
true
|
||||
else
|
||||
builtins.trace "Failed to access ${pkg.name} even though it exists" false
|
||||
)
|
||||
else
|
||||
builtins.trace "Failed to locate ${pkg.name}." false
|
||||
) enrichedAttrs;
|
||||
|
||||
attrsWithPackages = builtins.map (
|
||||
pkg: pkg // { package = lib.attrsets.attrByPath pkg.path null pkgs; }
|
||||
) validPackageAttributes;
|
||||
|
||||
attrsWithMaintainers = builtins.map (
|
||||
pkg: pkg // { maintainers = (pkg.package.meta or { }).maintainers or [ ]; }
|
||||
) attrsWithPackages;
|
||||
|
||||
attrsWeCanPing = builtins.filter (
|
||||
pkg:
|
||||
if (builtins.length pkg.maintainers) > 0 then
|
||||
true
|
||||
else
|
||||
builtins.trace "Package has no maintainers: ${pkg.name}" false
|
||||
) attrsWithMaintainers;
|
||||
|
||||
relevantFilenames =
|
||||
drv:
|
||||
(lib.lists.unique (
|
||||
builtins.map (pos: lib.strings.removePrefix (toString ../..) pos.file) (
|
||||
builtins.filter (x: x != null) [
|
||||
(builtins.unsafeGetAttrPos "maintainers" (drv.meta or { }))
|
||||
(builtins.unsafeGetAttrPos "src" drv)
|
||||
# broken because name is always set by stdenv:
|
||||
# # A hack to make `nix-env -qa` and `nix search` ignore broken packages.
|
||||
# # TODO(@oxij): remove this assert when something like NixOS/nix#1771 gets merged into nix.
|
||||
# name = assert validity.handled; name + lib.optionalString
|
||||
#(builtins.unsafeGetAttrPos "name" drv)
|
||||
(builtins.unsafeGetAttrPos "pname" drv)
|
||||
(builtins.unsafeGetAttrPos "version" drv)
|
||||
|
||||
# Use ".meta.position" for cases when most of the package is
|
||||
# defined in a "common" section and the only place where
|
||||
# reference to the file with a derivation the "pos"
|
||||
# attribute.
|
||||
#
|
||||
# ".meta.position" has the following form:
|
||||
# "pkgs/tools/package-management/nix/default.nix:155"
|
||||
# We transform it to the following:
|
||||
# { file = "pkgs/tools/package-management/nix/default.nix"; }
|
||||
{ file = lib.head (lib.splitString ":" (drv.meta.position or "")); }
|
||||
]
|
||||
)
|
||||
));
|
||||
|
||||
attrsWithFilenames = builtins.map (
|
||||
pkg: pkg // { filenames = relevantFilenames pkg.package; }
|
||||
) attrsWithMaintainers;
|
||||
|
||||
attrsWithModifiedFiles = builtins.filter (pkg: anyMatchingFiles pkg.filenames) attrsWithFilenames;
|
||||
|
||||
listToPing = lib.lists.flatten (
|
||||
builtins.map (
|
||||
pkg:
|
||||
builtins.map (maintainer: {
|
||||
id = maintainer.githubId;
|
||||
packageName = pkg.name;
|
||||
dueToFiles = pkg.filenames;
|
||||
}) pkg.maintainers
|
||||
) attrsWithModifiedFiles
|
||||
);
|
||||
|
||||
byMaintainer = lib.lists.foldr (
|
||||
ping: collector:
|
||||
collector
|
||||
// {
|
||||
"${toString ping.id}" = [
|
||||
{ inherit (ping) packageName dueToFiles; }
|
||||
] ++ (collector."${toString ping.id}" or [ ]);
|
||||
}
|
||||
) { } listToPing;
|
||||
|
||||
textForPackages =
|
||||
packages: lib.strings.concatStringsSep ", " (builtins.map (pkg: pkg.packageName) packages);
|
||||
|
||||
textPerMaintainer = lib.attrsets.mapAttrs (
|
||||
maintainer: packages: "- @${maintainer} for ${textForPackages packages}"
|
||||
) byMaintainer;
|
||||
|
||||
packagesPerMaintainer = lib.attrsets.mapAttrs (
|
||||
maintainer: packages: builtins.map (pkg: pkg.packageName) packages
|
||||
) byMaintainer;
|
||||
in
|
||||
packagesPerMaintainer
|
||||
+22
-18
@@ -11,6 +11,7 @@ rec {
|
||||
into
|
||||
{
|
||||
name = "hello";
|
||||
packagePath = [ "hello" ];
|
||||
platform = "aarch64-linux";
|
||||
}
|
||||
*/
|
||||
@@ -30,6 +31,9 @@ rec {
|
||||
null
|
||||
else
|
||||
{
|
||||
# [ "python312Packages" "numpy" ]
|
||||
inherit packagePath;
|
||||
|
||||
# python312Packages.numpy
|
||||
inherit name;
|
||||
|
||||
@@ -52,12 +56,12 @@ rec {
|
||||
]
|
||||
into
|
||||
[
|
||||
{ name = "hello"; platform = "aarch64-linux"; }
|
||||
{ name = "hello"; platform = "x86_64-linux"; }
|
||||
{ name = "hello"; platform = "aarch64-darwin"; }
|
||||
{ name = "hello"; platform = "x86_64-darwin"; }
|
||||
{ name = "bye"; platform = "aarch64-darwin"; }
|
||||
{ name = "bye"; platform = "x86_64-darwin"; }
|
||||
{ name = "hello"; platform = "aarch64-linux"; packagePath = [ "hello" ]; }
|
||||
{ name = "hello"; platform = "x86_64-linux"; packagePath = [ "hello" ]; }
|
||||
{ name = "hello"; platform = "aarch64-darwin"; packagePath = [ "hello" ]; }
|
||||
{ name = "hello"; platform = "x86_64-darwin"; packagePath = [ "hello" ]; }
|
||||
{ name = "bye"; platform = "aarch64-darwin"; packagePath = [ "hello" ]; }
|
||||
{ name = "bye"; platform = "x86_64-darwin"; packagePath = [ "hello" ]; }
|
||||
]
|
||||
*/
|
||||
convertToPackagePlatformAttrs =
|
||||
@@ -120,12 +124,12 @@ rec {
|
||||
|
||||
Turns
|
||||
[
|
||||
{ name = "hello"; platform = "aarch64-linux"; }
|
||||
{ name = "hello"; platform = "x86_64-linux"; }
|
||||
{ name = "hello"; platform = "aarch64-darwin"; }
|
||||
{ name = "hello"; platform = "x86_64-darwin"; }
|
||||
{ name = "bye"; platform = "aarch64-darwin"; }
|
||||
{ name = "bye"; platform = "x86_64-darwin"; }
|
||||
{ name = "hello"; platform = "aarch64-linux"; ... }
|
||||
{ name = "hello"; platform = "x86_64-linux"; ... }
|
||||
{ name = "hello"; platform = "aarch64-darwin"; ... }
|
||||
{ name = "hello"; platform = "x86_64-darwin"; ... }
|
||||
{ name = "bye"; platform = "aarch64-darwin"; ... }
|
||||
{ name = "bye"; platform = "x86_64-darwin"; ... }
|
||||
]
|
||||
into
|
||||
{
|
||||
@@ -145,12 +149,12 @@ rec {
|
||||
|
||||
# Turns
|
||||
# [
|
||||
# { name = "hello"; platform = "aarch64-linux"; }
|
||||
# { name = "hello"; platform = "x86_64-linux"; }
|
||||
# { name = "hello"; platform = "aarch64-darwin"; }
|
||||
# { name = "hello"; platform = "x86_64-darwin"; }
|
||||
# { name = "bye"; platform = "aarch64-darwin"; }
|
||||
# { name = "bye"; platform = "x86_64-darwin"; }
|
||||
# { name = "hello"; platform = "aarch64-linux"; ... }
|
||||
# { name = "hello"; platform = "x86_64-linux"; ... }
|
||||
# { name = "hello"; platform = "aarch64-darwin"; ... }
|
||||
# { name = "hello"; platform = "x86_64-darwin"; ... }
|
||||
# { name = "bye"; platform = "aarch64-darwin"; ... }
|
||||
# { name = "bye"; platform = "x86_64-darwin"; ... }
|
||||
# ]
|
||||
#
|
||||
# into
|
||||
|
||||
@@ -14,8 +14,9 @@ stdenvNoCC.mkDerivation {
|
||||
src = lib.fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = lib.fileset.unions [
|
||||
./get-reviewers.sh
|
||||
./request-reviews.sh
|
||||
./get-code-owners.sh
|
||||
./request-reviewers.sh
|
||||
./request-code-owner-reviews.sh
|
||||
./verify-base-branch.sh
|
||||
./dev-branches.txt
|
||||
];
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Get the code owners of the files changed by a PR,
|
||||
# suitable to be consumed by the API endpoint to request reviews:
|
||||
# https://docs.github.com/en/rest/pulls/review-requests?apiVersion=2022-11-28#request-reviewers-for-a-pull-request
|
||||
# Get the code owners of the files changed by a PR, returning one username per line
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -10,18 +8,15 @@ log() {
|
||||
echo "$@" >&2
|
||||
}
|
||||
|
||||
if (( "$#" < 7 )); then
|
||||
log "Usage: $0 GIT_REPO OWNERS_FILE BASE_REPO BASE_REF HEAD_REF PR_NUMBER PR_AUTHOR"
|
||||
if (( "$#" < 4 )); then
|
||||
log "Usage: $0 GIT_REPO OWNERS_FILE BASE_REF HEAD_REF"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gitRepo=$1
|
||||
ownersFile=$2
|
||||
baseRepo=$3
|
||||
baseRef=$4
|
||||
headRef=$5
|
||||
prNumber=$6
|
||||
prAuthor=$7
|
||||
baseRef=$3
|
||||
headRef=$4
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' exit
|
||||
@@ -98,29 +93,4 @@ for file in "${touchedFiles[@]}"; do
|
||||
|
||||
done
|
||||
|
||||
# Cannot request a review from the author
|
||||
if [[ -v users[${prAuthor,,}] ]]; then
|
||||
log "One or more files are owned by the PR author, ignoring"
|
||||
unset 'users[${prAuthor,,}]'
|
||||
fi
|
||||
|
||||
gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/$baseRepo/pulls/$prNumber/reviews" \
|
||||
--jq '.[].user.login' > "$tmp/already-reviewed-by"
|
||||
|
||||
# And we don't want to rerequest reviews from people who already reviewed
|
||||
while read -r user; do
|
||||
if [[ -v users[${user,,}] ]]; then
|
||||
log "User $user is a code owner but has already left a review, ignoring"
|
||||
unset 'users[${user,,}]'
|
||||
fi
|
||||
done < "$tmp/already-reviewed-by"
|
||||
|
||||
# Turn it into a JSON for the GitHub API call to request PR reviewers
|
||||
jq -n \
|
||||
--arg users "${!users[*]}" \
|
||||
'{
|
||||
reviewers: $users | split(" "),
|
||||
}'
|
||||
printf "%s\n" "${!users[@]}"
|
||||
+3
-16
@@ -77,19 +77,6 @@ if ! "$SCRIPT_DIR"/verify-base-branch.sh "$tmp/nixpkgs.git" "$headRef" "$baseRep
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Getting code owners to request reviews from"
|
||||
"$SCRIPT_DIR"/get-reviewers.sh "$tmp/nixpkgs.git" "$ownersFile" "$baseRepo" "$baseBranch" "$headRef" "$prNumber" "$prAuthor" > "$tmp/reviewers.json"
|
||||
|
||||
log "Requesting reviews from: $(<"$tmp/reviewers.json")"
|
||||
|
||||
if ! response=$(effect gh api \
|
||||
--method POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/$baseRepo/pulls/$prNumber/requested_reviewers" \
|
||||
--input "$tmp/reviewers.json"); then
|
||||
log "Failed to request reviews: $response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Successfully requested reviews"
|
||||
log "Requesting reviews from code owners"
|
||||
"$SCRIPT_DIR"/get-code-owners.sh "$tmp/nixpkgs.git" "$ownersFile" "$baseBranch" "$headRef" | \
|
||||
"$SCRIPT_DIR"/request-reviewers.sh "$baseRepo" "$prNumber" "$prAuthor"
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Request reviewers for a PR, reading line-separated usernames on stdin,
|
||||
# filtering for valid reviewers before using the API endpoint to request reviews:
|
||||
# https://docs.github.com/en/rest/pulls/review-requests?apiVersion=2022-11-28#request-reviewers-for-a-pull-request
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' exit
|
||||
|
||||
log() {
|
||||
echo "$@" >&2
|
||||
}
|
||||
|
||||
effect() {
|
||||
if [[ -n "${DRY_MODE:-}" ]]; then
|
||||
log "Skipping in dry mode:" "${@@Q}"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
if (( "$#" < 3 )); then
|
||||
log "Usage: $0 BASE_REPO PR_NUMBER PR_AUTHOR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
baseRepo=$1
|
||||
prNumber=$2
|
||||
prAuthor=$3
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' exit
|
||||
|
||||
declare -A users=()
|
||||
while read -r handle && [[ -n "$handle" ]]; do
|
||||
users[${handle,,}]=
|
||||
done
|
||||
|
||||
# Cannot request a review from the author
|
||||
if [[ -v users[${prAuthor,,}] ]]; then
|
||||
log "One or more files are owned by the PR author, ignoring"
|
||||
unset 'users[${prAuthor,,}]'
|
||||
fi
|
||||
|
||||
gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/$baseRepo/pulls/$prNumber/reviews" \
|
||||
--jq '.[].user.login' > "$tmp/already-reviewed-by"
|
||||
|
||||
# And we don't want to rerequest reviews from people who already reviewed
|
||||
while read -r user; do
|
||||
if [[ -v users[${user,,}] ]]; then
|
||||
log "User $user is a potential reviewer, but has already left a review, ignoring"
|
||||
unset 'users[${user,,}]'
|
||||
fi
|
||||
done < "$tmp/already-reviewed-by"
|
||||
|
||||
for user in "${!users[@]}"; do
|
||||
if ! gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/$baseRepo/collaborators/$user" >&2; then
|
||||
log "User $user is not a repository collaborator, probably missed the automated invite to the maintainers team (see <https://github.com/NixOS/nixpkgs/issues/234293>), ignoring"
|
||||
unset 'users[$user]'
|
||||
fi
|
||||
done
|
||||
|
||||
for user in "${!users[@]}"; do
|
||||
log "Requesting review from: $user"
|
||||
|
||||
if ! response=$(jq -n --arg user "$user" '{ reviewers: [ $user ] }' | \
|
||||
effect gh api \
|
||||
--method POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/$baseRepo/pulls/$prNumber/requested_reviewers" \
|
||||
--input -); then
|
||||
log "Failed to request review from $user: $response"
|
||||
fi
|
||||
done
|
||||
@@ -728,7 +728,7 @@ buildPythonPackage rec {
|
||||
(fetchDebianPatch {
|
||||
inherit pname version;
|
||||
debianRevision = "5";
|
||||
name = "Add-quotes-to-SOAPAction-header-in-SoapClient.patch";
|
||||
patch = "Add-quotes-to-SOAPAction-header-in-SoapClient.patch";
|
||||
hash = "sha256-xA8Wnrpr31H8wy3zHSNfezFNjUJt1HbSXn3qUMzeKc0=";
|
||||
})
|
||||
];
|
||||
|
||||
@@ -95,6 +95,5 @@ scheme.section.md
|
||||
swift.section.md
|
||||
tcl.section.md
|
||||
texlive.section.md
|
||||
titanium.section.md
|
||||
vim.section.md
|
||||
```
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
# Titanium {#titanium}
|
||||
|
||||
The Nixpkgs repository contains facilities to deploy a variety of versions of
|
||||
the [Titanium SDK](https://www.appcelerator.com) versions, a cross-platform
|
||||
mobile app development framework using JavaScript as an implementation language,
|
||||
and includes a function abstraction making it possible to build Titanium
|
||||
applications for Android and iOS devices from source code.
|
||||
|
||||
Not all Titanium features supported -- currently, it can only be used to build
|
||||
Android and iOS apps.
|
||||
|
||||
## Building a Titanium app {#building-a-titanium-app}
|
||||
|
||||
We can build a Titanium app from source for Android or iOS and for debugging or
|
||||
release purposes by invoking the `titaniumenv.buildApp {}` function:
|
||||
|
||||
```nix
|
||||
titaniumenv.buildApp {
|
||||
name = "myapp";
|
||||
src = ./myappsource;
|
||||
|
||||
preBuild = "";
|
||||
target = "android"; # or 'iphone'
|
||||
tiVersion = "7.1.0.GA";
|
||||
release = true;
|
||||
|
||||
androidsdkArgs = {
|
||||
platformVersions = [ "25" "26" ];
|
||||
};
|
||||
androidKeyStore = ./keystore;
|
||||
androidKeyAlias = "myfirstapp";
|
||||
androidKeyStorePassword = "secret";
|
||||
|
||||
xcodeBaseDir = "/Applications/Xcode.app";
|
||||
xcodewrapperArgs = {
|
||||
version = "9.3";
|
||||
};
|
||||
iosMobileProvisioningProfile = ./myprovisioning.profile;
|
||||
iosCertificateName = "My Company";
|
||||
iosCertificate = ./mycertificate.p12;
|
||||
iosCertificatePassword = "secret";
|
||||
iosVersion = "11.3";
|
||||
iosBuildStore = false;
|
||||
|
||||
enableWirelessDistribution = true;
|
||||
installURL = "/installipa.php";
|
||||
}
|
||||
```
|
||||
|
||||
The `titaniumenv.buildApp {}` function takes the following parameters:
|
||||
|
||||
* The `name` parameter refers to the name in the Nix store.
|
||||
* The `src` parameter refers to the source code location of the app that needs
|
||||
to be built.
|
||||
* `preRebuild` contains optional build instructions that are carried out before
|
||||
the build starts.
|
||||
* `target` indicates for which device the app must be built. Currently only
|
||||
'android' and 'iphone' (for iOS) are supported.
|
||||
* `tiVersion` can be used to optionally override the requested Titanium version
|
||||
in `tiapp.xml`. If not specified, it will use the version in `tiapp.xml`.
|
||||
* `release` should be set to true when building an app for submission to the
|
||||
Google Playstore or Apple Appstore. Otherwise, it should be false.
|
||||
|
||||
When the `target` has been set to `android`, we can configure the following
|
||||
parameters:
|
||||
|
||||
* The `androidSdkArgs` parameter refers to an attribute set that propagates all
|
||||
parameters to the `androidenv.composeAndroidPackages {}` function. This can
|
||||
be used to install all relevant Android plugins that may be needed to perform
|
||||
the Android build. If no parameters are given, it will deploy the platform
|
||||
SDKs for API-levels 25 and 26 by default.
|
||||
|
||||
When the `release` parameter has been set to true, you need to provide
|
||||
parameters to sign the app:
|
||||
|
||||
* `androidKeyStore` is the path to the keystore file
|
||||
* `androidKeyAlias` is the key alias
|
||||
* `androidKeyStorePassword` refers to the password to open the keystore file.
|
||||
|
||||
When the `target` has been set to `iphone`, we can configure the following
|
||||
parameters:
|
||||
|
||||
* The `xcodeBaseDir` parameter refers to the location where Xcode has been
|
||||
installed. When none value is given, the above value is the default.
|
||||
* The `xcodewrapperArgs` parameter passes arbitrary parameters to the
|
||||
`xcodeenv.composeXcodeWrapper {}` function. This can, for example, be used
|
||||
to adjust the default version of Xcode.
|
||||
|
||||
When `release` has been set to true, you also need to provide the following
|
||||
parameters:
|
||||
|
||||
* `iosMobileProvisioningProfile` refers to a mobile provisioning profile needed
|
||||
for signing.
|
||||
* `iosCertificateName` refers to the company name in the P12 certificate.
|
||||
* `iosCertificate` refers to the path to the P12 file.
|
||||
* `iosCertificatePassword` contains the password to open the P12 file.
|
||||
* `iosVersion` refers to the iOS SDK version to use. It defaults to the latest
|
||||
version.
|
||||
* `iosBuildStore` should be set to `true` when building for the Apple Appstore
|
||||
submission. For enterprise or ad-hoc builds it should be set to `false`.
|
||||
|
||||
When `enableWirelessDistribution` has been enabled, you must also provide the
|
||||
path of the PHP script (`installURL`) (that is included with the iOS build
|
||||
environment) to enable wireless ad-hoc installations.
|
||||
|
||||
## Emulating or simulating the app {#emulating-or-simulating-the-app}
|
||||
|
||||
It is also possible to simulate the correspond iOS simulator build by using
|
||||
`xcodeenv.simulateApp {}` and emulate an Android APK by using
|
||||
`androidenv.emulateApp {}`.
|
||||
@@ -14,3 +14,7 @@ development.md
|
||||
contributing.md
|
||||
interoperability.md
|
||||
```
|
||||
|
||||
```{=include=} appendix html:into-file=//release-notes.html
|
||||
release-notes/release-notes.md
|
||||
```
|
||||
|
||||
@@ -7,9 +7,12 @@ The module system is a language for handling configuration, implemented as a Nix
|
||||
Compared to plain Nix, it adds documentation, type checking and composition or extensibility.
|
||||
|
||||
::: {.note}
|
||||
This chapter is new and not complete yet. For a gentle introduction to the module system, in the context of NixOS, see [Writing NixOS Modules](https://nixos.org/manual/nixos/unstable/index.html#sec-writing-modules) in the NixOS manual.
|
||||
:::
|
||||
This chapter is new and not complete yet.
|
||||
|
||||
See also:
|
||||
- Introduction to the module system, in the context of NixOS, see [Writing NixOS Modules](https://nixos.org/manual/nixos/unstable/index.html#sec-writing-modules) in the NixOS manual.
|
||||
- Generic guide to the module system on [nix.dev](https://nix.dev/tutorials/module-system/index.html).
|
||||
:::
|
||||
|
||||
## `lib.evalModules` {#module-system-lib-evalModules}
|
||||
|
||||
|
||||
@@ -98,5 +98,5 @@ substituteAllFiles {
|
||||
}
|
||||
```
|
||||
|
||||
in the resulting derivation, every instance of `@hello@` will be replaced with `there` in `$out/foo.txt` and` `$out/bar.txt`; `baz.txt` will not be processed nor will it appear in `$out`.
|
||||
in the resulting derivation, every instance of `@hello@` will be replaced with `there` in `$out/foo.txt` and `$out/bar.txt`; `baz.txt` will not be processed nor will it appear in `$out`.
|
||||
:::
|
||||
|
||||
+21
-9
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"chap-release-notes": [
|
||||
"release-notes.html#chap-release-notes"
|
||||
],
|
||||
"nixpkgs-manual": [
|
||||
"index.html#nixpkgs-manual"
|
||||
],
|
||||
@@ -50,6 +53,24 @@
|
||||
"chap-overlays": [
|
||||
"index.html#chap-overlays"
|
||||
],
|
||||
"sec-nixpkgs-release-25.05": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05"
|
||||
],
|
||||
"sec-nixpkgs-release-25.05-incompatibilities": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05-incompatibilities"
|
||||
],
|
||||
"sec-nixpkgs-release-25.05-incompatibilities-titanium-removed": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05-incompatibilities-titanium-removed",
|
||||
"index.html#titanium",
|
||||
"index.html#building-a-titanium-app",
|
||||
"index.html#emulating-or-simulating-the-app"
|
||||
],
|
||||
"sec-nixpkgs-release-25.05-lib": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05-lib"
|
||||
],
|
||||
"sec-nixpkgs-release-25.05-lib-breaking": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05-lib-breaking"
|
||||
],
|
||||
"sec-overlays-install": [
|
||||
"index.html#sec-overlays-install"
|
||||
],
|
||||
@@ -3736,15 +3757,6 @@
|
||||
"sec-language-texlive-lualatex-font-cache": [
|
||||
"index.html#sec-language-texlive-lualatex-font-cache"
|
||||
],
|
||||
"titanium": [
|
||||
"index.html#titanium"
|
||||
],
|
||||
"building-a-titanium-app": [
|
||||
"index.html#building-a-titanium-app"
|
||||
],
|
||||
"emulating-or-simulating-the-app": [
|
||||
"index.html#emulating-or-simulating-the-app"
|
||||
],
|
||||
"vim": [
|
||||
"index.html#vim"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Release Notes {#chap-release-notes}
|
||||
|
||||
This section lists the release notes for each stable version of Nixpkgs and current unstable revision.
|
||||
|
||||
```{=include=} sections
|
||||
rl-2505.section.md
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
# Nixpkgs 25.05 (2025.05/??) {#sec-nixpkgs-release-25.05}
|
||||
|
||||
## Backward Incompatibilities {#sec-nixpkgs-release-25.05-incompatibilities}
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
### Titanium removed {#sec-nixpkgs-release-25.05-incompatibilities-titanium-removed}
|
||||
|
||||
- `titaniumenv`, `titanium`, and `titanium-alloy` have been removed due to lack of maintenance in Nixpkgs.
|
||||
|
||||
## Nixpkgs Library {#sec-nixpkgs-release-25.05-lib}
|
||||
|
||||
### Breaking changes {#sec-nixpkgs-release-25.05-lib-breaking}
|
||||
|
||||
- Structure of the `functor` of some types has changed. `functor` is an implementation detail and should not be relied upon. If you did rely on it let us know in this [PR](https://github.com/NixOS/nixpkgs/pull/363565).
|
||||
- [`lib.types.enum`](https://nixos.org/manual/nixos/unstable/#sec-option-types-basic): Previously the `functor.payload` was the list of enum values directly. Now it is an attribute set containing the values in the `values` attribute.
|
||||
- [`lib.types.separatedString`](https://nixos.org/manual/nixos/unstable/#sec-option-types-string): Previously the `functor.payload` was the seperator directly. Now it is an attribute set containing the seperator in the `sep` attribute.
|
||||
@@ -5892,6 +5892,11 @@
|
||||
githubId = 283316;
|
||||
name = "Dane Lipscombe";
|
||||
};
|
||||
dlurak = {
|
||||
github = "dlurak";
|
||||
githubId = 84224239;
|
||||
name = "dlurak";
|
||||
};
|
||||
dmadisetti = {
|
||||
email = "nix@madisetti.me";
|
||||
github = "dmadisetti";
|
||||
@@ -5988,6 +5993,12 @@
|
||||
githubId = 57304299;
|
||||
keys = [ { fingerprint = "33CD 5C0A 673C C54D 661E 5E4C 0DB5 361B EEE5 30AB"; } ];
|
||||
};
|
||||
dopplerian = {
|
||||
name = "Dopplerian";
|
||||
github = "Dopplerian";
|
||||
githubId = 53937537;
|
||||
keys = [ { fingerprint = "BBC4 C071 516B A147 8D07 F9DC D2FD E6EC 2E8C 2BF4"; } ];
|
||||
};
|
||||
doriath = {
|
||||
email = "tomasz.zurkowski@gmail.com";
|
||||
github = "doriath";
|
||||
@@ -9218,6 +9229,13 @@
|
||||
githubId = 287215;
|
||||
keys = [ { fingerprint = "D618 7A03 A40A 3D56 62F5 4B46 03EF BF83 9A5F DC15"; } ];
|
||||
};
|
||||
hlad = {
|
||||
email = "hlad+nix@hlad.org";
|
||||
name = "Marek Hladky";
|
||||
matrix = "@hlad:hlad.org";
|
||||
github = "hlad";
|
||||
githubId = 6285728;
|
||||
};
|
||||
hleboulanger = {
|
||||
email = "hleboulanger@protonmail.com";
|
||||
name = "Harold Leboulanger";
|
||||
@@ -10134,6 +10152,12 @@
|
||||
githubId = 45084216;
|
||||
keys = [ { fingerprint = "1BF9 8D10 E0D0 0B41 5723 5836 4C13 3A84 E646 9228"; } ];
|
||||
};
|
||||
jaculabilis = {
|
||||
name = "Tim Van Baak";
|
||||
email = "tim.vanbaak@gmail.com";
|
||||
github = "Jaculabilis";
|
||||
githubId = 10787844;
|
||||
};
|
||||
jaduff = {
|
||||
email = "jdduffpublic@proton.me";
|
||||
github = "jaduff";
|
||||
@@ -11135,11 +11159,11 @@
|
||||
githubId = 13179;
|
||||
};
|
||||
jopejoe1 = {
|
||||
email = "johannes@joens.email";
|
||||
email = "nixpkgs@missing.ninja";
|
||||
matrix = "@jopejoe1:matrix.org";
|
||||
github = "jopejoe1";
|
||||
githubId = 34899572;
|
||||
name = "Johannes Jöns";
|
||||
name = "jopejoe1";
|
||||
};
|
||||
jordanisaacs = {
|
||||
name = "Jordan Isaacs";
|
||||
@@ -12511,6 +12535,12 @@
|
||||
githubId = 168301;
|
||||
name = "Victor Engmark";
|
||||
};
|
||||
l1npengtul = {
|
||||
email = "l1npengtul@l1npengtul.lol";
|
||||
github = "l1npengtul";
|
||||
githubId = 35755164;
|
||||
name = "l1npengtul";
|
||||
};
|
||||
l33tname = {
|
||||
name = "l33tname";
|
||||
email = "hi@l33t.name";
|
||||
@@ -13732,6 +13762,17 @@
|
||||
githubId = 24735185;
|
||||
name = "Mahmoud Ayman";
|
||||
};
|
||||
mahtaran = {
|
||||
email = "luka.leer@gmail.com";
|
||||
github = "mahtaran";
|
||||
githubId = 22727323;
|
||||
name = "Luka Leer";
|
||||
keys = [
|
||||
{
|
||||
fingerprint = "C7FF B72E 0527 423A D470 E132 AA82 C4EB CB16 82E0";
|
||||
}
|
||||
];
|
||||
};
|
||||
majesticmullet = {
|
||||
email = "hoccthomas@gmail.com.au";
|
||||
github = "MajesticMullet";
|
||||
@@ -15300,6 +15341,12 @@
|
||||
githubId = 44561540;
|
||||
name = "Synth Morxemplum";
|
||||
};
|
||||
MostafaKhaled = {
|
||||
email = "mostafa.khaled.5422@gmail.com";
|
||||
github = "mostafa-khaled775";
|
||||
githubId = 112074172;
|
||||
name = "Mostafa Khaled";
|
||||
};
|
||||
MostAwesomeDude = {
|
||||
email = "cds@corbinsimpson.com";
|
||||
github = "MostAwesomeDude";
|
||||
@@ -15627,6 +15674,12 @@
|
||||
githubId = 38578268;
|
||||
name = "Morgan Wolfe";
|
||||
};
|
||||
mx2uller = {
|
||||
email = "mx2uller@pm.me";
|
||||
github = "mx2uller";
|
||||
githubId = 93703653;
|
||||
name = "Marko Müller";
|
||||
};
|
||||
mxkrsv = {
|
||||
email = "mxkrsv@disroot.org";
|
||||
github = "mxkrsv";
|
||||
@@ -15655,6 +15708,12 @@
|
||||
githubId = 9636071;
|
||||
name = "Myrl Hex";
|
||||
};
|
||||
myypo = {
|
||||
email = "nikirsmcgl@gmail.com";
|
||||
github = "myypo";
|
||||
githubId = 110892040;
|
||||
name = "Mykyta Polchanov";
|
||||
};
|
||||
mzacho = {
|
||||
email = "nixpkgs@martinzacho.net";
|
||||
github = "mzacho";
|
||||
@@ -16560,6 +16619,13 @@
|
||||
githubId = 22592293;
|
||||
name = "Kartik Gokte";
|
||||
};
|
||||
nullcube = {
|
||||
email = "nullcub3@gmail.com";
|
||||
name = "NullCube";
|
||||
github = "nullcub3";
|
||||
githubId = 51034487;
|
||||
matrix = "@nullcube:matrix.org";
|
||||
};
|
||||
nullishamy = {
|
||||
email = "spam@amyerskine.me";
|
||||
name = "nullishamy";
|
||||
@@ -18638,6 +18704,12 @@
|
||||
githubId = 40905037;
|
||||
name = "qwqawawow";
|
||||
};
|
||||
qxrein = {
|
||||
email = "mnv07@proton.me";
|
||||
github = "qxrein";
|
||||
githubId = 101001298;
|
||||
name = "qxrein";
|
||||
};
|
||||
qyliss = {
|
||||
email = "hi@alyssa.is";
|
||||
github = "alyssais";
|
||||
@@ -21657,6 +21729,13 @@
|
||||
githubId = 22163194;
|
||||
name = "Stel Abrego";
|
||||
};
|
||||
stellessia = {
|
||||
name = "Rachel Podya";
|
||||
email = "homicide@disroot.org";
|
||||
github = "stellessia";
|
||||
githubId = 81514356;
|
||||
keys = [ { fingerprint = "38E8 7F79 AE86 CA98 F8BC 45F8 1060 00A0 5E5B DB90"; } ];
|
||||
};
|
||||
stepbrobd = {
|
||||
name = "Yifei Sun";
|
||||
email = "ysun@hey.com";
|
||||
@@ -21738,6 +21817,13 @@
|
||||
githubId = 853468;
|
||||
name = "Steven Roose";
|
||||
};
|
||||
stevestreza = {
|
||||
email = "nixpkgs@stevestreza.com";
|
||||
github = "stevestreza";
|
||||
githubId = 28552;
|
||||
name = "Steve Streza";
|
||||
keys = [ { fingerprint = "DFED 4E42 34E7 348C 57D4 6568 C4DC 30F8 5ABC 6FA1"; } ];
|
||||
};
|
||||
stianlagstad = {
|
||||
email = "stianlagstad@gmail.com";
|
||||
github = "stianlagstad";
|
||||
@@ -22282,6 +22368,12 @@
|
||||
github = "teatwig";
|
||||
githubId = 18734648;
|
||||
};
|
||||
tebriel = {
|
||||
email = "tebriel@frodux.in";
|
||||
name = "tebriel";
|
||||
github = "tebriel";
|
||||
githubId = 821688;
|
||||
};
|
||||
techknowlogick = {
|
||||
email = "techknowlogick@gitea.com";
|
||||
github = "techknowlogick";
|
||||
|
||||
@@ -197,6 +197,7 @@ with lib.maintainers;
|
||||
siraben
|
||||
vbgl
|
||||
alizter
|
||||
stepbrobd
|
||||
];
|
||||
scope = "Maintain the Coq theorem prover and related packages.";
|
||||
shortName = "Coq";
|
||||
|
||||
@@ -79,6 +79,8 @@ let
|
||||
prepareManualFromMD = ''
|
||||
cp -r --no-preserve=all $inputs/* .
|
||||
|
||||
cp -r ${../../../doc/release-notes} ./release-notes-nixpkgs
|
||||
|
||||
substituteInPlace ./manual.md \
|
||||
--replace-fail '@NIXOS_VERSION@' "${version}"
|
||||
substituteInPlace ./configuration/configuration.md \
|
||||
|
||||
@@ -58,11 +58,16 @@ checks:
|
||||
before the activation script is run. This behavior is different when the
|
||||
service is socket-activated, as outlined in the following steps.
|
||||
|
||||
- The last thing that is taken into account is whether the unit is a service
|
||||
and socket-activated. If `X-StopIfChanged` is **not** set, the service
|
||||
is **restart**ed with the others. If it is set, both the service and the
|
||||
socket are **stop**ped and the socket is **start**ed, leaving socket
|
||||
activation to start the service when it's needed.
|
||||
- The last thing that is taken into account is whether the unit is a
|
||||
service and socket-activated. A correspondence between a
|
||||
`.service` and its `.socket` unit is detected automatically, but
|
||||
services can **opt out** of that detection by setting
|
||||
`X-NotSocketActivated` to `yes` in their `[Service]`
|
||||
section. Otherwise, if `X-StopIfChanged` is **not** set, the
|
||||
service is **restart**ed with the others. If it is set, both the
|
||||
service and the socket are **stop**ped and the socket is
|
||||
**start**ed, leaving socket activation to start the service when
|
||||
it's needed.
|
||||
|
||||
## Sysinit reactivation {#sec-sysinit-reactivation}
|
||||
|
||||
|
||||
@@ -1886,11 +1886,23 @@
|
||||
"sec-release-25.05-notable-changes": [
|
||||
"release-notes.html#sec-release-25.05-notable-changes"
|
||||
],
|
||||
"sec-release-25.05-lib": [
|
||||
"release-notes.html#sec-release-25.05-lib"
|
||||
"sec-nixpkgs-release-25.05": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05"
|
||||
],
|
||||
"sec-release-25.05-lib-breaking": [
|
||||
"release-notes.html#sec-release-25.05-lib-breaking"
|
||||
"sec-nixpkgs-release-25.05-incompatibilities": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05-incompatibilities"
|
||||
],
|
||||
"sec-nixpkgs-release-25.05-incompatibilities-titanium-removed": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05-incompatibilities-titanium-removed",
|
||||
"index.html#titanium",
|
||||
"index.html#building-a-titanium-app",
|
||||
"index.html#emulating-or-simulating-the-app"
|
||||
],
|
||||
"sec-nixpkgs-release-25.05-lib": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05-lib"
|
||||
],
|
||||
"sec-nixpkgs-release-25.05-lib-breaking": [
|
||||
"release-notes.html#sec-nixpkgs-release-25.05-lib-breaking"
|
||||
],
|
||||
"sec-release-24.11": [
|
||||
"release-notes.html#sec-release-24.11"
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
|
||||
- [networking.modemmanager](options.html#opt-networking.modemmanager) has been split out of [networking.networkmanager](options.html#opt-networking.networkmanager). NetworkManager still enables ModemManager by default, but options exist now to run NetworkManager without ModemManager.
|
||||
|
||||
- [ncps](https://github.com/kalbasit/ncps), a Nix binary cache proxy service implemented in Go using [go-nix](https://github.com/nix-community/go-nix). Available as [services.ncps](options.html#opt-services.ncps.enable).
|
||||
|
||||
- [Conduwuit](https://conduwuit.puppyirl.gay/), a federated chat server implementing the Matrix protocol, forked from Conduit. Available as [services.conduwuit](#opt-services.conduwuit.enable).
|
||||
|
||||
- [Traccar](https://www.traccar.org/), a modern GPS Tracking Platform. Available as [services.traccar](#opt-services.traccar.enable).
|
||||
@@ -79,6 +81,8 @@
|
||||
|
||||
- [Actual Budget](https://actualbudget.org/), a local-first personal finance app. Available as [services.actual](#opt-services.actual.enable).
|
||||
|
||||
- [immich-public-proxy](https://github.com/alangrainger/immich-public-proxy), a proxy for sharing Immich albums without exposing the Immich API. Available as [services.immich-public-proxy](#opt-services.immich-public-proxy.enable).
|
||||
|
||||
- [mqtt-exporter](https://github.com/kpetremann/mqtt-exporter/), a Prometheus exporter for exposing messages from MQTT. Available as [services.prometheus.exporters.mqtt](#opt-services.prometheus.exporters.mqtt.enable).
|
||||
|
||||
- [nvidia-gpu](https://github.com/utkuozdemir/nvidia_gpu_exporter), a Prometheus exporter that scrapes `nvidia-smi` for GPU metrics. Available as [services.prometheus.exporters.nvidia-gpu](#opt-services.prometheus.exporters.nvidia-gpu.enable).
|
||||
@@ -113,6 +117,15 @@
|
||||
|
||||
- `ps3-disc-dumper` was updated to 4.2.5, which removed the CLI project and now exclusively offers the GUI
|
||||
|
||||
- [](#opt-services.nextcloud.config.dbtype) is unset by default, the previous default was `sqlite`.
|
||||
This was done because `sqlite` is not a reasonable default since it's
|
||||
[not recommended by upstream](https://docs.nextcloud.com/server/30/admin_manual/installation/system_requirements.html)
|
||||
and thus doesn't qualify as default.
|
||||
|
||||
- Nextcloud's default FPM pool settings have been increased according to upstream recommentations. It's advised
|
||||
to review the new defaults and description of
|
||||
[](#opt-services.nextcloud.poolSettings).
|
||||
|
||||
- `asusd` has been upgraded to version 6 which supports multiple aura devices. To account for this, the single `auraConfig` configuration option has been replaced with `auraConfigs` which is an attribute set of config options per each device. The config files may also be now specified as either source files or text strings; to account for this you will need to specify that `text` is used for your existing configs, e.g.:
|
||||
```diff
|
||||
-services.asusd.asusdConfig = '''file contents'''
|
||||
@@ -127,6 +140,11 @@
|
||||
|
||||
- `zammad` has had its support for MySQL removed, since it was never working correctly and is now deprecated upstream. Check the [migration guide](https://docs.zammad.org/en/latest/appendix/migrate-to-postgresql.html) for how to convert your database to PostgreSQL.
|
||||
|
||||
- The `earlyoom` service is now using upstream systemd service, which enables
|
||||
hardening and filesystem isolation by default. If you need filesystem write
|
||||
access or want to access home directory via `killHook`, hardening setting can
|
||||
be changed via, e.g. `systemd.services.earlyoom.serviceConfig.ProtectSystem`.
|
||||
|
||||
- `nodePackages.vls` has been deprecated, as the upstream consumer of it, vetur, has been deprecated by upstream. Upstream suggests migrating to Volar for Vue LSP tooling instead.
|
||||
|
||||
- `nodePackages.create-react-native-app` has been removed, as it is deprecated. Upstream suggests using a framework for React Native apps instead.
|
||||
@@ -299,6 +317,10 @@
|
||||
|
||||
- `services.avahi.ipv6` now defaults to true.
|
||||
|
||||
- For matrix homeserver Synapse we are now following the upstream recommendation to enable jemalloc as the memory allocator by default.
|
||||
|
||||
- `services.kmonad` now creates a determinate symlink (in `/dev/input/by-id/`) to each of KMonad virtual devices.
|
||||
|
||||
- `bind.cacheNetworks` now only controls access for recursive queries, where it previously controlled access for all queries.
|
||||
|
||||
- Caddy can now be built with plugins by using `caddy.withPlugins`, a `passthru` function that accepts an attribute set as a parameter. The `plugins` argument represents a list of Caddy plugins, with each Caddy plugin being a versioned module. The `hash` argument represents the `vendorHash` of the resulting Caddy source code with the plugins added.
|
||||
@@ -333,10 +355,6 @@
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
## Nixpkgs Library {#sec-release-25.05-lib}
|
||||
|
||||
### Breaking changes {#sec-release-25.05-lib-breaking}
|
||||
|
||||
- Structure of the `functor` of some types has changed. `functor` is an implementation detail and should not be relied upon. If you did rely on it let us know in this [PR](https://github.com/NixOS/nixpkgs/pull/363565).
|
||||
- [`lib.types.enum`](https://nixos.org/manual/nixos/unstable/#sec-option-types-basic): Previously the `functor.payload` was the list of enum values directly. Now it is an attribute set containing the values in the `values` attribute.
|
||||
- [`lib.types.separatedString`](https://nixos.org/manual/nixos/unstable/#sec-option-types-string): Previously the `functor.payload` was the seperator directly. Now it is an attribute set containing the seperator in the `sep` attribute.
|
||||
```{=include=} sections
|
||||
../release-notes-nixpkgs/rl-2505.section.md
|
||||
```
|
||||
|
||||
@@ -65,6 +65,14 @@ let
|
||||
withWarnings = x:
|
||||
lib.warnIf (evalConfigArgs?extraArgs) "The extraArgs argument to eval-config.nix is deprecated. Please set config._module.args instead."
|
||||
lib.warnIf (evalConfigArgs?check) "The check argument to eval-config.nix is deprecated. Please set config._module.check instead."
|
||||
lib.warnIf (specialArgs?pkgs) ''
|
||||
You have set specialArgs.pkgs, which means that options like nixpkgs.config
|
||||
and nixpkgs.overlays will be ignored. If you wish to reuse an already created
|
||||
pkgs, which you know is configured correctly for this NixOS configuration,
|
||||
please import the `nixosModules.pkgsReadOnly` module from the nixpkgs flake or
|
||||
`(modulesPath + "/misc/nixpkgs/read-only.nix"), and set `{ nixpkgs.pkgs = <your pkgs>; }`.
|
||||
This properly disables the ignored options to prevent future surprises.
|
||||
''
|
||||
x;
|
||||
|
||||
legacyModules =
|
||||
|
||||
@@ -619,9 +619,6 @@ let format' = format; in let
|
||||
# __noChroot for example).
|
||||
export HOME=$TMPDIR
|
||||
NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root $mountPoint -- /nix/var/nix/profiles/system/bin/switch-to-configuration boot
|
||||
|
||||
# The above scripts will generate a random machine-id and we don't want to bake a single ID into all our images
|
||||
rm -f $mountPoint/etc/machine-id
|
||||
''}
|
||||
|
||||
# Set the ownerships of the contents. The modes are set in preVM.
|
||||
|
||||
@@ -579,6 +579,9 @@ in rec {
|
||||
'' else "")
|
||||
+ optionalString (def ? stopIfChanged && !def.stopIfChanged) ''
|
||||
X-StopIfChanged=false
|
||||
''
|
||||
+ optionalString (def ? notSocketActivated && def.notSocketActivated) ''
|
||||
X-NotSocketActivated=true
|
||||
'' + attrsToSection def.serviceConfig);
|
||||
};
|
||||
|
||||
|
||||
@@ -535,6 +535,18 @@ in rec {
|
||||
'';
|
||||
};
|
||||
|
||||
notSocketActivated = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
If set, a changed unit is never assumed to be
|
||||
socket-activated on configuration switch, even if
|
||||
it might have associated socket units. Instead, the unit
|
||||
will be restarted (or stopped/started) as if it had no
|
||||
associated sockets.
|
||||
'';
|
||||
};
|
||||
|
||||
startAt = mkOption {
|
||||
type = with types; either str (listOf str);
|
||||
default = [];
|
||||
|
||||
@@ -1160,6 +1160,7 @@
|
||||
./services/networking/nats.nix
|
||||
./services/networking/nbd.nix
|
||||
./services/networking/ncdns.nix
|
||||
./services/networking/ncps.nix
|
||||
./services/networking/ndppd.nix
|
||||
./services/networking/nebula.nix
|
||||
./services/networking/netbird.nix
|
||||
@@ -1485,6 +1486,7 @@
|
||||
./services/web-apps/icingaweb2/module-monitoring.nix
|
||||
./services/web-apps/ifm.nix
|
||||
./services/web-apps/immich.nix
|
||||
./services/web-apps/immich-public-proxy.nix
|
||||
./services/web-apps/invidious.nix
|
||||
./services/web-apps/invoiceplane.nix
|
||||
./services/web-apps/isso.nix
|
||||
|
||||
@@ -1,40 +1,46 @@
|
||||
# This module defines a small NixOS configuration. It does not
|
||||
# contain any graphical stuff.
|
||||
|
||||
{ config, lib, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
{
|
||||
documentation.enable = mkDefault false;
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkDefault;
|
||||
in
|
||||
{
|
||||
documentation = {
|
||||
enable = mkDefault false;
|
||||
doc.enable = mkDefault false;
|
||||
info.enable = mkDefault false;
|
||||
man.enable = mkDefault false;
|
||||
nixos.enable = mkDefault false;
|
||||
};
|
||||
|
||||
documentation.doc.enable = mkDefault false;
|
||||
environment = {
|
||||
# Perl is a default package.
|
||||
defaultPackages = mkDefault [ ];
|
||||
stub-ld.enable = mkDefault false;
|
||||
};
|
||||
|
||||
documentation.info.enable = mkDefault false;
|
||||
|
||||
documentation.man.enable = mkDefault false;
|
||||
|
||||
documentation.nixos.enable = mkDefault false;
|
||||
|
||||
# Perl is a default package.
|
||||
environment.defaultPackages = mkDefault [ ];
|
||||
|
||||
environment.stub-ld.enable = mkDefault false;
|
||||
|
||||
# The lessopen package pulls in Perl.
|
||||
programs.less.lessopen = mkDefault null;
|
||||
programs = {
|
||||
# The lessopen package pulls in Perl.
|
||||
less.lessopen = mkDefault null;
|
||||
command-not-found.enable = mkDefault false;
|
||||
};
|
||||
|
||||
# This pulls in nixos-containers which depends on Perl.
|
||||
boot.enableContainers = mkDefault false;
|
||||
|
||||
programs.command-not-found.enable = mkDefault false;
|
||||
services = {
|
||||
logrotate.enable = mkDefault false;
|
||||
udisks2.enable = mkDefault false;
|
||||
};
|
||||
|
||||
services.logrotate.enable = mkDefault false;
|
||||
|
||||
services.udisks2.enable = mkDefault false;
|
||||
|
||||
xdg.autostart.enable = mkDefault false;
|
||||
xdg.icons.enable = mkDefault false;
|
||||
xdg.mime.enable = mkDefault false;
|
||||
xdg.sounds.enable = mkDefault false;
|
||||
xdg = {
|
||||
autostart.enable = mkDefault false;
|
||||
icons.enable = mkDefault false;
|
||||
mime.enable = mkDefault false;
|
||||
sounds.enable = mkDefault false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ let
|
||||
{
|
||||
freeformType = attrsOf (either scalarType (listOf scalarType));
|
||||
# Client system-options file directives are explained here:
|
||||
# https://www.ibm.com/docs/en/storage-protect/8.1.24?topic=commands-processing-options
|
||||
# https://www.ibm.com/docs/en/storage-protect/8.1.25?topic=commands-processing-options
|
||||
options.servername = mkOption {
|
||||
type = servernameType;
|
||||
default = name;
|
||||
|
||||
@@ -45,6 +45,12 @@ in
|
||||
description = "Port to run Navidrome on.";
|
||||
type = port;
|
||||
};
|
||||
|
||||
EnableInsightsCollector = mkOption {
|
||||
default = false;
|
||||
description = "Enable anonymous usage data collection, see <https://www.navidrome.org/docs/getting-started/insights/> for details.";
|
||||
type = bool;
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
|
||||
@@ -44,8 +44,8 @@ let
|
||||
borgWrapper () {
|
||||
local result
|
||||
borg "$@" && result=$? || result=$?
|
||||
if [[ -z "${toString cfg.failOnWarnings}" ]] && [[ "$result" == 1 ]]; then
|
||||
echo "ignoring warning return value 1"
|
||||
if [[ -z "${toString cfg.failOnWarnings}" ]] && [[ "$result" == 1 || ("$result" -ge 100 && "$result" -le 127) ]]; then
|
||||
echo "ignoring warning return value $result"
|
||||
return 0
|
||||
else
|
||||
return "$result"
|
||||
|
||||
@@ -412,7 +412,7 @@ in
|
||||
]}
|
||||
PATH=${config.systemd.services."restic-backups-${name}".environment.PATH}:$PATH
|
||||
|
||||
exec ${resticCmd} $@
|
||||
exec ${resticCmd} "$@"
|
||||
'') (lib.filterAttrs (_: v: v.createWrapper) config.services.restic.backups);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ in
|
||||
environment.HOME = "/var/lib/tsm-backup";
|
||||
serviceConfig = {
|
||||
# for exit status description see
|
||||
# https://www.ibm.com/docs/en/storage-protect/8.1.24?topic=clients-client-return-codes
|
||||
# https://www.ibm.com/docs/en/storage-protect/8.1.25?topic=clients-client-return-codes
|
||||
SuccessExitStatus = "4 8";
|
||||
# The `-se` option must come after the command.
|
||||
# The `-optfile` option suppresses a `dsm.opt`-not-found warning.
|
||||
|
||||
@@ -80,9 +80,10 @@ in
|
||||
let
|
||||
args = lib.map lib.escapeShellArg (
|
||||
[
|
||||
"-l"
|
||||
"--verbose"
|
||||
"--user"
|
||||
cfg.username
|
||||
"-i"
|
||||
"--identity-file"
|
||||
cfg.identityFilePath
|
||||
cfg.host
|
||||
cfg.remoteFilesystem
|
||||
|
||||
@@ -205,9 +205,14 @@ in
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
description = "Kubernetes kubelet extra configuration file entries.";
|
||||
description = ''
|
||||
Kubernetes kubelet extra configuration file entries.
|
||||
|
||||
See also [Set Kubelet Parameters Via A Configuration File](https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/)
|
||||
and [Kubelet Configuration](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/).
|
||||
'';
|
||||
default = { };
|
||||
type = attrsOf attrs;
|
||||
type = attrsOf ((pkgs.formats.json { }).type);
|
||||
};
|
||||
|
||||
featureGates = mkOption {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
utils,
|
||||
...
|
||||
}:
|
||||
|
||||
@@ -15,6 +16,7 @@ let
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = name;
|
||||
example = "laptop-internal";
|
||||
description = "Keyboard name.";
|
||||
};
|
||||
@@ -35,16 +37,16 @@ let
|
||||
Since KMonad runs as an unprivileged user, it may sometimes
|
||||
need extra permissions in order to read the keyboard device
|
||||
file. If your keyboard's device file isn't in the input
|
||||
group you'll need to list its group in this option.
|
||||
group, you'll need to list its group in this option.
|
||||
'';
|
||||
};
|
||||
|
||||
defcfg = {
|
||||
enable = lib.mkEnableOption ''
|
||||
Automatically generate the defcfg block.
|
||||
automatic generation of the defcfg block.
|
||||
|
||||
When this is option is set to true the config option for
|
||||
this keyboard should not include a defcfg block.
|
||||
When this option is set to true, the config option for
|
||||
this keyboard should not include a defcfg block
|
||||
'';
|
||||
|
||||
compose = {
|
||||
@@ -55,15 +57,15 @@ let
|
||||
};
|
||||
|
||||
delay = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
type = lib.types.ints.unsigned;
|
||||
default = 5;
|
||||
description = "The delay (in milliseconds) between compose key sequences.";
|
||||
};
|
||||
};
|
||||
|
||||
fallthrough = lib.mkEnableOption "Re-emit unhandled key events.";
|
||||
fallthrough = lib.mkEnableOption "re-emitting unhandled key events";
|
||||
|
||||
allowCommands = lib.mkEnableOption "Allow keys to run shell commands.";
|
||||
allowCommands = lib.mkEnableOption "keys to run shell commands";
|
||||
};
|
||||
|
||||
config = lib.mkOption {
|
||||
@@ -71,12 +73,10 @@ let
|
||||
description = "Keyboard configuration.";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
name = lib.mkDefault name;
|
||||
};
|
||||
};
|
||||
|
||||
mkName = name: "kmonad-" + name;
|
||||
|
||||
# Create a complete KMonad configuration file:
|
||||
mkCfg =
|
||||
keyboard:
|
||||
@@ -84,7 +84,7 @@ let
|
||||
defcfg = ''
|
||||
(defcfg
|
||||
input (device-file "${keyboard.device}")
|
||||
output (uinput-sink "kmonad-${keyboard.name}")
|
||||
output (uinput-sink "${mkName keyboard.name}")
|
||||
${lib.optionalString (keyboard.defcfg.compose.key != null) ''
|
||||
cmp-seq ${keyboard.defcfg.compose.key}
|
||||
cmp-seq-delay ${toString keyboard.defcfg.compose.delay}
|
||||
@@ -95,9 +95,9 @@ let
|
||||
'';
|
||||
in
|
||||
pkgs.writeTextFile {
|
||||
name = "kmonad-${keyboard.name}.cfg";
|
||||
name = "${mkName keyboard.name}.kbd";
|
||||
text = lib.optionalString keyboard.defcfg.enable (defcfg + "\n") + keyboard.config;
|
||||
checkPhase = "${cfg.package}/bin/kmonad -d $out";
|
||||
checkPhase = "${lib.getExe cfg.package} -d $out";
|
||||
};
|
||||
|
||||
# Build a systemd path config that starts the service below when a
|
||||
@@ -105,7 +105,7 @@ let
|
||||
mkPath =
|
||||
keyboard:
|
||||
let
|
||||
name = "kmonad-${keyboard.name}";
|
||||
name = mkName keyboard.name;
|
||||
in
|
||||
lib.nameValuePair name {
|
||||
description = "KMonad trigger for ${keyboard.device}";
|
||||
@@ -119,19 +119,8 @@ let
|
||||
# Build a systemd service that starts KMonad:
|
||||
mkService =
|
||||
keyboard:
|
||||
let
|
||||
cmd =
|
||||
[
|
||||
(lib.getExe cfg.package)
|
||||
"--input"
|
||||
''device-file "${keyboard.device}"''
|
||||
]
|
||||
++ cfg.extraArgs
|
||||
++ [ "${mkCfg keyboard}" ];
|
||||
in
|
||||
lib.nameValuePair "kmonad-${keyboard.name}" {
|
||||
lib.nameValuePair (mkName keyboard.name) {
|
||||
description = "KMonad for ${keyboard.device}";
|
||||
script = lib.escapeShellArgs cmd;
|
||||
unitConfig = {
|
||||
# Control rate limiting.
|
||||
# Stop the restart logic if we restart more than
|
||||
@@ -140,6 +129,10 @@ let
|
||||
StartLimitBurst = 5;
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = ''
|
||||
${lib.getExe cfg.package} ${mkCfg keyboard} \
|
||||
${utils.escapeSystemdExecArgs cfg.extraArgs}
|
||||
'';
|
||||
Restart = "always";
|
||||
# Restart at increasing intervals from 2s to 1m
|
||||
RestartSec = 2;
|
||||
@@ -155,13 +148,26 @@ let
|
||||
config.users.groups.uinput.name
|
||||
] ++ keyboard.extraGroups;
|
||||
};
|
||||
# make sure the new config is used after nixos-rebuild switch
|
||||
# stopIfChanged controls[0] how a service is "restarted" during
|
||||
# nixos-rebuild switch. By default, stopIfChanged is true, which stops
|
||||
# the old service and then starts the new service after config updates.
|
||||
# Since we use path-based activation[1] here, the service unit will
|
||||
# immediately[2] be started by the path unit. Probably that start is
|
||||
# before config updates, whcih causes the service unit to use the old
|
||||
# config after nixos-rebuild switch. Setting stopIfChanged to false works
|
||||
# around this issue by restarting the service after config updates.
|
||||
# [0]: https://nixos.org/manual/nixos/unstable/#sec-switching-systems
|
||||
# [1]: man 7 daemon
|
||||
# [2]: man 5 systemd.path
|
||||
stopIfChanged = false;
|
||||
};
|
||||
in
|
||||
{
|
||||
options.services.kmonad = {
|
||||
enable = lib.mkEnableOption "KMonad: An advanced keyboard manager.";
|
||||
enable = lib.mkEnableOption "KMonad: an advanced keyboard manager";
|
||||
|
||||
package = lib.mkPackageOption pkgs "kmonad" { };
|
||||
package = lib.mkPackageOption pkgs "KMonad" { default = "kmonad"; };
|
||||
|
||||
keyboards = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule keyboard);
|
||||
@@ -183,9 +189,27 @@ in
|
||||
config = lib.mkIf cfg.enable {
|
||||
hardware.uinput.enable = true;
|
||||
|
||||
services.udev.extraRules =
|
||||
let
|
||||
mkRule = name: ''
|
||||
ACTION=="add", KERNEL=="event*", SUBSYSTEM=="input", ATTRS{name}=="${name}", ATTRS{id/product}=="5679", ATTRS{id/vendor}=="1235", SYMLINK+="input/by-id/${name}"
|
||||
'';
|
||||
in
|
||||
lib.foldlAttrs (
|
||||
rules: _: keyboard:
|
||||
rules + "\n" + mkRule (mkName keyboard.name)
|
||||
) "" cfg.keyboards;
|
||||
|
||||
systemd = {
|
||||
paths = lib.mapAttrs' (_: mkPath) cfg.keyboards;
|
||||
services = lib.mapAttrs' (_: mkService) cfg.keyboards;
|
||||
};
|
||||
};
|
||||
|
||||
meta = {
|
||||
maintainers = with lib.maintainers; [
|
||||
linj
|
||||
rvdp
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -459,10 +459,10 @@ in
|
||||
fi
|
||||
'';
|
||||
|
||||
systemd.services.systemd-udevd =
|
||||
{ restartTriggers = [ config.environment.etc."udev/rules.d".source ];
|
||||
};
|
||||
|
||||
systemd.services.systemd-udevd = {
|
||||
restartTriggers = [ config.environment.etc."udev/rules.d".source ];
|
||||
notSocketActivated = true;
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
|
||||
@@ -705,7 +705,7 @@ in
|
||||
|
||||
withJemalloc = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to preload jemalloc to reduce memory fragmentation and overall usage.
|
||||
'';
|
||||
@@ -1462,6 +1462,7 @@ in
|
||||
baseServiceConfig = {
|
||||
environment = optionalAttrs (cfg.withJemalloc) {
|
||||
LD_PRELOAD = "${pkgs.jemalloc}/lib/libjemalloc.so";
|
||||
PYTHONMALLOC = "malloc";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
|
||||
@@ -20,6 +20,12 @@ in
|
||||
default = 5055;
|
||||
description = ''The port which the Jellyseerr web UI should listen to.'';
|
||||
};
|
||||
|
||||
configDir = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
default = "/var/lib/jellyseerr/config";
|
||||
description = "Config data directory";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
@@ -27,14 +33,15 @@ in
|
||||
description = "Jellyseerr, a requests manager for Jellyfin";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment.PORT = toString cfg.port;
|
||||
environment = {
|
||||
PORT = toString cfg.port;
|
||||
CONFIG_DIRECTORY = cfg.configDir;
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "exec";
|
||||
StateDirectory = "jellyseerr";
|
||||
WorkingDirectory = "${cfg.package}/libexec/jellyseerr/deps/jellyseerr";
|
||||
DynamicUser = true;
|
||||
ExecStart = lib.getExe cfg.package;
|
||||
BindPaths = [ "/var/lib/jellyseerr/:${cfg.package}/libexec/jellyseerr/deps/jellyseerr/config/" ];
|
||||
Restart = "on-failure";
|
||||
ProtectHome = true;
|
||||
ProtectSystem = "strict";
|
||||
|
||||
@@ -24,9 +24,12 @@ in
|
||||
description = "Whether to enable the PyKMS service.";
|
||||
};
|
||||
|
||||
package = lib.mkPackageOption pkgs "pykms" { };
|
||||
|
||||
listenAddress = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "0.0.0.0";
|
||||
example = "::";
|
||||
description = "The IP address on which to listen.";
|
||||
};
|
||||
|
||||
@@ -78,13 +81,13 @@ in
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
# python programs with DynamicUser = true require HOME to be set
|
||||
environment.HOME = libDir;
|
||||
serviceConfig = with pkgs; {
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
StateDirectory = baseNameOf libDir;
|
||||
ExecStartPre = "${lib.getBin pykms}/libexec/create_pykms_db.sh ${libDir}/clients.db";
|
||||
ExecStartPre = "${lib.getBin cfg.package}/libexec/create_pykms_db.sh ${libDir}/clients.db";
|
||||
ExecStart = lib.concatStringsSep " " (
|
||||
[
|
||||
"${lib.getBin pykms}/bin/server"
|
||||
"${lib.getBin cfg.package}/bin/server"
|
||||
"--logfile=STDOUT"
|
||||
"--loglevel=${cfg.logLevel}"
|
||||
"--sqlite=${libDir}/clients.db"
|
||||
|
||||
@@ -31,18 +31,17 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
tokenPath = mkOption {
|
||||
environmentFile = mkOption {
|
||||
type = path;
|
||||
description = ''
|
||||
A run-time path to the token file, which is supposed to be provisioned
|
||||
outside of Nix store.
|
||||
An environment file containg at least the FASTLY_API_TOKEN= environment
|
||||
variable.
|
||||
'';
|
||||
};
|
||||
};
|
||||
serviceOpts = {
|
||||
serviceConfig = {
|
||||
LoadCredential = "fastly-api-token:${cfg.tokenPath}";
|
||||
Environment = [ "FASTLY_API_TOKEN=%d/fastly-api-token" ];
|
||||
EnvironmentFile = cfg.environmentFile;
|
||||
ExecStart = escapeSystemdExecArgs (
|
||||
[
|
||||
(getExe pkgs.prometheus-fastly-exporter)
|
||||
|
||||
@@ -36,8 +36,6 @@ let
|
||||
${lib.optionalString (cfg.cert != null) "cert=${cfg.cert}"}
|
||||
${lib.optionalString (cfg.pkey != null) "pkey=${cfg.pkey}"}
|
||||
${lib.optionalString (cfg.dh-file != null) "dh-file=${cfg.dh-file}"}
|
||||
no-stdout-log
|
||||
syslog
|
||||
pidfile=${pidfile}
|
||||
${lib.optionalString cfg.secure-stun "secure-stun"}
|
||||
${lib.optionalString cfg.no-cli "no-cli"}
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.ncps;
|
||||
|
||||
logLevels = [
|
||||
"trace"
|
||||
"debug"
|
||||
"info"
|
||||
"warn"
|
||||
"error"
|
||||
"fatal"
|
||||
"panic"
|
||||
];
|
||||
|
||||
globalFlags = lib.concatStringsSep " " (
|
||||
[ "--log-level='${cfg.logLevel}'" ]
|
||||
++ (lib.optionals cfg.openTelemetry.enable (
|
||||
[
|
||||
"--otel-enabled"
|
||||
]
|
||||
++ (lib.optional (
|
||||
cfg.openTelemetry.grpcURL != null
|
||||
) "--otel-grpc-url='${cfg.openTelemetry.grpcURL}'")
|
||||
))
|
||||
);
|
||||
|
||||
serveFlags = lib.concatStringsSep " " (
|
||||
[
|
||||
"--cache-hostname='${cfg.cache.hostName}'"
|
||||
"--cache-data-path='${cfg.cache.dataPath}'"
|
||||
"--cache-database-url='${cfg.cache.databaseURL}'"
|
||||
"--server-addr='${cfg.server.addr}'"
|
||||
]
|
||||
++ (lib.optional cfg.cache.allowDeleteVerb "--cache-allow-delete-verb")
|
||||
++ (lib.optional cfg.cache.allowPutVerb "--cache-allow-put-verb")
|
||||
++ (lib.optional (cfg.cache.maxSize != null) "--cache-max-size='${cfg.cache.maxSize}'")
|
||||
++ (lib.optionals (cfg.cache.lru.schedule != null) [
|
||||
"--cache-lru-schedule='${cfg.cache.lru.schedule}'"
|
||||
"--cache-lru-schedule-timezone='${cfg.cache.lru.scheduleTimeZone}'"
|
||||
])
|
||||
++ (lib.optional (cfg.cache.secretKeyPath != null) "--cache-secret-key-path='%d/secretKey'")
|
||||
++ (lib.forEach cfg.upstream.caches (url: "--upstream-cache='${url}'"))
|
||||
++ (lib.forEach cfg.upstream.publicKeys (pk: "--upstream-public-key='${pk}'"))
|
||||
);
|
||||
|
||||
isSqlite = lib.strings.hasPrefix "sqlite:" cfg.cache.databaseURL;
|
||||
|
||||
dbPath = lib.removePrefix "sqlite:" cfg.cache.databaseURL;
|
||||
dbDir = dirOf dbPath;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
services.ncps = {
|
||||
enable = lib.mkEnableOption "ncps: Nix binary cache proxy service implemented in Go";
|
||||
|
||||
package = lib.mkPackageOption pkgs "ncps" { };
|
||||
|
||||
dbmatePackage = lib.mkPackageOption pkgs "dbmate" { };
|
||||
|
||||
openTelemetry = {
|
||||
enable = lib.mkEnableOption "Enable OpenTelemetry logs, metrics, and tracing";
|
||||
|
||||
grpcURL = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
Configure OpenTelemetry gRPC URL. Missing or "https" scheme enables
|
||||
secure gRPC, "insecure" otherwise. Omit to emit telemetry to
|
||||
stdout.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
logLevel = lib.mkOption {
|
||||
type = lib.types.enum logLevels;
|
||||
default = "info";
|
||||
description = ''
|
||||
Set the level for logging. Refer to
|
||||
<https://pkg.go.dev/github.com/rs/zerolog#readme-leveled-logging> for
|
||||
more information.
|
||||
'';
|
||||
};
|
||||
|
||||
cache = {
|
||||
allowDeleteVerb = lib.mkEnableOption ''
|
||||
Whether to allow the DELETE verb to delete narinfo and nar files from
|
||||
the cache.
|
||||
'';
|
||||
|
||||
allowPutVerb = lib.mkEnableOption ''
|
||||
Whether to allow the PUT verb to push narinfo and nar files directly
|
||||
to the cache.
|
||||
'';
|
||||
|
||||
hostName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = ''
|
||||
The hostname of the cache server. **This is used to generate the
|
||||
private key used for signing store paths (.narinfo)**
|
||||
'';
|
||||
};
|
||||
|
||||
dataPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/ncps";
|
||||
description = ''
|
||||
The local directory for storing configuration and cached store paths
|
||||
'';
|
||||
};
|
||||
|
||||
databaseURL = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "sqlite:${cfg.cache.dataPath}/db/db.sqlite";
|
||||
defaultText = "sqlite:/var/lib/ncps/db/db.sqlite";
|
||||
description = ''
|
||||
The URL of the database (currently only SQLite is supported)
|
||||
'';
|
||||
};
|
||||
|
||||
lru = {
|
||||
schedule = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "0 2 * * *";
|
||||
description = ''
|
||||
The cron spec for cleaning the store to keep it under
|
||||
config.ncps.cache.maxSize. Refer to
|
||||
https://pkg.go.dev/github.com/robfig/cron/v3#hdr-Usage for
|
||||
documentation.
|
||||
'';
|
||||
};
|
||||
|
||||
scheduleTimeZone = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "Local";
|
||||
example = "America/Los_Angeles";
|
||||
description = ''
|
||||
The name of the timezone to use for the cron schedule. See
|
||||
<https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>
|
||||
for a comprehensive list of possible values for this setting.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
maxSize = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "100G";
|
||||
description = ''
|
||||
The maximum size of the store. It can be given with units such as
|
||||
5K, 10G etc. Supported units: B, K, M, G, T.
|
||||
'';
|
||||
};
|
||||
|
||||
secretKeyPath = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
The path to load the secretKey for signing narinfos. Leave this
|
||||
empty to automatically generate a private/public key.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
server = {
|
||||
addr = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = ":8501";
|
||||
description = ''
|
||||
The address and port the server listens on.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
upstream = {
|
||||
caches = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
example = [ "https://cache.nixos.org" ];
|
||||
description = ''
|
||||
A list of URLs of upstream binary caches.
|
||||
'';
|
||||
};
|
||||
|
||||
publicKeys = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
|
||||
description = ''
|
||||
A list of public keys of upstream caches in the format
|
||||
`host[-[0-9]*]:public-key`. This flag is used to verify the
|
||||
signatures of store paths downloaded from upstream caches.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.cache.lru.schedule == null || cfg.cache.maxSize != null;
|
||||
message = "You must specify config.ncps.cache.lru.schedule when config.ncps.cache.maxSize is set";
|
||||
}
|
||||
|
||||
{
|
||||
assertion = cfg.cache.secretKeyPath == null || (builtins.pathExists cfg.cache.secretKeyPath);
|
||||
message = "config.ncps.cache.secresecretKeyPath=${cfg.cache.secretKeyPath} must exist but does not";
|
||||
}
|
||||
];
|
||||
|
||||
users.users.ncps = {
|
||||
isSystemUser = true;
|
||||
group = "ncps";
|
||||
};
|
||||
users.groups.ncps = { };
|
||||
|
||||
systemd.services.ncps-create-datadirs = {
|
||||
description = "Created required directories by ncps";
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
UMask = "0066";
|
||||
};
|
||||
script =
|
||||
(lib.optionalString (cfg.cache.dataPath != "/var/lib/ncps") ''
|
||||
if ! test -d ${cfg.cache.dataPath}; then
|
||||
mkdir -p ${cfg.cache.dataPath}
|
||||
chown ncps:ncps ${cfg.cache.dataPath}
|
||||
fi
|
||||
'')
|
||||
+ (lib.optionalString isSqlite ''
|
||||
if ! test -d ${dbDir}; then
|
||||
mkdir -p ${dbDir}
|
||||
chown ncps:ncps ${dbDir}
|
||||
fi
|
||||
'');
|
||||
wantedBy = [ "ncps.service" ];
|
||||
before = [ "ncps.service" ];
|
||||
};
|
||||
|
||||
systemd.services.ncps = {
|
||||
description = "ncps binary cache proxy service";
|
||||
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
preStart = ''
|
||||
${lib.getExe cfg.dbmatePackage} --migrations-dir=${cfg.package}/share/ncps/db/migrations --url=${cfg.cache.databaseURL} up
|
||||
'';
|
||||
|
||||
serviceConfig = lib.mkMerge [
|
||||
{
|
||||
ExecStart = "${lib.getExe cfg.package} ${globalFlags} serve ${serveFlags}";
|
||||
User = "ncps";
|
||||
Group = "ncps";
|
||||
Restart = "on-failure";
|
||||
RuntimeDirectory = "ncps";
|
||||
}
|
||||
|
||||
# credentials
|
||||
(lib.mkIf (cfg.cache.secretKeyPath != null) {
|
||||
LoadCredential = "secretKey:${cfg.cache.secretKeyPath}";
|
||||
})
|
||||
|
||||
# ensure permissions on required directories
|
||||
(lib.mkIf (cfg.cache.dataPath != "/var/lib/ncps") {
|
||||
ReadWritePaths = [ cfg.cache.dataPath ];
|
||||
})
|
||||
(lib.mkIf (cfg.cache.dataPath == "/var/lib/ncps") {
|
||||
StateDirectory = "ncps";
|
||||
StateDirectoryMode = "0700";
|
||||
})
|
||||
(lib.mkIf (isSqlite && !lib.strings.hasPrefix "/var/lib/ncps" dbDir) {
|
||||
ReadWritePaths = [ dbDir ];
|
||||
})
|
||||
|
||||
# Hardening
|
||||
{
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged"
|
||||
"~@resources"
|
||||
];
|
||||
CapabilityBoundingSet = "";
|
||||
PrivateUsers = true;
|
||||
DevicePolicy = "closed";
|
||||
DeviceAllow = [ "" ];
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectProc = "invisible";
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
RestrictSUIDSGID = true;
|
||||
RestrictRealtime = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
ProcSubset = "pid";
|
||||
RestrictNamespaces = true;
|
||||
SystemCallArchitectures = "native";
|
||||
PrivateNetwork = false;
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
PrivateMounts = true;
|
||||
NoNewPrivileges = true;
|
||||
LockPersonality = true;
|
||||
RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
|
||||
LimitNOFILE = 65536;
|
||||
UMask = "0066";
|
||||
}
|
||||
];
|
||||
|
||||
unitConfig.RequiresMountsFor = lib.concatStringsSep " " (
|
||||
[ "${cfg.cache.dataPath}" ] ++ lib.optional (isSqlite) dbDir
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ kalbasit ];
|
||||
}
|
||||
@@ -143,10 +143,10 @@ in
|
||||
restartTriggers = [ cfg.unifiPackage cfg.mongodbPackage ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
Type = "notify";
|
||||
ExecStart = "${cmd} start";
|
||||
ExecStop = "${cmd} stop";
|
||||
Restart = "on-failure";
|
||||
Restart = "always";
|
||||
TimeoutSec = "5min";
|
||||
User = "unifi";
|
||||
UMask = "0077";
|
||||
|
||||
@@ -419,7 +419,7 @@ in
|
||||
|
||||
${optionalString (containsGutenprint cfg.drivers) ''
|
||||
if [ -d /var/lib/cups/ppd ]; then
|
||||
${getGutenprint cfg.drivers}/bin/cups-genppdupdate -p /var/lib/cups/ppd
|
||||
${getGutenprint cfg.drivers}/bin/cups-genppdupdate -x -p /var/lib/cups/ppd
|
||||
fi
|
||||
''}
|
||||
'';
|
||||
|
||||
@@ -130,7 +130,7 @@ in
|
||||
type = addCheck str (
|
||||
x:
|
||||
cfg.svcManager == "command"
|
||||
|| elem x [
|
||||
|| lib.elem x [
|
||||
"restart"
|
||||
"reload"
|
||||
"nop"
|
||||
|
||||
@@ -193,7 +193,7 @@ in
|
||||
ExecStart =
|
||||
with cfg;
|
||||
let
|
||||
opt = n: v: optionalString (v != null) ''-${n}="${v}"'';
|
||||
opt = n: v: lib.optionalString (v != null) ''-${n}="${v}"'';
|
||||
in
|
||||
lib.concatStringsSep " \\\n" [
|
||||
"${pkgs.cfssl}/bin/cfssl serve"
|
||||
|
||||
@@ -94,14 +94,14 @@ in
|
||||
Restart = "always";
|
||||
ExecStart =
|
||||
with cfg;
|
||||
concatStringsSep " " (
|
||||
lib.concatStringsSep " " (
|
||||
[
|
||||
(lib.getExe cfg.package)
|
||||
"-logtostderr"
|
||||
"-host=${listenAddress}"
|
||||
"-port=${toString port}"
|
||||
]
|
||||
++ optionals prometheus.enable [
|
||||
++ lib.optionals prometheus.enable [
|
||||
"-enable_prometheus"
|
||||
"-prometheus_host=${prometheus.listenAddress}"
|
||||
"-prometheus_port=${toString prometheus.port}"
|
||||
@@ -149,7 +149,7 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = with cfg; optionals openFirewall [ port ];
|
||||
networking.firewall.allowedTCPPorts = with cfg; lib.optionals openFirewall [ port ];
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ azahi ];
|
||||
|
||||
@@ -60,7 +60,7 @@ in
|
||||
Restart = "always";
|
||||
ExecStart =
|
||||
with cfg;
|
||||
concatStringsSep " " (
|
||||
lib.concatStringsSep " " (
|
||||
[
|
||||
"${pkgs.endlessh}/bin/endlessh"
|
||||
"-p ${toString port}"
|
||||
@@ -109,7 +109,7 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = with cfg; optionals openFirewall [ port ];
|
||||
networking.firewall.allowedTCPPorts = with cfg; lib.optionals openFirewall [ port ];
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ azahi ];
|
||||
|
||||
@@ -150,7 +150,7 @@ in
|
||||
};
|
||||
|
||||
Ebpf.ModulesPath = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default =
|
||||
if cfg.settings.ProcMonitorMethod == "ebpf" then
|
||||
"${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd"
|
||||
@@ -202,10 +202,16 @@ in
|
||||
services.opensnitchd = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = [
|
||||
""
|
||||
"${pkgs.opensnitch}/bin/opensnitchd --config-file ${format.generate "default-config.json" cfg.settings}"
|
||||
];
|
||||
ExecStart =
|
||||
let
|
||||
preparedSettings = removeAttrs cfg.settings (
|
||||
lib.optional (cfg.settings.ProcMonitorMethod != "ebpf") "Ebpf"
|
||||
);
|
||||
in
|
||||
[
|
||||
""
|
||||
"${pkgs.opensnitch}/bin/opensnitchd --config-file ${format.generate "default-config.json" preparedSettings}"
|
||||
];
|
||||
};
|
||||
preStart = lib.mkIf (cfg.rules != { }) (
|
||||
let
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with builtins;
|
||||
let
|
||||
cfg = config.services.tor;
|
||||
opt = options.services.tor;
|
||||
@@ -15,7 +14,7 @@ let
|
||||
See [torrc manual](https://2019.www.torproject.org/docs/tor-manual.html.en#${option}).
|
||||
'';
|
||||
bindsPrivilegedPort =
|
||||
any
|
||||
lib.any
|
||||
(
|
||||
p0:
|
||||
let
|
||||
@@ -25,20 +24,22 @@ let
|
||||
false
|
||||
else
|
||||
let
|
||||
p2 = if isInt p1 then p1 else toInt p1;
|
||||
p2 = if lib.isInt p1 then p1 else lib.toInt p1;
|
||||
in
|
||||
p1 != null && 0 < p2 && p2 < 1024
|
||||
)
|
||||
(flatten [
|
||||
cfg.settings.ORPort
|
||||
cfg.settings.DirPort
|
||||
cfg.settings.DNSPort
|
||||
cfg.settings.ExtORPort
|
||||
cfg.settings.HTTPTunnelPort
|
||||
cfg.settings.NATDPort
|
||||
cfg.settings.SOCKSPort
|
||||
cfg.settings.TransPort
|
||||
]);
|
||||
(
|
||||
lib.flatten [
|
||||
cfg.settings.ORPort
|
||||
cfg.settings.DirPort
|
||||
cfg.settings.DNSPort
|
||||
cfg.settings.ExtORPort
|
||||
cfg.settings.HTTPTunnelPort
|
||||
cfg.settings.NATDPort
|
||||
cfg.settings.SOCKSPort
|
||||
cfg.settings.TransPort
|
||||
]
|
||||
);
|
||||
optionBool =
|
||||
optionName:
|
||||
lib.mkOption {
|
||||
@@ -68,7 +69,7 @@ let
|
||||
description = (descriptionGeneric optionName);
|
||||
};
|
||||
optionAddress = lib.mkOption {
|
||||
type = with types; nullOr str;
|
||||
type = with lib.types; nullOr str;
|
||||
default = null;
|
||||
example = "0.0.0.0";
|
||||
description = ''
|
||||
@@ -76,7 +77,7 @@ let
|
||||
'';
|
||||
};
|
||||
optionUnix = lib.mkOption {
|
||||
type = with types; nullOr path;
|
||||
type = with lib.types; nullOr path;
|
||||
default = null;
|
||||
description = ''
|
||||
Unix domain socket path to use.
|
||||
@@ -84,7 +85,7 @@ let
|
||||
};
|
||||
optionPort = lib.mkOption {
|
||||
type =
|
||||
with types;
|
||||
with lib.types;
|
||||
nullOr (oneOf [
|
||||
port
|
||||
(enum [ "auto" ])
|
||||
@@ -125,8 +126,8 @@ let
|
||||
);
|
||||
config = {
|
||||
flags =
|
||||
filter (name: config.${name} == true) isolateFlags
|
||||
++ optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
|
||||
lib.filter (name: config.${name} == true) isolateFlags
|
||||
++ lib.optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
|
||||
};
|
||||
}
|
||||
))
|
||||
@@ -195,14 +196,14 @@ let
|
||||
config = lib.mkIf doConfig {
|
||||
# Only add flags in SOCKSPort to avoid duplicates
|
||||
flags =
|
||||
filter (name: config.${name} == true) flags
|
||||
++ optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
|
||||
lib.filter (name: config.${name} == true) flags
|
||||
++ lib.optional (config.SessionGroup != null) "SessionGroup=${toString config.SessionGroup}";
|
||||
};
|
||||
}
|
||||
))
|
||||
];
|
||||
optionFlags = lib.mkOption {
|
||||
type = with types; listOf str;
|
||||
type = with lib.types; listOf str;
|
||||
default = [ ];
|
||||
};
|
||||
optionORPort =
|
||||
@@ -243,7 +244,7 @@ let
|
||||
}
|
||||
);
|
||||
config = {
|
||||
flags = filter (name: config.${name} == true) flags;
|
||||
flags = lib.filter (name: config.${name} == true) flags;
|
||||
};
|
||||
}
|
||||
))
|
||||
@@ -270,33 +271,33 @@ let
|
||||
k: v:
|
||||
if v == null then
|
||||
""
|
||||
else if isBool v then
|
||||
else if lib.isBool v then
|
||||
(if v then "1" else "0")
|
||||
else if v ? "unix" && v.unix != null then
|
||||
"unix:" + v.unix + optionalString (v ? "flags") (" " + concatStringsSep " " v.flags)
|
||||
"unix:" + v.unix + lib.optionalString (v ? "flags") (" " + lib.concatStringsSep " " v.flags)
|
||||
else if v ? "port" && v.port != null then
|
||||
optionalString (v ? "addr" && v.addr != null) "${v.addr}:"
|
||||
lib.optionalString (v ? "addr" && v.addr != null) "${v.addr}:"
|
||||
+ toString v.port
|
||||
+ optionalString (v ? "flags") (" " + concatStringsSep " " v.flags)
|
||||
+ lib.optionalString (v ? "flags") (" " + lib.concatStringsSep " " v.flags)
|
||||
else if k == "ServerTransportPlugin" then
|
||||
optionalString (v.transports != [ ]) "${concatStringsSep "," v.transports} exec ${v.exec}"
|
||||
lib.optionalString (v.transports != [ ]) "${lib.concatStringsSep "," v.transports} exec ${v.exec}"
|
||||
else if k == "HidServAuth" then
|
||||
v.onion + " " + v.auth
|
||||
else
|
||||
generators.mkValueStringDefault { } v;
|
||||
lib.generators.mkValueStringDefault { } v;
|
||||
genTorrc =
|
||||
settings:
|
||||
generators.toKeyValue
|
||||
lib.generators.toKeyValue
|
||||
{
|
||||
listsAsDuplicateKeys = true;
|
||||
mkKeyValue = k: generators.mkKeyValueDefault { mkValueString = mkValueString k; } " " k;
|
||||
mkKeyValue = k: lib.generators.mkKeyValueDefault { mkValueString = mkValueString k; } " " k;
|
||||
}
|
||||
(
|
||||
lib.mapAttrs (
|
||||
k: v:
|
||||
# Not necesssary, but prettier rendering
|
||||
if
|
||||
elem k [
|
||||
lib.elem k [
|
||||
"AutomapHostsSuffixes"
|
||||
"DirPolicy"
|
||||
"ExitPolicy"
|
||||
@@ -304,15 +305,15 @@ let
|
||||
]
|
||||
&& v != [ ]
|
||||
then
|
||||
concatStringsSep "," v
|
||||
lib.concatStringsSep "," v
|
||||
else
|
||||
v
|
||||
) (lib.filterAttrs (k: v: !(v == null || v == "")) settings)
|
||||
);
|
||||
torrc = pkgs.writeText "torrc" (
|
||||
genTorrc cfg.settings
|
||||
+ concatStrings (
|
||||
mapAttrsToList (
|
||||
+ lib.concatStrings (
|
||||
lib.mapAttrsToList (
|
||||
name: onion: "HiddenServiceDir ${onion.path}\n" + genTorrc onion.settings
|
||||
) cfg.relay.onionServices
|
||||
)
|
||||
@@ -506,7 +507,7 @@ in
|
||||
};
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ name, config, ... }:
|
||||
{ ... }:
|
||||
{
|
||||
options.clientAuthorizations = lib.mkOption {
|
||||
description = ''
|
||||
@@ -748,7 +749,7 @@ in
|
||||
]);
|
||||
apply = map (
|
||||
v:
|
||||
if isInt v then
|
||||
if lib.isInt v then
|
||||
{
|
||||
port = v;
|
||||
target = null;
|
||||
@@ -810,11 +811,11 @@ in
|
||||
};
|
||||
};
|
||||
config = {
|
||||
path = mkDefault ((if config.secretKey == null then stateDir else runDir) + "/onion/${name}");
|
||||
path = lib.mkDefault ((if config.secretKey == null then stateDir else runDir) + "/onion/${name}");
|
||||
settings.HiddenServiceVersion = config.version;
|
||||
settings.HiddenServiceAuthorizeClient =
|
||||
if config.authorizeClient != null then
|
||||
config.authorizeClient.authType + " " + concatStringsSep "," config.authorizeClient.clientNames
|
||||
config.authorizeClient.authType + " " + lib.concatStringsSep "," config.authorizeClient.clientNames
|
||||
else
|
||||
null;
|
||||
settings.HiddenServicePort = map (
|
||||
@@ -890,7 +891,7 @@ in
|
||||
options.ConnDirectionStatistics = optionBool "ConnDirectionStatistics";
|
||||
options.ConstrainedSockets = optionBool "ConstrainedSockets";
|
||||
options.ContactInfo = optionString "ContactInfo";
|
||||
options.ControlPort = lib.mkOption rec {
|
||||
options.ControlPort = lib.mkOption {
|
||||
description = (descriptionGeneric "ControlPort");
|
||||
default = [ ];
|
||||
example = [ { port = 9051; } ];
|
||||
@@ -927,7 +928,7 @@ in
|
||||
}
|
||||
);
|
||||
config = {
|
||||
flags = filter (name: config.${name} == true) flags;
|
||||
flags = lib.filter (name: config.${name} == true) flags;
|
||||
};
|
||||
}
|
||||
))
|
||||
@@ -996,7 +997,7 @@ in
|
||||
}
|
||||
))
|
||||
]);
|
||||
apply = p: if isInt p || isString p then { port = p; } else p;
|
||||
apply = p: if lib.isInt p || lib.isString p then { port = p; } else p;
|
||||
};
|
||||
options.ExtORPortCookieAuthFile = optionPath "ExtORPortCookieAuthFile";
|
||||
options.ExtORPortCookieAuthFileGroupReadable = optionBool "ExtORPortCookieAuthFileGroupReadable";
|
||||
@@ -1177,8 +1178,11 @@ in
|
||||
# Not sure if `cfg.relay.role == "private-bridge"` helps as tor
|
||||
# sends a lot of stats
|
||||
warnings =
|
||||
optional
|
||||
(cfg.settings.BridgeRelay && flatten (mapAttrsToList (n: o: o.map) cfg.relay.onionServices) != [ ])
|
||||
lib.optional
|
||||
(
|
||||
cfg.settings.BridgeRelay
|
||||
&& lib.flatten (lib.mapAttrsToList (n: o: o.map) cfg.relay.onionServices) != [ ]
|
||||
)
|
||||
''
|
||||
Running Tor hidden services on a public relay makes the
|
||||
presence of hidden services visible through simple statistical
|
||||
@@ -1189,21 +1193,21 @@ in
|
||||
actually hide your hidden services. In either case, you can
|
||||
always create a container/VM with a separate Tor daemon instance.
|
||||
''
|
||||
++ flatten (
|
||||
mapAttrsToList (
|
||||
++ lib.flatten (
|
||||
lib.mapAttrsToList (
|
||||
n: o:
|
||||
optionals (o.settings.HiddenServiceVersion == 2) [
|
||||
(optional (o.settings.HiddenServiceExportCircuitID != null) ''
|
||||
lib.optionals (o.settings.HiddenServiceVersion == 2) [
|
||||
(lib.optional (o.settings.HiddenServiceExportCircuitID != null) ''
|
||||
HiddenServiceExportCircuitID is used in the HiddenService: ${n}
|
||||
but this option is only for v3 hidden services.
|
||||
'')
|
||||
]
|
||||
++ optionals (o.settings.HiddenServiceVersion != 2) [
|
||||
(optional (o.settings.HiddenServiceAuthorizeClient != null) ''
|
||||
++ lib.optionals (o.settings.HiddenServiceVersion != 2) [
|
||||
(lib.optional (o.settings.HiddenServiceAuthorizeClient != null) ''
|
||||
HiddenServiceAuthorizeClient is used in the HiddenService: ${n}
|
||||
but this option is only for v2 hidden services.
|
||||
'')
|
||||
(optional (o.settings.RendPostPeriod != null) ''
|
||||
(lib.optional (o.settings.RendPostPeriod != null) ''
|
||||
RendPostPeriod is used in the HiddenService: ${n}
|
||||
but this option is only for v2 hidden services.
|
||||
'')
|
||||
@@ -1220,12 +1224,12 @@ in
|
||||
uid = config.ids.uids.tor;
|
||||
};
|
||||
|
||||
services.tor.settings = mkMerge [
|
||||
(mkIf cfg.enableGeoIP {
|
||||
services.tor.settings = lib.mkMerge [
|
||||
(lib.mkIf cfg.enableGeoIP {
|
||||
GeoIPFile = "${cfg.package.geoip}/share/tor/geoip";
|
||||
GeoIPv6File = "${cfg.package.geoip}/share/tor/geoip6";
|
||||
})
|
||||
(mkIf cfg.controlSocket.enable {
|
||||
(lib.mkIf cfg.controlSocket.enable {
|
||||
ControlPort = [
|
||||
{
|
||||
unix = runDir + "/control";
|
||||
@@ -1234,47 +1238,47 @@ in
|
||||
}
|
||||
];
|
||||
})
|
||||
(mkIf cfg.relay.enable (
|
||||
optionalAttrs (cfg.relay.role != "exit") {
|
||||
ExitPolicy = mkForce [ "reject *:*" ];
|
||||
(lib.mkIf cfg.relay.enable (
|
||||
lib.optionalAttrs (cfg.relay.role != "exit") {
|
||||
ExitPolicy = lib.mkForce [ "reject *:*" ];
|
||||
}
|
||||
//
|
||||
optionalAttrs
|
||||
(elem cfg.relay.role [
|
||||
lib.optionalAttrs
|
||||
(lib.elem cfg.relay.role [
|
||||
"bridge"
|
||||
"private-bridge"
|
||||
])
|
||||
{
|
||||
BridgeRelay = true;
|
||||
ExtORPort.port = mkDefault "auto";
|
||||
ServerTransportPlugin.transports = mkDefault [ "obfs4" ];
|
||||
ServerTransportPlugin.exec = mkDefault "${lib.getExe pkgs.obfs4} managed";
|
||||
ExtORPort.port = lib.mkDefault "auto";
|
||||
ServerTransportPlugin.transports = lib.mkDefault [ "obfs4" ];
|
||||
ServerTransportPlugin.exec = lib.mkDefault "${lib.getExe pkgs.obfs4} managed";
|
||||
}
|
||||
// optionalAttrs (cfg.relay.role == "private-bridge") {
|
||||
// lib.optionalAttrs (cfg.relay.role == "private-bridge") {
|
||||
ExtraInfoStatistics = false;
|
||||
PublishServerDescriptor = false;
|
||||
}
|
||||
))
|
||||
(mkIf (!cfg.relay.enable) {
|
||||
(lib.mkIf (!cfg.relay.enable) {
|
||||
# Avoid surprises when leaving ORPort/DirPort configurations in cfg.settings,
|
||||
# because it would still enable Tor as a relay,
|
||||
# which can trigger all sort of problems when not carefully done,
|
||||
# like the blocklisting of the machine's IP addresses
|
||||
# by some hosting providers...
|
||||
DirPort = mkForce [ ];
|
||||
ORPort = mkForce [ ];
|
||||
PublishServerDescriptor = mkForce false;
|
||||
DirPort = lib.mkForce [ ];
|
||||
ORPort = lib.mkForce [ ];
|
||||
PublishServerDescriptor = lib.mkForce false;
|
||||
})
|
||||
(mkIf (!cfg.client.enable) {
|
||||
(lib.mkIf (!cfg.client.enable) {
|
||||
# Make sure application connections via SOCKS are disabled
|
||||
# when services.tor.client.enable is false
|
||||
SOCKSPort = mkForce [ 0 ];
|
||||
SOCKSPort = lib.mkForce [ 0 ];
|
||||
})
|
||||
(mkIf cfg.client.enable (
|
||||
(lib.mkIf cfg.client.enable (
|
||||
{
|
||||
SOCKSPort = [ cfg.client.socksListenAddress ];
|
||||
}
|
||||
// optionalAttrs cfg.client.transparentProxy.enable {
|
||||
// lib.optionalAttrs cfg.client.transparentProxy.enable {
|
||||
TransPort = [
|
||||
{
|
||||
addr = "127.0.0.1";
|
||||
@@ -1282,7 +1286,7 @@ in
|
||||
}
|
||||
];
|
||||
}
|
||||
// optionalAttrs cfg.client.dns.enable {
|
||||
// lib.optionalAttrs cfg.client.dns.enable {
|
||||
DNSPort = [
|
||||
{
|
||||
addr = "127.0.0.1";
|
||||
@@ -1292,8 +1296,8 @@ in
|
||||
AutomapHostsOnResolve = true;
|
||||
}
|
||||
//
|
||||
optionalAttrs
|
||||
(flatten (mapAttrsToList (n: o: o.clientAuthorizations) cfg.client.onionServices) != [ ])
|
||||
lib.optionalAttrs
|
||||
(lib.flatten (lib.mapAttrsToList (n: o: o.clientAuthorizations) cfg.client.onionServices) != [ ])
|
||||
{
|
||||
ClientOnionAuthDir = runDir + "/ClientOnionAuthDir";
|
||||
}
|
||||
@@ -1302,15 +1306,20 @@ in
|
||||
|
||||
networking.firewall = lib.mkIf cfg.openFirewall {
|
||||
allowedTCPPorts =
|
||||
concatMap
|
||||
lib.concatMap
|
||||
(
|
||||
o:
|
||||
if isInt o && o > 0 then [ o ] else optionals (o ? "port" && isInt o.port && o.port > 0) [ o.port ]
|
||||
if lib.isInt o && o > 0 then
|
||||
[ o ]
|
||||
else
|
||||
lib.optionals (o ? "port" && lib.isInt o.port && o.port > 0) [ o.port ]
|
||||
)
|
||||
(flatten [
|
||||
cfg.settings.ORPort
|
||||
cfg.settings.DirPort
|
||||
]);
|
||||
(
|
||||
lib.flatten [
|
||||
cfg.settings.ORPort
|
||||
cfg.settings.DirPort
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
systemd.services.tor = {
|
||||
@@ -1331,40 +1340,40 @@ in
|
||||
(
|
||||
"+"
|
||||
+ pkgs.writeShellScript "ExecStartPre" (
|
||||
concatStringsSep "\n" (
|
||||
flatten (
|
||||
lib.concatStringsSep "\n" (
|
||||
lib.flatten (
|
||||
[ "set -eu" ]
|
||||
++ mapAttrsToList (
|
||||
++ lib.mapAttrsToList (
|
||||
name: onion:
|
||||
optional (onion.authorizedClients != [ ]) ''
|
||||
rm -rf ${escapeShellArg onion.path}/authorized_clients
|
||||
install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path} ${escapeShellArg onion.path}/authorized_clients
|
||||
lib.optional (onion.authorizedClients != [ ]) ''
|
||||
rm -rf ${lib.escapeShellArg onion.path}/authorized_clients
|
||||
install -d -o tor -g tor -m 0700 ${lib.escapeShellArg onion.path} ${lib.escapeShellArg onion.path}/authorized_clients
|
||||
''
|
||||
++ imap0 (i: pubKey: ''
|
||||
++ lib.imap0 (i: pubKey: ''
|
||||
echo ${pubKey} |
|
||||
install -o tor -g tor -m 0400 /dev/stdin ${escapeShellArg onion.path}/authorized_clients/${toString i}.auth
|
||||
install -o tor -g tor -m 0400 /dev/stdin ${lib.escapeShellArg onion.path}/authorized_clients/${toString i}.auth
|
||||
'') onion.authorizedClients
|
||||
++ optional (onion.secretKey != null) ''
|
||||
install -d -o tor -g tor -m 0700 ${escapeShellArg onion.path}
|
||||
key="$(cut -f1 -d: ${escapeShellArg onion.secretKey} | head -1)"
|
||||
++ lib.optional (onion.secretKey != null) ''
|
||||
install -d -o tor -g tor -m 0700 ${lib.escapeShellArg onion.path}
|
||||
key="$(cut -f1 -d: ${lib.escapeShellArg onion.secretKey} | head -1)"
|
||||
case "$key" in
|
||||
("== ed25519v"*"-secret")
|
||||
install -o tor -g tor -m 0400 ${escapeShellArg onion.secretKey} ${escapeShellArg onion.path}/hs_ed25519_secret_key;;
|
||||
install -o tor -g tor -m 0400 ${lib.escapeShellArg onion.secretKey} ${lib.escapeShellArg onion.path}/hs_ed25519_secret_key;;
|
||||
(*) echo >&2 "NixOS does not (yet) support secret key type for onion: ${name}"; exit 1;;
|
||||
esac
|
||||
''
|
||||
) cfg.relay.onionServices
|
||||
++ mapAttrsToList (
|
||||
++ lib.mapAttrsToList (
|
||||
name: onion:
|
||||
imap0 (
|
||||
lib.imap0 (
|
||||
i: prvKeyPath:
|
||||
let
|
||||
hostname = removeSuffix ".onion" name;
|
||||
hostname = lib.removeSuffix ".onion" name;
|
||||
in
|
||||
''
|
||||
printf "%s:" ${escapeShellArg hostname} | cat - ${escapeShellArg prvKeyPath} |
|
||||
printf "%s:" ${lib.escapeShellArg hostname} | cat - ${lib.escapeShellArg prvKeyPath} |
|
||||
install -o tor -g tor -m 0700 /dev/stdin \
|
||||
${runDir}/ClientOnionAuthDir/${escapeShellArg hostname}.${toString i}.auth_private
|
||||
${runDir}/ClientOnionAuthDir/${lib.escapeShellArg hostname}.${toString i}.auth_private
|
||||
''
|
||||
) onion.clientAuthorizations
|
||||
) cfg.client.onionServices
|
||||
@@ -1393,9 +1402,9 @@ in
|
||||
"tor"
|
||||
"tor/onion"
|
||||
]
|
||||
++ flatten (
|
||||
mapAttrsToList (
|
||||
name: onion: optional (onion.secretKey == null) "tor/onion/${name}"
|
||||
++ lib.flatten (
|
||||
lib.mapAttrsToList (
|
||||
name: onion: lib.optional (onion.secretKey == null) "tor/onion/${name}"
|
||||
) cfg.relay.onionServices
|
||||
);
|
||||
# The following options are only to optimize:
|
||||
@@ -1407,10 +1416,10 @@ in
|
||||
BindPaths = [ stateDir ];
|
||||
BindReadOnlyPaths =
|
||||
[
|
||||
storeDir
|
||||
builtins.storeDir
|
||||
"/etc"
|
||||
]
|
||||
++ optionals config.services.resolved.enable [
|
||||
++ lib.optionals config.services.resolved.enable [
|
||||
"/run/systemd/resolve/stub-resolv.conf"
|
||||
"/run/systemd/resolve/resolv.conf"
|
||||
];
|
||||
@@ -1423,7 +1432,7 @@ in
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateMounts = true;
|
||||
PrivateNetwork = mkDefault false;
|
||||
PrivateNetwork = lib.mkDefault false;
|
||||
PrivateTmp = true;
|
||||
# Tor cannot currently bind privileged port when PrivateUsers=true,
|
||||
# see https://gitlab.torproject.org/legacy/trac/-/issues/20930
|
||||
|
||||
@@ -17,9 +17,9 @@ let
|
||||
util-linux
|
||||
busybox
|
||||
]
|
||||
++ optional cfg.btrfs.enable btrfs-progs
|
||||
++ optional cfg.ext4.enable e2fsprogs
|
||||
++ optional cfg.xfs.enable xfsprogs
|
||||
++ lib.optional cfg.btrfs.enable btrfs-progs
|
||||
++ lib.optional cfg.ext4.enable e2fsprogs
|
||||
++ lib.optional cfg.xfs.enable xfsprogs
|
||||
++ cfg.extraPackages;
|
||||
hasFs = fsName: lib.any (fs: fs.fsType == fsName) (lib.attrValues config.fileSystems);
|
||||
settingsFormat = pkgs.formats.yaml { };
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.services.earlyoom;
|
||||
|
||||
inherit (lib)
|
||||
concatStringsSep
|
||||
escapeShellArg
|
||||
literalExpression
|
||||
mkDefault
|
||||
mkEnableOption
|
||||
@@ -15,7 +18,8 @@ let
|
||||
mkRemovedOptionModule
|
||||
optionalString
|
||||
optionals
|
||||
types;
|
||||
types
|
||||
;
|
||||
in
|
||||
{
|
||||
meta = {
|
||||
@@ -115,6 +119,11 @@ in
|
||||
[README](https://github.com/rfjakob/earlyoom#notifications) and
|
||||
[the man page](https://github.com/rfjakob/earlyoom/blob/master/MANPAGE.md#-n-pathtoscript)
|
||||
for details.
|
||||
|
||||
WARNING: earlyoom is running in a sandbox with ProtectSystem="strict"
|
||||
by default, so filesystem write is also prohibited for the hook.
|
||||
If you want to change these protection rules, override the systemd
|
||||
service via `systemd.services.earlyoom.serviceConfig.ProtectSystem`.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -127,8 +136,11 @@ in
|
||||
|
||||
extraArgs = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = [ "-g" "--prefer '(^|/)(java|chromium)$'" ];
|
||||
default = [ ];
|
||||
example = [
|
||||
"-g"
|
||||
"--prefer '(^|/)(java|chromium)$'"
|
||||
];
|
||||
description = "Extra command-line arguments to be passed to earlyoom.";
|
||||
};
|
||||
};
|
||||
@@ -149,25 +161,33 @@ in
|
||||
config = mkIf cfg.enable {
|
||||
services.systembus-notify.enable = mkDefault cfg.enableNotifications;
|
||||
|
||||
systemd.packages = [ cfg.package ];
|
||||
|
||||
systemd.services.earlyoom = {
|
||||
description = "Early OOM Daemon for Linux";
|
||||
overrideStrategy = "asDropin";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = optionals cfg.enableNotifications [ pkgs.dbus ];
|
||||
serviceConfig = {
|
||||
StandardError = "journal";
|
||||
ExecStart = concatStringsSep " " ([
|
||||
"${lib.getExe cfg.package}"
|
||||
("-m ${toString cfg.freeMemThreshold}"
|
||||
+ optionalString (cfg.freeMemKillThreshold != null) ",${toString cfg.freeMemKillThreshold}")
|
||||
("-s ${toString cfg.freeSwapThreshold}"
|
||||
+ optionalString (cfg.freeSwapKillThreshold != null) ",${toString cfg.freeSwapKillThreshold}")
|
||||
"-r ${toString cfg.reportInterval}"
|
||||
]
|
||||
++ optionals cfg.enableDebugInfo [ "-d" ]
|
||||
++ optionals cfg.enableNotifications [ "-n" ]
|
||||
++ optionals (cfg.killHook != null) [ "-N ${escapeShellArg cfg.killHook}" ]
|
||||
++ cfg.extraArgs);
|
||||
};
|
||||
|
||||
# We setup `EARLYOOM_ARGS` via drop-ins, so disable the default import
|
||||
# from /etc/default/earlyoom.
|
||||
serviceConfig.EnvironmentFile = "";
|
||||
|
||||
environment.EARLYOOM_ARGS =
|
||||
lib.cli.toGNUCommandLineShell { } {
|
||||
m =
|
||||
"${toString cfg.freeMemThreshold}"
|
||||
+ optionalString (cfg.freeMemKillThreshold != null) ",${toString cfg.freeMemKillThreshold}";
|
||||
s =
|
||||
"${toString cfg.freeSwapThreshold}"
|
||||
+ optionalString (cfg.freeSwapKillThreshold != null) ",${toString cfg.freeSwapKillThreshold}";
|
||||
r = "${toString cfg.reportInterval}";
|
||||
d = cfg.enableDebugInfo;
|
||||
n = cfg.enableNotifications;
|
||||
N = if cfg.killHook != null then cfg.killHook else null;
|
||||
}
|
||||
+ " "
|
||||
+ lib.escapeShellArgs cfg.extraArgs;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,11 +9,27 @@ let
|
||||
cfg = config.services.swapspace;
|
||||
inherit (lib)
|
||||
types
|
||||
mkIf
|
||||
mkOption
|
||||
mkPackageOption
|
||||
mkEnableOption
|
||||
;
|
||||
configFile = pkgs.writeText "swapspace.conf" (lib.generators.toKeyValue { } cfg.settings);
|
||||
inherit (pkgs)
|
||||
makeWrapper
|
||||
runCommand
|
||||
writeText
|
||||
;
|
||||
configFile = writeText "swapspace.conf" (lib.generators.toKeyValue { } cfg.settings);
|
||||
userWrapper =
|
||||
runCommand "swapspace"
|
||||
{
|
||||
buildInputs = [ makeWrapper ];
|
||||
}
|
||||
''
|
||||
mkdir -p "$out/bin"
|
||||
makeWrapper '${lib.getExe cfg.package}' "$out/bin/swapspace" \
|
||||
--add-flags "-c '${configFile}'"
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.services.swapspace = {
|
||||
@@ -28,6 +44,13 @@ in
|
||||
];
|
||||
description = "Any extra arguments to pass to swapspace";
|
||||
};
|
||||
installWrapper = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
This will add swapspace wrapped with the generated config, to environment.systemPackages
|
||||
'';
|
||||
};
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
@@ -92,8 +115,8 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = [ (if cfg.installWrapper then userWrapper else cfg.package) ];
|
||||
systemd.packages = [ cfg.package ];
|
||||
systemd.services.swapspace = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
@@ -15,7 +15,7 @@ let
|
||||
credentialsFile
|
||||
else
|
||||
pkgs.writeText "magnetico-credentials" (
|
||||
concatStrings (mapAttrsToList (user: hash: "${user}:${hash}\n") cfg.web.credentials)
|
||||
lib.concatStrings (lib.mapAttrsToList (user: hash: "${user}:${hash}\n") cfg.web.credentials)
|
||||
);
|
||||
|
||||
# default options in magneticod/main.go
|
||||
@@ -28,7 +28,7 @@ let
|
||||
|
||||
crawlerArgs =
|
||||
with cfg.crawler;
|
||||
escapeShellArgs (
|
||||
lib.escapeShellArgs (
|
||||
[
|
||||
"--database=${dbURI}"
|
||||
"--indexer-addr=${address}:${toString port}"
|
||||
@@ -40,7 +40,7 @@ let
|
||||
|
||||
webArgs =
|
||||
with cfg.web;
|
||||
escapeShellArgs (
|
||||
lib.escapeShellArgs (
|
||||
[
|
||||
"--database=${dbURI}"
|
||||
(
|
||||
|
||||
@@ -477,6 +477,10 @@ in
|
||||
};
|
||||
};
|
||||
extraConfig = ''
|
||||
# Frigate wants to connect on 127.0.0.1:5000 for unauthenticated requests
|
||||
# https://github.com/NixOS/nixpkgs/issues/370349
|
||||
listen 127.0.0.1:5000;
|
||||
|
||||
# vod settings
|
||||
vod_base_url "";
|
||||
vod_segments_base_url "";
|
||||
|
||||
@@ -152,7 +152,7 @@ in
|
||||
WorkingDirectory = cfg.stateDir;
|
||||
ExecStart = ''${cfg.package}/bin/galene \
|
||||
${optionalString (cfg.insecure) "-insecure"} \
|
||||
-http ${cfg.httpAddress}:${cfg.httpPort} \
|
||||
-http ${cfg.httpAddress}:${toString cfg.httpPort} \
|
||||
-turn ${cfg.turnAddress} \
|
||||
-data ${cfg.dataDir} \
|
||||
-groups ${cfg.groupsDir} \
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.immich-public-proxy;
|
||||
format = pkgs.formats.json { };
|
||||
inherit (lib)
|
||||
types
|
||||
mkIf
|
||||
mkOption
|
||||
mkEnableOption
|
||||
;
|
||||
in
|
||||
{
|
||||
options.services.immich-public-proxy = {
|
||||
enable = mkEnableOption "Immich Public Proxy";
|
||||
package = lib.mkPackageOption pkgs "immich-public-proxy" { };
|
||||
|
||||
immichUrl = mkOption {
|
||||
type = types.str;
|
||||
description = "URL of the Immich instance";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 3000;
|
||||
description = "The port that IPP will listen on.";
|
||||
};
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to open the IPP port in the firewall";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = format.type;
|
||||
};
|
||||
default = { };
|
||||
description = ''
|
||||
Configuration for IPP. See <https://github.com/alangrainger/immich-public-proxy/blob/main/README.md#additional-configuration> for options and defaults.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services.immich-public-proxy = {
|
||||
description = "Immich public proxy for sharing albums publicly without exposing your Immich instance";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = {
|
||||
IMMICH_URL = cfg.immichUrl;
|
||||
IPP_PORT = builtins.toString cfg.port;
|
||||
IPP_CONFIG = "${format.generate "config.json" cfg.settings}";
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = lib.getExe cfg.package;
|
||||
SyslogIdentifier = "ipp";
|
||||
User = "ipp";
|
||||
Group = "ipp";
|
||||
DynamicUser = true;
|
||||
Type = "simple";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 3;
|
||||
|
||||
# Hardening
|
||||
CapabilityBoundingSet = "";
|
||||
NoNewPrivileges = true;
|
||||
PrivateUsers = true;
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
PrivateMounts = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_UNIX"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ jaculabilis ];
|
||||
};
|
||||
}
|
||||
@@ -9,18 +9,31 @@ let
|
||||
cfg = config.services.komga;
|
||||
inherit (lib) mkOption mkEnableOption maintainers;
|
||||
inherit (lib.types) port str bool;
|
||||
|
||||
settingsFormat = pkgs.formats.yaml { };
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule
|
||||
[
|
||||
"services"
|
||||
"komga"
|
||||
"port"
|
||||
]
|
||||
[
|
||||
"services"
|
||||
"komga"
|
||||
"settings"
|
||||
"server"
|
||||
"port"
|
||||
]
|
||||
)
|
||||
];
|
||||
|
||||
options = {
|
||||
services.komga = {
|
||||
enable = mkEnableOption "Komga, a free and open source comics/mangas media server";
|
||||
|
||||
port = mkOption {
|
||||
type = port;
|
||||
default = 8080;
|
||||
description = "The port that Komga will listen on.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = str;
|
||||
default = "komga";
|
||||
@@ -39,10 +52,25 @@ in
|
||||
description = "State and configuration directory Komga will use.";
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
inherit (settingsFormat) type;
|
||||
default = { };
|
||||
defaultText = lib.literalExpression ''
|
||||
{
|
||||
server.port = 8080;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Komga configuration.
|
||||
|
||||
See [documentation](https://komga.org/docs/installation/configuration).
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = bool;
|
||||
default = false;
|
||||
description = "Whether to open the firewall for the port in {option}`services.komga.port`.";
|
||||
description = "Whether to open the firewall for the port in {option}`services.komga.settings.server.port`.";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -52,6 +80,16 @@ in
|
||||
inherit (lib) mkIf getExe;
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = (cfg.settings.komga.config-dir or cfg.stateDir) == cfg.stateDir;
|
||||
message = "You must use the `services.komga.stateDir` option to properly configure `komga.config-dir`.";
|
||||
}
|
||||
];
|
||||
|
||||
services.komga.settings = {
|
||||
server.port = lib.mkDefault 8080;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
|
||||
|
||||
@@ -66,9 +104,17 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
systemd.tmpfiles.settings."10-komga" = {
|
||||
${cfg.stateDir}.d = {
|
||||
inherit (cfg) user group;
|
||||
};
|
||||
"${cfg.stateDir}/application.yml"."L+" = {
|
||||
argument = builtins.toString (settingsFormat.generate "application.yml" cfg.settings);
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.komga = {
|
||||
environment = {
|
||||
SERVER_PORT = builtins.toString cfg.port;
|
||||
KOMGA_CONFIGDIR = cfg.stateDir;
|
||||
};
|
||||
|
||||
|
||||
@@ -374,14 +374,18 @@ in {
|
||||
type = with types; attrsOf (oneOf [ str int bool ]);
|
||||
default = {
|
||||
"pm" = "dynamic";
|
||||
"pm.max_children" = "32";
|
||||
"pm.start_servers" = "2";
|
||||
"pm.min_spare_servers" = "2";
|
||||
"pm.max_spare_servers" = "4";
|
||||
"pm.max_children" = "120";
|
||||
"pm.start_servers" = "12";
|
||||
"pm.min_spare_servers" = "6";
|
||||
"pm.max_spare_servers" = "18";
|
||||
"pm.max_requests" = "500";
|
||||
};
|
||||
description = ''
|
||||
Options for nextcloud's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
|
||||
Options for nextcloud's PHP pool. See the documentation on `php-fpm.conf` for details on
|
||||
configuration directives. The above are recommended for a server with 4GiB of RAM.
|
||||
|
||||
It's advisable to read the [section about PHPFPM tuning in the upstream manual](https://docs.nextcloud.com/server/30/admin_manual/installation/server_tuning.html#tune-php-fpm)
|
||||
and consider customizing the values.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -416,7 +420,6 @@ in {
|
||||
config = {
|
||||
dbtype = mkOption {
|
||||
type = types.enum [ "sqlite" "pgsql" "mysql" ];
|
||||
default = "sqlite";
|
||||
description = "Database type.";
|
||||
};
|
||||
dbname = mkOption {
|
||||
|
||||
@@ -6,15 +6,20 @@ let
|
||||
|
||||
cfg = config.services.xserver.windowManager.dwm;
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
in {
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
services.xserver.windowManager.dwm = {
|
||||
enable = mkEnableOption "dwm";
|
||||
extraSessionCommands = mkOption {
|
||||
default = "";
|
||||
type = types.lines;
|
||||
description = ''
|
||||
Shell commands executed just before dwm is started.
|
||||
'';
|
||||
};
|
||||
package = mkPackageOption pkgs "dwm" {
|
||||
example = ''
|
||||
pkgs.dwm.overrideAttrs (oldAttrs: rec {
|
||||
@@ -30,20 +35,20 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
services.xserver.windowManager.session = singleton
|
||||
{ name = "dwm";
|
||||
start =
|
||||
''
|
||||
export _JAVA_AWT_WM_NONREPARENTING=1
|
||||
dwm &
|
||||
waitPID=$!
|
||||
'';
|
||||
};
|
||||
services.xserver.windowManager.session = singleton {
|
||||
name = "dwm";
|
||||
start = ''
|
||||
${cfg.extraSessionCommands}
|
||||
|
||||
export _JAVA_AWT_WM_NONREPARENTING=1
|
||||
dwm &
|
||||
waitPID=$!
|
||||
'';
|
||||
};
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
|
||||
@@ -544,6 +544,13 @@ sub handle_modified_unit { ## no critic(Subroutines::ProhibitManyArgs, Subroutin
|
||||
}
|
||||
}
|
||||
|
||||
if (parse_systemd_bool(\%new_unit_info, "Service", "X-NotSocketActivated", 0)) {
|
||||
# If the unit explicitly opts out of socket
|
||||
# activation, restart it as if it weren't (but do
|
||||
# restart its sockets, that's fine):
|
||||
$socket_activated = 0;
|
||||
}
|
||||
|
||||
# If the unit is not socket-activated, record
|
||||
# that this unit needs to be started below.
|
||||
# We write this to a file to ensure that the
|
||||
|
||||
@@ -159,7 +159,7 @@ def copy_from_file(file: str, dry_run: bool = False) -> str:
|
||||
|
||||
|
||||
def write_entry(profile: str | None, generation: int, specialisation: str | None,
|
||||
machine_id: str, bootspec: BootSpec, current: bool) -> None:
|
||||
machine_id: str | None, bootspec: BootSpec, current: bool) -> None:
|
||||
if specialisation:
|
||||
bootspec = bootspec.specialisations[specialisation]
|
||||
kernel = copy_from_file(bootspec.kernel)
|
||||
@@ -281,11 +281,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
|
||||
except IOError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
# Since systemd version 232 a machine ID is required and it might not
|
||||
# be there on newly installed systems, so let's generate one so that
|
||||
# bootctl can find it and we can also pass it to write_entry() later.
|
||||
cmd = [f"{SYSTEMD}/bin/systemd-machine-id-setup", "--print"]
|
||||
machine_id = run(cmd, stdout=subprocess.PIPE).stdout.rstrip()
|
||||
machine_id = None
|
||||
|
||||
if os.getenv("NIXOS_INSTALL_GRUB") == "1":
|
||||
warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning)
|
||||
|
||||
@@ -2901,6 +2901,7 @@ let
|
||||
config.environment.etc."systemd/networkd.conf".source
|
||||
];
|
||||
aliases = [ "dbus-org.freedesktop.network1.service" ];
|
||||
notSocketActivated = true;
|
||||
};
|
||||
|
||||
networking.iproute2 = mkIf (cfg.config.addRouteTablesToIPRoute2 && cfg.config.routeTables != { }) {
|
||||
|
||||
@@ -88,9 +88,13 @@ in
|
||||
]
|
||||
}
|
||||
pushd $out
|
||||
tar -Sc $diskImage | gzip -${toString cfg.compressionLevel} > \
|
||||
# RTFM:
|
||||
# https://cloud.google.com/compute/docs/images/create-custom
|
||||
# https://cloud.google.com/compute/docs/import/import-existing-image
|
||||
mv $diskImage disk.raw
|
||||
tar -Sc disk.raw | gzip -${toString cfg.compressionLevel} > \
|
||||
${config.image.fileName}
|
||||
rm $diskImage
|
||||
rm disk.raw
|
||||
popd
|
||||
'';
|
||||
format = "raw";
|
||||
|
||||
@@ -13,7 +13,12 @@ let
|
||||
'';
|
||||
qemuConfigFile = pkgs.writeText "qemu.conf" ''
|
||||
${optionalString cfg.qemu.ovmf.enable ''
|
||||
nvram = [ "/run/libvirt/nix-ovmf/AAVMF_CODE.fd:/run/libvirt/nix-ovmf/AAVMF_VARS.fd", "/run/libvirt/nix-ovmf/OVMF_CODE.fd:/run/libvirt/nix-ovmf/OVMF_VARS.fd" ]
|
||||
nvram = [
|
||||
"/run/libvirt/nix-ovmf/AAVMF_CODE.fd:/run/libvirt/nix-ovmf/AAVMF_VARS.fd",
|
||||
"/run/libvirt/nix-ovmf/AAVMF_CODE.ms.fd:/run/libvirt/nix-ovmf/AAVMF_VARS.ms.fd",
|
||||
"/run/libvirt/nix-ovmf/OVMF_CODE.fd:/run/libvirt/nix-ovmf/OVMF_VARS.fd",
|
||||
"/run/libvirt/nix-ovmf/OVMF_CODE.ms.fd:/run/libvirt/nix-ovmf/OVMF_VARS.ms.fd"
|
||||
]
|
||||
''}
|
||||
${optionalString (!cfg.qemu.runAsRoot) ''
|
||||
user = "qemu-libvirtd"
|
||||
@@ -451,10 +456,10 @@ in
|
||||
};
|
||||
in
|
||||
''
|
||||
ln -s --force ${ovmfpackage}/FV/AAVMF_CODE.fd /run/${dirName}/nix-ovmf/
|
||||
ln -s --force ${ovmfpackage}/FV/OVMF_CODE.fd /run/${dirName}/nix-ovmf/
|
||||
ln -s --force ${ovmfpackage}/FV/AAVMF_VARS.fd /run/${dirName}/nix-ovmf/
|
||||
ln -s --force ${ovmfpackage}/FV/OVMF_VARS.fd /run/${dirName}/nix-ovmf/
|
||||
ln -s --force ${ovmfpackage}/FV/AAVMF_CODE{,.ms}.fd /run/${dirName}/nix-ovmf/
|
||||
ln -s --force ${ovmfpackage}/FV/OVMF_CODE{,.ms}.fd /run/${dirName}/nix-ovmf/
|
||||
ln -s --force ${ovmfpackage}/FV/AAVMF_VARS{,.ms}.fd /run/${dirName}/nix-ovmf/
|
||||
ln -s --force ${ovmfpackage}/FV/OVMF_VARS{,.ms}.fd /run/${dirName}/nix-ovmf/
|
||||
'')}
|
||||
|
||||
# Symlink hooks to /var/lib/libvirt
|
||||
|
||||
@@ -14,7 +14,7 @@ let
|
||||
defaultBackend = options.virtualisation.oci-containers.backend.default;
|
||||
|
||||
containerOptions =
|
||||
{ ... }:
|
||||
{ name, ... }:
|
||||
{
|
||||
|
||||
options = {
|
||||
@@ -59,6 +59,13 @@ let
|
||||
example = literalExpression "pkgs.dockerTools.streamLayeredImage {...};";
|
||||
};
|
||||
|
||||
serviceName = mkOption {
|
||||
type = types.str;
|
||||
default = "${cfg.backend}-${name}";
|
||||
defaultText = "<backend>-<name>";
|
||||
description = "Systemd service name that manages the container";
|
||||
};
|
||||
|
||||
login = {
|
||||
|
||||
username = mkOption {
|
||||
@@ -525,9 +532,7 @@ in
|
||||
config = lib.mkIf (cfg.containers != { }) (
|
||||
lib.mkMerge [
|
||||
{
|
||||
systemd.services = mapAttrs' (
|
||||
n: v: nameValuePair "${cfg.backend}-${n}" (mkService n v)
|
||||
) cfg.containers;
|
||||
systemd.services = mapAttrs' (n: v: nameValuePair v.serviceName (mkService n v)) cfg.containers;
|
||||
|
||||
assertions =
|
||||
let
|
||||
|
||||
@@ -1314,6 +1314,7 @@ in
|
||||
{
|
||||
device = "tmpfs";
|
||||
fsType = "tmpfs";
|
||||
options = [ "mode=755" ];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ let
|
||||
inherit (lib)
|
||||
getExe'
|
||||
literalExpression
|
||||
maintainers
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
@@ -25,7 +26,7 @@ in
|
||||
];
|
||||
|
||||
meta = {
|
||||
maintainers = [ lib.maintainers.kjeremy ];
|
||||
maintainers = [ maintainers.kjeremy ];
|
||||
};
|
||||
|
||||
options.virtualisation.vmware.guest = {
|
||||
|
||||
@@ -144,6 +144,7 @@ in {
|
||||
audiobookshelf = handleTest ./audiobookshelf.nix {};
|
||||
auth-mysql = handleTest ./auth-mysql.nix {};
|
||||
authelia = handleTest ./authelia.nix {};
|
||||
auto-cpufreq = handleTest ./auto-cpufreq.nix {};
|
||||
avahi = handleTest ./avahi.nix {};
|
||||
avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
|
||||
ayatana-indicators = runTest ./ayatana-indicators.nix;
|
||||
@@ -467,6 +468,7 @@ in {
|
||||
ifm = handleTest ./ifm.nix {};
|
||||
iftop = handleTest ./iftop.nix {};
|
||||
immich = handleTest ./web-apps/immich.nix {};
|
||||
immich-public-proxy = handleTest ./web-apps/immich-public-proxy.nix {};
|
||||
incron = handleTest ./incron.nix {};
|
||||
incus = pkgs.recurseIntoAttrs (handleTest ./incus { lts = false; inherit system pkgs; });
|
||||
incus-lts = pkgs.recurseIntoAttrs (handleTest ./incus { inherit system pkgs; });
|
||||
@@ -520,6 +522,7 @@ in {
|
||||
keyd = handleTest ./keyd.nix {};
|
||||
keymap = handleTest ./keymap.nix {};
|
||||
kimai = handleTest ./kimai.nix {};
|
||||
kmonad = runTest ./kmonad.nix;
|
||||
knot = handleTest ./knot.nix {};
|
||||
komga = handleTest ./komga.nix {};
|
||||
krb5 = discoverTests (import ./krb5);
|
||||
@@ -658,6 +661,11 @@ in {
|
||||
navidrome = handleTest ./navidrome.nix {};
|
||||
nbd = handleTest ./nbd.nix {};
|
||||
ncdns = handleTest ./ncdns.nix {};
|
||||
ncps = runTest ./ncps.nix;
|
||||
ncps-custom-cache-datapath = runTest {
|
||||
imports = [ ./ncps.nix ];
|
||||
defaults.services.ncps.cache.dataPath = "/path/to/ncps";
|
||||
};
|
||||
ndppd = handleTest ./ndppd.nix {};
|
||||
nix-channel = pkgs.callPackage ../modules/config/nix-channel/test.nix { };
|
||||
nebula = handleTest ./nebula.nix {};
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import ./make-test-python.nix (
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
name = "auto-cpufreq-server";
|
||||
|
||||
nodes = {
|
||||
machine =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
# service will still start but since vm inside qemu cpufreq adjustments
|
||||
# cannot be made. This will resource in the following error but the service
|
||||
# remains up:
|
||||
# ERROR:
|
||||
# Couldn't find any of the necessary scaling governors.
|
||||
services.auto-cpufreq = {
|
||||
enable = true;
|
||||
settings = {
|
||||
charger = {
|
||||
turbo = "auto";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.start()
|
||||
machine.wait_for_unit("auto-cpufreq.service")
|
||||
machine.succeed("auto-cpufreq --force reset")
|
||||
'';
|
||||
}
|
||||
)
|
||||
+41
-14
@@ -1,16 +1,43 @@
|
||||
import ./make-test-python.nix ({ lib, ... }: {
|
||||
name = "earlyoom";
|
||||
meta = {
|
||||
maintainers = with lib.maintainers; [ ncfavier AndersonTorres ];
|
||||
};
|
||||
|
||||
machine = {
|
||||
services.earlyoom = {
|
||||
enable = true;
|
||||
import ./make-test-python.nix (
|
||||
{ lib, ... }:
|
||||
{
|
||||
name = "earlyoom";
|
||||
meta = {
|
||||
maintainers = with lib.maintainers; [
|
||||
ncfavier
|
||||
AndersonTorres
|
||||
oxalica
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("earlyoom.service")
|
||||
'';
|
||||
})
|
||||
nodes.machine =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
# Limit VM resource usage.
|
||||
virtualisation.memorySize = 1024;
|
||||
|
||||
services.earlyoom = {
|
||||
enable = true;
|
||||
# Use SIGKILL, or `tail` will catch SIGTERM and exit successfully.
|
||||
freeMemKillThreshold = 90;
|
||||
};
|
||||
|
||||
systemd.services.testbloat = {
|
||||
description = "Create a lot of memory pressure";
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.coreutils}/bin/tail /dev/zero";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("earlyoom.service")
|
||||
|
||||
with subtest("earlyoom should kill the bad service"):
|
||||
machine.fail("systemctl start --wait testbloat.service")
|
||||
assert machine.get_unit_info("testbloat.service")["Result"] == "signal"
|
||||
output = machine.succeed('journalctl -u earlyoom.service -b0')
|
||||
assert 'low memory! at or below SIGKILL limits' in output
|
||||
'';
|
||||
}
|
||||
)
|
||||
|
||||
@@ -66,9 +66,12 @@ import ./make-test-python.nix (
|
||||
# login and store session
|
||||
machine.log(machine.succeed(f"http --check-status --session=frigate post http://localhost/api/login user=admin password={password}"))
|
||||
|
||||
# make authenticated api requested
|
||||
# make authenticated api request
|
||||
machine.log(machine.succeed("http --check-status --session=frigate get http://localhost/api/version"))
|
||||
|
||||
# make unauthenticated api request
|
||||
machine.log(machine.succeed("http --check-status get http://localhost:5000/api/version"))
|
||||
|
||||
# wait for a recording to appear
|
||||
machine.wait_for_file("/var/cache/frigate/test@*.mp4")
|
||||
'';
|
||||
|
||||
@@ -36,7 +36,6 @@ let
|
||||
meta.maintainers = with maintainers; [
|
||||
aanderse
|
||||
kolaente
|
||||
ma27
|
||||
];
|
||||
|
||||
nodes = {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
name = "kmonad";
|
||||
|
||||
meta = {
|
||||
maintainers = with lib.maintainers; [ linj ];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
machine = {
|
||||
services.kmonad = {
|
||||
enable = true;
|
||||
extraArgs = [
|
||||
"--log-level=debug"
|
||||
];
|
||||
keyboards = {
|
||||
defaultKbd = {
|
||||
device = "/dev/input/by-id/vm-default-kbd";
|
||||
defcfg = {
|
||||
enable = true;
|
||||
fallthrough = true;
|
||||
};
|
||||
config = ''
|
||||
(defsrc :name default-src
|
||||
1)
|
||||
(deflayer default-layer :source default-src
|
||||
@T2)
|
||||
(defalias
|
||||
T2 2)
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# make a determinate symlink to the default vm keyboard for kmonad to use
|
||||
services.udev.extraRules = ''
|
||||
ACTION=="add", KERNEL=="event*", SUBSYSTEM=="input", ATTRS{name}=="QEMU Virtio Keyboard", ATTRS{id/product}=="0001", ATTRS{id/vendor}=="0627", SYMLINK+="input/by-id/vm-default-kbd"
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
service_name = "kmonad-defaultKbd"
|
||||
machine.wait_for_unit(f"{service_name}.service")
|
||||
|
||||
with subtest("kmonad is running"):
|
||||
machine.succeed(f"systemctl status {service_name}")
|
||||
with subtest("kmonad symlink is created"):
|
||||
machine.wait_for_file(f"/dev/input/by-id/{service_name}", timeout=5)
|
||||
'';
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import ./make-test-python.nix ({ lib, ... }:
|
||||
{ pkgs, ... }:
|
||||
{ services.komga = {
|
||||
enable = true;
|
||||
port = 1234;
|
||||
settings.server.port = 1234;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
pkgs ? import ../../.. { inherit system config; },
|
||||
lib ? pkgs.lib,
|
||||
kernelVersionsToTest ? [
|
||||
"4.19"
|
||||
"5.4"
|
||||
"5.10"
|
||||
"5.15"
|
||||
"6.1"
|
||||
"6.6"
|
||||
"latest"
|
||||
],
|
||||
}:
|
||||
@@ -33,7 +33,7 @@ let
|
||||
# systemd in stage 1
|
||||
raid-sd-stage-1 = {
|
||||
test = callTest ./systemd-stage-1.nix;
|
||||
kernelFilter = lib.id;
|
||||
kernelFilter = lib.filter (v: v != "5.15");
|
||||
flavour = "raid";
|
||||
};
|
||||
thinpool-sd-stage-1 = {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
name = "ncps";
|
||||
|
||||
nodes = {
|
||||
harmonia = {
|
||||
services.harmonia = {
|
||||
enable = true;
|
||||
signKeyPaths = [
|
||||
(pkgs.writeText "cache-key" "cache.example.com-1:9FhO0w+7HjZrhvmzT1VlAZw4OSAlFGTgC24Seg3tmPl4gZBdwZClzTTHr9cVzJpwsRSYLTu7hEAQe3ljy92CWg==")
|
||||
];
|
||||
settings.priority = 35;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 5000 ];
|
||||
system.extraDependencies = [ pkgs.emptyFile ];
|
||||
};
|
||||
|
||||
ncps = {
|
||||
services.ncps = {
|
||||
enable = true;
|
||||
|
||||
cache = {
|
||||
hostName = "ncps";
|
||||
secretKeyPath = builtins.toString (
|
||||
pkgs.writeText "ncps-cache-key" "ncps:dcrGsrku0KvltFhrR5lVIMqyloAdo0y8vYZOeIFUSLJS2IToL7dPHSSCk/fi+PJf8EorpBn8PU7MNhfvZoI8mA=="
|
||||
);
|
||||
};
|
||||
|
||||
upstream = {
|
||||
caches = [ "http://harmonia:5000" ];
|
||||
publicKeys = [
|
||||
"cache.example.com-1:eIGQXcGQpc00x6/XFcyacLEUmC07u4RAEHt5Y8vdglo="
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8501 ];
|
||||
};
|
||||
|
||||
client01 = {
|
||||
nix.settings = {
|
||||
substituters = lib.mkForce [ "http://ncps:8501" ];
|
||||
trusted-public-keys = lib.mkForce [
|
||||
"ncps:UtiE6C+3Tx0kgpP34vjyX/BKK6QZ/D1OzDYX72aCPJg="
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
{ nodes, ... }:
|
||||
let
|
||||
narinfoName =
|
||||
(lib.strings.removePrefix "/nix/store/" (
|
||||
lib.strings.removeSuffix "-empty-file" pkgs.emptyFile.outPath
|
||||
))
|
||||
+ ".narinfo";
|
||||
|
||||
narinfoNameChars = lib.strings.stringToCharacters narinfoName;
|
||||
|
||||
narinfoPath = lib.concatStringsSep "/" [
|
||||
nodes.ncps.services.ncps.cache.dataPath
|
||||
"store/narinfo"
|
||||
(lib.lists.elemAt narinfoNameChars 0)
|
||||
((lib.lists.elemAt narinfoNameChars 0) + (lib.lists.elemAt narinfoNameChars 1))
|
||||
narinfoName
|
||||
];
|
||||
in
|
||||
''
|
||||
start_all()
|
||||
|
||||
harmonia.wait_for_unit("harmonia.service")
|
||||
|
||||
ncps.wait_for_unit("ncps.service")
|
||||
|
||||
client01.wait_until_succeeds("curl -f http://ncps:8501/ | grep '\"hostname\":\"${toString nodes.ncps.services.ncps.cache.hostName}\"' >&2")
|
||||
|
||||
client01.succeed("cat /etc/nix/nix.conf >&2")
|
||||
client01.succeed("nix-store --realise ${pkgs.emptyFile}")
|
||||
|
||||
ncps.succeed("cat ${narinfoPath} >&2")
|
||||
'';
|
||||
}
|
||||
@@ -59,6 +59,7 @@ runTest (
|
||||
|
||||
services.nextcloud = {
|
||||
enable = true;
|
||||
config.dbtype = "sqlite";
|
||||
datadir = "/var/lib/nextcloud-data";
|
||||
autoUpdateApps = {
|
||||
enable = true;
|
||||
|
||||
@@ -69,14 +69,13 @@ runTest (
|
||||
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
package = pkgs.postgresql_14;
|
||||
};
|
||||
systemd.services.postgresql.postStart = pkgs.lib.mkAfter ''
|
||||
password=$(cat ${config.services.nextcloud.config.dbpassFile})
|
||||
${config.services.postgresql.package}/bin/psql <<EOF
|
||||
CREATE ROLE ${adminuser} WITH LOGIN PASSWORD '$password' CREATEDB;
|
||||
CREATE DATABASE nextcloud;
|
||||
GRANT ALL PRIVILEGES ON DATABASE nextcloud TO ${adminuser};
|
||||
ALTER DATABASE nextcloud OWNER to ${adminuser};
|
||||
EOF
|
||||
'';
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ runTest (
|
||||
networking.firewall.allowedTCPPorts = [ 9000 ];
|
||||
environment.systemPackages = [ pkgs.minio-client ];
|
||||
|
||||
services.nextcloud.config.dbtype = "sqlite";
|
||||
|
||||
services.nextcloud.config.objectstore.s3 = {
|
||||
enable = true;
|
||||
bucket = "nextcloud";
|
||||
|
||||
+62
-45
@@ -1,5 +1,13 @@
|
||||
import ./make-test-python.nix (
|
||||
{ pkgs, ... }:
|
||||
{ pkgs, lib, ... }:
|
||||
let
|
||||
monitorMethods = [
|
||||
"ebpf"
|
||||
"proc"
|
||||
"ftrace"
|
||||
"audit"
|
||||
];
|
||||
in
|
||||
{
|
||||
name = "opensnitch";
|
||||
|
||||
@@ -7,10 +15,9 @@ import ./make-test-python.nix (
|
||||
maintainers = [ onny ];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
server =
|
||||
{ ... }:
|
||||
{
|
||||
nodes =
|
||||
{
|
||||
server = {
|
||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
@@ -19,50 +26,60 @@ import ./make-test-python.nix (
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
clientBlocked =
|
||||
{ ... }:
|
||||
{
|
||||
services.opensnitch = {
|
||||
enable = true;
|
||||
settings.DefaultAction = "deny";
|
||||
};
|
||||
};
|
||||
|
||||
clientAllowed =
|
||||
{ ... }:
|
||||
{
|
||||
services.opensnitch = {
|
||||
enable = true;
|
||||
settings.DefaultAction = "deny";
|
||||
rules = {
|
||||
curl = {
|
||||
name = "curl";
|
||||
enabled = true;
|
||||
action = "allow";
|
||||
duration = "always";
|
||||
operator = {
|
||||
type = "simple";
|
||||
sensitive = false;
|
||||
operand = "process.path";
|
||||
data = "${pkgs.curl}/bin/curl";
|
||||
}
|
||||
// (lib.listToAttrs (
|
||||
map (
|
||||
m:
|
||||
lib.nameValuePair "client_blocked_${m}" {
|
||||
services.opensnitch = {
|
||||
enable = true;
|
||||
settings.DefaultAction = "deny";
|
||||
settings.ProcMonitorMethod = m;
|
||||
};
|
||||
}
|
||||
) monitorMethods
|
||||
))
|
||||
// (lib.listToAttrs (
|
||||
map (
|
||||
m:
|
||||
lib.nameValuePair "client_allowed_${m}" {
|
||||
services.opensnitch = {
|
||||
enable = true;
|
||||
settings.DefaultAction = "deny";
|
||||
settings.ProcMonitorMethod = m;
|
||||
rules = {
|
||||
curl = {
|
||||
name = "curl";
|
||||
enabled = true;
|
||||
action = "allow";
|
||||
duration = "always";
|
||||
operator = {
|
||||
type = "simple";
|
||||
sensitive = false;
|
||||
operand = "process.path";
|
||||
data = "${pkgs.curl}/bin/curl";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
) monitorMethods
|
||||
));
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
server.wait_for_unit("caddy.service")
|
||||
server.wait_for_open_port(80)
|
||||
testScript =
|
||||
''
|
||||
start_all()
|
||||
server.wait_for_unit("caddy.service")
|
||||
server.wait_for_open_port(80)
|
||||
''
|
||||
+ lib.concatLines (
|
||||
map (m: ''
|
||||
client_blocked_${m}.wait_for_unit("opensnitchd.service")
|
||||
client_blocked_${m}.fail("curl http://server")
|
||||
|
||||
clientBlocked.wait_for_unit("opensnitchd.service")
|
||||
clientBlocked.fail("curl http://server")
|
||||
|
||||
clientAllowed.wait_for_unit("opensnitchd.service")
|
||||
clientAllowed.succeed("curl http://server")
|
||||
'';
|
||||
client_allowed_${m}.wait_for_unit("opensnitchd.service")
|
||||
client_allowed_${m}.succeed("curl http://server")
|
||||
'') monitorMethods
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
@@ -399,7 +399,7 @@ let
|
||||
fastly = {
|
||||
exporterConfig = {
|
||||
enable = true;
|
||||
tokenPath = pkgs.writeText "token" "abc123";
|
||||
environmentFile = pkgs.writeText "fastly-exporter-env" "FASTLY_API_TOKEN=abc123";
|
||||
};
|
||||
|
||||
exporterTest = ''
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import ./make-test-python.nix (
|
||||
{ pkgs, lib, ... }:
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
name = "swapspace";
|
||||
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [
|
||||
Luflosi
|
||||
phanirithvij
|
||||
@@ -38,6 +38,9 @@ import ./make-test-python.nix (
|
||||
machine.wait_for_unit("swapspace.service")
|
||||
machine.wait_for_unit("root-swapfile.swap")
|
||||
|
||||
# ensure swapspace wrapper command runs
|
||||
machine.succeed("swapspace --inspect")
|
||||
|
||||
swamp = False
|
||||
with subtest("swapspace works"):
|
||||
machine.execute("mkdir /root/memfs")
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import ../make-test-python.nix (
|
||||
{ pkgs, lib, ... }:
|
||||
{
|
||||
name = "immich-public-proxy";
|
||||
|
||||
nodes.machine =
|
||||
{ pkgs, ... }@args:
|
||||
{
|
||||
environment.systemPackages = [
|
||||
pkgs.imagemagick
|
||||
pkgs.immich-cli
|
||||
];
|
||||
services.immich = {
|
||||
enable = true;
|
||||
port = 2283;
|
||||
# disable a lot of features that aren't needed for this test
|
||||
machine-learning.enable = false;
|
||||
settings = {
|
||||
backup.database.enabled = false;
|
||||
machineLearning.enabled = false;
|
||||
map.enabled = false;
|
||||
reverseGeocoding.enabled = false;
|
||||
metadata.faces.import = false;
|
||||
newVersionCheck.enabled = false;
|
||||
notifications.smtp.enabled = false;
|
||||
};
|
||||
};
|
||||
services.immich-public-proxy = {
|
||||
enable = true;
|
||||
immichUrl = "http://localhost:2283";
|
||||
port = 8002;
|
||||
settings.ipp.responseHeaders."X-NixOS" = "Rules";
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
import json
|
||||
|
||||
machine.wait_for_unit("immich-server.service")
|
||||
machine.wait_for_unit("immich-public-proxy.service")
|
||||
machine.wait_for_open_port(2283)
|
||||
machine.wait_for_open_port(8002)
|
||||
|
||||
# The proxy should be up
|
||||
machine.succeed("curl -sf http://localhost:8002")
|
||||
|
||||
# Verify the static assets are served
|
||||
machine.succeed("curl -sf http://localhost:8002/robots.txt")
|
||||
machine.succeed("curl -sf http://localhost:8002/share/static/style.css")
|
||||
|
||||
# Check that the response header in the settings is sent
|
||||
res = machine.succeed("""
|
||||
curl -sD - http://localhost:8002 -o /dev/null
|
||||
""")
|
||||
assert "x-nixos: rules" in res.lower(), res
|
||||
|
||||
# Log in to Immich and create an access key
|
||||
machine.succeed("""
|
||||
curl -sf --json '{ "email": "test@example.com", "name": "Admin", "password": "admin" }' http://localhost:2283/api/auth/admin-sign-up
|
||||
""")
|
||||
res = machine.succeed("""
|
||||
curl -sf --json '{ "email": "test@example.com", "password": "admin" }' http://localhost:2283/api/auth/login
|
||||
""")
|
||||
token = json.loads(res)['accessToken']
|
||||
res = machine.succeed("""
|
||||
curl -sf -H 'Cookie: immich_access_token=%s' --json '{ "name": "API Key", "permissions": ["all"] }' http://localhost:2283/api/api-keys
|
||||
""" % token)
|
||||
key = json.loads(res)['secret']
|
||||
machine.succeed(f"immich login http://localhost:2283/api {key}")
|
||||
res = machine.succeed("immich server-info")
|
||||
print(res)
|
||||
|
||||
# Upload some blank images to a new album
|
||||
# If there's only one image, the proxy serves the image directly
|
||||
machine.succeed("magick -size 800x600 canvas:white /tmp/white.png")
|
||||
machine.succeed("immich upload -A '✨ Reproducible Moments ✨' /tmp/white.png")
|
||||
machine.succeed("magick -size 800x600 canvas:black /tmp/black.png")
|
||||
machine.succeed("immich upload -A '✨ Reproducible Moments ✨' /tmp/black.png")
|
||||
res = machine.succeed("immich server-info")
|
||||
print(res)
|
||||
|
||||
# Get the new album id
|
||||
res = machine.succeed("""
|
||||
curl -sf -H 'Cookie: immich_access_token=%s' http://localhost:2283/api/albums
|
||||
""" % token)
|
||||
album_id = json.loads(res)[0]['id']
|
||||
|
||||
# Create a shared link
|
||||
res = machine.succeed("""
|
||||
curl -sf -H 'Cookie: immich_access_token=%s' --json '{ "albumId": "%s", "type": "ALBUM" }' http://localhost:2283/api/shared-links
|
||||
""" % (token, album_id))
|
||||
share_key = json.loads(res)['key']
|
||||
|
||||
# Access the share
|
||||
machine.succeed("""
|
||||
curl -sf http://localhost:2283/share/%s
|
||||
""" % share_key)
|
||||
|
||||
# Access the share through the proxy
|
||||
machine.succeed("""
|
||||
curl -sf http://localhost:8002/share/%s
|
||||
""" % share_key)
|
||||
'';
|
||||
}
|
||||
)
|
||||
@@ -28,7 +28,6 @@
|
||||
liblo,
|
||||
libogg,
|
||||
libpulseaudio,
|
||||
librdf_raptor,
|
||||
librdf_rasqal,
|
||||
libsamplerate,
|
||||
libsigcxx,
|
||||
@@ -94,6 +93,12 @@ stdenv.mkDerivation rec {
|
||||
url = "https://github.com/Ardour/ardour/commit/338cd09a4aa1b36b8095dfc14ab534395f9a4a92.patch?full_index=1";
|
||||
hash = "sha256-AvV4aLdkfrxPkE4NX2ETSagq4GjEC+sHCEqdcYvL+CY=";
|
||||
})
|
||||
|
||||
# Fix build with boost >= 1.85
|
||||
(fetchpatch {
|
||||
url = "https://github.com/Ardour/ardour/commit/f94bde59d740d65e67c5cd13af4d7ea51453aeaa.patch";
|
||||
hash = "sha256-dGRjkdF3REkANytDR17wIh8J2+AcLFmV4tKZied/OZg=";
|
||||
})
|
||||
];
|
||||
|
||||
# Ardour's wscript requires git revision and date to be available.
|
||||
@@ -139,7 +144,6 @@ stdenv.mkDerivation rec {
|
||||
liblo
|
||||
libogg
|
||||
libpulseaudio
|
||||
librdf_raptor
|
||||
librdf_rasqal
|
||||
libsamplerate
|
||||
libsigcxx
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "ft2-clone";
|
||||
version = "1.89";
|
||||
version = "1.93";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "8bitbubsy";
|
||||
repo = "ft2-clone";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-s2HMzHJB85kLe3TuKH3oFloWUirmbSYDwh298dZlP6M=";
|
||||
hash = "sha256-B91kLShg3nvOyOlBkLSpTydhUs5yHa+C/OWe8N+MB9c=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ cmake ];
|
||||
|
||||
@@ -14,7 +14,7 @@ buildPythonApplication rec {
|
||||
src = fetchFromGitHub {
|
||||
owner = "manolomartinez";
|
||||
repo = pname;
|
||||
rev = "refs/tags/v${version}";
|
||||
tag = "v${version}";
|
||||
sha256 = "sha256-o4+tXVJTgT52JyJOC+Glr2cvZjbTaZL8TIsmz+A4vE4=";
|
||||
};
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "kmetronome";
|
||||
version = "1.4.0";
|
||||
version = "1.4.1";
|
||||
|
||||
src = fetchurl {
|
||||
url = "mirror://sourceforge/${pname}/${version}/${pname}-${version}.tar.bz2";
|
||||
hash = "sha256-51uFAPR0xsY3z9rFc8SdSGu4ae/VzUmC1qC8RGdt48Y=";
|
||||
hash = "sha256-FJVmSMu0KDoq8DHRxxGyHQQflPCvH1h+WdsV9wcPAPA=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
|
||||
@@ -20,7 +20,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
src = fetchFromGitHub {
|
||||
owner = "djdiskmachine";
|
||||
repo = "littlegptracker";
|
||||
rev = "refs/tags/${finalAttrs.version}";
|
||||
tag = finalAttrs.version;
|
||||
hash = "sha256-1uXC5nJ63YguQuNIkuK0yx9lmrMBqw0WdlmCV8o11cE=";
|
||||
};
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
qtnetworkauth,
|
||||
qttools,
|
||||
nixosTests,
|
||||
apple-sdk_11,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
@@ -127,9 +126,6 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
alsa-lib
|
||||
qtwayland
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isDarwin [
|
||||
apple-sdk_11
|
||||
];
|
||||
|
||||
postInstall =
|
||||
|
||||
@@ -24,7 +24,7 @@ pythonPackages.buildPythonApplication rec {
|
||||
src = fetchFromGitHub {
|
||||
owner = pname;
|
||||
repo = pname;
|
||||
rev = "refs/tags/${version}";
|
||||
tag = version;
|
||||
hash = "sha256-3j6IoMi30BQ8WHK4BxbsW+/3XZx7rBFd47EBENa2GiQ=";
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user