Merge commit 61630d4ab5 into haskell-updates
This commit is contained in:
@@ -12,9 +12,6 @@ on:
|
||||
required: true
|
||||
NIXPKGS_CI_APP_PRIVATE_KEY:
|
||||
required: true
|
||||
OWNER_APP_PRIVATE_KEY:
|
||||
# The Test workflow should not actually request reviews from owners.
|
||||
required: false
|
||||
|
||||
concurrency:
|
||||
group: pr-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
|
||||
+12
-16
@@ -14,15 +14,20 @@ defaults:
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
if: github.event_name != 'schedule' || github.repository_owner == 'NixOS'
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
# Use a GitHub App to create the PR so that CI gets triggered and to
|
||||
# request team member lists.
|
||||
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
|
||||
id: team-token
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.OWNER_APP_ID }}
|
||||
private-key: ${{ secrets.OWNER_APP_PRIVATE_KEY }}
|
||||
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
|
||||
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
permission-administration: read
|
||||
permission-contents: write
|
||||
permission-members: read
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Fetch source
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
@@ -38,7 +43,7 @@ jobs:
|
||||
- name: Synchronise teams
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
github-token: ${{ steps.team-token.outputs.token }}
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
require('./ci/github-script/get-teams.js')({
|
||||
github,
|
||||
@@ -47,20 +52,11 @@ jobs:
|
||||
outFile: "maintainers/github-teams.json"
|
||||
})
|
||||
|
||||
# Use a GitHub App to create the PR so that CI gets triggered
|
||||
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
|
||||
id: sync-token
|
||||
with:
|
||||
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
|
||||
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
|
||||
permission-contents: write
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Get GitHub App User Git String
|
||||
id: user
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.sync-token.outputs.token }}
|
||||
APP_SLUG: ${{ steps.sync-token.outputs.app-slug }}
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
|
||||
run: |
|
||||
name="${APP_SLUG}[bot]"
|
||||
userId=$(gh api "/users/$name" --jq .id)
|
||||
@@ -70,7 +66,7 @@ jobs:
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
token: ${{ steps.sync-token.outputs.token }}
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
add-paths: maintainers/github-teams.json
|
||||
author: ${{ steps.user.outputs.git-string }}
|
||||
committer: ${{ steps.user.outputs.git-string }}
|
||||
|
||||
+10
-3
@@ -210,7 +210,7 @@ module.exports = async ({ github, context, core, dry }) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any human reviews other than GitHub actions and other GitHub apps.
|
||||
// Check for any human reviews other than the PR author, GitHub actions and other GitHub apps.
|
||||
// Accounts could be deleted as well, so don't count them.
|
||||
const reviews = (
|
||||
await github.paginate(github.rest.pulls.listReviews, {
|
||||
@@ -218,7 +218,11 @@ module.exports = async ({ github, context, core, dry }) => {
|
||||
pull_number,
|
||||
})
|
||||
).filter(
|
||||
(r) => r.user && !r.user.login.endsWith('[bot]') && r.user.type !== 'Bot',
|
||||
(r) =>
|
||||
r.user &&
|
||||
!r.user.login.endsWith('[bot]') &&
|
||||
r.user.type !== 'Bot' &&
|
||||
r.user.id !== pull_request.user?.id,
|
||||
)
|
||||
|
||||
const approvals = new Set(
|
||||
@@ -576,7 +580,10 @@ module.exports = async ({ github, context, core, dry }) => {
|
||||
|
||||
// Controls level of parallelism. Applies to both the number of concurrent requests
|
||||
// as well as the number of concurrent workers going through the list of PRs.
|
||||
const maxConcurrent = 20
|
||||
// We'll only boost concurrency when we're running many PRs in parallel on a schedule,
|
||||
// but not for single PRs. This avoids things going wild, when we accidentally make
|
||||
// too many API requests on treewides.
|
||||
const maxConcurrent = context.payload.pull_request ? 1 : 20
|
||||
|
||||
await withRateLimit({ github, core, maxConcurrent }, async (stats) => {
|
||||
if (context.payload.pull_request) {
|
||||
|
||||
@@ -128,11 +128,20 @@ async function handleMerge({
|
||||
(await getTeamMembers('nixpkgs-committers')).map(({ id }) => id),
|
||||
)
|
||||
|
||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
||||
...context.repo,
|
||||
pull_number,
|
||||
per_page: 100,
|
||||
})
|
||||
const files = (
|
||||
await github.rest.pulls.listFiles({
|
||||
...context.repo,
|
||||
pull_number,
|
||||
per_page: 100,
|
||||
})
|
||||
).data
|
||||
|
||||
// Early exit to prevent treewides from using up a lot of API requests (and time!) to list
|
||||
// all the files in the pull request. For now, the merge-bot will not work when 100 or more
|
||||
// files are touched in a PR - which should be more than fine.
|
||||
// TODO: Find a more efficient way of downloading all the *names* of the touched files,
|
||||
// including an early exit when the first non-by-name file is found.
|
||||
if (files.length >= 100) return false
|
||||
|
||||
// Only look through comments *after* the latest (force) push.
|
||||
const lastPush = events.findLastIndex(
|
||||
@@ -182,7 +191,9 @@ async function handleMerge({
|
||||
}`,
|
||||
{ node_id: pull_request.node_id, sha: pull_request.head.sha },
|
||||
)
|
||||
return `[Queued](${resp.enqueuePullRequest.mergeQueueEntry.mergeQueue.url}) for merge`
|
||||
return [
|
||||
`:heavy_check_mark: [Queued](${resp.enqueuePullRequest.mergeQueueEntry.mergeQueue.url}) for merge (#306934)`,
|
||||
]
|
||||
} catch (e) {
|
||||
log('Enqueing failed', e.response.errors[0].message)
|
||||
}
|
||||
@@ -201,7 +212,12 @@ async function handleMerge({
|
||||
}`,
|
||||
{ node_id: pull_request.node_id, sha: pull_request.head.sha },
|
||||
)
|
||||
return 'Enabled Auto Merge'
|
||||
return [
|
||||
`:heavy_check_mark: Enabled Auto Merge (#306934)`,
|
||||
'',
|
||||
'> [!TIP]',
|
||||
'> Sometimes GitHub gets stuck after enabling Auto Merge. In this case, leaving another approval should trigger the merge.',
|
||||
]
|
||||
} catch (e) {
|
||||
log('Auto Merge failed', e.response.errors[0].message)
|
||||
throw new Error(e.response.errors[0].message)
|
||||
@@ -287,7 +303,7 @@ async function handleMerge({
|
||||
if (result) {
|
||||
await react('ROCKET')
|
||||
try {
|
||||
body.push(`:heavy_check_mark: ${await merge()} (#306934)`)
|
||||
body.push(...(await merge()))
|
||||
} catch (e) {
|
||||
// Remove the HTML comment with node_id reference to allow retrying this merge on the next run.
|
||||
body.shift()
|
||||
|
||||
@@ -13,6 +13,34 @@ async function handleReviewers({
|
||||
}) {
|
||||
const pull_number = pull_request.number
|
||||
|
||||
const requested_reviewers = new Set(
|
||||
pull_request.requested_reviewers.map(({ login }) => login),
|
||||
)
|
||||
log(
|
||||
'reviewers - requested_reviewers',
|
||||
Array.from(requested_reviewers).join(', '),
|
||||
)
|
||||
|
||||
const existing_reviewers = new Set(
|
||||
reviews.map(({ user }) => user?.login).filter(Boolean),
|
||||
)
|
||||
log(
|
||||
'reviewers - existing_reviewers',
|
||||
Array.from(existing_reviewers).join(', '),
|
||||
)
|
||||
|
||||
// Early sanity check, before we start making any API requests. The list of maintainers
|
||||
// does not have duplicates so the only user to filter out from this list would be the
|
||||
// PR author. Therefore, we check for a limit of 15+1, where 15 is the limit we check
|
||||
// further down again.
|
||||
// This is to protect against huge treewides consuming all our API requests for no
|
||||
// reason.
|
||||
if (maintainers.length > 16) {
|
||||
core.warning('Too many potential reviewers, skipping review requests.')
|
||||
// Return a boolean on whether the "needs: reviewers" label should be set.
|
||||
return existing_reviewers.size === 0 && requested_reviewers.size === 0
|
||||
}
|
||||
|
||||
const users = new Set([
|
||||
...(await Promise.all(
|
||||
maintainers.map(async (id) => (await getUser(id)).login),
|
||||
@@ -47,6 +75,9 @@ async function handleReviewers({
|
||||
const reviewers = (
|
||||
await Promise.all(
|
||||
Array.from(new_reviewers, async (username) => {
|
||||
// TODO: Restructure this file to only do the collaborator check for those users
|
||||
// who were not already part of a team. Being a member of a team makes them
|
||||
// collaborators by definition.
|
||||
try {
|
||||
await github.rest.repos.checkCollaborator({
|
||||
...context.repo,
|
||||
@@ -65,29 +96,13 @@ async function handleReviewers({
|
||||
log('reviewers - reviewers', reviewers.join(', '))
|
||||
|
||||
if (reviewers.length > 15) {
|
||||
log(
|
||||
core.warning(
|
||||
`Too many reviewers (${reviewers.join(', ')}), skipping review requests.`,
|
||||
)
|
||||
// false indicates, that we do have reviewers and don't need the "needs: reviewers" label.
|
||||
return false
|
||||
// Return a boolean on whether the "needs: reviewers" label should be set.
|
||||
return existing_reviewers.size === 0 && requested_reviewers.size === 0
|
||||
}
|
||||
|
||||
const requested_reviewers = new Set(
|
||||
pull_request.requested_reviewers.map(({ login }) => login),
|
||||
)
|
||||
log(
|
||||
'reviewers - requested_reviewers',
|
||||
Array.from(requested_reviewers).join(', '),
|
||||
)
|
||||
|
||||
const existing_reviewers = new Set(
|
||||
reviews.map(({ user }) => user?.login).filter(Boolean),
|
||||
)
|
||||
log(
|
||||
'reviewers - existing_reviewers',
|
||||
Array.from(existing_reviewers).join(', '),
|
||||
)
|
||||
|
||||
const non_requested_reviewers = new Set(reviewers)
|
||||
.difference(requested_reviewers)
|
||||
// We don't want to rerequest reviews from people who already reviewed.
|
||||
@@ -117,7 +132,7 @@ async function handleReviewers({
|
||||
|
||||
// Return a boolean on whether the "needs: reviewers" label should be set.
|
||||
return (
|
||||
new_reviewers.size === 0 &&
|
||||
non_requested_reviewers.size === 0 &&
|
||||
existing_reviewers.size === 0 &&
|
||||
requested_reviewers.size === 0
|
||||
)
|
||||
|
||||
@@ -129,7 +129,7 @@ The update script does the following:
|
||||
`fetchDeps` takes the following arguments:
|
||||
|
||||
- `attrPath` - the path to the package in nixpkgs (for example,
|
||||
`"javaPackages.openjfx22"`). Used for update script metadata.
|
||||
`"javaPackages.openjfx25"`). Used for update script metadata.
|
||||
- `pname` - an alias for `attrPath` for convenience. This is what you
|
||||
will generally use instead of `pkg` or `attrPath`.
|
||||
- `pkg` - the package to be used for fetching the dependencies. Defaults
|
||||
|
||||
@@ -20,7 +20,7 @@ Each supported language or software ecosystem has its own package set named `<la
|
||||
$ nix repl '<nixpkgs>' -I nixpkgs=channel:nixpkgs-unstable
|
||||
nix-repl> javaPackages.<tab>
|
||||
javaPackages.compiler javaPackages.openjfx15 javaPackages.openjfx21 javaPackages.recurseForDerivations
|
||||
javaPackages.jogl_2_4_0 javaPackages.openjfx17 javaPackages.openjfx22
|
||||
javaPackages.jogl_2_4_0 javaPackages.openjfx17 javaPackages.openjfx25
|
||||
javaPackages.mavenfod javaPackages.openjfx19 javaPackages.override
|
||||
javaPackages.openjfx11 javaPackages.openjfx20 javaPackages.overrideDerivation
|
||||
```
|
||||
|
||||
+29
-4
@@ -119,6 +119,12 @@
|
||||
"inkscape-plugins": [
|
||||
"index.html#inkscape-plugins"
|
||||
],
|
||||
"libcxxhardeningextensive": [
|
||||
"index.html#libcxxhardeningextensive"
|
||||
],
|
||||
"libcxxhardeningfast": [
|
||||
"index.html#libcxxhardeningfast"
|
||||
],
|
||||
"julec-hook": [
|
||||
"index.html#julec-hook"
|
||||
],
|
||||
@@ -247,6 +253,9 @@
|
||||
"sec-building-packages-with-llvm-using-clang-stdenv": [
|
||||
"index.html#sec-building-packages-with-llvm-using-clang-stdenv"
|
||||
],
|
||||
"sec-darwin-availability-checks": [
|
||||
"index.html#sec-darwin-availability-checks"
|
||||
],
|
||||
"sec-darwin-libcxx-deployment-targets": [
|
||||
"index.html#sec-darwin-libcxx-deployment-targets"
|
||||
],
|
||||
@@ -389,6 +398,24 @@
|
||||
"sec-pkg-overrideDerivation": [
|
||||
"index.html#sec-pkg-overrideDerivation"
|
||||
],
|
||||
"sec-platform-breakdown": [
|
||||
"index.html#sec-platform-breakdown"
|
||||
],
|
||||
"sec-platform-tier1": [
|
||||
"index.html#sec-platform-tier1"
|
||||
],
|
||||
"sec-platform-tier2": [
|
||||
"index.html#sec-platform-tier2"
|
||||
],
|
||||
"sec-platform-tier3": [
|
||||
"index.html#sec-platform-tier3"
|
||||
],
|
||||
"sec-platform-tier4-7": [
|
||||
"index.html#sec-platform-tier4-7"
|
||||
],
|
||||
"sec-platform-tiers": [
|
||||
"index.html#sec-platform-tiers"
|
||||
],
|
||||
"sec-lib-makeOverridable": [
|
||||
"index.html#sec-lib-makeOverridable"
|
||||
],
|
||||
@@ -1224,7 +1251,8 @@
|
||||
"index.html#sec-purity-in-nixpkgs"
|
||||
],
|
||||
"sec-hardening-in-nixpkgs": [
|
||||
"index.html#sec-hardening-in-nixpkgs"
|
||||
"index.html#sec-hardening-in-nixpkgs",
|
||||
"index.html#pie"
|
||||
],
|
||||
"sec-hardening-flags-enabled-by-default": [
|
||||
"index.html#sec-hardening-flags-enabled-by-default"
|
||||
@@ -1259,9 +1287,6 @@
|
||||
"sec-hardening-flags-disabled-by-default": [
|
||||
"index.html#sec-hardening-flags-disabled-by-default"
|
||||
],
|
||||
"pie": [
|
||||
"index.html#pie"
|
||||
],
|
||||
"shadowstack": [
|
||||
"index.html#shadowstack"
|
||||
],
|
||||
|
||||
@@ -12,6 +12,18 @@
|
||||
- The default GHC version has been updated from 9.8 to 9.10.
|
||||
`haskellPackages` correspondingly uses Stackage LTS 24 (instead of LTS 23) as a baseline.
|
||||
|
||||
- **This release of Nixpkgs requires macOS Sonoma 14.0 or newer, as announced in the 25.05 release notes.**
|
||||
The default SDK is now 14.4, but the minimum version is 14.0.
|
||||
cc-wrapper will enforce that availability annotations are used or an appropriate deployment target is set.
|
||||
See the Darwin platform notes for details.
|
||||
|
||||
- **We expect to drop support for `x86_64-darwin` by Nixpkgs 26.11,** in light of Apple’s announcement that macOS 26 will be the final version to support Intel Macs.
|
||||
When support is fully removed, we will no longer build packages for the platform or guarantee that it can build at all.
|
||||
This may happen in stages, depending on our available build and maintenance resources and decisions made by projects we rely on.
|
||||
|
||||
By the time of 26.11’s release, Homebrew will offer only limited [Tier 3](https://docs.brew.sh/Support-Tiers#tier-3) support for the platform, but MacPorts will likely continue to support it for a long time.
|
||||
We also recommend users consider installing NixOS, which should continue to run on essentially all Intel Macs, especially after Apple stops security support for macOS 26 in 2028.
|
||||
|
||||
- Darwin has switched to using the system libc++. This was done for improved compatibility and to avoid ODR violations.
|
||||
If a newer C++ library feature is not available on the default deployment target, you will need to increase the deployment target.
|
||||
See the Darwin platform documentation for more details.
|
||||
@@ -98,12 +110,16 @@
|
||||
|
||||
- `spidermonkey_91` has been removed, as it has been EOL since September 2022.
|
||||
|
||||
- `ddccontrol` service now enables `hardware.i2c` by default, and adds `ddcci_backlight` to the kernel modules, based on [experiences reported on discourse](https://discourse.nixos.org/t/brightness-control-of-external-monitors-with-ddcci-backlight/8639/).
|
||||
|
||||
- The license of duckstation has changed from `gpl3Only` to `cc-by-nc-nd-40` making it unfree in newer releases. The `duckstation` package has been overhauled to support the new releases and `duckstation-bin` has been aliased to `duckstation` to support darwin binary builds.
|
||||
|
||||
- `hiawata` has been removed, due to lack of active development upstream, lack of maintainership downstream and upcoming security issues.
|
||||
|
||||
- `forgejo` main program has been renamed to `bin/forgejo` from the previous `bin/gitea`.
|
||||
|
||||
- the "pie" hardening flag has been removed. compilers are expected to enable PIE by default, as has been common practice since 2016 outside of nixpkgs. If a package needs "pie" disabled pass `-no-pie` in `CFLAGS`. It is unlikely this will be necessary in many cases; due to the prevalance of default PIE toolchains most packages incompatible with PIE already pass no-pie.
|
||||
|
||||
- `wayclip` now uses the `ext-data-control-v1` Wayland protocol instead of `wlr-data-control-unstable-v1`.
|
||||
|
||||
- `cudaPackages.cudatoolkit-legacy-runfile` has been removed.
|
||||
@@ -154,6 +170,8 @@
|
||||
|
||||
- `meilisearch_1_11` has been removed, as it is no longer supported.
|
||||
|
||||
- `budgie-desktop` has been updated [10.9.4](https://github.com/BuddiesOfBudgie/budgie-desktop/releases/tag/v10.9.4). This changes `XDG_CURRENT_DESKTOP` from `Budgie:GNOME` to `Budgie` and contains ABI bumps for libpeas2 migration.
|
||||
|
||||
- Greetd and its original greeters (`tuigreet`, `gtkgreet`, `qtgreet`, `regreet`, `wlgreet`) were moved from `greetd` namespace to top level (`greetd.tuigreet` -> `tuigreet`, `greetd.greetd` -> `greetd`, etc). The original attrs are available for compatibility as passthrus of `greetd`, but will emit a warning. They will be removed in future releases.
|
||||
|
||||
- `carla` no longer support `gtk2` override.
|
||||
@@ -217,14 +235,17 @@
|
||||
- `podofo` has been updated from `0.9.8` to `1.0.0`. These releases are by nature very incompatible due to major API changes. The legacy versions can be found under `podofo_0_10` and `podofo_0_9`.
|
||||
Changelog: https://github.com/podofo/podofo/blob/1.0.0/CHANGELOG.md, API-Migration-Guide: https://github.com/podofo/podofo/blob/1.0.0/API-MIGRATION.md.
|
||||
|
||||
- NetBox was updated to `>= 4.3.0`. Have a look at the breaking changes
|
||||
of the [4.3 release](https://github.com/netbox-community/netbox/releases/tag/v4.2.0),
|
||||
make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_3;` in your configuration.
|
||||
- NetBox was updated to `>= 4.4.0`. Have a look at the breaking changes
|
||||
of the [4.3 release](https://github.com/netbox-community/netbox/releases/tag/v4.3.0)
|
||||
and the [4.4 release](https://github.com/netbox-community/netbox/releases/tag/v4.4.0),
|
||||
make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_4;` in your configuration.
|
||||
|
||||
- `privatebin` has been updated to `2.0.0`. This release changes configuration defaults including switching the template and removing legacy features. See the [v2.0.0 changelog entry](https://github.com/PrivateBin/PrivateBin/releases/tag/2.0.0) for details on how to upgrade.
|
||||
|
||||
- `rocmPackages.triton` has been removed in favor of `python3Packages.triton`.
|
||||
|
||||
- `oink` service no longer accepts `settings.apiKey` and `settings.secretApiKey` options as these have been replaced by `apiKeyFile` and `secretApiKeyFile`.
|
||||
|
||||
- `linpinyin`, which is used for Chinese character input, has migrated from the unmaintained BDB database format to the newer KyotoCabinet database format. If you want to migrate your user input statistics you can consider using [bdbtokyotodb](https://codeberg.org/raboof/bdbtokyotodb).
|
||||
|
||||
- `go-mockery` has been updated to v3. For migration instructions see the [upstream documentation](https://vektra.github.io/mockery/latest/v3/). If v2 is still required `go-mockery_v2` has been added but will be removed on or before 2029-12-31 in-line with its [upstream support lifecycle](https://vektra.github.io/mockery/)
|
||||
@@ -233,6 +254,8 @@
|
||||
|
||||
- [private-gpt](https://github.com/zylon-ai/private-gpt) service has been removed by lack of maintenance upstream.
|
||||
|
||||
- `rabbitmq-server` has been updated from 4.0.9 to 4.1.4. The 4.1.0 release includes breaking changes. For more information read the [changelog of 4.1.0](https://github.com/rabbitmq/rabbitmq-server/releases/tag/v4.1.0)
|
||||
|
||||
- `lxde` scope has been removed, and its packages have been moved the top-level.
|
||||
|
||||
- `pulsemeeter` has been updated to `2.0.0`. The configuration file from older versions has to be deleted. For more information and instructions see the [v2.0.0 changelog entry](https://github.com/theRealCarneiro/pulsemeeter/releases/tag/v2.0.0).
|
||||
@@ -243,6 +266,8 @@
|
||||
|
||||
- The main binary of `tomlq` has been renamed from `tomlq` to `tq`.
|
||||
|
||||
- `opensoldat` binaries and user configuration directory names have been prefixed by 'open', becoming opensoldat and opensoldatserver. Configuration will be moved automatically before launch when possible.
|
||||
|
||||
- `emacs-macport` has been moved to a fork of Mitsuharu Yamamoto's patched source code starting with Emacs v30 as the original project seems to be currently dormant. All older versions of this package have been dropped.
|
||||
This introduces some backwards‐incompatible changes; see the NEWS for details.
|
||||
NEWS can be viewed from Emacs by typing `C-h n`, or by clicking `Help->Emacs News` from the menu bar.
|
||||
@@ -271,6 +296,8 @@
|
||||
|
||||
- Added `gitConfig` and `gitConfigFile` option to the nixpkgs `config`, to allow for setting a default `gitConfigFile` for all `fetchgit` invocations.
|
||||
|
||||
- Added `npmRegistryOverrides` and `npmRegistryOverridesString` option to the nixpkgs `config`, to allow for setting a default `npmRegistryOverridesString` for all `fetchNpmDeps` invocations.
|
||||
|
||||
- The `dockerTools.streamLayeredImage` builder now uses a better algorithm for generating layered docker images, such that much more sharing is possible when the number of store paths exceeds the layer limit. It gives each of the largest store paths its own layer and adds dependencies to those layers when they aren't used elsewhere.
|
||||
|
||||
- The systemd initrd will now respect `x-systemd.wants` and `x-systemd.requires` for reliably unlocking multi-disk bcachefs volumes.
|
||||
@@ -291,7 +318,7 @@
|
||||
|
||||
- `idris2` supports being instantiated with a package environment with `idris.withPackages (p: [ ])`
|
||||
|
||||
- New hardening flags, `strictflexarrays1` and `strictflexarrays3` were made available, corresponding to the gcc/clang options `-fstrict-flex-arrays=1` and `-fstrict-flex-arrays=3` respectively.
|
||||
- New hardening flags `strictflexarrays1`, `strictflexarrays3`, `glibcxxassertions`, `libcxxhardeningfast` and `libcxxhardeningextensive` were made available.
|
||||
|
||||
- `gramps` has been updated to 6.0.0
|
||||
Upstream recommends [backing up your Family Trees](https://gramps-project.org/wiki/index.php/Gramps_6.0_Wiki_Manual_-_Manage_Family_Trees#Backing_up_a_Family_Tree) before upgrading.
|
||||
@@ -316,8 +343,6 @@
|
||||
and beware that the migration may take several hours depending on your library size and state.
|
||||
The process must not be interrupted.
|
||||
|
||||
- A new hardening flag, `glibcxxassertions` was made available, corresponding to the glibc `_GLIBCXX_ASSERTIONS` option.
|
||||
|
||||
- `versionCheckHook`: Packages that previously relied solely on `pname` to locate the program used to version check, but have a differing `meta.mainProgram` entry, might now fail.
|
||||
|
||||
|
||||
@@ -334,6 +359,8 @@
|
||||
|
||||
- `nix-prefetch-git`: Added a `--no-add-path` argument to disable adding the path to the store; this is useful when working with a [read-only store](https://nix.dev/manual/nix/2.28/command-ref/new-cli/nix3-help-stores#store-experimental-local-overlay-store-read-only).
|
||||
|
||||
- `fetchNpmDeps`: Add `npmRegistryOverridesString` argument to pass NPM registry overrides to the fetcher.
|
||||
|
||||
- `sftpman` has been updated to version 2, a rewrite in Rust which is mostly backward compatible but does include some changes to the CLI.
|
||||
For more information, [check the project's README](https://github.com/spantaleev/sftpman-rs#is-sftpman-v2-compatible-with-sftpman-v1).
|
||||
|
||||
@@ -351,6 +378,8 @@
|
||||
|
||||
- `plasma6`: Fixed the `ksycoca` cache not being re-built when `$XDG_CACHE_HOME` is set to something that isn't `$HOME/.cache`.
|
||||
|
||||
- `dragonflydb` has been updated from version 0.1.0 to version 1.34.2.
|
||||
|
||||
## Nixpkgs Library {#sec-nixpkgs-release-25.11-lib}
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
@@ -47,6 +47,17 @@ See below for how to use a newer deployment target.
|
||||
For example, `std::print` depends on features that are only available on macOS 13.3 or newer.
|
||||
To make them available, set the deployment target to 13.3 using `darwinMinVersionHook`.
|
||||
|
||||
#### Package fails to build due to missing API availability checks {#sec-darwin-availability-checks}
|
||||
|
||||
This is normally a bug in the package or a misconfigured deployment target.
|
||||
* If it is using an API from a newer release (e.g., from macOS 26.0 while targeting macOS 14.0), it needs to use an availability check.
|
||||
The code should be patched to use [`__builtin_available`](https://clang.llvm.org/docs/LanguageExtensions.html#objective-c-available).
|
||||
Note that while the linked documentation is for Objective-C, it is applicable to C and C++ except that you use `__builtin_available` in place of `@available`.
|
||||
* If the package intends to require the newer platform (i.e., it does not support running on older versions with reduced functionality), use `darwinMinVersionHook` to set the deployment target to the required version.
|
||||
See below for how to use a newer deployment target.
|
||||
* If the package actually handles this through some other mechanism (e.g., MoltenVK relies on the running platform’s MSL version), the error can be suppressed.
|
||||
To suppress the error, add `-Wno-error=unguarded-availability` to `env.NIX_CFLAGS_COMPILE`.
|
||||
|
||||
#### Package requires a non-default SDK or fails to build due to missing frameworks or symbols {#sec-darwin-troubleshooting-using-sdks}
|
||||
|
||||
In some cases, you may have to use a non-default SDK.
|
||||
@@ -106,13 +117,10 @@ The following is a list of Xcode versions, the SDK version in Nixpkgs, and the a
|
||||
Check your package’s documentation (platform support or installation instructions) to find which Xcode or SDK version to use.
|
||||
Generally, only the last SDK release for a major version is packaged.
|
||||
|
||||
| Xcode version | SDK version | Nixpkgs attribute |
|
||||
|--------------------|--------------------|------------------------------|
|
||||
| 12.0–12.5.1 | 11.3 | `apple-sdk_11` / `apple-sdk` |
|
||||
| 13.0–13.4.1 | 12.3 | `apple-sdk_12` |
|
||||
| 14.0–14.3.1 | 13.3 | `apple-sdk_13` |
|
||||
| 15.0–15.4 | 14.4 | `apple-sdk_14` |
|
||||
| 16.0 | 15.0 | `apple-sdk_15` |
|
||||
| Xcode version | SDK version | Nixpkgs attribute |
|
||||
|--------------------|--------------------|-------------------------------|
|
||||
| 15.0–15.4 | 14.4 | `apple-sdk_14` / `apple-sdk` |
|
||||
| 16.0 | 15.0 | `apple-sdk_15` |
|
||||
|
||||
|
||||
#### Darwin Default SDK versions {#sec-darwin-troubleshooting-darwin-defaults}
|
||||
|
||||
@@ -1631,19 +1631,6 @@ The following flags are disabled by default and should be enabled with `hardenin
|
||||
|
||||
This flag adds the `-fno-strict-aliasing` compiler option, which prevents the compiler from assuming code has been written strictly following the standard in regards to pointer aliasing and therefore performing optimizations that may be unsafe for code that has not followed these rules.
|
||||
|
||||
#### `pie` {#pie}
|
||||
|
||||
This flag is disabled by default for normal `glibc` based NixOS package builds, but enabled by default for
|
||||
|
||||
- `musl`-based package builds, except on Aarch64 and Aarch32, where there are issues.
|
||||
|
||||
- Statically-linked for OpenBSD builds, where it appears to be required to get a working binary.
|
||||
|
||||
Adds the `-fPIE` compiler and `-pie` linker options. Position Independent Executables are needed to take advantage of Address Space Layout Randomization, supported by modern kernel versions. While ASLR can already be enforced for data areas in the stack and heap (brk and mmap), the code areas must be compiled as position-independent. Shared libraries already do this with the `pic` flag, so they gain ASLR automatically, but binary .text regions need to be build with `pie` to gain ASLR. When this happens, ROP attacks are much harder since there are no static locations to bounce off of during a memory corruption attack.
|
||||
|
||||
Static libraries need to be compiled with `-fPIE` so that executables can link them in with the `-pie` linker option.
|
||||
If the libraries lack `-fPIE`, you will get the error `recompile with -fPIE`.
|
||||
|
||||
#### `strictflexarrays1` {#strictflexarrays1}
|
||||
|
||||
This flag adds the `-fstrict-flex-arrays=1` compiler option, which reduces the cases the compiler treats as "flexible arrays" to those declared with length `[1]`, `[0]` or (the correct) `[]`. This increases the coverage of fortify checks, because such arrays declared as the trailing element of a structure can normally not have their intended length determined by the compiler.
|
||||
@@ -1688,6 +1675,18 @@ Adds the `-D_GLIBCXX_ASSERTIONS` compiler flag. This flag only has an effect on
|
||||
|
||||
These checks may have an impact on performance in some cases.
|
||||
|
||||
#### `libcxxhardeningfast` {#libcxxhardeningfast}
|
||||
|
||||
Adds the `-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST` compiler flag. This flag only has an effect on libc++ targets, and when defined, enables a set of assertions that prevent undefined behavior caused by violating preconditions of the standard library. libc++ provides several hardening modes, and this "fast" mode contains a set of security-critical checks that can be done with relatively little overhead in constant time.
|
||||
|
||||
Disabling `libcxxhardeningfast` implies disablement of checks from `libcxxhardeningextensive`.
|
||||
|
||||
#### `libcxxhardeningextensive` {#libcxxhardeningextensive}
|
||||
|
||||
Adds the `-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE` compiler flag. This flag only has an effect on libc++ targets, and when defined, enables a set of assertions that prevent undefined behavior caused by violating preconditions of the standard library. libc++ provides several hardening modes, and this "extensive" mode adds checks for undefined behavior that incur relatively little overhead but aren’t security-critical. The additional rigour impacts performance more than fast mode: benchmarking is recommended to determine if it is acceptable for a particular application.
|
||||
|
||||
Enabling this flag implies enablement of checks from `libcxxhardeningfast`. Disabling this flag does not imply disablement of checks from `libcxxhardeningfast`.
|
||||
|
||||
#### `pacret` {#pacret}
|
||||
|
||||
This flag adds the `-mbranch-protection=pac-ret` compiler option on aarch64-linux targets. This uses ARM v8.3's Pointer Authentication feature to sign function return pointers before adding them to the stack. The pointer's authenticity is then validated before returning to its destination. This dramatically increases the difficulty of ROP exploitation techniques.
|
||||
|
||||
@@ -1,18 +1,49 @@
|
||||
# Platform Support {#chap-platform-support}
|
||||
|
||||
Packages receive varying degrees of support, both in terms of maintainer attention and available computation resources for continuous integration (CI).
|
||||
Packages receive varying degrees of support, both in terms of maintainer attention and available computation resources for continuous integration (CI). We have 7 defined tiers denoting how well supported each platform is.
|
||||
|
||||
Below is the list of the best supported platforms:
|
||||
## Tiers {#sec-platform-tiers}
|
||||
|
||||
- `x86_64-linux`: Highest level of support.
|
||||
- `aarch64-linux`: Well supported, with most packages building successfully in CI.
|
||||
- `aarch64-darwin`: Receives better support than `x86_64-darwin`.
|
||||
- `x86_64-darwin`: Receives some support.
|
||||
### Tier 1 {#sec-platform-tier1}
|
||||
|
||||
There are many other platforms with varying levels of support.
|
||||
The provisional platform list in [Appendix A] of [RFC046], while not up to date, can be used as guidance.
|
||||
[Tier 1](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-1) platforms receive the highest level of support where problems can block updates, platform-specific patches are freely applied, and most packages are expected to work.
|
||||
|
||||
A more formal definition of the platform support tiers is provided in [RFC046], but has not been fully implemented yet.
|
||||
### Tier 2 {#sec-platform-tier2}
|
||||
|
||||
[RFC046]: https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md
|
||||
[Appendix A]: https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#appendix-a-non-normative-description-of-platforms-in-november-2019
|
||||
[Tier 2](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-2) platforms are expected to remain functional with updates, receive platform-specific patches as needed, and have many packages built by Hydra with full ofBorg support.
|
||||
|
||||
### Tier 3 {#sec-platform-tier3}
|
||||
|
||||
[Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) platforms may receive non-intrusive platform-specific fixes, have native bootstrap tools available with cross-build toolchains in binary cache, but updates might break builds on these platforms.
|
||||
|
||||
### Tier 4-7 {#sec-platform-tier4-7}
|
||||
|
||||
Platform Tiers [4 through 7](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-4) indicate varying levels of minimal support going from receiving only limited fixes to platforms with no support, but a path to support.
|
||||
|
||||
## Breakdown {#sec-platform-breakdown}
|
||||
|
||||
| Triple | Support Tier | Channel Blockers | Hydra Support | Ofborg Support | Bootstrap Tarballs | Cross Compiling Support |
|
||||
| ------------------------------------- | ------------ | ---------------- | ------------- | -------------- | ------------------ | ----------------------- |
|
||||
| `x86_64-unknown-linux-gnu` | [Tier 1](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-1) | Many | ✔️ | ✔️ | ✔️ | ✔️ |
|
||||
| `aarch64-unknown-linux-gnu` | [Tier 2](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-2) | Some | ✔️ | ✔️ | ✔️ | ✔️ |
|
||||
| `x86_64-unknown-linux-musl` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | Limited | ❌ | ✔️ | ✔️ |
|
||||
| `aarch64-unknown-linux-musl` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | Limited | ❌ | ✔️ | ✔️ |
|
||||
| `x86_64-unknown-unknown-freebsd` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `arm64-apple-darwin` | [Tier 2](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-2) | Some | ✔️ | ✔️ | ✔️ | ❌\* |
|
||||
| `x86_64-apple-darwin` | [Tier 2](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-2) | Some | ✔️ | ✔️ | ✔️ | ❌\* |
|
||||
| `i686-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | Limited | ❌ | ✔️ | ✔️ |
|
||||
| `riscv32-unknown-linux-gnu` | [Tier 4](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-4) | None | ❌ | ❌ | ❌ | ✔️ |
|
||||
| `riscv64-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `loongarch64-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `armv6l-unknown-linux-gnueabihf` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `armv6l-unknown-linux-musleabihf` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `armv7l-unknown-linux-gnueabihf` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `armv5tel-unknown-linux-gnueabi` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `mips64el-unknown-linux-gnuabi64` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `mips64el-unknown-linux-gnuabin32` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `mipsel-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `powerpc64-unknown-linux-gnuabielfv2` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `powerpc64le-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
| `s390x-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
|
||||
|
||||
\* - Cross compiling is only supported on Darwin hosts.
|
||||
|
||||
+28
-9
@@ -763,11 +763,26 @@ rec {
|
||||
# Inputs
|
||||
|
||||
`extendMkDerivation`-specific configurations
|
||||
: `constructDrv`: Base build helper, the `mkDerivation`-like build helper to extend.
|
||||
: `excludeDrvArgNames`: Argument names not to pass from the input fixed-point arguments to `constructDrv`. Note: It doesn't apply to the updating arguments returned by `extendDrvArgs`.
|
||||
: `extendDrvArgs` : An extension (overlay) of the argument set, like the one taken by [overrideAttrs](#sec-pkg-overrideAttrs) but applied before passing to `constructDrv`.
|
||||
: `inheritFunctionArgs`: Whether to inherit `__functionArgs` from the base build helper (default to `true`).
|
||||
: `transformDrv`: Function to apply to the result derivation (default to `lib.id`).
|
||||
: `constructDrv` (required)
|
||||
: Base build helper, the `mkDerivation`-like build helper to extend.
|
||||
|
||||
`excludeDrvArgNames` (default to `[ ]`)
|
||||
: Argument names not to pass from the input fixed-point arguments to `constructDrv`.
|
||||
It doesn't apply to the updating arguments returned by `extendDrvArgs`.
|
||||
|
||||
`excludeFunctionArgNames` (default to `[ ]`)
|
||||
: `__functionArgs` attribute names to remove from the result build helper.
|
||||
`excludeFunctionArgNames` is useful for argument deprecation while avoiding ellipses.
|
||||
|
||||
`extendDrvArgs` (required)
|
||||
: An extension (overlay) of the argument set, like the one taken by [overrideAttrs](#sec-pkg-overrideAttrs) but applied before passing to `constructDrv`.
|
||||
|
||||
`inheritFunctionArgs` (default to `true`)
|
||||
: Whether to inherit `__functionArgs` from the base build helper.
|
||||
Set `inheritFunctionArgs` to `false` when `extendDrvArgs`'s `args` set pattern does not contain an ellipsis.
|
||||
|
||||
`transformDrv` (default to `lib.id`)
|
||||
: Function to apply to the result derivation.
|
||||
|
||||
# Type
|
||||
|
||||
@@ -776,6 +791,7 @@ rec {
|
||||
{
|
||||
constructDrv :: ((FixedPointArgs | AttrSet) -> a)
|
||||
excludeDrvArgNames :: [ String ],
|
||||
excludeFunctionArgNames :: [ String ]
|
||||
extendDrvArgs :: (AttrSet -> AttrSet -> AttrSet)
|
||||
inheritFunctionArgs :: Bool,
|
||||
transformDrv :: a -> a,
|
||||
@@ -836,6 +852,7 @@ rec {
|
||||
{
|
||||
constructDrv,
|
||||
excludeDrvArgNames ? [ ],
|
||||
excludeFunctionArgNames ? [ ],
|
||||
extendDrvArgs,
|
||||
inheritFunctionArgs ? true,
|
||||
transformDrv ? id,
|
||||
@@ -850,10 +867,12 @@ rec {
|
||||
)
|
||||
# Add __functionArgs
|
||||
(
|
||||
# Inherit the __functionArgs from the base build helper
|
||||
optionalAttrs inheritFunctionArgs (removeAttrs (functionArgs constructDrv) excludeDrvArgNames)
|
||||
# Recover the __functionArgs from the derived build helper
|
||||
// functionArgs (extendDrvArgs { })
|
||||
removeAttrs (
|
||||
# Inherit the __functionArgs from the base build helper
|
||||
optionalAttrs inheritFunctionArgs (removeAttrs (functionArgs constructDrv) excludeDrvArgNames)
|
||||
# Recover the __functionArgs from the derived build helper
|
||||
// functionArgs (extendDrvArgs { })
|
||||
) excludeFunctionArgNames
|
||||
)
|
||||
// {
|
||||
inherit
|
||||
|
||||
@@ -747,6 +747,11 @@ lib.mapAttrs mkLicense (
|
||||
fullName = "Historical Permission Notice and Disclaimer - University of California variant";
|
||||
};
|
||||
|
||||
hyphenBulgarian = {
|
||||
fullName = "hyphen-bulgarian License";
|
||||
spdxId = "hyphen-bulgarian";
|
||||
};
|
||||
|
||||
# Intel's license, seems free
|
||||
iasl = {
|
||||
spdxId = "Intel-ACPI";
|
||||
@@ -993,6 +998,11 @@ lib.mapAttrs mkLicense (
|
||||
fullName = "CMU License";
|
||||
};
|
||||
|
||||
mit-enna = {
|
||||
spdxId = "MIT-enna";
|
||||
fullName = "enna License";
|
||||
};
|
||||
|
||||
mit-feh = {
|
||||
spdxId = "MIT-feh";
|
||||
fullName = "feh License";
|
||||
|
||||
@@ -1147,6 +1147,9 @@ let
|
||||
instead of:
|
||||
|
||||
baseType // { check = value: /* your check */; }
|
||||
|
||||
Alternatively, this message may also occur as false positive when mixing Nixpkgs
|
||||
versions, if one Nixpkgs is between 83fed2e6..58696117 (Aug 28 - Oct 28 2025)
|
||||
'';
|
||||
|
||||
# Merge definitions of a value of a given type.
|
||||
|
||||
@@ -360,8 +360,8 @@ let
|
||||
null;
|
||||
# The canonical name for this attribute is darwinSdkVersion, but some
|
||||
# platforms define the old name "sdkVer".
|
||||
darwinSdkVersion = final.sdkVer or "11.3";
|
||||
darwinMinVersion = final.darwinSdkVersion;
|
||||
darwinSdkVersion = final.sdkVer or "14.4";
|
||||
darwinMinVersion = "14.0";
|
||||
darwinMinVersionVariable =
|
||||
if final.isMacOS then
|
||||
"MACOSX_DEPLOYMENT_TARGET"
|
||||
|
||||
@@ -223,6 +223,15 @@ checkConfigError 'A definition for option .* is not of type .path in the Nix sto
|
||||
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/.links"' config.pathInStore.bad4 ./types.nix
|
||||
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: "/foo/bar"' config.pathInStore.bad5 ./types.nix
|
||||
|
||||
# types.externalPath
|
||||
checkConfigOutput '".*/foo/bar"' config.externalPath.ok1 ./types.nix
|
||||
checkConfigOutput '".*/"' config.externalPath.ok2 ./types.nix
|
||||
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad1 ./types.nix
|
||||
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad2 ./types.nix
|
||||
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad3 ./types.nix
|
||||
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad4 ./types.nix
|
||||
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad5 ./types.nix
|
||||
|
||||
# types.fileset
|
||||
checkConfigOutput '^0$' config.filesetCardinal.ok1 ./fileset.nix
|
||||
checkConfigOutput '^1$' config.filesetCardinal.ok2 ./fileset.nix
|
||||
|
||||
@@ -15,6 +15,7 @@ in
|
||||
{
|
||||
options = {
|
||||
pathInStore = mkOption { type = types.lazyAttrsOf types.pathInStore; };
|
||||
externalPath = mkOption { type = types.lazyAttrsOf types.externalPath; };
|
||||
assertions = mkOption { };
|
||||
};
|
||||
config = {
|
||||
@@ -26,6 +27,13 @@ in
|
||||
pathInStore.bad3 = "${storeDir}/";
|
||||
pathInStore.bad4 = "${storeDir}/.links"; # technically true, but not reasonable
|
||||
pathInStore.bad5 = "/foo/bar";
|
||||
externalPath.bad1 = "${storeDir}/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv";
|
||||
externalPath.bad2 = "${storeDir}/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15";
|
||||
externalPath.bad3 = "${storeDir}/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash";
|
||||
externalPath.bad4 = "";
|
||||
externalPath.bad5 = "./foo/bar";
|
||||
externalPath.ok1 = "/foo/bar";
|
||||
externalPath.ok2 = "/";
|
||||
|
||||
assertions =
|
||||
with lib.types;
|
||||
|
||||
@@ -676,6 +676,11 @@ let
|
||||
inStore = true;
|
||||
};
|
||||
|
||||
externalPath = pathWith {
|
||||
absolute = true;
|
||||
inStore = false;
|
||||
};
|
||||
|
||||
pathWith =
|
||||
{
|
||||
inStore ? null,
|
||||
|
||||
@@ -220,7 +220,6 @@
|
||||
"gshpychka": 23005347,
|
||||
"hexagonal-sun": 222664,
|
||||
"heywoodlh": 18178614,
|
||||
"iedame": 60272,
|
||||
"isabelroses": 71222764,
|
||||
"jonhermansen": 660911,
|
||||
"juliusrickert": 5007494,
|
||||
@@ -724,7 +723,6 @@
|
||||
"description": "Provides leadership for and has authority over Nixpkgs.",
|
||||
"id": 14317027,
|
||||
"maintainers": {
|
||||
"K900": 386765,
|
||||
"alyssais": 2768870,
|
||||
"emilazy": 18535642,
|
||||
"wolfgangwalther": 9132420
|
||||
|
||||
@@ -588,7 +588,7 @@
|
||||
adamperkowski = {
|
||||
name = "Adam Perkowski";
|
||||
email = "me@adamperkowski.dev";
|
||||
matrix = "@xx0a_q:matrix.org";
|
||||
matrix = "@adam:matrix.system72.dev";
|
||||
github = "adamperkowski";
|
||||
githubId = 75480869;
|
||||
keys = [
|
||||
@@ -1069,6 +1069,13 @@
|
||||
}
|
||||
];
|
||||
};
|
||||
alch-emi = {
|
||||
email = "emi@alchemi.dev";
|
||||
github = "Alch-Emi";
|
||||
githubId = 38897201;
|
||||
name = "Emi";
|
||||
matrix = "@emi:the-apothecary.club";
|
||||
};
|
||||
aldenparker = {
|
||||
github = "aldenparker";
|
||||
githubId = 32986873;
|
||||
@@ -1149,10 +1156,11 @@
|
||||
name = "Alexandru Nechita";
|
||||
};
|
||||
alexandrutocar = {
|
||||
email = "at@myquiet.place";
|
||||
email = "alexandru.tocar@outlook.com";
|
||||
github = "alexandrutocar";
|
||||
githubId = 65486851;
|
||||
name = "Alexandru Tocar";
|
||||
keys = [ { fingerprint = "B617 DD24 3AB0 2E3F 2E67 DBFD 1305 2A85 D7A4 2AA4"; } ];
|
||||
};
|
||||
alexarice = {
|
||||
email = "alexrice999@hotmail.co.uk";
|
||||
@@ -1184,6 +1192,12 @@
|
||||
githubId = 2335822;
|
||||
name = "Alexandre Esteves";
|
||||
};
|
||||
alexland7219 = {
|
||||
email = "alexland7219@gmail.com";
|
||||
github = "alexland7219";
|
||||
githubId = 58669111;
|
||||
name = "Alexandre Ros";
|
||||
};
|
||||
alexnabokikh = {
|
||||
email = "nabokikh@duck.com";
|
||||
github = "alexnabokikh";
|
||||
@@ -5410,12 +5424,6 @@
|
||||
githubId = 202474;
|
||||
name = "Jens Reimann";
|
||||
};
|
||||
ctucx = {
|
||||
email = "katja@ctu.cx";
|
||||
github = "katjakwast";
|
||||
githubId = 176372446;
|
||||
name = "Katja Kwast";
|
||||
};
|
||||
cupcakearmy = {
|
||||
name = "Niccolo Borgioli";
|
||||
email = "nix@nicco.io";
|
||||
@@ -5556,7 +5564,7 @@
|
||||
};
|
||||
da157 = {
|
||||
email = "da157@voidq.com";
|
||||
matrix = "@awwpotato:envs.net";
|
||||
matrix = "@da157:catgirl.cloud";
|
||||
github = "0xda157";
|
||||
githubId = 153149335;
|
||||
name = "0xda157";
|
||||
@@ -9102,6 +9110,12 @@
|
||||
githubId = 7047019;
|
||||
name = "Florent Becker";
|
||||
};
|
||||
galabovaa = {
|
||||
name = "Ivet Galabova";
|
||||
email = "galabovaa@gmail.com";
|
||||
github = "galabovaa";
|
||||
githubId = 4016566;
|
||||
};
|
||||
galagora = {
|
||||
email = "lightningstrikeiv@gmail.com";
|
||||
github = "Galagora";
|
||||
@@ -10833,12 +10847,6 @@
|
||||
githubId = 1550265;
|
||||
name = "Dominic Steinitz";
|
||||
};
|
||||
iedame = {
|
||||
email = "git@ieda.me";
|
||||
github = "iedame";
|
||||
githubId = 60272;
|
||||
name = "Rafael Ieda";
|
||||
};
|
||||
if-loop69420 = {
|
||||
github = "if-loop69420";
|
||||
githubId = 81078181;
|
||||
@@ -11737,6 +11745,12 @@
|
||||
githubId = 29395089;
|
||||
name = "Jay Rovacsek";
|
||||
};
|
||||
jaysa68 = {
|
||||
email = "gh@jaysa.net";
|
||||
github = "jaysa68";
|
||||
githubId = 114126812;
|
||||
name = "Jaysa Maria Garcia";
|
||||
};
|
||||
jb55 = {
|
||||
email = "jb55@jb55.com";
|
||||
github = "jb55";
|
||||
@@ -12589,7 +12603,7 @@
|
||||
name = "Ioannis Koutras";
|
||||
};
|
||||
jolars = {
|
||||
email = "jolars@posteo.com";
|
||||
email = "johan@jolars.co";
|
||||
matrix = "@jola:mozilla.org";
|
||||
github = "jolars";
|
||||
githubId = 13087841;
|
||||
@@ -13986,6 +14000,14 @@
|
||||
githubId = 4032;
|
||||
name = "Kristoffer Thømt Ravneberg";
|
||||
};
|
||||
krishnans2006 = {
|
||||
email = "krishnans2006@gmail.com";
|
||||
matrix = "@krishnans2006:matrix.org";
|
||||
github = "krishnans2006";
|
||||
githubId = 62958782;
|
||||
name = "Krishnan Shankar";
|
||||
keys = [ { fingerprint = "A30C 1843 F470 4843 5D54 3D68 29CB 06A8 40D0 E14A"; } ];
|
||||
};
|
||||
kristian-brucaj = {
|
||||
email = "kbrucaj@gmail.com";
|
||||
github = "Flameslice";
|
||||
@@ -14308,6 +14330,17 @@
|
||||
githubId = 55911173;
|
||||
name = "Gwendolyn Quasebarth";
|
||||
};
|
||||
lajp = {
|
||||
email = "lajp@iki.fi";
|
||||
github = "lajp";
|
||||
githubId = 31856315;
|
||||
name = "Luukas Pörtfors";
|
||||
keys = [
|
||||
{
|
||||
fingerprint = "24E8 E4CC 0295 F4ED B9E0 B4A6 C913 9B8D EA65 BD82";
|
||||
}
|
||||
];
|
||||
};
|
||||
lamarios = {
|
||||
matrix = "@lamarios:matrix.org";
|
||||
github = "lamarios";
|
||||
@@ -19578,6 +19611,12 @@
|
||||
githubId = 52569953;
|
||||
name = "Oskar Manhart";
|
||||
};
|
||||
oskarwires = {
|
||||
email = "me@usbcable.io";
|
||||
github = "oskarwires";
|
||||
githubId = 115482671;
|
||||
name = "Oskar";
|
||||
};
|
||||
osnyx = {
|
||||
email = "os@flyingcircus.io";
|
||||
github = "osnyx";
|
||||
@@ -21762,12 +21801,6 @@
|
||||
{ fingerprint = "01D7 5486 3A6D 64EA AC77 0D26 FBF1 9A98 2CCE 0048"; }
|
||||
];
|
||||
};
|
||||
redbaron = {
|
||||
email = "ivanov.maxim@gmail.com";
|
||||
github = "redbaron";
|
||||
githubId = 16624;
|
||||
name = "Maxim Ivanov";
|
||||
};
|
||||
redfish64 = {
|
||||
email = "engler@gmail.com";
|
||||
github = "redfish64";
|
||||
@@ -23222,6 +23255,12 @@
|
||||
github = "scd31";
|
||||
githubId = 57571338;
|
||||
};
|
||||
SchahinRohani = {
|
||||
email = "schahin@rouhanizadeh.de";
|
||||
github = "SchahinRohani";
|
||||
githubId = 24507823;
|
||||
name = "Schahin Rouhanizadeh";
|
||||
};
|
||||
schinmai-akamai = {
|
||||
email = "schinmai@akamai.com";
|
||||
github = "tchinmai7";
|
||||
@@ -23929,6 +23968,7 @@
|
||||
githubId = 49711232;
|
||||
github = "ShyAssassin";
|
||||
email = "ShyAssassin@assassin.dev";
|
||||
keys = [ { fingerprint = "370E 8296 12AF D0E1 9A1A 88F6 AA81 2447 4716 E56D"; } ];
|
||||
};
|
||||
shyim = {
|
||||
email = "s.sayakci@gmail.com";
|
||||
@@ -25963,6 +26003,11 @@
|
||||
githubId = 91203390;
|
||||
name = "Felix Kimmel";
|
||||
};
|
||||
thesn = {
|
||||
github = "thesn10";
|
||||
githubId = 38666407;
|
||||
name = "TheSN";
|
||||
};
|
||||
thesola10 = {
|
||||
email = "me@thesola.io";
|
||||
github = "Thesola10";
|
||||
@@ -27103,6 +27148,11 @@
|
||||
githubId = 120451;
|
||||
name = "Urban Skudnik";
|
||||
};
|
||||
usovalx = {
|
||||
name = "Oleksandr Usov";
|
||||
github = "usovalx";
|
||||
githubId = 1041626;
|
||||
};
|
||||
usrfriendly = {
|
||||
name = "Arin Lares";
|
||||
email = "arinlares@gmail.com";
|
||||
@@ -28869,6 +28919,16 @@
|
||||
name = "Zexin Yuan";
|
||||
keys = [ { fingerprint = "FE16 B281 90EF 6C3F F661 6441 C2DD 1916 FE47 1BE2"; } ];
|
||||
};
|
||||
zacharyarnaise = {
|
||||
name = "Zachary Arnaise";
|
||||
github = "zacharyarnaise";
|
||||
githubId = 121795280;
|
||||
keys = [
|
||||
{
|
||||
fingerprint = "5C1A 2BE8 C6B4 AFC0 DE82 73E9 B1F3 94D8 D460 C3B2";
|
||||
}
|
||||
];
|
||||
};
|
||||
zacharyweiss = {
|
||||
name = "Zachary Weiss";
|
||||
email = "me@zachary.ws";
|
||||
@@ -28959,6 +29019,12 @@
|
||||
githubId = 450885;
|
||||
name = "Francesco Zanini";
|
||||
};
|
||||
zaphyra = {
|
||||
email = "katja@zaphyra.eu";
|
||||
github = "katjakwast";
|
||||
githubId = 176372446;
|
||||
name = "Katja Kwast";
|
||||
};
|
||||
zareix = {
|
||||
email = "contact@raphael-catarino.fr";
|
||||
github = "zareix";
|
||||
|
||||
@@ -52,17 +52,6 @@ The maintainer is designated by a `selector` which must be one of:
|
||||
|
||||
[`maintainer-list.nix`]: ../maintainer-list.nix
|
||||
|
||||
### `get-maintainer-pings-between.sh`
|
||||
|
||||
Gets which maintainers would be pinged between two Nixpkgs revisions.
|
||||
Outputs a JSON object on stdout mapping GitHub usernames to the attributes that they would be getting pinged for.
|
||||
|
||||
Example:
|
||||
|
||||
```sh
|
||||
maintainers/scripts/get-maintainer-pings-between.sh HEAD^ HEAD
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
### `sha-to-sri.py`
|
||||
|
||||
@@ -92,7 +92,15 @@ async def attr_instantiation_worker(
|
||||
) -> tuple[Path, str]:
|
||||
async with semaphore:
|
||||
eprint(f"Instantiating {attr_path}…")
|
||||
return (await nix_instantiate(attr_path), attr_path)
|
||||
try:
|
||||
return (await nix_instantiate(attr_path), attr_path)
|
||||
except Exception as e:
|
||||
# Failure should normally terminate the script but
|
||||
# looks like Python is buggy so we need to do it ourselves.
|
||||
eprint(f"Failed to instantiate {attr_path}")
|
||||
if e.stderr:
|
||||
eprint(e.stderr.decode("utf-8"))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
async def requisites_worker(
|
||||
|
||||
+4
-1
@@ -1,12 +1,15 @@
|
||||
{
|
||||
configuration ? import ./lib/from-env.nix "NIXOS_CONFIG" <nixos-config>,
|
||||
system ? builtins.currentSystem,
|
||||
# This should only be used for special arguments that need to be evaluated when resolving module structure (like in imports).
|
||||
# For everything else, there's _module.args.
|
||||
specialArgs ? { },
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
eval = import ./lib/eval-config.nix {
|
||||
inherit system;
|
||||
inherit system specialArgs;
|
||||
modules = [ configuration ];
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,21 @@ merging is handled.
|
||||
: A path that is contained in the Nix store. This can be a top-level store
|
||||
path like `pkgs.hello` or a descendant like `"${pkgs.hello}/bin/hello"`.
|
||||
|
||||
`types.externalPath`
|
||||
|
||||
: A path that is not contained in the Nix store. Typical use cases are:
|
||||
secrets, password or any other external file.
|
||||
|
||||
::: {.warning}
|
||||
This type only validates that the path is not *currently* in the Nix store.
|
||||
It does NOT prevent the value from being copied to the store later when:
|
||||
- Referenced in a derivation
|
||||
- Used in certain path operations (e.g., `${path}` interpolation)
|
||||
- Passed to functions that copy to the store
|
||||
|
||||
Users must still be careful about how they reference these paths.
|
||||
:::
|
||||
|
||||
`types.pathWith` { *`inStore`* ? `null`, *`absolute`* ? `null` }
|
||||
|
||||
: A filesystem path. Either a string or something that can be coerced
|
||||
|
||||
@@ -22,6 +22,16 @@
|
||||
|
||||
- COSMIC DE has been updated to the beta version, bringing it closer to its first stable release. This includes updates to its core components, applications, and overall stability.
|
||||
|
||||
- GNOME has been updated to version 49.
|
||||
|
||||
- Removes X11 session support. Though you can still run X11 apps using XWayland.
|
||||
- gnome-session’s custom service manager was removed in favour of using systemd.
|
||||
- GDM now allows multiple seats, which is useful for e.g. remote logins. Though we currently [limit this to five greeter instances](https://github.com/NixOS/nixpkgs/issues/458058).
|
||||
- `papers` document viewer is now installed by default, replacing `evince`. Though we still include `evince` transitively by `sushi` (quick previewer used by Files/Nautilus) You can disable either using [](#opt-environment.gnome.excludePackages) and restore `evince` with [](#opt-programs.evince.enable).
|
||||
- `showtime` video player is now installed by default, replacing `totem`. You can disable it using [](#opt-environment.gnome.excludePackages) and restore `totem` with [](#opt-environment.systemPackages).
|
||||
|
||||
Refer to the [GNOME release notes](https://release.gnome.org/49/) for more details.
|
||||
|
||||
## New Modules {#sec-release-25.11-new-modules}
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
@@ -195,6 +205,8 @@
|
||||
- `boot.enableContainers` is only turned on when a declarative NixOS container is defined in `containers`.
|
||||
If you use the `nixos-container` tool for imperative container management, set `boot.enableContainers = true;` explicitly.
|
||||
|
||||
- `services.parsoid` and the `nodePackages.parsoid` package have been removed, as the JavaScript-based version this module uses is not compatible with modern MediaWiki versions.
|
||||
|
||||
- `virtualisation.lxd` has been removed due to lack of Nixpkgs maintenance. Users can migrate to `virtualisation.incus`, a fork of LXD, as a replacement. See [Incus migration documentation](https://linuxcontainers.org/incus/docs/main/howto/server_migrate_lxd/) for migration information.
|
||||
|
||||
- `virtualisation.libvirtd` now uses OVMF images shipped with QEMU for UEFI machines. `virtualisation.libvirtd.qemu.ovmf` has been removed.
|
||||
@@ -277,6 +289,10 @@
|
||||
|
||||
- `services.gateone` has been removed as the package was removed such that it does not work.
|
||||
|
||||
- `services.quorum` has been removed as the `quorum` package was broken and abandoned upstream.
|
||||
|
||||
- `orjail` package has been removed as it is broken by the latest firejail release and seems unmaintained.
|
||||
|
||||
- `teleport` has been upgraded from major version 17 to major version 18.
|
||||
Refer to [upstream upgrade instructions](https://goteleport.com/docs/upgrading/overview/)
|
||||
and [release notes for v18](https://goteleport.com/docs/changelog/#1800-070325).
|
||||
@@ -444,6 +460,8 @@ and [release notes for v18](https://goteleport.com/docs/changelog/#1800-070325).
|
||||
|
||||
- `services.xserver.desktopManager.deepin` and associated packages have been removed due to being unmaintained. See issue [#422090](https://github.com/NixOS/nixpkgs/issues/422090) for more details.
|
||||
|
||||
- `services.matter-server` now hosts a debug dashboard on the configured port. Open the port on the firewall with `services.matter-server.openFirewall`.
|
||||
|
||||
- The new option [networking.ipips](#opt-networking.ipips) has been added to create IP within IP kind of tunnels (including 4in6, ip6ip6 and ipip).
|
||||
With the existing [networking.sits](#opt-networking.sits) option (6in4), it is now possible to create all combinations of IPv4 and IPv6 encapsulation.
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ let
|
||||
with config.boot;
|
||||
[
|
||||
kernelPackages.kernel
|
||||
(lib.getOutput "modules" kernelPackages.kernel)
|
||||
kernelPackages.${pkgs.zfs.kernelModuleAttribute}
|
||||
]
|
||||
);
|
||||
|
||||
@@ -114,6 +114,7 @@ let
|
||||
with config.boot;
|
||||
[
|
||||
kernelPackages.kernel
|
||||
(lib.getOutput "modules" kernelPackages.kernel)
|
||||
kernelPackages.${pkgs.zfs.kernelModuleAttribute}
|
||||
]
|
||||
);
|
||||
|
||||
+137
-38
@@ -229,7 +229,7 @@ let
|
||||
listToAttrs (flatten (recurse "." item));
|
||||
|
||||
/*
|
||||
Takes an attrset and a file path and generates a bash snippet that
|
||||
Takes some options, an attrset and a file path and generates a bash snippet that
|
||||
outputs a JSON file at the file path with all instances of
|
||||
|
||||
{ _secret = "/path/to/secret" }
|
||||
@@ -237,6 +237,28 @@ let
|
||||
in the attrset replaced with the contents of the file
|
||||
"/path/to/secret" in the output JSON.
|
||||
|
||||
The first argument exposes the following options:
|
||||
|
||||
- attr: The name of the secret attribute that will be processed, defaults to "_secret"
|
||||
- loadCredential: A boolean determining whether the script should load secrets directly (false)
|
||||
or load them from $CREDENTIALS_DIRECTORY (true). In the latter case the output attribute set
|
||||
will contain a .credentials attribute with the necessary credential list that can be passed
|
||||
to systemd's `LoadCredential=` option.
|
||||
|
||||
The output of this utility is an attribute set containing the main script and optionally
|
||||
a list of credentials:
|
||||
|
||||
{
|
||||
# The main script
|
||||
script = "...";
|
||||
|
||||
# If the loadCredential option was set:
|
||||
credentials = [
|
||||
"secret1:/path/to/secret1"
|
||||
#...
|
||||
];
|
||||
}
|
||||
|
||||
When a configuration option accepts an attrset that is finally
|
||||
converted to JSON, this makes it possible to let the user define
|
||||
arbitrary secret values.
|
||||
@@ -245,7 +267,7 @@ let
|
||||
If the file "/path/to/secret" contains the string
|
||||
"topsecretpassword1234",
|
||||
|
||||
genJqSecretsReplacementSnippet {
|
||||
genJqSecretsReplacement { } {
|
||||
example = [
|
||||
{
|
||||
irrelevant = "not interesting";
|
||||
@@ -293,7 +315,7 @@ let
|
||||
{ "b": "topsecretpassword5678" }
|
||||
]
|
||||
|
||||
genJqSecretsReplacementSnippet {
|
||||
genJqSecretsReplacement { } {
|
||||
example = [
|
||||
{
|
||||
irrelevant = "not interesting";
|
||||
@@ -330,12 +352,12 @@ let
|
||||
]
|
||||
}
|
||||
*/
|
||||
genJqSecretsReplacementSnippet = genJqSecretsReplacementSnippet' "_secret";
|
||||
|
||||
# Like genJqSecretsReplacementSnippet, but allows the name of the
|
||||
# attr which identifies the secret to be changed.
|
||||
genJqSecretsReplacementSnippet' =
|
||||
attr: set: output:
|
||||
genJqSecretsReplacement =
|
||||
{
|
||||
attr ? "_secret",
|
||||
loadCredential ? false,
|
||||
}:
|
||||
set: output:
|
||||
let
|
||||
secretsRaw = recursiveGetAttrsetWithJqPrefix set attr;
|
||||
# Set default option values
|
||||
@@ -347,38 +369,115 @@ let
|
||||
// set
|
||||
) secretsRaw;
|
||||
stringOrDefault = str: def: if str == "" then def else str;
|
||||
in
|
||||
''
|
||||
if [[ -h '${output}' ]]; then
|
||||
rm '${output}'
|
||||
fi
|
||||
|
||||
inherit_errexit_enabled=0
|
||||
shopt -pq inherit_errexit && inherit_errexit_enabled=1
|
||||
shopt -s inherit_errexit
|
||||
''
|
||||
+ concatStringsSep "\n" (
|
||||
imap1 (index: name: ''
|
||||
secret${toString index}=$(<'${secrets.${name}.${attr}}')
|
||||
export secret${toString index}
|
||||
'') (attrNames secrets)
|
||||
)
|
||||
+ "\n"
|
||||
+ "${pkgs.jq}/bin/jq >'${output}' "
|
||||
+ escapeShellArg (
|
||||
stringOrDefault (concatStringsSep " | " (
|
||||
# Sanitize path to create a valid credential tag (same as in genLoadCredentialForJqSecretsReplacementSnippet)
|
||||
sanitizePath =
|
||||
path: lib.stringAsChars (c: if builtins.match "[a-zA-Z0-9_.#=!-]" c != null then c else "_") path;
|
||||
|
||||
# Generate credential tag for a given index and path
|
||||
credentialTag = index: path: "${toString index}_${sanitizePath (secrets.${path}.${attr})}";
|
||||
|
||||
credentialPath =
|
||||
index: name:
|
||||
if loadCredential then
|
||||
''"$CREDENTIALS_DIRECTORY/${credentialTag index name}"''
|
||||
else
|
||||
"'${secrets.${name}.${attr}}'";
|
||||
in
|
||||
{
|
||||
script = ''
|
||||
if [[ -h '${output}' ]]; then
|
||||
rm '${output}'
|
||||
fi
|
||||
|
||||
inherit_errexit_enabled=0
|
||||
shopt -pq inherit_errexit && inherit_errexit_enabled=1
|
||||
shopt -s inherit_errexit
|
||||
''
|
||||
+ concatStringsSep "\n" (
|
||||
imap1 (
|
||||
index: name:
|
||||
''${name} = ($ENV.secret${toString index}${optionalString (!secrets.${name}.quote) " | fromjson"})''
|
||||
) (attrNames secrets)
|
||||
)) "."
|
||||
)
|
||||
+ ''
|
||||
<<'EOF'
|
||||
${toJSON set}
|
||||
EOF
|
||||
(( ! inherit_errexit_enabled )) && shopt -u inherit_errexit
|
||||
'';
|
||||
# We keep variable assignment and export separated to avoid masking the return code of the file access.
|
||||
# With `set -e` this will now fail if a file doesn't exist.
|
||||
''
|
||||
secret${toString index}=$(<${credentialPath index name})
|
||||
export secret${toString index}
|
||||
'') (attrNames secrets)
|
||||
)
|
||||
+ "\n"
|
||||
+ "${pkgs.jq}/bin/jq >'${output}' "
|
||||
+ escapeShellArg (
|
||||
stringOrDefault (concatStringsSep " | " (
|
||||
imap1 (
|
||||
index: name:
|
||||
''${name} = ($ENV.secret${toString index}${optionalString (!secrets.${name}.quote) " | fromjson"})''
|
||||
) (attrNames secrets)
|
||||
)) "."
|
||||
)
|
||||
+ ''
|
||||
<<'EOF'
|
||||
${toJSON set}
|
||||
EOF
|
||||
(( ! inherit_errexit_enabled )) && shopt -u inherit_errexit
|
||||
'';
|
||||
|
||||
/*
|
||||
Generates a list of systemd LoadCredential entries if loadCredential was set,
|
||||
otherwise returns null.
|
||||
|
||||
The tag is sanitized to only contain characters a-zA-Z0-9_-.#=! and prefixed
|
||||
with an index to ensure uniqueness.
|
||||
|
||||
Example:
|
||||
genLoadCredentialForJqSecretsReplacementSnippet { } {
|
||||
example = {
|
||||
secret1 = { _secret = "/path/to/secret"; };
|
||||
secret2 = { _secret = "/another/secret"; };
|
||||
};
|
||||
}
|
||||
-> [ "0_path_to_secret:/path/to/secret" "1_another_secret:/another/secret" ]
|
||||
*/
|
||||
credentials =
|
||||
if loadCredential then
|
||||
imap1 (
|
||||
index: path:
|
||||
"${toString index}_${sanitizePath (secretsRaw.${path}.${attr})}:${secretsRaw.${path}.${attr}}"
|
||||
) (attrNames secretsRaw)
|
||||
else
|
||||
null;
|
||||
};
|
||||
|
||||
/*
|
||||
A convenience function around `genJqSecretsReplacement` without any additional
|
||||
settings that returns just the script that does the secret replacing. Make sure
|
||||
to have a look at `genJqSecretsReplacement` first to decide whether you need
|
||||
the additional functionality.
|
||||
|
||||
Example:
|
||||
If the file "/path/to/secret" contains the string
|
||||
"topsecretpassword1234",
|
||||
|
||||
genJqSecretsReplacementSnippet {
|
||||
example = [
|
||||
{
|
||||
irrelevant = "not interesting";
|
||||
}
|
||||
{
|
||||
ignored = "ignored attr";
|
||||
relevant = {
|
||||
secret = {
|
||||
_secret = "/path/to/secret";
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
} "/path/to/output.json"
|
||||
|
||||
will return a set of bash commands that replaces the secret values
|
||||
in the given attrset with values from the respective files and saves the result
|
||||
as a JSON file.
|
||||
*/
|
||||
genJqSecretsReplacementSnippet = set: output: (genJqSecretsReplacement { } set output).script;
|
||||
|
||||
/*
|
||||
Remove packages of packagesToRemove from packages, based on their names.
|
||||
|
||||
@@ -71,7 +71,7 @@ in
|
||||
defaultChannel = mkOption {
|
||||
internal = true;
|
||||
type = types.str;
|
||||
default = "https://nixos.org/channels/nixos-unstable";
|
||||
default = "https://channels.nixos.org/nixos-unstable";
|
||||
description = "Default NixOS channel to which the root user is subscribed.";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ let
|
||||
vteInitSnippet = ''
|
||||
# Show current working directory in VTE terminals window title.
|
||||
# Supports both bash and zsh, requires interactive shell.
|
||||
. ${pkgs.vte.override { gtkVersion = null; }}/etc/profile.d/vte.sh
|
||||
. ${pkgs.vte-gtk4}/etc/profile.d/vte.sh
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
@@ -384,8 +384,8 @@ in
|
||||
you're at version 9, you cannot increment this to 10.
|
||||
''
|
||||
++ lib.optional (partitionConfig.stripNixStorePrefix != "_mkMergedOptionModule") ''
|
||||
The option definition `image.repart.paritions.${fileName}.stripNixStorePrefix`
|
||||
has changed to `image.repart.paritions.${fileName}.nixStorePrefix` and now
|
||||
The option definition `image.repart.partitions.${fileName}.stripNixStorePrefix`
|
||||
has changed to `image.repart.partitions.${fileName}.nixStorePrefix` and now
|
||||
accepts the path to use as prefix directly. Use `nixStorePrefix = "/"` to
|
||||
achieve the same effect as setting `stripNixStorePrefix = true`.
|
||||
''
|
||||
|
||||
@@ -569,6 +569,7 @@
|
||||
./services/desktops/gnome/gnome-online-miners.nix
|
||||
./services/desktops/gnome/gnome-remote-desktop.nix
|
||||
./services/desktops/gnome/gnome-settings-daemon.nix
|
||||
./services/desktops/gnome/gnome-software.nix
|
||||
./services/desktops/gnome/gnome-user-share.nix
|
||||
./services/desktops/gnome/localsearch.nix
|
||||
./services/desktops/gnome/rygel.nix
|
||||
@@ -902,7 +903,6 @@
|
||||
./services/misc/packagekit.nix
|
||||
./services/misc/paisa.nix
|
||||
./services/misc/paperless.nix
|
||||
./services/misc/parsoid.nix
|
||||
./services/misc/persistent-evdev.nix
|
||||
./services/misc/pghero.nix
|
||||
./services/misc/pinchflat.nix
|
||||
@@ -1320,7 +1320,6 @@
|
||||
./services/networking/pyload.nix
|
||||
./services/networking/quassel.nix
|
||||
./services/networking/quicktun.nix
|
||||
./services/networking/quorum.nix
|
||||
./services/networking/r53-ddns.nix
|
||||
./services/networking/radicale.nix
|
||||
./services/networking/radvd.nix
|
||||
|
||||
@@ -234,6 +234,10 @@ in
|
||||
(mkRemovedOptionModule [ "services" "pantheon" "files" ] ''
|
||||
This module was removed, please add pkgs.pantheon.elementary-files to environment.systemPackages directly.
|
||||
'')
|
||||
(mkRemovedOptionModule [ "services" "parsoid" ] ''
|
||||
The Javascript version of Parsoid configured through this module does not work with modern MediaWiki versions,
|
||||
and has been deprecated by upstream, so it has been removed. MediaWiki comes with a new PHP-based parser built-in, so there is no need for this module.
|
||||
'')
|
||||
(mkRemovedOptionModule [ "services" "polipo" ] ''
|
||||
The polipo project is unmaintained and archived upstream.
|
||||
'')
|
||||
@@ -244,6 +248,9 @@ in
|
||||
"services"
|
||||
"quagga"
|
||||
] "the corresponding package has been removed from nixpkgs")
|
||||
(mkRemovedOptionModule [ "services" "quorum" ] ''
|
||||
The corresponding package was broken, abandoned upstream and thus removed from nixpkgs.
|
||||
'')
|
||||
(mkRemovedOptionModule [
|
||||
"services"
|
||||
"railcar"
|
||||
|
||||
@@ -2312,8 +2312,7 @@ in
|
||||
environment.etc = lib.mapAttrs' makePAMService enabledServices;
|
||||
|
||||
systemd =
|
||||
lib.optionalAttrs
|
||||
(lib.any (service: service.updateWtmp) (lib.attrValues config.security.pam.services))
|
||||
lib.mkIf (lib.any (service: service.updateWtmp) (lib.attrValues config.security.pam.services))
|
||||
{
|
||||
tmpfiles.packages = [ pkgs.util-linux.lastlog ]; # /lib/tmpfiles.d/lastlog2-tmpfiles.conf
|
||||
services.lastlog2-import = {
|
||||
|
||||
@@ -20,9 +20,7 @@ in
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
systemd.services.display-manager = lib.mkIf config.services.displayManager.enable {
|
||||
path = [ cfg.package ];
|
||||
};
|
||||
systemd.packages = [ cfg.package ];
|
||||
services.speechd.enable = true;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ let
|
||||
mkKeyValue = lib.generators.mkKeyValueDefault { } " ";
|
||||
listsAsDuplicateKeys = true;
|
||||
};
|
||||
assertKey = key: {
|
||||
assertion = cfg.settings ? ${key};
|
||||
message = "Please set services.gonic.settings.${key}. See https://github.com/sentriz/gonic#configuration-options for supported values.";
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
@@ -29,6 +33,7 @@ in
|
||||
example = {
|
||||
music-path = [ "/mnt/music" ];
|
||||
podcast-path = "/mnt/podcasts";
|
||||
playlists-path = "/mnt/playlists";
|
||||
};
|
||||
description = ''
|
||||
Configuration for Gonic, see <https://github.com/sentriz/gonic#configuration-options> for supported values.
|
||||
@@ -39,6 +44,12 @@ in
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
(assertKey "music-path")
|
||||
(assertKey "podcast-path")
|
||||
(assertKey "playlists-path")
|
||||
];
|
||||
|
||||
systemd.services.gonic = {
|
||||
description = "Gonic Media Server";
|
||||
after = [ "network.target" ];
|
||||
@@ -62,6 +73,7 @@ in
|
||||
BindPaths = [
|
||||
cfg.settings.playlists-path
|
||||
cfg.settings.podcast-path
|
||||
cfg.settings.cache-path
|
||||
];
|
||||
BindReadOnlyPaths = [
|
||||
# gonic can access scrobbling services
|
||||
@@ -94,7 +106,6 @@ in
|
||||
];
|
||||
RestrictRealtime = true;
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
UMask = "0066";
|
||||
ProtectHostname = true;
|
||||
};
|
||||
|
||||
@@ -83,10 +83,7 @@ let
|
||||
secretPathOption =
|
||||
with lib.types;
|
||||
lib.mkOption {
|
||||
type = nullOr (pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
});
|
||||
type = nullOr externalPath;
|
||||
default = null;
|
||||
internal = true;
|
||||
};
|
||||
@@ -142,10 +139,7 @@ in
|
||||
};
|
||||
|
||||
options.sftp-private-key-file = lib.mkOption {
|
||||
type = nullOr (pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
});
|
||||
type = nullOr externalPath;
|
||||
default = null;
|
||||
description = ''
|
||||
SFTP private key file.
|
||||
|
||||
@@ -31,12 +31,7 @@ in
|
||||
enable = lib.mkEnableOption "postgres-websockets";
|
||||
|
||||
pgpassFile = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
});
|
||||
type = with lib.types; nullOr externalPath;
|
||||
default = null;
|
||||
example = "/run/keys/db_password";
|
||||
description = ''
|
||||
@@ -54,12 +49,7 @@ in
|
||||
};
|
||||
|
||||
jwtSecretFile = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
});
|
||||
type = with lib.types; nullOr externalPath;
|
||||
example = "/run/keys/jwt_secret";
|
||||
description = ''
|
||||
Secret used to sign JWT tokens used to open communications channels.
|
||||
|
||||
@@ -54,12 +54,7 @@ in
|
||||
enable = lib.mkEnableOption "PostgREST";
|
||||
|
||||
pgpassFile = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
});
|
||||
type = with lib.types; nullOr externalPath;
|
||||
default = null;
|
||||
example = "/run/keys/db_password";
|
||||
description = ''
|
||||
@@ -77,12 +72,7 @@ in
|
||||
};
|
||||
|
||||
jwtSecretFile = lib.mkOption {
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
});
|
||||
type = with lib.types; nullOr externalPath;
|
||||
default = null;
|
||||
example = "/run/keys/jwt_secret";
|
||||
description = ''
|
||||
|
||||
@@ -232,7 +232,7 @@ in
|
||||
|
||||
environment.gnome.excludePackages = mkOption {
|
||||
default = [ ];
|
||||
example = literalExpression "[ pkgs.totem ]";
|
||||
example = literalExpression "[ pkgs.showtime ]";
|
||||
type = types.listOf types.package;
|
||||
description = "Which packages gnome should exclude from the default environment";
|
||||
};
|
||||
@@ -297,8 +297,12 @@ in
|
||||
|
||||
systemd.packages = [
|
||||
pkgs.gnome-flashback
|
||||
pkgs.metacity
|
||||
(pkgs.gnome-panel-with-modules.override {
|
||||
panelModulePackages = cfg.flashback.panelModulePackages;
|
||||
})
|
||||
]
|
||||
++ map pkgs.gnome-flashback.mkSystemdTargetForWm flashbackWms;
|
||||
++ map pkgs.gnome-flashback.mkSystemdTargetForWm cfg.flashback.customSessions;
|
||||
|
||||
environment.systemPackages = [
|
||||
pkgs.gnome-flashback
|
||||
@@ -311,9 +315,7 @@ in
|
||||
wm: pkgs.gnome-flashback.mkWmApplication { inherit (wm) wmName wmLabel wmCommand; }
|
||||
) flashbackWms)
|
||||
# For /share/pkgs.gnome-session/sessions/gnome-flashback-${wmName}.session
|
||||
++ (map (
|
||||
wm: pkgs.gnome-flashback.mkGnomeSession { inherit (wm) wmName wmLabel enableGnomePanel; }
|
||||
) flashbackWms);
|
||||
++ (map (wm: pkgs.gnome-flashback.mkGnomeSession { inherit (wm) wmName wmLabel; }) flashbackWms);
|
||||
})
|
||||
|
||||
(lib.mkIf serviceCfg.core-os-services.enable {
|
||||
@@ -444,50 +446,49 @@ in
|
||||
|
||||
# Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/-/blob/gnome-48/elements/core/meta-gnome-core-apps.bst
|
||||
(lib.mkIf serviceCfg.core-apps.enable {
|
||||
environment.systemPackages = utils.removePackagesByName (
|
||||
[
|
||||
pkgs.baobab
|
||||
pkgs.decibels
|
||||
pkgs.epiphany
|
||||
pkgs.gnome-text-editor
|
||||
pkgs.gnome-calculator
|
||||
pkgs.gnome-calendar
|
||||
pkgs.gnome-characters
|
||||
pkgs.gnome-clocks
|
||||
pkgs.gnome-console
|
||||
pkgs.gnome-contacts
|
||||
pkgs.gnome-font-viewer
|
||||
pkgs.gnome-logs
|
||||
pkgs.gnome-maps
|
||||
pkgs.gnome-music
|
||||
pkgs.gnome-system-monitor
|
||||
pkgs.gnome-weather
|
||||
pkgs.loupe
|
||||
pkgs.nautilus
|
||||
pkgs.gnome-connections
|
||||
pkgs.simple-scan
|
||||
pkgs.snapshot
|
||||
pkgs.totem
|
||||
pkgs.yelp
|
||||
]
|
||||
++ lib.optionals config.services.flatpak.enable [
|
||||
# Since PackageKit Nix support is not there yet,
|
||||
# only install gnome-software if flatpak is enabled.
|
||||
pkgs.gnome-software
|
||||
]
|
||||
) config.environment.gnome.excludePackages;
|
||||
environment.systemPackages = utils.removePackagesByName [
|
||||
pkgs.baobab
|
||||
pkgs.decibels
|
||||
pkgs.epiphany
|
||||
pkgs.gnome-text-editor
|
||||
pkgs.gnome-calculator
|
||||
pkgs.gnome-calendar
|
||||
pkgs.gnome-characters
|
||||
pkgs.gnome-clocks
|
||||
pkgs.gnome-console
|
||||
pkgs.gnome-contacts
|
||||
pkgs.gnome-font-viewer
|
||||
pkgs.gnome-logs
|
||||
pkgs.gnome-maps
|
||||
pkgs.gnome-music
|
||||
pkgs.gnome-system-monitor
|
||||
pkgs.gnome-weather
|
||||
pkgs.loupe
|
||||
pkgs.nautilus
|
||||
pkgs.papers
|
||||
pkgs.gnome-connections
|
||||
pkgs.showtime
|
||||
pkgs.simple-scan
|
||||
pkgs.snapshot
|
||||
pkgs.yelp
|
||||
] config.environment.gnome.excludePackages;
|
||||
|
||||
# Enable default program modules
|
||||
# Since some of these have a corresponding package, we only
|
||||
# enable that program module if the package hasn't been excluded
|
||||
# through `environment.gnome.excludePackages`
|
||||
programs.evince.enable = notExcluded pkgs.evince;
|
||||
programs.file-roller.enable = notExcluded pkgs.file-roller;
|
||||
programs.geary.enable = notExcluded pkgs.geary;
|
||||
programs.gnome-disks.enable = notExcluded pkgs.gnome-disk-utility;
|
||||
programs.seahorse.enable = notExcluded pkgs.seahorse;
|
||||
services.gnome.sushi.enable = notExcluded pkgs.sushi;
|
||||
|
||||
# Since PackageKit Nix support is not there yet,
|
||||
# only install gnome-software if flatpak is enabled.
|
||||
services.gnome.gnome-software.enable = lib.mkIf config.services.flatpak.enable (
|
||||
notExcluded pkgs.gnome-software
|
||||
);
|
||||
|
||||
# VTE shell integration for gnome-console
|
||||
programs.bash.vteIntegration = mkDefault true;
|
||||
programs.zsh.vteIntegration = mkDefault true;
|
||||
|
||||
@@ -268,6 +268,7 @@ in
|
||||
services.upower.enable = config.powerManagement.enable;
|
||||
services.libinput.enable = mkDefault true;
|
||||
services.geoclue2.enable = mkDefault true;
|
||||
services.fwupd.enable = mkDefault true;
|
||||
|
||||
# Extra UDEV rules used by Solid
|
||||
services.udev.packages = [
|
||||
|
||||
@@ -77,9 +77,11 @@ in
|
||||
];
|
||||
|
||||
systemd.user.targets."gnome-session".wants = [
|
||||
"gnome-initial-setup-copy-worker.service"
|
||||
"gnome-initial-setup-first-login.service"
|
||||
"gnome-welcome-tour.service"
|
||||
];
|
||||
|
||||
systemd.user.targets."graphical-session-pre".wants = [
|
||||
"gnome-initial-setup-copy-worker.service"
|
||||
];
|
||||
|
||||
systemd.user.targets."gnome-session@gnome-initial-setup".wants = [
|
||||
@@ -89,6 +91,11 @@ in
|
||||
programs.dconf.profiles.gnome-initial-setup.databases = [
|
||||
"${pkgs.gnome-initial-setup}/share/gnome-initial-setup/initial-setup-dconf-defaults"
|
||||
];
|
||||
|
||||
users = {
|
||||
# TODO: switch to using provided gnome-initial-setup sysusers.d
|
||||
groups.gnome-initial-setup = { };
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
meta = {
|
||||
maintainers = lib.teams.gnome.members;
|
||||
};
|
||||
|
||||
options = {
|
||||
services.gnome.gnome-software = {
|
||||
enable = lib.mkEnableOption "GNOME Software, package manager for GNOME";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf config.services.gnome.gnome-software.enable {
|
||||
environment.systemPackages = [
|
||||
pkgs.gnome-software
|
||||
];
|
||||
|
||||
systemd.packages = [
|
||||
pkgs.gnome-software
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -183,10 +183,44 @@ in
|
||||
name = "gdm";
|
||||
uid = config.ids.uids.gdm;
|
||||
group = "gdm";
|
||||
home = "/run/gdm";
|
||||
description = "GDM user";
|
||||
};
|
||||
|
||||
users.users.gdm-greeter = {
|
||||
isSystemUser = true;
|
||||
uid = 60578;
|
||||
group = "gdm";
|
||||
home = "/run/gdm";
|
||||
};
|
||||
|
||||
users.users.gdm-greeter-1 = {
|
||||
isSystemUser = true;
|
||||
uid = 60579;
|
||||
group = "gdm";
|
||||
home = "/run/gdm-1";
|
||||
};
|
||||
|
||||
users.users.gdm-greeter-2 = {
|
||||
isSystemUser = true;
|
||||
uid = 60580;
|
||||
group = "gdm";
|
||||
home = "/run/gdm-2";
|
||||
};
|
||||
|
||||
users.users.gdm-greeter-3 = {
|
||||
isSystemUser = true;
|
||||
uid = 60581;
|
||||
group = "gdm";
|
||||
home = "/run/gdm-3";
|
||||
};
|
||||
|
||||
users.users.gdm-greeter-4 = {
|
||||
isSystemUser = true;
|
||||
uid = 60582;
|
||||
group = "gdm";
|
||||
home = "/run/gdm-4";
|
||||
};
|
||||
|
||||
users.groups.gdm.gid = config.ids.gids.gdm;
|
||||
|
||||
# GDM needs different xserverArgs, presumable because using wayland by default.
|
||||
@@ -348,15 +382,15 @@ in
|
||||
# GDM LFS PAM modules, adapted somehow to NixOS
|
||||
security.pam.services = {
|
||||
gdm-launch-environment.text = ''
|
||||
auth required pam_succeed_if.so audit quiet_success user = gdm
|
||||
auth required pam_succeed_if.so audit quiet_success user ingroup gdm
|
||||
auth optional pam_permit.so
|
||||
|
||||
account required pam_succeed_if.so audit quiet_success user = gdm
|
||||
account required pam_succeed_if.so audit quiet_success user ingroup gdm
|
||||
account sufficient pam_unix.so
|
||||
|
||||
password required pam_deny.so
|
||||
|
||||
session required pam_succeed_if.so audit quiet_success user = gdm
|
||||
session required pam_succeed_if.so audit quiet_success user ingroup gdm
|
||||
session required pam_env.so conffile=/etc/pam/environment readenv=0
|
||||
session optional ${config.systemd.package}/lib/security/pam_systemd.so
|
||||
session optional pam_keyinit.so force revoke
|
||||
|
||||
@@ -10,31 +10,49 @@ let
|
||||
in
|
||||
|
||||
{
|
||||
meta.maintainers = with lib.maintainers; [ doronbehar ];
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
services.ddccontrol = {
|
||||
enable = lib.mkEnableOption "ddccontrol for controlling displays";
|
||||
enable = lib.mkEnableOption ''
|
||||
ddccontrol for controlling displays.
|
||||
|
||||
This [enables `hardware.i2c`](#opt-hardware.i2c.enable), so note to add
|
||||
yourself to [`hardware.i2c.group`](#opt-hardware.i2c.group).
|
||||
'';
|
||||
package =
|
||||
lib.mkPackageOption pkgs
|
||||
"package with which to control brightness; added also to [services.dbus.packages](#opt-services.dbus.packages)."
|
||||
{
|
||||
default = [ "ddccontrol" ];
|
||||
example = [ "ddcutil-service" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
###### implementation
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
boot.kernelModules = [
|
||||
"ddcci_backlight"
|
||||
];
|
||||
# Load the i2c-dev module
|
||||
boot.kernelModules = [ "i2c_dev" ];
|
||||
hardware.i2c = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
# Give users access to the "gddccontrol" tool
|
||||
environment.systemPackages = [
|
||||
pkgs.ddccontrol
|
||||
cfg.package
|
||||
];
|
||||
|
||||
services.dbus.packages = [
|
||||
pkgs.ddccontrol
|
||||
cfg.package
|
||||
];
|
||||
|
||||
systemd.packages = [
|
||||
pkgs.ddccontrol
|
||||
cfg.package
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@ in
|
||||
description = "Port to expose the matter-server service on.";
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether to open the port in the firewall.";
|
||||
};
|
||||
|
||||
logLevel = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"critical"
|
||||
@@ -48,6 +54,8 @@ in
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ];
|
||||
|
||||
systemd.services.matter-server = {
|
||||
after = [ "network-online.target" ];
|
||||
before = [ "home-assistant.service" ];
|
||||
|
||||
@@ -522,6 +522,7 @@ in
|
||||
nullOr (oneOf [
|
||||
bool
|
||||
int
|
||||
path
|
||||
str
|
||||
(listOf str)
|
||||
])
|
||||
|
||||
@@ -9,6 +9,7 @@ let
|
||||
inherit (lib)
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkMerge
|
||||
mkOption
|
||||
mkPackageOption
|
||||
types
|
||||
@@ -59,8 +60,6 @@ let
|
||||
reportdConfigFile = format.generate "tlsrpt-reportd.cfg" {
|
||||
tlsrpt_reportd = dropNullValues cfg.reportd.settings;
|
||||
};
|
||||
|
||||
withPostfix = config.services.postfix.enable && cfg.configurePostfix;
|
||||
in
|
||||
|
||||
{
|
||||
@@ -286,71 +285,83 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.etc = {
|
||||
"tlsrpt/collectd.cfg".source = collectdConfigFile;
|
||||
"tlsrpt/fetcher.cfg".source = fetcherConfigFile;
|
||||
"tlsrpt/reportd.cfg".source = reportdConfigFile;
|
||||
};
|
||||
config = mkMerge [
|
||||
(mkIf (cfg.enable && config.services.postfix.enable && cfg.configurePostfix) {
|
||||
users.users.postfix.extraGroups = [
|
||||
"tlsrpt"
|
||||
];
|
||||
|
||||
users.users.tlsrpt = {
|
||||
isSystemUser = true;
|
||||
group = "tlsrpt";
|
||||
};
|
||||
users.groups.tlsrpt = { };
|
||||
|
||||
users.users.postfix.extraGroups = lib.mkIf withPostfix [
|
||||
"tlsrpt"
|
||||
];
|
||||
|
||||
systemd.services.tlsrpt-collectd = {
|
||||
description = "TLSRPT datagram collector";
|
||||
documentation = [ "man:tlsrpt-collectd(1)" ];
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
restartTriggers = [ collectdConfigFile ];
|
||||
|
||||
serviceConfig = commonServiceSettings // {
|
||||
ExecStart = toString (
|
||||
[
|
||||
(lib.getExe' cfg.package "tlsrpt-collectd")
|
||||
]
|
||||
++ cfg.collectd.extraFlags
|
||||
);
|
||||
IPAddressDeny = "any";
|
||||
PrivateNetwork = true;
|
||||
RestrictAddressFamilies = [ "AF_UNIX" ];
|
||||
RuntimeDirectory = "tlsrpt";
|
||||
RuntimeDirectoryMode = "0750";
|
||||
UMask = "0157";
|
||||
services.postfix.settings.main = {
|
||||
smtp_tlsrpt_enable = true;
|
||||
smtp_tlsrpt_socket_name = cfg.collectd.settings.socketname;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.tlsrpt-reportd = {
|
||||
description = "TLSRPT report generator";
|
||||
documentation = [ "man:tlsrpt-reportd(1)" ];
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
restartTriggers = [ reportdConfigFile ];
|
||||
|
||||
serviceConfig = commonServiceSettings // {
|
||||
ExecStart = toString (
|
||||
[
|
||||
(lib.getExe' cfg.package "tlsrpt-reportd")
|
||||
]
|
||||
++ cfg.reportd.extraFlags
|
||||
);
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_NETLINK"
|
||||
];
|
||||
ReadWritePaths = lib.optionals withPostfix [ "/var/lib/postfix/queue/maildrop" ];
|
||||
SupplementaryGroups = lib.optionals withPostfix [ "postdrop" ];
|
||||
UMask = "0077";
|
||||
systemd.services.tlsrpt-reportd.serviceConfig = {
|
||||
ReadWritePaths = [ "/var/lib/postfix/queue/maildrop" ];
|
||||
SupplementaryGroups = [ "postdrop" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
(mkIf cfg.enable {
|
||||
environment.etc = {
|
||||
"tlsrpt/collectd.cfg".source = collectdConfigFile;
|
||||
"tlsrpt/fetcher.cfg".source = fetcherConfigFile;
|
||||
"tlsrpt/reportd.cfg".source = reportdConfigFile;
|
||||
};
|
||||
|
||||
users.users.tlsrpt = {
|
||||
isSystemUser = true;
|
||||
group = "tlsrpt";
|
||||
};
|
||||
users.groups.tlsrpt = { };
|
||||
|
||||
systemd.services.tlsrpt-collectd = {
|
||||
description = "TLSRPT datagram collector";
|
||||
documentation = [ "man:tlsrpt-collectd(1)" ];
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
restartTriggers = [ collectdConfigFile ];
|
||||
|
||||
serviceConfig = commonServiceSettings // {
|
||||
ExecStart = toString (
|
||||
[
|
||||
(lib.getExe' cfg.package "tlsrpt-collectd")
|
||||
]
|
||||
++ cfg.collectd.extraFlags
|
||||
);
|
||||
IPAddressDeny = "any";
|
||||
PrivateNetwork = true;
|
||||
RestrictAddressFamilies = [ "AF_UNIX" ];
|
||||
RuntimeDirectory = "tlsrpt";
|
||||
RuntimeDirectoryMode = "0750";
|
||||
UMask = "0157";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.tlsrpt-reportd = {
|
||||
description = "TLSRPT report generator";
|
||||
documentation = [ "man:tlsrpt-reportd(1)" ];
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
restartTriggers = [ reportdConfigFile ];
|
||||
|
||||
serviceConfig = commonServiceSettings // {
|
||||
ExecStart = toString (
|
||||
[
|
||||
(lib.getExe' cfg.package "tlsrpt-reportd")
|
||||
]
|
||||
++ cfg.reportd.extraFlags
|
||||
);
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_NETLINK"
|
||||
];
|
||||
UMask = "0077";
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
@@ -76,17 +76,11 @@ in
|
||||
description = "Makes the bot mention @room when posting an alert";
|
||||
};
|
||||
tokenFile = lib.mkOption {
|
||||
type = lib.types.pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
};
|
||||
type = lib.types.externalPath;
|
||||
description = "File that contains a valid Matrix token for the Matrix user.";
|
||||
};
|
||||
secretFile = lib.mkOption {
|
||||
type = lib.types.pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
};
|
||||
type = lib.types.externalPath;
|
||||
description = "File that contains a secret for the Alertmanager webhook.";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -217,7 +217,7 @@ in
|
||||
}
|
||||
]
|
||||
++ lib.mapAttrsToList (mcu: firmware: {
|
||||
assertion = firmware.enable -> firmware.serial != null;
|
||||
assertion = firmware.enableKlipperFlash -> firmware.serial != null;
|
||||
message = ''
|
||||
Unable to determine the serial connection for services.klipper.firmwares."${mcu}". Please set one of the following:
|
||||
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
cfg = config.services.parsoid;
|
||||
|
||||
parsoid = pkgs.nodePackages.parsoid;
|
||||
|
||||
confTree = {
|
||||
worker_heartbeat_timeout = 300000;
|
||||
logging = {
|
||||
level = "info";
|
||||
};
|
||||
services = [
|
||||
{
|
||||
module = "lib/index.js";
|
||||
entrypoint = "apiServiceWorker";
|
||||
conf = {
|
||||
mwApis = map (x: if lib.isAttrs x then x else { uri = x; }) cfg.wikis;
|
||||
serverInterface = cfg.interface;
|
||||
serverPort = cfg.port;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
confFile = pkgs.writeText "config.yml" (
|
||||
builtins.toJSON (lib.recursiveUpdate confTree cfg.extraConfig)
|
||||
);
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [
|
||||
"services"
|
||||
"parsoid"
|
||||
"interwikis"
|
||||
] "Use services.parsoid.wikis instead")
|
||||
];
|
||||
|
||||
##### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.parsoid = {
|
||||
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable Parsoid -- bidirectional
|
||||
wikitext parser.
|
||||
'';
|
||||
};
|
||||
|
||||
wikis = lib.mkOption {
|
||||
type = lib.types.listOf (lib.types.either lib.types.str lib.types.attrs);
|
||||
example = [ "http://localhost/api.php" ];
|
||||
description = ''
|
||||
Used MediaWiki API endpoints.
|
||||
'';
|
||||
};
|
||||
|
||||
workers = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 2;
|
||||
description = ''
|
||||
Number of Parsoid workers.
|
||||
'';
|
||||
};
|
||||
|
||||
interface = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "127.0.0.1";
|
||||
description = ''
|
||||
Interface to listen on.
|
||||
'';
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8000;
|
||||
description = ''
|
||||
Port to listen on.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
default = { };
|
||||
description = ''
|
||||
Extra configuration to add to parsoid configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
##### implementation
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
systemd.services.parsoid = {
|
||||
description = "Bidirectional wikitext parser";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}";
|
||||
|
||||
DynamicUser = true;
|
||||
User = "parsoid";
|
||||
Group = "parsoid";
|
||||
|
||||
CapabilityBoundingSet = "";
|
||||
NoNewPrivileges = true;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
PrivateDevices = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
LockPersonality = true;
|
||||
#MemoryDenyWriteExecute = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
RemoveIPC = true;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -15,10 +15,7 @@ in
|
||||
extraOpts = {
|
||||
package = mkPackageOption pkgs "prometheus-storagebox-exporter" { };
|
||||
tokenFile = lib.mkOption {
|
||||
type = lib.types.pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
};
|
||||
type = lib.types.externalPath;
|
||||
description = "File that contains the Hetzner API token to use.";
|
||||
};
|
||||
|
||||
|
||||
@@ -15,11 +15,7 @@ in
|
||||
package = lib.mkPackageOption pkgs "cloudflare-dyndns" { };
|
||||
|
||||
apiTokenFile = lib.mkOption {
|
||||
type = lib.types.pathWith {
|
||||
absolute = true;
|
||||
inStore = false;
|
||||
};
|
||||
|
||||
type = lib.types.externalPath;
|
||||
description = ''
|
||||
The path to a file containing the CloudFlare API token.
|
||||
'';
|
||||
|
||||
@@ -20,15 +20,17 @@ in
|
||||
options.services.oink = {
|
||||
enable = lib.mkEnableOption "Oink, a dynamic DNS client for Porkbun";
|
||||
package = lib.mkPackageOption pkgs "oink" { };
|
||||
apiKeyFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
example = "/run/keys/oink-api-key";
|
||||
description = "Path to a file containing the API key to use when modifying DNS records.";
|
||||
};
|
||||
secretApiKeyFile = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
example = "/run/keys/oink-secret-api-key";
|
||||
description = "Path to a file containing the secret API key to use when modifying DNS records.";
|
||||
};
|
||||
settings = {
|
||||
apiKey = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "API key to use when modifying DNS records.";
|
||||
};
|
||||
secretApiKey = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Secret API key to use when modifying DNS records.";
|
||||
};
|
||||
interval = lib.mkOption {
|
||||
# https://github.com/rlado/oink/blob/v1.1.1/src/main.go#L364
|
||||
type = lib.types.ints.between 60 172800; # 48 hours
|
||||
@@ -79,12 +81,29 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "services" "oink" "settings" "apiKey" ] ''
|
||||
This option has been removed because it would make the API key world-readable.
|
||||
Use {option}`apiKeyFile` instead.
|
||||
If you insist on keeping the API key world-readable, you can use `oink.apiKeyFile = pkgs.writeText "api-key" "secret";`.
|
||||
'')
|
||||
(lib.mkRemovedOptionModule [ "services" "oink" "settings" "secretApiKey" ] ''
|
||||
This option has been removed because it would make the API key world-readable.
|
||||
Use {option}`secretApiKeyFile` instead.
|
||||
If you insist on keeping the API key world-readable, you can use `oink.secretApiKeyFile = pkgs.writeText "secret-api-key" "secret";`.
|
||||
'')
|
||||
];
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.services.oink = {
|
||||
description = "Dynamic DNS client for Porkbun";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
script = "${cfg.package}/bin/oink -c ${oinkConfig}";
|
||||
script =
|
||||
lib.optionalString (cfg.apiKeyFile != null) "OINK_OVERRIDE_APIKEY=\"$(cat ${cfg.apiKeyFile})\" "
|
||||
+ lib.optionalString (
|
||||
cfg.secretApiKeyFile != null
|
||||
) "OINK_OVERRIDE_SECRETAPIKEY=\"$(cat ${cfg.secretApiKeyFile})\" "
|
||||
+ "${cfg.package}/bin/oink -c ${oinkConfig}";
|
||||
serviceConfig = {
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10";
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
{
|
||||
config,
|
||||
options,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
inherit (lib)
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
literalExpression
|
||||
types
|
||||
optionalString
|
||||
;
|
||||
|
||||
cfg = config.services.quorum;
|
||||
opt = options.services.quorum;
|
||||
dataDir = "/var/lib/quorum";
|
||||
genesisFile = pkgs.writeText "genesis.json" (builtins.toJSON cfg.genesis);
|
||||
staticNodesFile = pkgs.writeText "static-nodes.json" (builtins.toJSON cfg.staticNodes);
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
|
||||
services.quorum = {
|
||||
enable = mkEnableOption "Quorum blockchain daemon";
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "quorum";
|
||||
description = "The user as which to run quorum.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = cfg.user;
|
||||
defaultText = literalExpression "config.${opt.user}";
|
||||
description = "The group as which to run quorum.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 21000;
|
||||
description = "Override the default port on which to listen for connections.";
|
||||
};
|
||||
|
||||
nodekeyFile = mkOption {
|
||||
type = types.path;
|
||||
default = "${dataDir}/nodekey";
|
||||
description = "Path to the nodekey.";
|
||||
};
|
||||
|
||||
staticNodes = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [
|
||||
"enode://dd333ec28f0a8910c92eb4d336461eea1c20803eed9cf2c056557f986e720f8e693605bba2f4e8f289b1162e5ac7c80c914c7178130711e393ca76abc1d92f57@0.0.0.0:30303?discport=0"
|
||||
];
|
||||
description = "List of validator nodes.";
|
||||
};
|
||||
|
||||
privateconfig = mkOption {
|
||||
type = types.str;
|
||||
default = "ignore";
|
||||
description = "Configuration of privacy transaction manager.";
|
||||
};
|
||||
|
||||
syncmode = mkOption {
|
||||
type = types.enum [
|
||||
"fast"
|
||||
"full"
|
||||
"light"
|
||||
];
|
||||
default = "full";
|
||||
description = "Blockchain sync mode.";
|
||||
};
|
||||
|
||||
blockperiod = mkOption {
|
||||
type = types.int;
|
||||
default = 5;
|
||||
description = "Default minimum difference between two consecutive block's timestamps in seconds.";
|
||||
};
|
||||
|
||||
permissioned = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Allow only a defined list of nodes to connect.";
|
||||
};
|
||||
|
||||
rpc = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable RPC interface.";
|
||||
};
|
||||
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "0.0.0.0";
|
||||
description = "Listening address for RPC connections.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 22004;
|
||||
description = "Override the default port on which to listen for RPC connections.";
|
||||
};
|
||||
|
||||
api = mkOption {
|
||||
type = types.str;
|
||||
default = "admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul";
|
||||
description = "API's offered over the HTTP-RPC interface.";
|
||||
};
|
||||
};
|
||||
|
||||
ws = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable WS-RPC interface.";
|
||||
};
|
||||
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "0.0.0.0";
|
||||
description = "Listening address for WS-RPC connections.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 8546;
|
||||
description = "Override the default port on which to listen for WS-RPC connections.";
|
||||
};
|
||||
|
||||
api = mkOption {
|
||||
type = types.str;
|
||||
default = "admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul";
|
||||
description = "API's offered over the WS-RPC interface.";
|
||||
};
|
||||
|
||||
origins = mkOption {
|
||||
type = types.str;
|
||||
default = "*";
|
||||
description = "Origins from which to accept websockets requests";
|
||||
};
|
||||
};
|
||||
|
||||
genesis = mkOption {
|
||||
type = types.nullOr types.attrs;
|
||||
default = null;
|
||||
example = literalExpression ''
|
||||
{
|
||||
alloc = {
|
||||
a47385db68718bdcbddc2d2bb7c54018066ec111 = {
|
||||
balance = "1000000000000000000000000000";
|
||||
};
|
||||
};
|
||||
coinbase = "0x0000000000000000000000000000000000000000";
|
||||
config = {
|
||||
byzantiumBlock = 4;
|
||||
chainId = 494702925;
|
||||
eip150Block = 2;
|
||||
eip155Block = 3;
|
||||
eip158Block = 3;
|
||||
homesteadBlock = 1;
|
||||
isQuorum = true;
|
||||
istanbul = {
|
||||
epoch = 30000;
|
||||
policy = 0;
|
||||
};
|
||||
};
|
||||
difficulty = "0x1";
|
||||
extraData = "0x0000000000000000000000000000000000000000000000000000000000000000f85ad59438f0508111273d8e482f49410ca4078afc86a961b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0";
|
||||
gasLimit = "0x2FEFD800";
|
||||
mixHash = "0x63746963616c2062797a616e74696e65201111756c7420746f6c6572616e6365";
|
||||
nonce = "0x0";
|
||||
parentHash = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
||||
timestamp = "0x00";
|
||||
}'';
|
||||
description = "Blockchain genesis settings.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = [ pkgs.quorum ];
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '${dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
|
||||
];
|
||||
systemd.services.quorum = {
|
||||
description = "Quorum daemon";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = {
|
||||
PRIVATE_CONFIG = "${cfg.privateconfig}";
|
||||
};
|
||||
preStart = ''
|
||||
if [ ! -d ${dataDir}/geth ]; then
|
||||
if [ ! -d ${dataDir}/keystore ]; then
|
||||
echo ERROR: You need to create a wallet before initializing your genesis file, run:
|
||||
echo # su -s /bin/sh - quorum
|
||||
echo $ geth --datadir ${dataDir} account new
|
||||
echo and configure your genesis file accordingly.
|
||||
exit 1;
|
||||
fi
|
||||
ln -s ${staticNodesFile} ${dataDir}/static-nodes.json
|
||||
${pkgs.quorum}/bin/geth --datadir ${dataDir} init ${genesisFile}
|
||||
fi
|
||||
'';
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ExecStart = ''
|
||||
${pkgs.quorum}/bin/geth \
|
||||
--nodiscover \
|
||||
--verbosity 5 \
|
||||
--nodekey ${cfg.nodekeyFile} \
|
||||
--istanbul.blockperiod ${toString cfg.blockperiod} \
|
||||
--syncmode ${cfg.syncmode} \
|
||||
${optionalString (cfg.permissioned) "--permissioned"} \
|
||||
--mine --miner.threads 1 \
|
||||
${optionalString (cfg.rpc.enable) "--rpc --rpcaddr ${cfg.rpc.address} --rpcport ${toString cfg.rpc.port} --rpcapi ${cfg.rpc.api}"} \
|
||||
${optionalString (cfg.ws.enable) "--ws --ws.addr ${cfg.ws.address} --ws.port ${toString cfg.ws.port} --ws.api ${cfg.ws.api} --ws.origins ${cfg.ws.origins}"} \
|
||||
--emitcheckpoints \
|
||||
--datadir ${dataDir} \
|
||||
--port ${toString cfg.port}'';
|
||||
Restart = "on-failure";
|
||||
|
||||
# Hardening measures
|
||||
PrivateTmp = "true";
|
||||
ProtectSystem = "full";
|
||||
NoNewPrivileges = "true";
|
||||
PrivateDevices = "true";
|
||||
MemoryDenyWriteExecute = "true";
|
||||
};
|
||||
};
|
||||
users.users.${cfg.user} = {
|
||||
name = cfg.user;
|
||||
group = cfg.group;
|
||||
description = "Quorum daemon user";
|
||||
home = dataDir;
|
||||
isSystemUser = true;
|
||||
};
|
||||
users.groups.${cfg.group} = { };
|
||||
};
|
||||
}
|
||||
@@ -559,12 +559,7 @@ in
|
||||
'';
|
||||
};
|
||||
encryptionPasswordFile = mkOption {
|
||||
type = types.nullOr (
|
||||
types.pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
}
|
||||
);
|
||||
type = types.nullOr types.externalPath;
|
||||
default = null;
|
||||
description = ''
|
||||
Path to encryption password. If set, the file will be read during
|
||||
|
||||
@@ -101,15 +101,22 @@ let
|
||||
iproute2
|
||||
systemd
|
||||
];
|
||||
# networkd doesn't provide a mechanism for refreshing endpoints.
|
||||
# networkd doesn't automatically refresh peer endpoints.
|
||||
# See: https://github.com/systemd/systemd/issues/9911
|
||||
# This hack does the job but takes down the whole interface to do it.
|
||||
script = ''
|
||||
ip link delete ${name} || :
|
||||
touch /etc/systemd/network/40-${name}.netdev
|
||||
networkctl reload
|
||||
'';
|
||||
};
|
||||
|
||||
# netdev config must be a real file (not a symlink to a store file)
|
||||
# so the refresh service can 'touch' it.
|
||||
generateRefreshNetdevMode =
|
||||
name: interface:
|
||||
nameValuePair "systemd/network/40-${name}.netdev" {
|
||||
mode = "0444";
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
meta.maintainers = [ lib.maintainers.majiir ];
|
||||
@@ -225,6 +232,7 @@ in
|
||||
networks = mapAttrs generateNetwork cfg.interfaces;
|
||||
};
|
||||
|
||||
environment.etc = mapAttrs' generateRefreshNetdevMode refreshEnabledInterfaces;
|
||||
systemd.timers = mapAttrs' generateRefreshTimer refreshEnabledInterfaces;
|
||||
systemd.services = (mapAttrs' generateRefreshService refreshEnabledInterfaces) // {
|
||||
systemd-networkd.serviceConfig.LoadCredential = flatten (
|
||||
|
||||
@@ -215,15 +215,6 @@ let
|
||||
This option can be set or overridden for individual peers.
|
||||
|
||||
Setting this to `0` disables periodic refresh.
|
||||
|
||||
::: {.warning}
|
||||
When {option}`networking.wireguard.useNetworkd` is enabled, this
|
||||
option deletes the Wireguard interface and brings it back up by
|
||||
reconfiguring the network with `networkctl reload` on every refresh.
|
||||
This could have adverse effects on your network and cause brief
|
||||
connectivity blips. See [systemd/systemd#9911](https://github.com/systemd/systemd/issues/9911)
|
||||
for an upstream feature request that can make this less hacky.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
@@ -182,8 +182,6 @@ in
|
||||
no_analytics = lib.mkDefault true;
|
||||
};
|
||||
|
||||
services.meilisearch.package = lib.mkDefault pkgs.meilisearch;
|
||||
|
||||
# used to restore dumps
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
|
||||
@@ -55,10 +55,8 @@ in
|
||||
'';
|
||||
};
|
||||
intermediatePasswordFile = lib.mkOption {
|
||||
type = lib.types.pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
};
|
||||
type = lib.types.nullOr lib.types.externalPath;
|
||||
default = null;
|
||||
example = "/run/keys/smallstep-password";
|
||||
description = ''
|
||||
Path to the file containing the password for the intermediate
|
||||
@@ -104,11 +102,18 @@ in
|
||||
ReadWritePaths = ""; # override upstream
|
||||
|
||||
# LocalCredential handles file permission problems arising from the use of DynamicUser.
|
||||
LoadCredential = "intermediate_password:${cfg.intermediatePasswordFile}";
|
||||
LoadCredential = lib.mkIf (
|
||||
cfg.intermediatePasswordFile != null
|
||||
) "intermediate_password:${cfg.intermediatePasswordFile}";
|
||||
|
||||
ExecStart = [
|
||||
"" # override upstream
|
||||
"${cfg.package}/bin/step-ca /etc/smallstep/ca.json --password-file \${CREDENTIALS_DIRECTORY}/intermediate_password"
|
||||
(
|
||||
"${cfg.package}/bin/step-ca /etc/smallstep/ca.json"
|
||||
+ lib.optionalString (
|
||||
cfg.intermediatePasswordFile != null
|
||||
) " --password-file \${CREDENTIALS_DIRECTORY}/intermediate_password"
|
||||
)
|
||||
];
|
||||
|
||||
# ProtectProc = "invisible"; # not supported by upstream yet
|
||||
|
||||
@@ -578,7 +578,7 @@ in
|
||||
add_header Cache-Control "public";
|
||||
'';
|
||||
};
|
||||
"~ ^/.*-([A-Za-z0-9]+)\.webmanifest$" = {
|
||||
"~ ^/.*-([A-Za-z0-9]+)\\.webmanifest$" = {
|
||||
root = cfg.package.web;
|
||||
extraConfig = ''
|
||||
access_log off;
|
||||
@@ -637,6 +637,7 @@ in
|
||||
'';
|
||||
};
|
||||
appendConfig = ''
|
||||
# frigate
|
||||
rtmp {
|
||||
server {
|
||||
listen 1935;
|
||||
@@ -653,6 +654,7 @@ in
|
||||
}
|
||||
'';
|
||||
appendHttpConfig = ''
|
||||
# frigate
|
||||
map $sent_http_content_type $should_not_cache {
|
||||
'application/json' 0;
|
||||
default 1;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
utils,
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -9,10 +10,9 @@ let
|
||||
format = pkgs.formats.json { };
|
||||
isPostgresUnixSocket = lib.hasPrefix "/" cfg.database.host;
|
||||
isRedisUnixSocket = lib.hasPrefix "/" cfg.redis.host;
|
||||
|
||||
# convert a Nix attribute path to jq object identifier-index:
|
||||
# https://jqlang.org/manual/#object-identifier-index
|
||||
attrPathToIndex = attrPath: "." + lib.concatStringsSep "." attrPath;
|
||||
secretsReplacement = utils.genJqSecretsReplacement {
|
||||
loadCredential = true;
|
||||
} cfg.settings "/run/immich/config.json";
|
||||
|
||||
commonServiceConfig = {
|
||||
Type = "simple";
|
||||
@@ -55,6 +55,22 @@ let
|
||||
if cfg.database.enable then config.services.postgresql.package else pkgs.postgresql;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule
|
||||
[
|
||||
"services"
|
||||
"immich"
|
||||
"secretSettings"
|
||||
]
|
||||
''
|
||||
`secretSettings` has been deprecated as secrets can now be specified
|
||||
directly in `settings`. To do so, set `_secret` of the desired
|
||||
attribute to a file path, for example:
|
||||
`services.immich.settings.oauth.clientSecret._secret = "/path/to/secret/file";`
|
||||
''
|
||||
)
|
||||
];
|
||||
|
||||
options.services.immich = {
|
||||
enable = mkEnableOption "Immich";
|
||||
package = lib.mkPackageOption pkgs "immich" { };
|
||||
@@ -128,6 +144,7 @@ in
|
||||
<https://my.immich.app/admin/system-settings> for
|
||||
options and defaults.
|
||||
Setting it to `null` allows configuring Immich in the web interface.
|
||||
You can load secret values from a file in this configuration by setting `somevalue._secret = "/path/to/file"` instead of setting `somevalue` directly.
|
||||
'';
|
||||
type = types.nullOr (
|
||||
types.submodule {
|
||||
@@ -151,27 +168,6 @@ in
|
||||
);
|
||||
};
|
||||
|
||||
secretSettings = mkOption {
|
||||
default = { };
|
||||
description = ''
|
||||
Secrets to to be added to the JSON file generated from {option}`settings`, read from files.
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
notifications.smtp.transport.password = "/path/to/secret";
|
||||
oauth.clientSecret = "/path/to/other/secret";
|
||||
}
|
||||
'';
|
||||
type =
|
||||
let
|
||||
inherit (types) attrsOf either path;
|
||||
recursiveType = either (attrsOf recursiveType) path // {
|
||||
description = "nested " + (attrsOf path).description;
|
||||
};
|
||||
in
|
||||
recursiveType;
|
||||
};
|
||||
|
||||
machine-learning = {
|
||||
enable =
|
||||
mkEnableOption "immich's machine-learning functionality to detect faces and search for objects"
|
||||
@@ -424,24 +420,10 @@ in
|
||||
postgresqlPackage
|
||||
];
|
||||
|
||||
preStart = mkIf (cfg.settings != null) (
|
||||
''
|
||||
cat '${format.generate "immich-config.json" cfg.settings}' > /run/immich/config.json
|
||||
''
|
||||
+ lib.concatStrings (
|
||||
lib.mapAttrsToListRecursive (attrPath: _: ''
|
||||
tmp="$(mktemp)"
|
||||
${lib.getExe pkgs.jq} --rawfile secret "$CREDENTIALS_DIRECTORY/${attrPathToIndex attrPath}" \
|
||||
'${attrPathToIndex attrPath} = ($secret | rtrimstr("\n"))' /run/immich/config.json > "$tmp"
|
||||
mv "$tmp" /run/immich/config.json
|
||||
'') cfg.secretSettings
|
||||
)
|
||||
);
|
||||
preStart = mkIf (cfg.settings != null) secretsReplacement.script;
|
||||
|
||||
serviceConfig = commonServiceConfig // {
|
||||
LoadCredential = lib.mapAttrsToListRecursive (
|
||||
attrPath: file: "${attrPathToIndex attrPath}:${file}"
|
||||
) cfg.secretSettings;
|
||||
LoadCredential = secretsReplacement.credentials;
|
||||
ExecStart = lib.getExe cfg.package;
|
||||
EnvironmentFile = mkIf (cfg.secretsFile != null) cfg.secretsFile;
|
||||
Slice = "system-immich.slice";
|
||||
|
||||
@@ -71,14 +71,7 @@ in
|
||||
The contents of the specified paths will be read at service start time and merged with the attributes provided in `settings`.
|
||||
'';
|
||||
default = { };
|
||||
type =
|
||||
with lib.types;
|
||||
nullOr (
|
||||
attrsOf (pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
})
|
||||
);
|
||||
type = with lib.types; nullOr (attrsOf externalPath);
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
|
||||
@@ -838,6 +838,12 @@ in
|
||||
));
|
||||
message = "There must be exactly one Sidekiq queue in services.mastodon.sidekiqProcesses with jobClass \"scheduler\".";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
databaseActuallyCreateLocally
|
||||
-> lib.versionAtLeast config.services.postgresql.finalPackage.version "14";
|
||||
message = "Mastodon requires at least PostgreSQL 14.";
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = [ mastodonTootctl ];
|
||||
|
||||
@@ -109,10 +109,10 @@ in
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default =
|
||||
if lib.versionAtLeast config.system.stateVersion "25.11" then pkgs.netbox_4_3 else pkgs.netbox_4_2;
|
||||
if lib.versionAtLeast config.system.stateVersion "25.11" then pkgs.netbox_4_4 else pkgs.netbox_4_2;
|
||||
defaultText = lib.literalExpression ''
|
||||
if lib.versionAtLeast config.system.stateVersion "25.11"
|
||||
then pkgs.netbox_4_3
|
||||
then pkgs.netbox_4_4
|
||||
else pkgs.netbox_4_2;
|
||||
'';
|
||||
description = ''
|
||||
|
||||
@@ -1235,6 +1235,8 @@ in
|
||||
path = [ occ ];
|
||||
restartTriggers = [ overrideConfig ];
|
||||
script = ''
|
||||
export OCC_BIN="${lib.getExe occ}"
|
||||
|
||||
${lib.optionalString (c.dbpassFile != null) ''
|
||||
if [ -z "$(<"$CREDENTIALS_DIRECTORY/dbpass")" ]; then
|
||||
echo "dbpassFile ${c.dbpassFile} is empty!"
|
||||
@@ -1275,13 +1277,13 @@ in
|
||||
${occInstallCmd}
|
||||
fi
|
||||
|
||||
${lib.getExe occ} upgrade
|
||||
$OCC_BIN upgrade
|
||||
|
||||
${lib.getExe occ} config:system:delete trusted_domains
|
||||
$OCC_BIN config:system:delete trusted_domains
|
||||
|
||||
${lib.optionalString (cfg.extraAppsEnable && cfg.extraApps != { }) ''
|
||||
# Try to enable apps
|
||||
${lib.getExe occ} app:enable ${lib.concatStringsSep " " (lib.attrNames cfg.extraApps)}
|
||||
$OCC_BIN app:enable ${lib.concatStringsSep " " (lib.attrNames cfg.extraApps)}
|
||||
''}
|
||||
|
||||
${occSetTrustedDomainsCmd}
|
||||
|
||||
@@ -78,10 +78,7 @@ in
|
||||
};
|
||||
|
||||
secretFile = lib.mkOption {
|
||||
type = lib.types.pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
};
|
||||
type = lib.types.externalPath;
|
||||
example = "/run/keys/oncall-dbpassword";
|
||||
description = ''
|
||||
A YAML file containing secrets such as database or user passwords.
|
||||
|
||||
@@ -33,12 +33,7 @@ in
|
||||
enable = lib.mkEnableOption "Photoprism web server";
|
||||
|
||||
passwordFile = lib.mkOption {
|
||||
type = lib.types.nullOr (
|
||||
lib.types.pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
}
|
||||
);
|
||||
type = lib.types.nullOr lib.types.externalPath;
|
||||
default = null;
|
||||
description = ''
|
||||
Admin password file.
|
||||
@@ -46,12 +41,7 @@ in
|
||||
};
|
||||
|
||||
databasePasswordFile = lib.mkOption {
|
||||
type = lib.types.nullOr (
|
||||
lib.types.pathWith {
|
||||
inStore = false;
|
||||
absolute = true;
|
||||
}
|
||||
);
|
||||
type = lib.types.nullOr lib.types.externalPath;
|
||||
default = null;
|
||||
description = ''
|
||||
Database password file.
|
||||
|
||||
@@ -68,7 +68,11 @@ in
|
||||
xserver.windowManager.qtile.finalPackage = cfg.package.override {
|
||||
extraPackages = cfg.extraPackages cfg.package.pythonModule.pkgs;
|
||||
};
|
||||
|
||||
displayManager.sessionPackages = [ cfg.finalPackage ];
|
||||
|
||||
# Recommended by upstream for libqtile/widget/imapwidget.py
|
||||
gnome.gnome-keyring.enable = lib.mkDefault true;
|
||||
};
|
||||
|
||||
environment = {
|
||||
|
||||
@@ -229,10 +229,10 @@ in
|
||||
};
|
||||
|
||||
wallpaperStyle = lib.mkOption {
|
||||
default = "streched";
|
||||
default = "stretched";
|
||||
type = lib.types.enum [
|
||||
"centered"
|
||||
"streched"
|
||||
"stretched"
|
||||
"tiled"
|
||||
];
|
||||
description = ''
|
||||
@@ -375,7 +375,7 @@ in
|
||||
}
|
||||
(lib.mkIf (cfg.style.wallpapers == [ defaultWallpaper ]) {
|
||||
boot.loader.limine.style.backdrop = lib.mkDefault "2F302F";
|
||||
boot.loader.limine.style.wallpaperStyle = lib.mkDefault "streched";
|
||||
boot.loader.limine.style.wallpaperStyle = lib.mkDefault "stretched";
|
||||
})
|
||||
(lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
|
||||
@@ -376,6 +376,7 @@ let
|
||||
"TOS"
|
||||
"TTL"
|
||||
"DiscoverPathMTU"
|
||||
"IgnoreDontFragment"
|
||||
"IPv6FlowLabel"
|
||||
"CopyDSCP"
|
||||
"EncapsulationLimit"
|
||||
@@ -398,6 +399,7 @@ let
|
||||
(assertInt "TTL")
|
||||
(assertRange "TTL" 0 255)
|
||||
(assertValueOneOf "DiscoverPathMTU" boolValues)
|
||||
(assertValueOneOf "IgnoreDontFragment" boolValues)
|
||||
(assertValueOneOf "CopyDSCP" boolValues)
|
||||
(assertValueOneOf "Mode" [
|
||||
"ip6ip6"
|
||||
|
||||
@@ -41,10 +41,10 @@ in
|
||||
default = "never";
|
||||
example = "within_size";
|
||||
description = ''
|
||||
never - Do not allocate huge memory pages. This is the default.
|
||||
always - Attempt to allocate huge memory page every time a new page is needed.
|
||||
within_size - Only allocate huge memory pages if it will be fully within i_size. Also respect madvise(2) hints. Recommended.
|
||||
advise - Only allocate huge memory pages if requested with madvise(2).
|
||||
- `never` - Do not allocate huge memory pages. This is the default.
|
||||
- `always` - Attempt to allocate huge memory page every time a new page is needed.
|
||||
- `within_size` - Only allocate huge memory pages if it will be fully within i_size. Also respect madvise(2) hints. Recommended.
|
||||
- `advise` - Only allocate huge memory pages if requested with madvise(2).
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
@@ -57,6 +57,12 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.googleComputeImage.buildMemSize = mkOption {
|
||||
type = types.int;
|
||||
default = 1024;
|
||||
description = "Memory size (in MiB) for the temporary VM used to build the image.";
|
||||
};
|
||||
|
||||
virtualisation.googleComputeImage.contents = mkOption {
|
||||
type = with types; listOf attrs;
|
||||
default = [ ];
|
||||
@@ -129,6 +135,7 @@ in
|
||||
inherit (cfg) contents;
|
||||
partitionTableType = if cfg.efi then "efi" else "legacy";
|
||||
inherit (config.virtualisation) diskSize;
|
||||
memSize = cfg.buildMemSize;
|
||||
inherit config lib pkgs;
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ in
|
||||
|
||||
partitionTableType = "efi";
|
||||
format = "qcow2-compressed";
|
||||
copyChannel = true;
|
||||
copyChannel = config.system.installer.channel.enable;
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
services.openssh.startWhenNeeded = lib.mkDefault true;
|
||||
|
||||
# As this is intended as a standalone image, undo some of the minimal profile stuff
|
||||
documentation.enable = true;
|
||||
documentation.nixos.enable = true;
|
||||
documentation.enable = lib.mkDefault true;
|
||||
documentation.nixos.enable = lib.mkDefault true;
|
||||
services.logrotate.enable = true;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ in
|
||||
testScript = ''
|
||||
# agda and agda-mode are in path
|
||||
machine.succeed("agda --version")
|
||||
machine.succeed("agda-mode")
|
||||
|
||||
# Minimal script that typechecks
|
||||
machine.succeed("touch TestEmpty.agda")
|
||||
|
||||
+21
-11
@@ -673,7 +673,10 @@ in
|
||||
guacamole-server = runTest ./guacamole-server.nix;
|
||||
guix = handleTest ./guix { };
|
||||
gvisor = runTest ./gvisor.nix;
|
||||
h2o = import ./web-servers/h2o { inherit runTest; };
|
||||
h2o = import ./web-servers/h2o {
|
||||
inherit runTest;
|
||||
inherit (pkgs) lib;
|
||||
};
|
||||
hadoop = import ./hadoop {
|
||||
inherit handleTestOn;
|
||||
package = pkgs.hadoop;
|
||||
@@ -743,13 +746,14 @@ in
|
||||
immich-vectorchord-migration = runTest ./web-apps/immich-vectorchord-migration.nix;
|
||||
immich-vectorchord-reindex = runTest ./web-apps/immich-vectorchord-reindex.nix;
|
||||
incron = runTest ./incron.nix;
|
||||
incus = recurseIntoAttrs (
|
||||
handleTest ./incus {
|
||||
lts = false;
|
||||
inherit system pkgs;
|
||||
}
|
||||
);
|
||||
incus-lts = recurseIntoAttrs (handleTest ./incus { inherit system pkgs; });
|
||||
incus = import ./incus {
|
||||
inherit runTestOn;
|
||||
package = pkgs.incus;
|
||||
};
|
||||
incus-lts = import ./incus {
|
||||
inherit runTestOn;
|
||||
package = pkgs.incus-lts;
|
||||
};
|
||||
influxdb = runTest ./influxdb.nix;
|
||||
influxdb2 = runTest ./influxdb2.nix;
|
||||
initrd-luks-empty-passphrase = runTest ./initrd-luks-empty-passphrase.nix;
|
||||
@@ -814,7 +818,10 @@ in
|
||||
ksm = runTest ./ksm.nix;
|
||||
kthxbye = runTest ./kthxbye.nix;
|
||||
kubernetes = handleTestOn [ "x86_64-linux" ] ./kubernetes { };
|
||||
kubo = import ./kubo { inherit runTest; };
|
||||
kubo = import ./kubo {
|
||||
inherit runTest;
|
||||
inherit (pkgs) lib;
|
||||
};
|
||||
lact = runTest ./lact.nix;
|
||||
ladybird = runTest ./ladybird.nix;
|
||||
languagetool = runTest ./languagetool.nix;
|
||||
@@ -959,7 +966,10 @@ in
|
||||
mopidy = runTest ./mopidy.nix;
|
||||
morph-browser = runTest ./morph-browser.nix;
|
||||
mosquitto = runTest ./mosquitto.nix;
|
||||
movim = import ./web-apps/movim { inherit runTest; };
|
||||
movim = import ./web-apps/movim {
|
||||
inherit runTest;
|
||||
inherit (pkgs) lib;
|
||||
};
|
||||
mpd = runTest ./mpd.nix;
|
||||
mpv = runTest ./mpv.nix;
|
||||
mtp = runTest ./mtp.nix;
|
||||
@@ -1007,6 +1017,7 @@ in
|
||||
netbox-upgrade = runTest ./web-apps/netbox-upgrade.nix;
|
||||
netbox_4_2 = handleTest ./web-apps/netbox/default.nix { netbox = pkgs.netbox_4_2; };
|
||||
netbox_4_3 = handleTest ./web-apps/netbox/default.nix { netbox = pkgs.netbox_4_3; };
|
||||
netbox_4_4 = handleTest ./web-apps/netbox/default.nix { netbox = pkgs.netbox_4_4; };
|
||||
netdata = runTest ./netdata.nix;
|
||||
networking.networkd = handleTest ./networking/networkd-and-scripted.nix { networkd = true; };
|
||||
networking.networkmanager = handleTest ./networking/networkmanager.nix { };
|
||||
@@ -1294,7 +1305,6 @@ in
|
||||
quake3 = runTest ./quake3.nix;
|
||||
quicktun = runTest ./quicktun.nix;
|
||||
quickwit = runTest ./quickwit.nix;
|
||||
quorum = runTest ./quorum.nix;
|
||||
rabbitmq = runTest ./rabbitmq.nix;
|
||||
radarr = runTest ./radarr.nix;
|
||||
radicale = runTest ./radicale.nix;
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
|
||||
environment.systemPackages = with pkgs; [ breitbandmessung ];
|
||||
environment.variables.XAUTHORITY = "/home/alice/.Xauthority";
|
||||
|
||||
# breitbandmessung is unfree
|
||||
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "breitbandmessung" ];
|
||||
};
|
||||
|
||||
enableOCR = true;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
node.pkgsReadOnly = false;
|
||||
|
||||
nodes.machine = {
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
hardware.sane = {
|
||||
enable = true;
|
||||
brscan5 = {
|
||||
|
||||
+5
-10
@@ -42,13 +42,7 @@
|
||||
in
|
||||
''
|
||||
with subtest("Wait for login"):
|
||||
# wait_for_x() checks graphical-session.target, which is expected to be
|
||||
# inactive on Budgie before Budgie manages user session with systemd.
|
||||
# https://github.com/BuddiesOfBudgie/budgie-desktop/blob/39e9f0895c978f76/src/session/budgie-desktop.in#L16
|
||||
#
|
||||
# Previously this was unconditionally touched by xsessionWrapper but was
|
||||
# changed in #233981 (we have Budgie:GNOME in XDG_CURRENT_DESKTOP).
|
||||
# machine.wait_for_x()
|
||||
machine.wait_for_x()
|
||||
machine.wait_until_succeeds('journalctl -t budgie-session-binary --grep "Entering running state"')
|
||||
machine.wait_for_file("${user.home}/.Xauthority")
|
||||
machine.succeed("xauth merge ${user.home}/.Xauthority")
|
||||
@@ -58,8 +52,9 @@
|
||||
machine.succeed("getfacl -p /dev/dri/card0 | grep -q ${user.name}")
|
||||
|
||||
with subtest("Check if Budgie session components actually start"):
|
||||
for i in ["budgie-daemon", "budgie-panel", "budgie-wm", "budgie-desktop-view", "gsd-media-keys"]:
|
||||
machine.wait_until_succeeds(f"pgrep -f {i}")
|
||||
for i in ["budgie-daemon", "budgie-panel", "budgie-wm", "bsd-media-keys", "gsd-xsettings"]:
|
||||
machine.wait_until_succeeds(f"pgrep {i}")
|
||||
machine.wait_until_succeeds("pgrep -xf /run/current-system/sw/bin/org.buddiesofbudgie.budgie-desktop-view")
|
||||
# We don't check xwininfo for budgie-wm.
|
||||
# See https://github.com/NixOS/nixpkgs/pull/216737#discussion_r1155312754
|
||||
machine.wait_for_window("budgie-daemon")
|
||||
@@ -67,7 +62,7 @@
|
||||
|
||||
with subtest("Check if various environment variables are set"):
|
||||
cmd = "xargs --null --max-args=1 echo < /proc/$(pgrep -xf /run/current-system/sw/bin/budgie-wm)/environ"
|
||||
machine.succeed(f"{cmd} | grep 'XDG_CURRENT_DESKTOP' | grep 'Budgie:GNOME'")
|
||||
machine.succeed(f"{cmd} | grep 'XDG_CURRENT_DESKTOP' | grep 'Budgie'")
|
||||
machine.succeed(f"{cmd} | grep 'BUDGIE_PLUGIN_DATADIR' | grep '${pkgs.budgie-desktop-with-plugins.pname}'")
|
||||
# From the nixos/budgie module
|
||||
machine.succeed(f"{cmd} | grep 'SSH_AUTH_SOCK' | grep 'gcr'")
|
||||
|
||||
@@ -28,4 +28,10 @@
|
||||
inherit package;
|
||||
};
|
||||
};
|
||||
ui = runTest {
|
||||
imports = [ ./ui.nix ];
|
||||
_module.args = {
|
||||
inherit package;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
package,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
name = "clickhouse-ui";
|
||||
|
||||
nodes = {
|
||||
browser =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
environment.systemPackages =
|
||||
let
|
||||
clickhouseSeleniumScript =
|
||||
pkgs.writers.writePython3Bin "clickhouse-selenium-script"
|
||||
{
|
||||
libraries = with pkgs.python3Packages; [ selenium ];
|
||||
}
|
||||
''
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
options = Options()
|
||||
options.add_argument("--headless")
|
||||
service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
|
||||
|
||||
driver = webdriver.Firefox(options=options, service=service)
|
||||
driver.implicitly_wait(10)
|
||||
driver.get("http://clickhouse:8123/play")
|
||||
|
||||
wait = WebDriverWait(driver, 60)
|
||||
|
||||
assert len(driver.find_elements(
|
||||
By.ID, "query_div")) == 1
|
||||
|
||||
server_info_element = driver.find_element(
|
||||
By.XPATH, "//span[@id='server_info']")
|
||||
assert "${
|
||||
lib.strings.replaceStrings [ "-stable" "-lts" ] [ "" "" ] package.version
|
||||
}" in server_info_element.text
|
||||
|
||||
# Shouldn't show before query done
|
||||
assert len(driver.find_elements(
|
||||
By.CSS_SELECTOR, ".row-number")) == 0
|
||||
|
||||
query_box = driver.find_element(
|
||||
By.XPATH, "//textarea[@id='query']")
|
||||
query_box.click()
|
||||
query_box.send_keys("SELECT 1")
|
||||
|
||||
query_run_button = driver.find_element(
|
||||
By.XPATH, "//button[@id='run']").click()
|
||||
|
||||
# Now verify results shown
|
||||
assert len(driver.find_elements(
|
||||
By.XPATH, "//div[@id='check-mark']")) == 1
|
||||
|
||||
assert len(driver.find_elements(
|
||||
By.CSS_SELECTOR, ".row-number")) == 2
|
||||
|
||||
driver.close()
|
||||
'';
|
||||
in
|
||||
with pkgs;
|
||||
[
|
||||
curl
|
||||
firefox-unwrapped
|
||||
geckodriver
|
||||
clickhouseSeleniumScript
|
||||
];
|
||||
};
|
||||
|
||||
clickhouse =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
8123
|
||||
9000
|
||||
];
|
||||
|
||||
environment.etc = {
|
||||
"clickhouse-server/config.d/listen.xml".text = ''
|
||||
<clickhouse>
|
||||
<listen_host>::</listen_host>
|
||||
</clickhouse>
|
||||
'';
|
||||
};
|
||||
|
||||
services.clickhouse = {
|
||||
enable = true;
|
||||
inherit package;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
clickhouse.wait_for_unit("clickhouse")
|
||||
clickhouse.wait_for_open_port(8123)
|
||||
clickhouse.wait_for_open_port(9000)
|
||||
|
||||
browser.systemctl("start network-online.target")
|
||||
browser.wait_for_unit("network-online.target")
|
||||
|
||||
browser.succeed("curl -kLs http://clickhouse:8123/play | grep 'ClickHouse Query'")
|
||||
|
||||
# Ensure the application is actually rendered by the Javascript
|
||||
browser.succeed("PYTHONUNBUFFERED=1 clickhouse-selenium-script")
|
||||
'';
|
||||
}
|
||||
@@ -4,11 +4,8 @@
|
||||
pkgs ? import <nixpkgs> { },
|
||||
minica ? pkgs.minica,
|
||||
mkDerivation ? pkgs.stdenv.mkDerivation,
|
||||
domain ? (import ./snakeoil-certs.nix).domain,
|
||||
}:
|
||||
let
|
||||
conf = import ./snakeoil-certs.nix;
|
||||
domain = conf.domain;
|
||||
in
|
||||
mkDerivation {
|
||||
name = "test-certs";
|
||||
buildInputs = [
|
||||
|
||||
@@ -57,8 +57,6 @@ let
|
||||
];
|
||||
networking.firewall = firewallSettings;
|
||||
|
||||
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "consul" ];
|
||||
|
||||
services.consul = {
|
||||
enable = true;
|
||||
inherit webUi;
|
||||
@@ -87,8 +85,6 @@ let
|
||||
];
|
||||
networking.firewall = firewallSettings;
|
||||
|
||||
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "consul" ];
|
||||
|
||||
services.consul =
|
||||
assert builtins.elem thisConsensusServerHost allConsensusServerHosts;
|
||||
{
|
||||
|
||||
@@ -12,7 +12,6 @@ in
|
||||
node.pkgsReadOnly = false;
|
||||
|
||||
nodes.machine = {
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
services.deconz = {
|
||||
enable = true;
|
||||
inherit httpPort;
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
};
|
||||
extraPythonPackages = p: [
|
||||
p.lxml
|
||||
p.lxml-stubs
|
||||
p.types-lxml
|
||||
];
|
||||
skipTypeCheck = true;
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
@@ -26,8 +26,17 @@
|
||||
|
||||
services.desktopManager.gnome.enable = true;
|
||||
services.desktopManager.gnome.debug = true;
|
||||
services.desktopManager.gnome.flashback.enableMetacity = true;
|
||||
services.displayManager.defaultSession = "gnome-flashback-metacity";
|
||||
|
||||
services.desktopManager.gnome.flashback.customSessions = [
|
||||
{
|
||||
# Intentionally a different name to test mkSystemdTargetForWm.
|
||||
wmName = "metacitytest";
|
||||
wmLabel = "Metacity";
|
||||
wmCommand = "${pkgs.metacity}/bin/metacity";
|
||||
enableGnomePanel = true;
|
||||
}
|
||||
];
|
||||
services.displayManager.defaultSession = "gnome-flashback-metacitytest";
|
||||
};
|
||||
|
||||
testScript =
|
||||
@@ -40,7 +49,7 @@
|
||||
''
|
||||
with subtest("Login to GNOME Flashback with GDM"):
|
||||
machine.wait_for_x()
|
||||
machine.wait_until_succeeds('journalctl -t gnome-session-binary --grep "Entering running state"')
|
||||
machine.wait_until_succeeds('journalctl -t gnome-session-service --grep "Entering running state"')
|
||||
# Wait for alice to be logged in"
|
||||
machine.wait_for_unit("default.target", "${user.name}")
|
||||
machine.wait_for_file("${xauthority}")
|
||||
|
||||
@@ -1,51 +1,98 @@
|
||||
{
|
||||
system ? builtins.currentSystem,
|
||||
config ? { },
|
||||
pkgs ? import ../../.. { inherit system config; },
|
||||
lts ? true,
|
||||
...
|
||||
package,
|
||||
runTestOn,
|
||||
}:
|
||||
let
|
||||
incusTest = import ./incus-tests.nix;
|
||||
incusRunTest =
|
||||
config:
|
||||
runTestOn [ "x86_64-linux" "aarch64-linux" ] {
|
||||
imports = [
|
||||
./incus-tests-module.nix
|
||||
./incus-tests.nix
|
||||
];
|
||||
|
||||
tests.incus = {
|
||||
inherit package;
|
||||
}
|
||||
// config;
|
||||
};
|
||||
in
|
||||
{
|
||||
all = incusTest {
|
||||
inherit lts pkgs system;
|
||||
allTests = true;
|
||||
};
|
||||
|
||||
container = incusTest {
|
||||
inherit lts pkgs system;
|
||||
instanceContainer = true;
|
||||
};
|
||||
|
||||
lvm = incusTest {
|
||||
inherit lts pkgs system;
|
||||
storageLvm = true;
|
||||
};
|
||||
|
||||
openvswitch = incusTest {
|
||||
inherit lts pkgs system;
|
||||
networkOvs = true;
|
||||
};
|
||||
|
||||
ui = import ./ui.nix {
|
||||
inherit lts pkgs system;
|
||||
};
|
||||
|
||||
virtual-machine = incusTest {
|
||||
inherit lts pkgs system;
|
||||
instanceVm = true;
|
||||
};
|
||||
|
||||
zfs = incusTest {
|
||||
inherit lts pkgs system;
|
||||
storageZfs = true;
|
||||
};
|
||||
|
||||
appArmor = incusTest {
|
||||
inherit lts pkgs system;
|
||||
# this is the main test which will test as much as possible
|
||||
# run this for testing incus upgrades, also available in incus package tests
|
||||
all = incusRunTest {
|
||||
name = "all";
|
||||
appArmor = true;
|
||||
allTests = true;
|
||||
feature.user = true;
|
||||
|
||||
instances = {
|
||||
c1 = {
|
||||
type = "container";
|
||||
};
|
||||
|
||||
vm1 = {
|
||||
type = "virtual-machine";
|
||||
};
|
||||
};
|
||||
|
||||
network = {
|
||||
ovs = true;
|
||||
};
|
||||
|
||||
storage = {
|
||||
lvm = true;
|
||||
zfs = true;
|
||||
};
|
||||
};
|
||||
|
||||
# used in lxc tests to verify container functionality
|
||||
container = incusRunTest {
|
||||
name = "container";
|
||||
|
||||
instances.c1 = {
|
||||
type = "container";
|
||||
};
|
||||
};
|
||||
|
||||
lvm = incusRunTest {
|
||||
name = "lvm";
|
||||
|
||||
storage.lvm = true;
|
||||
};
|
||||
|
||||
openvswitch = incusRunTest {
|
||||
name = "openvswitch";
|
||||
|
||||
network.ovs = true;
|
||||
};
|
||||
|
||||
ui = runTestOn [ "x86_64-linux" "aarch64-linux" ] {
|
||||
imports = [ ./ui.nix ];
|
||||
|
||||
_module.args = { inherit package; };
|
||||
};
|
||||
|
||||
virtual-machine = incusRunTest {
|
||||
name = "virtual-machine";
|
||||
|
||||
instances = {
|
||||
vm1 = {
|
||||
type = "virtual-machine";
|
||||
};
|
||||
|
||||
# disabled because never becomes available
|
||||
# csm = {
|
||||
# type = "virtual-machine";
|
||||
# incusConfig.config = {
|
||||
# "security.csm" = true;
|
||||
# };
|
||||
# };
|
||||
};
|
||||
};
|
||||
|
||||
zfs = incusRunTest {
|
||||
name = "zfs";
|
||||
|
||||
storage.zfs = true;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
jsonFormat = pkgs.formats.json { };
|
||||
in
|
||||
{
|
||||
options.tests.incus = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "name appended to test";
|
||||
};
|
||||
|
||||
package = lib.mkPackageOption pkgs "incus" { };
|
||||
|
||||
preseed = lib.mkOption {
|
||||
description = "configuration provided to incus preseed. https://linuxcontainers.org/incus/docs/main/howto/initialize/#non-interactive-configuration";
|
||||
type = lib.types.submodule {
|
||||
freeformType = jsonFormat.type;
|
||||
};
|
||||
};
|
||||
|
||||
instances = lib.mkOption {
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ name, config, ... }:
|
||||
{
|
||||
options = {
|
||||
name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = name;
|
||||
};
|
||||
|
||||
type = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
"container"
|
||||
"virtual-machine"
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
imageAlias = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "name of image when imported";
|
||||
default = "nixos/${name}/${config.type}";
|
||||
};
|
||||
|
||||
nixosConfig = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.anything;
|
||||
default = { };
|
||||
};
|
||||
|
||||
incusConfig = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
freeformType = jsonFormat.type;
|
||||
};
|
||||
description = "incus configuration provided at launch";
|
||||
default = { };
|
||||
};
|
||||
|
||||
testScript = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "final script provided to test runner";
|
||||
readOnly = true;
|
||||
};
|
||||
};
|
||||
config =
|
||||
let
|
||||
releases = import ../../release.nix {
|
||||
configuration = config.nixosConfig;
|
||||
};
|
||||
|
||||
images = {
|
||||
container = {
|
||||
metadata =
|
||||
releases.incusContainerMeta.${pkgs.stdenv.hostPlatform.system}
|
||||
+ "/tarball/nixos-image-lxc-*-${pkgs.stdenv.hostPlatform.system}.tar.xz";
|
||||
|
||||
root =
|
||||
releases.incusContainerImage.${pkgs.stdenv.hostPlatform.system}
|
||||
+ "/nixos-lxc-image-${pkgs.stdenv.hostPlatform.system}.squashfs";
|
||||
};
|
||||
|
||||
virtual-machine = {
|
||||
metadata = releases.incusVirtualMachineImageMeta.${pkgs.stdenv.hostPlatform.system} + "/*/*.tar.xz";
|
||||
root = releases.incusVirtualMachineImage.${pkgs.stdenv.hostPlatform.system} + "/nixos.qcow2";
|
||||
};
|
||||
};
|
||||
|
||||
root = images.${config.type}.root;
|
||||
metadata = images.${config.type}.metadata;
|
||||
|
||||
image_id = "${config.type}/${config.name}";
|
||||
in
|
||||
{
|
||||
incusConfig = lib.optionalAttrs (config.type == "virtual-machine") {
|
||||
config."security.secureboot" = false;
|
||||
};
|
||||
|
||||
nixosConfig = {
|
||||
# Building documentation makes the test unnecessarily take a longer time:
|
||||
documentation.enable = lib.mkForce false;
|
||||
documentation.nixos.enable = lib.mkForce false;
|
||||
# including a channel forces images to be rebuilt on any changes
|
||||
system.installer.channel.enable = lib.mkForce false;
|
||||
|
||||
environment.etc."nix/registry.json".text = lib.mkForce "{}";
|
||||
|
||||
# Arbitrary sysctl setting changed from nixos default
|
||||
# used for verifying `distrobuilder.generator` properly allows
|
||||
# for containers to modify sysctl
|
||||
boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
|
||||
};
|
||||
|
||||
testScript = # python
|
||||
''
|
||||
with subtest("[${image_id}] image can be imported"):
|
||||
server.succeed("incus image import ${metadata} ${root} --alias ${config.imageAlias}")
|
||||
|
||||
with subtest("[${image_id}] can be launched and managed"):
|
||||
instance_name = server.succeed("incus launch ${config.imageAlias}${
|
||||
lib.optionalString (config.type == "virtual-machine") " --vm"
|
||||
} --quiet < ${jsonFormat.generate "${config.name}.json" config.incusConfig}").split(":")[1].strip()
|
||||
server.wait_for_instance(instance_name)
|
||||
|
||||
with subtest("[${image_id}] can successfully restart"):
|
||||
server.succeed(f"incus restart {instance_name}")
|
||||
server.wait_for_instance(instance_name)
|
||||
|
||||
with subtest("[${image_id}] remains running when softDaemonRestart is enabled and service is stopped"):
|
||||
pid = server.succeed(f"incus info {instance_name} | grep 'PID'").split(":")[1].strip()
|
||||
server.succeed(f"ps {pid}")
|
||||
server.succeed("systemctl stop incus")
|
||||
server.succeed(f"ps {pid}")
|
||||
server.succeed("systemctl start incus")
|
||||
|
||||
with subtest("[${image_id}] CPU limits can be managed"):
|
||||
server.set_instance_config(instance_name, "limits.cpu 1", restart=True)
|
||||
server.wait_instance_exec_success(instance_name, "nproc | grep '^1$'", timeout=90)
|
||||
|
||||
with subtest("[${image_id}] CPU limits can be hotplug changed"):
|
||||
server.set_instance_config(instance_name, "limits.cpu 2")
|
||||
server.wait_instance_exec_success(instance_name, "nproc | grep '^2$'", timeout=90)
|
||||
|
||||
with subtest("[${image_id}] exec has a valid path"):
|
||||
server.succeed(f"incus exec {instance_name} -- bash -c 'true'")
|
||||
|
||||
with subtest("[${image_id}] software tpm can be configured"):
|
||||
# this can be hot added to containers, but stopping for vm
|
||||
server.succeed(f"incus stop {instance_name}")
|
||||
server.succeed(f"incus config device add {instance_name} vtpm tpm path=/dev/tpm0 pathrm=/dev/tpmrm0")
|
||||
server.succeed(f"incus start {instance_name}")
|
||||
server.wait_for_instance(instance_name)
|
||||
|
||||
server.succeed(f"incus exec {instance_name} -- test -e /dev/tpm0")
|
||||
server.succeed(f"incus exec {instance_name} -- test -e /dev/tpmrm0")
|
||||
''
|
||||
#
|
||||
# container specific
|
||||
#
|
||||
+
|
||||
lib.optionalString (config.type == "container")
|
||||
# python
|
||||
''
|
||||
# TODO troubleshoot VM hot memory resizing which was introduced in 6.12
|
||||
with subtest("[${image_id}] memory limits can be hotplug changed"):
|
||||
server.set_instance_config(instance_name, "limits.memory 512MB")
|
||||
# can't use lsmem since it sees the host's memory size
|
||||
server.wait_instance_exec_success(instance_name, "grep 'MemTotal:[[:space:]]*500000 kB' /proc/meminfo", timeout=1)
|
||||
|
||||
# verify the patched container systemd generator from `pkgs.distrobuilder.generator`
|
||||
with subtest("[${image_id}] lxc-generator compatibility"):
|
||||
with subtest("[${image_id}] lxc-container generator configures plain container"):
|
||||
# default container is plain
|
||||
server.succeed(f"incus exec {instance_name} test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
|
||||
|
||||
server.check_instance_sysctl(instance_name)
|
||||
|
||||
with subtest("[${image_id}] lxc-container generator configures nested container"):
|
||||
server.set_instance_config(instance_name, "security.nesting=true", restart=True)
|
||||
|
||||
server.fail(f"incus exec {instance_name} test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
|
||||
target = server.succeed(f"incus exec {instance_name} readlink -- -f /run/systemd/system/systemd-binfmt.service").strip()
|
||||
assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service"
|
||||
|
||||
server.check_instance_sysctl(instance_name)
|
||||
|
||||
with subtest("[${image_id}] lxcfs"):
|
||||
with subtest("[${image_id}] mounts lxcfs overlays"):
|
||||
server.succeed(f"incus exec {instance_name} mount | grep 'lxcfs on /proc/cpuinfo type fuse.lxcfs'")
|
||||
server.succeed(f"incus exec {instance_name} mount | grep 'lxcfs on /proc/meminfo type fuse.lxcfs'")
|
||||
|
||||
with subtest("[${image_id}] supports per-instance lxcfs"):
|
||||
server.succeed(f"incus stop {instance_name}")
|
||||
server.fail(f"pgrep -a lxcfs | grep 'incus/devices/{instance_name}/lxcfs'")
|
||||
|
||||
server.succeed("incus config set instances.lxcfs.per_instance=true")
|
||||
|
||||
server.succeed(f"incus start {instance_name}")
|
||||
server.wait_for_instance(instance_name)
|
||||
server.succeed(f"pgrep -a lxcfs | grep 'incus/devices/{instance_name}/lxcfs'")
|
||||
''
|
||||
|
||||
#
|
||||
# virtual-machine specific
|
||||
#
|
||||
+
|
||||
lib.optionalString (config.type == "virtual-machine")
|
||||
# python
|
||||
''
|
||||
with subtest("[${image_id}] memory limits can be managed"):
|
||||
server.set_instance_config(instance_name, "limits.memory 384MB", restart=True)
|
||||
lsmem = json.loads(server.instance_succeed(instance_name, "lsmem --json"))
|
||||
memsize = lsmem["memory"][0]["size"]
|
||||
assert memsize == "384M", f"failed to manage memory limit. {memsize} != 384M"
|
||||
|
||||
with subtest("[${image_id}] incus-agent is started"):
|
||||
server.succeed(f"incus exec {instance_name} systemctl is-active incus-agent")
|
||||
''
|
||||
|
||||
+
|
||||
#
|
||||
# finalize
|
||||
#
|
||||
# python
|
||||
''
|
||||
# this will leave the instances stopped
|
||||
with subtest("[${image_id}] stop with incus-startup.service"):
|
||||
pid = server.succeed(f"incus info {instance_name} | grep 'PID'").split(":")[1].strip()
|
||||
server.succeed(f"ps {pid}")
|
||||
server.succeed("systemctl stop incus-startup.service")
|
||||
server.wait_until_fails(f"ps {pid}", timeout=120)
|
||||
server.succeed("systemctl start incus-startup.service")
|
||||
|
||||
'';
|
||||
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
description = "";
|
||||
default = { };
|
||||
};
|
||||
|
||||
appArmor = lib.mkEnableOption "AppArmor during tests";
|
||||
|
||||
feature.user = lib.mkEnableOption "Validate incus user access feature";
|
||||
|
||||
network.ovs = lib.mkEnableOption "Validate OVS network integration";
|
||||
|
||||
storage = {
|
||||
lvm = lib.mkEnableOption "Validate LVM storage integration";
|
||||
zfs = lib.mkEnableOption "Validate ZFS storage integration";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
tests.incus = { };
|
||||
};
|
||||
}
|
||||
+172
-454
@@ -1,500 +1,218 @@
|
||||
import ../make-test-python.nix (
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
lts ? true,
|
||||
let
|
||||
cfg = config.tests.incus;
|
||||
|
||||
allTests ? false,
|
||||
# limit building of VMs to these systems as nested virtualization is
|
||||
# required to test VMs, but support for this is poor outside x86
|
||||
# will print warnings on those systems rather than failing outright
|
||||
vmsEnabled = lib.elem pkgs.stdenv.system [ "x86_64-linux" ];
|
||||
|
||||
appArmor ? false,
|
||||
featureUser ? allTests,
|
||||
initLegacy ? true,
|
||||
initSystemd ? true,
|
||||
instanceContainer ? allTests,
|
||||
instanceVm ? allTests,
|
||||
networkOvs ? allTests,
|
||||
storageLvm ? allTests,
|
||||
storageZfs ? allTests,
|
||||
...
|
||||
}:
|
||||
instanceScript = lib.pipe cfg.instances [
|
||||
(lib.filterAttrs (
|
||||
name: instance:
|
||||
let
|
||||
keep = instance.type != "virtual-machine" || vmsEnabled;
|
||||
in
|
||||
lib.warnIf (!keep) ''
|
||||
Skipping virtual-machine ${name} as VMs are disabled on ${pkgs.stdenv.system}
|
||||
'' keep
|
||||
))
|
||||
(lib.foldlAttrs (
|
||||
acc: name: instance:
|
||||
acc + instance.testScript
|
||||
) "")
|
||||
];
|
||||
in
|
||||
{
|
||||
name = "${cfg.package.name}-${cfg.name}";
|
||||
|
||||
let
|
||||
releases =
|
||||
init:
|
||||
import ../../release.nix {
|
||||
configuration = {
|
||||
# Building documentation makes the test unnecessarily take a longer time:
|
||||
documentation.enable = lib.mkForce false;
|
||||
meta = {
|
||||
maintainers = lib.teams.lxc.members;
|
||||
};
|
||||
|
||||
boot.initrd.systemd.enable = init == "systemd";
|
||||
nodes.server = {
|
||||
virtualisation = {
|
||||
cores = 2;
|
||||
memorySize = 4096;
|
||||
diskSize = 20 * 1024;
|
||||
emptyDiskImages = [
|
||||
# vdb for zfs
|
||||
2048
|
||||
# vdc for lvm
|
||||
2048
|
||||
];
|
||||
|
||||
# Arbitrary sysctl modification to ensure containers can update sysctl
|
||||
boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
|
||||
incus = {
|
||||
enable = true;
|
||||
package = cfg.package;
|
||||
|
||||
preseed = {
|
||||
networks = [
|
||||
{
|
||||
name = "incusbr0";
|
||||
type = "bridge";
|
||||
config = {
|
||||
"ipv4.address" = "10.0.10.1/24";
|
||||
"ipv4.nat" = "true";
|
||||
};
|
||||
}
|
||||
]
|
||||
++ lib.optionals cfg.network.ovs [
|
||||
{
|
||||
name = "ovsbr0";
|
||||
type = "bridge";
|
||||
config = {
|
||||
"bridge.driver" = "openvswitch";
|
||||
"ipv4.address" = "10.0.20.1/24";
|
||||
"ipv4.nat" = "true";
|
||||
};
|
||||
}
|
||||
];
|
||||
profiles = [
|
||||
{
|
||||
name = "default";
|
||||
devices = {
|
||||
eth0 = {
|
||||
name = "eth0";
|
||||
network = "incusbr0";
|
||||
type = "nic";
|
||||
};
|
||||
root = {
|
||||
path = "/";
|
||||
pool = "default";
|
||||
size = "35GiB";
|
||||
type = "disk";
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
storage_pools = [
|
||||
{
|
||||
name = "default";
|
||||
driver = "dir";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
images = init: {
|
||||
container = {
|
||||
metadata =
|
||||
(releases init).incusContainerMeta.${pkgs.stdenv.hostPlatform.system}
|
||||
+ "/tarball/nixos-image-lxc-*-${pkgs.stdenv.hostPlatform.system}.tar.xz";
|
||||
|
||||
rootfs =
|
||||
(releases init).incusContainerImage.${pkgs.stdenv.hostPlatform.system}
|
||||
+ "/nixos-lxc-image-${pkgs.stdenv.hostPlatform.system}.squashfs";
|
||||
};
|
||||
|
||||
virtual-machine = {
|
||||
metadata =
|
||||
(releases init).incusVirtualMachineImageMeta.${pkgs.stdenv.hostPlatform.system} + "/*/*.tar.xz";
|
||||
disk = (releases init).incusVirtualMachineImage.${pkgs.stdenv.hostPlatform.system} + "/nixos.qcow2";
|
||||
};
|
||||
vswitch.enable = cfg.network.ovs;
|
||||
};
|
||||
|
||||
initVariants = lib.optionals initLegacy [ "legacy" ] ++ lib.optionals initSystemd [ "systemd" ];
|
||||
boot.supportedFilesystems = { inherit (cfg.storage) zfs; };
|
||||
boot.zfs.forceImportRoot = false;
|
||||
|
||||
canTestVm = instanceVm && pkgs.stdenv.isLinux && pkgs.stdenv.isx86_64;
|
||||
in
|
||||
{
|
||||
name = "incus" + lib.optionalString lts "-lts";
|
||||
environment.systemPackages = [ pkgs.parted ];
|
||||
|
||||
meta = {
|
||||
maintainers = lib.teams.lxc.members;
|
||||
networking.hostId = "01234567";
|
||||
networking.firewall.trustedInterfaces = [ "incusbr0" ];
|
||||
networking.nftables.enable = true;
|
||||
|
||||
security.apparmor.enable = cfg.appArmor;
|
||||
services.dbus.apparmor = (if cfg.appArmor then "enabled" else "disabled");
|
||||
|
||||
services.lvm = {
|
||||
boot.thin.enable = cfg.storage.lvm;
|
||||
dmeventd.enable = cfg.storage.lvm;
|
||||
};
|
||||
|
||||
nodes.machine = {
|
||||
virtualisation = {
|
||||
cores = 2;
|
||||
memorySize = 2048;
|
||||
diskSize = 12 * 1024;
|
||||
emptyDiskImages = [
|
||||
# vdb for zfs
|
||||
2048
|
||||
# vdc for lvm
|
||||
2048
|
||||
];
|
||||
|
||||
incus = {
|
||||
enable = true;
|
||||
package = if lts then pkgs.incus-lts else pkgs.incus;
|
||||
|
||||
preseed = {
|
||||
networks = [
|
||||
{
|
||||
name = "incusbr0";
|
||||
type = "bridge";
|
||||
config = {
|
||||
"ipv4.address" = "10.0.10.1/24";
|
||||
"ipv4.nat" = "true";
|
||||
};
|
||||
}
|
||||
]
|
||||
++ lib.optionals networkOvs [
|
||||
{
|
||||
name = "ovsbr0";
|
||||
type = "bridge";
|
||||
config = {
|
||||
"bridge.driver" = "openvswitch";
|
||||
"ipv4.address" = "10.0.20.1/24";
|
||||
"ipv4.nat" = "true";
|
||||
};
|
||||
}
|
||||
];
|
||||
profiles = [
|
||||
{
|
||||
name = "default";
|
||||
devices = {
|
||||
eth0 = {
|
||||
name = "eth0";
|
||||
network = "incusbr0";
|
||||
type = "nic";
|
||||
};
|
||||
root = {
|
||||
path = "/";
|
||||
pool = "default";
|
||||
size = "35GiB";
|
||||
type = "disk";
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
storage_pools = [
|
||||
{
|
||||
name = "default";
|
||||
driver = "dir";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
vswitch.enable = networkOvs;
|
||||
};
|
||||
|
||||
boot.supportedFilesystems = lib.optionals storageZfs [ "zfs" ];
|
||||
boot.zfs.forceImportRoot = false;
|
||||
|
||||
environment.systemPackages = [ pkgs.parted ];
|
||||
|
||||
networking.hostId = "01234567";
|
||||
networking.firewall.trustedInterfaces = [ "incusbr0" ];
|
||||
|
||||
security.apparmor.enable = appArmor;
|
||||
services.dbus.apparmor = (if appArmor then "enabled" else "disabled");
|
||||
|
||||
services.lvm = {
|
||||
boot.thin.enable = storageLvm;
|
||||
dmeventd.enable = storageLvm;
|
||||
};
|
||||
|
||||
networking.nftables.enable = true;
|
||||
|
||||
users.users.testuser = {
|
||||
isNormalUser = true;
|
||||
shell = pkgs.bashInteractive;
|
||||
group = "incus";
|
||||
uid = 1000;
|
||||
};
|
||||
users.users.testuser = {
|
||||
isNormalUser = true;
|
||||
shell = pkgs.bashInteractive;
|
||||
group = "incus";
|
||||
uid = 1000;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = # python
|
||||
''
|
||||
import json
|
||||
|
||||
def wait_for_instance(name: str, project: str = "default"):
|
||||
machine.wait_until_succeeds(f"incus exec {name} --disable-stdin --force-interactive --project {project} -- /run/current-system/sw/bin/systemctl is-system-running")
|
||||
|
||||
|
||||
def wait_incus_exec_success(name: str, command: str, timeout: int = 900, project: str = "default"):
|
||||
def check_command(_) -> bool:
|
||||
status, _ = machine.execute(f"incus exec {name} --disable-stdin --force-interactive --project {project} -- {command}")
|
||||
return status == 0
|
||||
|
||||
with machine.nested(f"Waiting for successful exec: {command}"):
|
||||
retry(check_command, timeout)
|
||||
|
||||
|
||||
def set_config(name: str, config: str, restart: bool = False, unset: bool = False):
|
||||
if restart:
|
||||
machine.succeed(f"incus stop {name}")
|
||||
|
||||
if unset:
|
||||
machine.succeed(f"incus config unset {name} {config}")
|
||||
else:
|
||||
machine.succeed(f"incus config set {name} {config}")
|
||||
|
||||
if restart:
|
||||
machine.succeed(f"incus start {name}")
|
||||
wait_for_instance(name)
|
||||
else:
|
||||
# give a moment to settle
|
||||
machine.sleep(1)
|
||||
|
||||
|
||||
def cleanup():
|
||||
# avoid conflict between preseed and cleanup operations
|
||||
machine.execute("systemctl kill incus-preseed.service")
|
||||
|
||||
instances = json.loads(machine.succeed("incus list --format json --all-projects"))
|
||||
with subtest("Stopping all running instances"):
|
||||
for instance in [a for a in instances if a['status'] == 'Running']:
|
||||
machine.execute(f"incus stop --force {instance['name']} --project {instance['project']}")
|
||||
machine.execute(f"incus delete --force {instance['name']} --project {instance['project']}")
|
||||
|
||||
|
||||
def check_sysctl(name: str):
|
||||
with subtest("systemd sysctl settings are applied"):
|
||||
machine.succeed(f"incus exec {name} -- systemctl status systemd-sysctl")
|
||||
sysctl = machine.succeed(f"incus exec {name} -- sysctl net.ipv4.ip_forward").strip().split(" ")[-1]
|
||||
assert "1" == sysctl, f"systemd-sysctl configuration not correctly applied, {sysctl} != 1"
|
||||
|
||||
|
||||
with subtest("Wait for startup"):
|
||||
machine.wait_for_unit("incus.service")
|
||||
machine.wait_for_unit("incus-preseed.service")
|
||||
|
||||
|
||||
with subtest("Verify preseed resources created"):
|
||||
machine.succeed("incus profile show default")
|
||||
machine.succeed("incus network info incusbr0")
|
||||
machine.succeed("incus storage show default")
|
||||
|
||||
''
|
||||
+ lib.optionalString appArmor ''
|
||||
with subtest("Verify AppArmor service is started without issue"):
|
||||
# restart AppArmor service since the Incus AppArmor folders are
|
||||
# created after AA service is started
|
||||
machine.systemctl("restart apparmor.service")
|
||||
machine.succeed("systemctl --no-pager -l status apparmor.service")
|
||||
machine.wait_for_unit("apparmor.service")
|
||||
''
|
||||
+ lib.optionalString instanceContainer (
|
||||
lib.foldl (
|
||||
acc: variant:
|
||||
acc
|
||||
# python
|
||||
+ ''
|
||||
metadata = "${(images variant).container.metadata}"
|
||||
rootfs = "${(images variant).container.rootfs}"
|
||||
alias = "nixos/container/${variant}"
|
||||
variant = "${variant}"
|
||||
|
||||
with subtest("container image can be imported"):
|
||||
machine.succeed(f"incus image import {metadata} {rootfs} --alias {alias}")
|
||||
|
||||
|
||||
with subtest("container can be launched and managed"):
|
||||
machine.succeed(f"incus launch {alias} container-{variant}1")
|
||||
wait_for_instance(f"container-{variant}1")
|
||||
|
||||
|
||||
with subtest("container mounts lxcfs overlays"):
|
||||
machine.succeed(f"incus exec container-{variant}1 mount | grep 'lxcfs on /proc/cpuinfo type fuse.lxcfs'")
|
||||
machine.succeed(f"incus exec container-{variant}1 mount | grep 'lxcfs on /proc/meminfo type fuse.lxcfs'")
|
||||
|
||||
|
||||
with subtest("container CPU limits can be managed"):
|
||||
set_config(f"container-{variant}1", "limits.cpu 1", restart=True)
|
||||
wait_incus_exec_success(f"container-{variant}1", "nproc | grep '^1$'", timeout=90)
|
||||
|
||||
|
||||
with subtest("container CPU limits can be hotplug changed"):
|
||||
set_config(f"container-{variant}1", "limits.cpu 2")
|
||||
wait_incus_exec_success(f"container-{variant}1", "nproc | grep '^2$'", timeout=90)
|
||||
|
||||
|
||||
with subtest("container memory limits can be managed"):
|
||||
set_config(f"container-{variant}1", "limits.memory 128MB", restart=True)
|
||||
wait_incus_exec_success(f"container-{variant}1", "grep 'MemTotal:[[:space:]]*125000 kB' /proc/meminfo", timeout=90)
|
||||
|
||||
|
||||
with subtest("container memory limits can be hotplug changed"):
|
||||
set_config(f"container-{variant}1", "limits.memory 256MB")
|
||||
wait_incus_exec_success(f"container-{variant}1", "grep 'MemTotal:[[:space:]]*250000 kB' /proc/meminfo", timeout=90)
|
||||
|
||||
|
||||
with subtest("container software tpm can be configured"):
|
||||
machine.succeed(f"incus config device add container-{variant}1 vtpm tpm path=/dev/tpm0 pathrm=/dev/tpmrm0")
|
||||
machine.succeed(f"incus exec container-{variant}1 -- test -e /dev/tpm0")
|
||||
machine.succeed(f"incus exec container-{variant}1 -- test -e /dev/tpmrm0")
|
||||
machine.succeed(f"incus config device remove container-{variant}1 vtpm")
|
||||
machine.fail(f"incus exec container-{variant}1 -- test -e /dev/tpm0")
|
||||
|
||||
|
||||
with subtest("container lxc-generator compatibility"):
|
||||
with subtest("lxc-container generator configures plain container"):
|
||||
# default container is plain
|
||||
machine.succeed(f"incus exec container-{variant}1 test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
|
||||
|
||||
check_sysctl(f"container-{variant}1")
|
||||
|
||||
with subtest("lxc-container generator configures nested container"):
|
||||
set_config(f"container-{variant}1", "security.nesting=true", restart=True)
|
||||
|
||||
machine.fail(f"incus exec container-{variant}1 test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
|
||||
target = machine.succeed(f"incus exec container-{variant}1 readlink -- -f /run/systemd/system/systemd-binfmt.service").strip()
|
||||
assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service"
|
||||
|
||||
check_sysctl(f"container-{variant}1")
|
||||
|
||||
with subtest("lxc-container generator configures privileged container"):
|
||||
# Create a new instance for a clean state
|
||||
machine.succeed(f"incus launch {alias} container-{variant}2")
|
||||
wait_for_instance(f"container-{variant}2")
|
||||
|
||||
machine.succeed(f"incus exec container-{variant}2 test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
|
||||
|
||||
check_sysctl(f"container-{variant}2")
|
||||
|
||||
with subtest("container supports per-instance lxcfs"):
|
||||
machine.succeed(f"incus stop container-{variant}1")
|
||||
machine.fail(f"pgrep -a lxcfs | grep 'incus/devices/container-{variant}1/lxcfs'")
|
||||
|
||||
machine.succeed("incus config set instances.lxcfs.per_instance=true")
|
||||
|
||||
machine.succeed(f"incus start container-{variant}1")
|
||||
wait_for_instance(f"container-{variant}1")
|
||||
machine.succeed(f"pgrep -a lxcfs | grep 'incus/devices/container-{variant}1/lxcfs'")
|
||||
|
||||
|
||||
with subtest("container can successfully restart"):
|
||||
machine.succeed(f"incus restart container-{variant}1")
|
||||
wait_for_instance(f"container-{variant}1")
|
||||
|
||||
|
||||
with subtest("container remains running when softDaemonRestart is enabled and service is stopped"):
|
||||
pid = machine.succeed(f"incus info container-{variant}1 | grep 'PID'").split(":")[1].strip()
|
||||
machine.succeed(f"ps {pid}")
|
||||
machine.succeed("systemctl stop incus")
|
||||
machine.succeed(f"ps {pid}")
|
||||
machine.succeed("systemctl start incus")
|
||||
|
||||
with subtest("containers stop with incus-startup.service"):
|
||||
pid = machine.succeed(f"incus info container-{variant}1 | grep 'PID'").split(":")[1].strip()
|
||||
machine.succeed(f"ps {pid}")
|
||||
machine.succeed("systemctl stop incus-startup.service")
|
||||
machine.wait_until_fails(f"ps {pid}", timeout=120)
|
||||
machine.succeed("systemctl start incus-startup.service")
|
||||
|
||||
|
||||
cleanup()
|
||||
''
|
||||
) "" initVariants
|
||||
)
|
||||
+ lib.optionalString canTestVm (
|
||||
(lib.foldl (
|
||||
acc: variant:
|
||||
acc
|
||||
# python
|
||||
+ ''
|
||||
metadata = "${(images variant).virtual-machine.metadata}"
|
||||
disk = "${(images variant).virtual-machine.disk}"
|
||||
alias = "nixos/virtual-machine/${variant}"
|
||||
variant = "${variant}"
|
||||
|
||||
with subtest("virtual-machine image can be imported"):
|
||||
machine.succeed(f"incus image import {metadata} {disk} --alias {alias}")
|
||||
|
||||
|
||||
with subtest("virtual-machine can be created"):
|
||||
machine.succeed(f"incus create {alias} vm-{variant}1 --vm --config limits.memory=512MB --config security.secureboot=false")
|
||||
|
||||
|
||||
with subtest("virtual-machine software tpm can be configured"):
|
||||
machine.succeed(f"incus config device add vm-{variant}1 vtpm tpm path=/dev/tpm0")
|
||||
|
||||
|
||||
with subtest("virtual-machine can be launched and become available"):
|
||||
machine.succeed(f"incus start vm-{variant}1")
|
||||
wait_for_instance(f"vm-{variant}1")
|
||||
|
||||
|
||||
with subtest("virtual-machine incus-agent is started"):
|
||||
machine.succeed(f"incus exec vm-{variant}1 systemctl is-active incus-agent")
|
||||
|
||||
|
||||
with subtest("virtual-machine incus-agent has a valid path"):
|
||||
machine.succeed(f"incus exec vm-{variant}1 -- bash -c 'true'")
|
||||
|
||||
|
||||
with subtest("virtual-machine CPU limits can be managed"):
|
||||
set_config(f"vm-{variant}1", "limits.cpu 1", restart=True)
|
||||
wait_incus_exec_success(f"vm-{variant}1", "nproc | grep '^1$'", timeout=90)
|
||||
|
||||
|
||||
with subtest("virtual-machine CPU limits can be hotplug changed"):
|
||||
set_config(f"vm-{variant}1", "limits.cpu 2")
|
||||
wait_incus_exec_success(f"vm-{variant}1", "nproc | grep '^2$'", timeout=90)
|
||||
|
||||
|
||||
with subtest("virtual-machine can successfully restart"):
|
||||
machine.succeed(f"incus restart vm-{variant}1")
|
||||
wait_for_instance(f"vm-{variant}1")
|
||||
|
||||
|
||||
with subtest("virtual-machine remains running when softDaemonRestart is enabled and service is stopped"):
|
||||
pid = machine.succeed(f"incus info vm-{variant}1 | grep 'PID'").split(":")[1].strip()
|
||||
machine.succeed(f"ps {pid}")
|
||||
machine.succeed("systemctl stop incus")
|
||||
machine.succeed(f"ps {pid}")
|
||||
machine.succeed("systemctl start incus")
|
||||
|
||||
|
||||
with subtest("virtual-machines stop with incus-startup.service"):
|
||||
pid = machine.succeed(f"incus info vm-{variant}1 | grep 'PID'").split(":")[1].strip()
|
||||
machine.succeed(f"ps {pid}")
|
||||
machine.succeed("systemctl stop incus-startup.service")
|
||||
machine.wait_until_fails(f"ps {pid}", timeout=120)
|
||||
machine.succeed("systemctl start incus-startup.service")
|
||||
|
||||
|
||||
cleanup()
|
||||
''
|
||||
) "" initVariants)
|
||||
+
|
||||
# python
|
||||
''
|
||||
with subtest("virtual-machine can launch CSM (BIOS)"):
|
||||
machine.succeed("incus init csm --vm --empty -c security.csm=true -c security.secureboot=false")
|
||||
machine.succeed("incus start csm")
|
||||
|
||||
|
||||
cleanup()
|
||||
''
|
||||
)
|
||||
testScript =
|
||||
lib.readFile ./incus_machine.py
|
||||
+
|
||||
lib.optionalString featureUser # python
|
||||
''
|
||||
with subtest("incus-user allows restricted access for users"):
|
||||
machine.fail("incus project show user-1000")
|
||||
machine.succeed("su - testuser bash -c 'incus list'")
|
||||
# a project is created dynamically for the user
|
||||
machine.succeed("incus project show user-1000")
|
||||
# users shouldn't be able to list storage pools
|
||||
machine.fail("su - testuser bash -c 'incus storage list'")
|
||||
|
||||
|
||||
with subtest("incus-user allows users to launch instances"):
|
||||
machine.succeed("su - testuser bash -c 'incus image import ${(images "systemd").container.metadata} ${(images "systemd").container.rootfs} --alias nixos'")
|
||||
machine.succeed("su - testuser bash -c 'incus launch nixos instance2'")
|
||||
wait_for_instance("instance2", "user-1000")
|
||||
|
||||
cleanup()
|
||||
''
|
||||
# python
|
||||
''
|
||||
server = IncusHost(machine)
|
||||
''
|
||||
+
|
||||
lib.optionalString networkOvs # python
|
||||
lib.optionalString cfg.network.ovs # python
|
||||
''
|
||||
with subtest("Verify openvswitch bridge"):
|
||||
machine.succeed("incus network info ovsbr0")
|
||||
server.succeed("incus network info ovsbr0")
|
||||
|
||||
|
||||
with subtest("Verify openvswitch bridge"):
|
||||
machine.succeed("ovs-vsctl br-exists ovsbr0")
|
||||
server.succeed("ovs-vsctl br-exists ovsbr0")
|
||||
''
|
||||
|
||||
+
|
||||
lib.optionalString storageZfs # python
|
||||
lib.optionalString cfg.storage.zfs # python
|
||||
''
|
||||
with subtest("Verify zfs pool created and usable"):
|
||||
machine.succeed(
|
||||
server.succeed(
|
||||
"zpool status",
|
||||
"parted --script /dev/vdb mklabel gpt",
|
||||
"zpool create zfs_pool /dev/vdb",
|
||||
)
|
||||
|
||||
machine.succeed("incus storage create zfs_pool zfs source=zfs_pool/incus")
|
||||
machine.succeed("zfs list zfs_pool/incus")
|
||||
server.succeed("incus storage create zfs_pool zfs source=zfs_pool/incus")
|
||||
server.succeed("zfs list zfs_pool/incus")
|
||||
|
||||
machine.succeed("incus storage volume create zfs_pool test_fs --type filesystem")
|
||||
machine.succeed("incus storage volume create zfs_pool test_vol --type block")
|
||||
server.succeed("incus storage volume create zfs_pool test_fs --type filesystem")
|
||||
server.succeed("incus storage volume create zfs_pool test_vol --type block")
|
||||
|
||||
machine.succeed("incus storage show zfs_pool")
|
||||
machine.succeed("incus storage volume list zfs_pool")
|
||||
machine.succeed("incus storage volume show zfs_pool test_fs")
|
||||
machine.succeed("incus storage volume show zfs_pool test_vol")
|
||||
server.succeed("incus storage show zfs_pool")
|
||||
server.succeed("incus storage volume list zfs_pool")
|
||||
server.succeed("incus storage volume show zfs_pool test_fs")
|
||||
server.succeed("incus storage volume show zfs_pool test_vol")
|
||||
|
||||
machine.succeed("incus create zfs1 --empty --storage zfs_pool")
|
||||
machine.succeed("incus list zfs1")
|
||||
server.succeed("incus create zfs1 --empty --storage zfs_pool")
|
||||
server.succeed("incus list zfs1")
|
||||
''
|
||||
|
||||
+
|
||||
lib.optionalString storageLvm # python
|
||||
lib.optionalString cfg.storage.lvm # python
|
||||
''
|
||||
with subtest("Verify lvm pool created and usable"):
|
||||
machine.succeed("incus storage create lvm_pool lvm source=/dev/vdc lvm.vg_name=incus_pool")
|
||||
machine.succeed("vgs incus_pool")
|
||||
server.succeed("incus storage create lvm_pool lvm source=/dev/vdc lvm.vg_name=incus_pool")
|
||||
server.succeed("vgs incus_pool")
|
||||
|
||||
machine.succeed("incus storage volume create lvm_pool test_fs --type filesystem")
|
||||
machine.succeed("incus storage volume create lvm_pool test_vol --type block")
|
||||
server.succeed("incus storage volume create lvm_pool test_fs --type filesystem")
|
||||
server.succeed("incus storage volume create lvm_pool test_vol --type block")
|
||||
|
||||
machine.succeed("incus storage show lvm_pool")
|
||||
server.succeed("incus storage show lvm_pool")
|
||||
|
||||
machine.succeed("incus storage volume list lvm_pool")
|
||||
machine.succeed("incus storage volume show lvm_pool test_fs")
|
||||
machine.succeed("incus storage volume show lvm_pool test_vol")
|
||||
server.succeed("incus storage volume list lvm_pool")
|
||||
server.succeed("incus storage volume show lvm_pool test_fs")
|
||||
server.succeed("incus storage volume show lvm_pool test_vol")
|
||||
|
||||
machine.succeed("incus create lvm1 --empty --storage lvm_pool")
|
||||
machine.succeed("incus list lvm1")
|
||||
'';
|
||||
}
|
||||
)
|
||||
server.succeed("incus create lvm1 --empty --storage lvm_pool")
|
||||
server.succeed("incus list lvm1")
|
||||
''
|
||||
+
|
||||
lib.optionalString cfg.appArmor # python
|
||||
''
|
||||
with subtest("Verify AppArmor service is started without issue"):
|
||||
# restart AppArmor service since the Incus AppArmor folders are
|
||||
# created after AA service is started
|
||||
server.systemctl("restart apparmor.service")
|
||||
server.succeed("systemctl --no-pager -l status apparmor.service")
|
||||
server.wait_for_unit("apparmor.service")
|
||||
''
|
||||
+
|
||||
lib.optionalString cfg.feature.user # python
|
||||
''
|
||||
with subtest("incus-user allows restricted access for users"):
|
||||
server.fail("incus project show user-1000")
|
||||
server.succeed("su - testuser bash -c 'incus list'")
|
||||
# a project is created dynamically for the user
|
||||
server.succeed("incus project show user-1000")
|
||||
# users shouldn't be able to list storage pools
|
||||
server.fail("su - testuser bash -c 'incus storage list'")
|
||||
# user can create an instance
|
||||
server.succeed("su - testuser bash -c 'incus create --empty'")
|
||||
''
|
||||
+ instanceScript;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import json
|
||||
|
||||
|
||||
class IncusHost(Machine):
|
||||
def __init__(self, base):
|
||||
with subtest("Wait for startup"):
|
||||
base.wait_for_unit("incus.service")
|
||||
base.wait_for_unit("incus-preseed.service")
|
||||
|
||||
with subtest("Verify preseed resources created"):
|
||||
base.succeed("incus profile show default")
|
||||
base.succeed("incus network info incusbr0")
|
||||
base.succeed("incus storage show default")
|
||||
|
||||
self._parent = base
|
||||
|
||||
# delegate attribute access to the parent
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._parent, name)
|
||||
|
||||
def instance_exec(self, name: str, command: str, project: str = "default"):
|
||||
return super().execute(
|
||||
f"incus exec {name} --disable-stdin --force-interactive --project {project} -- {command}"
|
||||
)
|
||||
|
||||
def instance_succeed(self, name: str, command: str, project: str = "default"):
|
||||
return super().succeed(
|
||||
f"incus exec {name} --disable-stdin --force-interactive --project {project} -- {command}"
|
||||
)
|
||||
|
||||
def wait_for_instance(self, name: str, project: str = "default"):
|
||||
self.wait_instance_exec_success(
|
||||
name,
|
||||
"/run/current-system/sw/bin/systemctl is-system-running",
|
||||
project=project,
|
||||
)
|
||||
|
||||
def wait_instance_exec_success(
|
||||
self, name: str, command: str, timeout: int = 900, project: str = "default"
|
||||
):
|
||||
def check_command(_) -> bool:
|
||||
status, _ = self.instance_exec(name, command, project)
|
||||
return status == 0
|
||||
|
||||
with super().nested(
|
||||
f"Waiting for successful instance exec, instance={name}, project={project}, command={command}"
|
||||
):
|
||||
retry(check_command, timeout)
|
||||
|
||||
def set_instance_config(
|
||||
self, name: str, config: str, restart: bool = False, unset: bool = False
|
||||
):
|
||||
if restart:
|
||||
super().succeed(f"incus stop {name}")
|
||||
|
||||
if unset:
|
||||
super().succeed(f"incus config unset {name} {config}")
|
||||
else:
|
||||
super().succeed(f"incus config set {name} {config}")
|
||||
|
||||
if restart:
|
||||
super().succeed(f"incus start {name}")
|
||||
self.wait_for_instance(name)
|
||||
else:
|
||||
# give a moment to settle
|
||||
super().sleep(1)
|
||||
|
||||
def cleanup(self):
|
||||
# avoid conflict between preseed and cleanup operations
|
||||
super().execute("systemctl kill incus-preseed.service")
|
||||
|
||||
instances = json.loads(
|
||||
super().succeed("incus list --format json --all-projects")
|
||||
)
|
||||
for instance in [a for a in instances if a["status"] == "Running"]:
|
||||
super().execute(
|
||||
f"incus stop --force {instance['name']} --project {instance['project']}"
|
||||
)
|
||||
super().execute(
|
||||
f"incus delete --force {instance['name']} --project {instance['project']}"
|
||||
)
|
||||
|
||||
def check_instance_sysctl(self, name: str, project: str = "default"):
|
||||
self.instance_succeed(name, "systemctl status systemd-sysctl", project)
|
||||
sysctl = (
|
||||
self.instance_succeed(name, "sysctl net.ipv4.ip_forward", project)
|
||||
.strip()
|
||||
.split(" ")[-1]
|
||||
)
|
||||
assert "1" == sysctl, (
|
||||
f"systemd-sysctl configuration not correctly applied, {sysctl} != 1"
|
||||
)
|
||||
+72
-74
@@ -1,84 +1,82 @@
|
||||
import ../make-test-python.nix (
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
lts ? true,
|
||||
...
|
||||
}:
|
||||
{
|
||||
name = "incus-ui";
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
package,
|
||||
...
|
||||
}:
|
||||
{
|
||||
name = "incus-ui";
|
||||
|
||||
meta = {
|
||||
maintainers = lib.teams.lxc.members;
|
||||
};
|
||||
meta = {
|
||||
maintainers = lib.teams.lxc.members;
|
||||
};
|
||||
|
||||
nodes.machine =
|
||||
{ lib, ... }:
|
||||
{
|
||||
nodes.machine =
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
virtualisation.incus = {
|
||||
enable = true;
|
||||
package = if lts then pkgs.incus-lts else pkgs.incus;
|
||||
virtualisation.incus = {
|
||||
enable = true;
|
||||
inherit package;
|
||||
|
||||
preseed.config."core.https_address" = ":8443";
|
||||
ui.enable = true;
|
||||
};
|
||||
|
||||
networking.nftables.enable = true;
|
||||
|
||||
environment.systemPackages =
|
||||
let
|
||||
seleniumScript =
|
||||
pkgs.writers.writePython3Bin "selenium-script"
|
||||
{
|
||||
libraries = with pkgs.python3Packages; [ selenium ];
|
||||
}
|
||||
''
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
options = Options()
|
||||
options.add_argument("--headless")
|
||||
service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
|
||||
|
||||
driver = webdriver.Firefox(options=options, service=service)
|
||||
driver.implicitly_wait(10)
|
||||
driver.get("https://localhost:8443/ui")
|
||||
|
||||
wait = WebDriverWait(driver, 60)
|
||||
|
||||
assert len(driver.find_elements(By.CLASS_NAME, "l-application")) > 0
|
||||
assert len(driver.find_elements(By.CLASS_NAME, "l-navigation__drawer")) > 0
|
||||
|
||||
driver.close()
|
||||
'';
|
||||
in
|
||||
with pkgs;
|
||||
[
|
||||
curl
|
||||
firefox-unwrapped
|
||||
geckodriver
|
||||
seleniumScript
|
||||
];
|
||||
preseed.config."core.https_address" = ":8443";
|
||||
ui.enable = true;
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("incus.service")
|
||||
machine.wait_for_unit("incus-preseed.service")
|
||||
networking.nftables.enable = true;
|
||||
|
||||
# Check that the INCUS_UI environment variable is populated in the systemd unit
|
||||
machine.succeed("systemctl cat incus.service | grep 'INCUS_UI'")
|
||||
environment.systemPackages =
|
||||
let
|
||||
seleniumScript =
|
||||
pkgs.writers.writePython3Bin "selenium-script"
|
||||
{
|
||||
libraries = with pkgs.python3Packages; [ selenium ];
|
||||
}
|
||||
''
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
# Ensure the endpoint returns an HTML page with 'Incus UI' in the title
|
||||
machine.succeed("curl -kLs https://localhost:8443/ui | grep '<title>Incus UI</title>'")
|
||||
options = Options()
|
||||
options.add_argument("--headless")
|
||||
service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
|
||||
|
||||
# Ensure the documentation is rendering correctly
|
||||
machine.succeed("curl -kLs https://localhost:8443/documentation/ | grep '<title>Incus documentation</title>'")
|
||||
driver = webdriver.Firefox(options=options, service=service)
|
||||
driver.implicitly_wait(10)
|
||||
driver.get("https://localhost:8443/ui")
|
||||
|
||||
# Ensure the application is actually rendered by the Javascript
|
||||
machine.succeed("PYTHONUNBUFFERED=1 selenium-script")
|
||||
'';
|
||||
}
|
||||
)
|
||||
wait = WebDriverWait(driver, 60)
|
||||
|
||||
assert len(driver.find_elements(By.CLASS_NAME, "l-application")) > 0
|
||||
assert len(driver.find_elements(By.CLASS_NAME, "l-navigation__drawer")) > 0
|
||||
|
||||
driver.close()
|
||||
'';
|
||||
in
|
||||
with pkgs;
|
||||
[
|
||||
curl
|
||||
firefox-unwrapped
|
||||
geckodriver
|
||||
seleniumScript
|
||||
];
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("incus.service")
|
||||
machine.wait_for_unit("incus-preseed.service")
|
||||
|
||||
# Check that the INCUS_UI environment variable is populated in the systemd unit
|
||||
machine.succeed("systemctl cat incus.service | grep 'INCUS_UI'")
|
||||
|
||||
# Ensure the endpoint returns an HTML page with 'Incus UI' in the title
|
||||
machine.succeed("curl -kLs https://localhost:8443/ui | grep '<title>Incus UI</title>'")
|
||||
|
||||
# Ensure the documentation is rendering correctly
|
||||
machine.succeed("curl -kLs https://localhost:8443/documentation/ | grep '<title>Incus documentation</title>'")
|
||||
|
||||
# Ensure the application is actually rendered by the Javascript
|
||||
machine.succeed("PYTHONUNBUFFERED=1 selenium-script")
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -48,13 +48,12 @@
|
||||
machine.wait_for_file("/home/alice/done")
|
||||
|
||||
with subtest("Systemd gives and removes device ownership as needed"):
|
||||
# Change back to /dev/snd/timer after systemd-258.1
|
||||
machine.succeed("getfacl /dev/dri/card0 | grep -q alice")
|
||||
machine.succeed("getfacl /dev/snd/timer | grep -q alice")
|
||||
machine.send_key("alt-f1")
|
||||
machine.wait_until_succeeds("[ $(fgconsole) = 1 ]")
|
||||
machine.fail("getfacl /dev/dri/card0 | grep -q alice")
|
||||
machine.fail("getfacl /dev/snd/timer | grep -q alice")
|
||||
machine.succeed("chvt 2")
|
||||
machine.wait_until_succeeds("getfacl /dev/dri/card0 | grep -q alice")
|
||||
machine.wait_until_succeeds("getfacl /dev/snd/timer | grep -q alice")
|
||||
|
||||
with subtest("Virtual console logout"):
|
||||
machine.send_chars("exit\n")
|
||||
|
||||
@@ -148,10 +148,9 @@ in
|
||||
machine.wait_for_text("Albums")
|
||||
machine.succeed("xdotool mousemove 25 45 click 1") # Open categories
|
||||
machine.sleep(2)
|
||||
machine.wait_for_text("Tracks")
|
||||
machine.succeed("xdotool mousemove 25 240 click 1") # Switch to Tracks category
|
||||
machine.sleep(2)
|
||||
machine.wait_for_text("${musicFileName}") # the test file
|
||||
machine.wait_for_text("Tracks") # Written in larger text now, easier for OCR
|
||||
machine.screenshot("lomiri-music_listing")
|
||||
|
||||
# Ensure pause colours isn't present already
|
||||
@@ -180,8 +179,9 @@ in
|
||||
|
||||
machine.screenshot("lomiri-music_paused")
|
||||
|
||||
# Lastly, check if generated cover image got extracted properly
|
||||
# Lastly, check if song details like title & generated cover image got extracted properly
|
||||
# if not, indicates an issue with mediascanner / lomiri-thumbnailer
|
||||
machine.wait_for_text("${musicFileName}")
|
||||
machine.wait_for_text("${ocrContent}")
|
||||
|
||||
machine.succeed("pkill -f lomiri-music-app")
|
||||
|
||||
@@ -16,6 +16,7 @@ in
|
||||
services.matter-server = {
|
||||
enable = true;
|
||||
port = 1234;
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -43,6 +44,9 @@ in
|
||||
with subtest("Check storage directory is created"):
|
||||
machine.succeed("ls /var/lib/matter-server/chip.json")
|
||||
|
||||
with subtest("Check dashboard loads"):
|
||||
machine.succeed("curl -f 127.0.0.1:1234")
|
||||
|
||||
with subtest("Check systemd hardening"):
|
||||
_, output = machine.execute("systemd-analyze security matter-server.service | grep -v '✓'")
|
||||
machine.log(output)
|
||||
|
||||
@@ -15,8 +15,6 @@ in
|
||||
{
|
||||
environment.systemPackages = [ pkgs.mcrcon ];
|
||||
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
services.minecraft-server = {
|
||||
declarative = true;
|
||||
enable = true;
|
||||
|
||||
@@ -15,12 +15,6 @@ in
|
||||
nodes.machine =
|
||||
{ ... }:
|
||||
{
|
||||
nixpkgs.config.allowUnfreePredicate =
|
||||
pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"n8n"
|
||||
];
|
||||
|
||||
services.n8n = {
|
||||
enable = true;
|
||||
environment.WEBHOOK_URL = webhookUrl;
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
start_all()
|
||||
machine.wait_for_unit("nix-daemon.socket")
|
||||
# regression test for the workaround for https://github.com/NixOS/nix/issues/9487
|
||||
print(machine.succeed("nix-instantiate --find-file extra"))
|
||||
print(machine.succeed("nix-instantiate --find-file nonextra"))
|
||||
# unset NIX_PATH because environtment overrides the config
|
||||
print(machine.succeed("env -u NIX_PATH nix-instantiate --find-file extra"))
|
||||
print(machine.succeed("env -u NIX_PATH nix-instantiate --find-file nonextra"))
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
nodes.outline = {
|
||||
virtualisation.memorySize = 2 * 1024;
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
services.outline = {
|
||||
enable = true;
|
||||
forceHttps = false;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user