mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-10-01 04:10:04 +00:00
Compare commits
1 Commits
haskell-up
...
ibus-no-fo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
460226e12c |
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -130,7 +130,7 @@ jobs:
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { default: checkCommitMessages } = await import('${{ github.workspace }}/trusted/ci/github-script/lint-commits.ts')
|
||||
const { default: checkCommitMessages } = await import('${{ github.workspace }}/trusted/ci/github-script/lint-commits.js')
|
||||
|
||||
await checkCommitMessages({
|
||||
github,
|
||||
|
||||
4
.github/workflows/merge-group.yml
vendored
4
.github/workflows/merge-group.yml
vendored
@@ -38,8 +38,8 @@ jobs:
|
||||
TARGET_SHA: ${{ inputs.targetSha }}
|
||||
with:
|
||||
script: |
|
||||
const { classify } = await import('${{ github.workspace }}/ci/github-script/supportedBranches.ts')
|
||||
const { default: supportedSystems } = await import('${{ github.workspace }}/ci/github-script/supportedSystems.ts')
|
||||
const { classify } = await import('${{ github.workspace }}/ci/github-script/supportedBranches.js')
|
||||
const { default: supportedSystems } = await import('${{ github.workspace }}/ci/github-script/supportedSystems.js')
|
||||
|
||||
const baseBranch = (
|
||||
context.payload.merge_group?.base_ref ??
|
||||
|
||||
14
.github/workflows/test.yml
vendored
14
.github/workflows/test.yml
vendored
@@ -64,8 +64,8 @@ jobs:
|
||||
'.github/workflows/test.yml',
|
||||
'ci/github-script/package.json',
|
||||
'ci/github-script/package-lock.json',
|
||||
'ci/github-script/supportedBranches.ts',
|
||||
'ci/github-script/supportedSystems.ts',
|
||||
'ci/github-script/supportedBranches.js',
|
||||
'ci/github-script/supportedSystems.js',
|
||||
'ci/pinned.json',
|
||||
'pkgs/top-level/release-supported-systems.json',
|
||||
].includes(file))) core.setOutput('merge-group', true)
|
||||
@@ -82,8 +82,8 @@ jobs:
|
||||
'ci/github-script/bot.js',
|
||||
'ci/github-script/check-target-branch.ts',
|
||||
'ci/github-script/commits.ts',
|
||||
'ci/github-script/get-pr-commit-details.ts',
|
||||
'ci/github-script/lint-commits.ts',
|
||||
'ci/github-script/get-pr-commit-details.js',
|
||||
'ci/github-script/lint-commits.js',
|
||||
'ci/github-script/manual-file-edits.ts',
|
||||
'ci/github-script/merge.js',
|
||||
'ci/github-script/package.json',
|
||||
@@ -91,9 +91,9 @@ jobs:
|
||||
'ci/github-script/prepare.js',
|
||||
'ci/github-script/reminders.ts',
|
||||
'ci/github-script/reviewers.js',
|
||||
'ci/github-script/reviews.ts',
|
||||
'ci/github-script/supportedBranches.ts',
|
||||
'ci/github-script/supportedSystems.ts',
|
||||
'ci/github-script/reviews.js',
|
||||
'ci/github-script/supportedBranches.js',
|
||||
'ci/github-script/supportedSystems.js',
|
||||
'ci/github-script/withRateLimit.js',
|
||||
'ci/pinned.json',
|
||||
'pkgs/top-level/release-supported-systems.json',
|
||||
|
||||
@@ -444,9 +444,9 @@ nixos/tests/forgejo.nix @adamcstephens @bendlas @christoph-heiss @
|
||||
/doc/hooks/zig.section.md @RossComputerGuy
|
||||
|
||||
# Buildbot
|
||||
nixos/modules/services/continuous-integration/buildbot @Mic92
|
||||
nixos/tests/buildbot.nix @Mic92
|
||||
pkgs/development/tools/continuous-integration/buildbot @Mic92
|
||||
nixos/modules/services/continuous-integration/buildbot @Mic92 @zowoq
|
||||
nixos/tests/buildbot.nix @Mic92 @zowoq
|
||||
pkgs/development/tools/continuous-integration/buildbot @Mic92 @zowoq
|
||||
|
||||
# Pretix
|
||||
pkgs/by-name/pr/pretix/ @mweinelt
|
||||
|
||||
@@ -104,7 +104,7 @@ For the purposes of CI, branches in the NixOS/nixpkgs repository are classified
|
||||
|
||||
Some branches also have a version component, which is either `unstable` or `YY.MM`.
|
||||
|
||||
`ci/github-script/supportedBranches.ts` is a script imported by CI to classify the base and head branches of a Pull Request.
|
||||
`ci/github-script/supportedBranches.js` is a script imported by CI to classify the base and head branches of a Pull Request.
|
||||
This classification will then be used to skip certain jobs.
|
||||
This script can also be run locally to print basic test cases.
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{ lib, ... }:
|
||||
rec {
|
||||
inherit (lib) uniqueStrings;
|
||||
# Borrowed from https://github.com/NixOS/nixpkgs/pull/355616
|
||||
uniqueStrings = list: builtins.attrNames (builtins.groupBy lib.id list);
|
||||
|
||||
/*
|
||||
Converts a `packagePlatformPath` into a `packagePlatformAttr`
|
||||
|
||||
@@ -4,7 +4,7 @@ import path from 'node:path'
|
||||
import { DefaultArtifactClient } from '@actions/artifact'
|
||||
import { handleMerge } from './merge.js'
|
||||
import { handleReviewers } from './reviewers.js'
|
||||
import { classify } from './supportedBranches.ts'
|
||||
import { classify } from './supportedBranches.js'
|
||||
import withRateLimit from './withRateLimit.js'
|
||||
|
||||
export default async ({ github, context, core, dry }) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { classify, split } from './supportedBranches.ts'
|
||||
import { classify, split } from './supportedBranches.js'
|
||||
|
||||
type TargetBranchPolicyFacts = {
|
||||
base: string
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
evaluateTargetBranchPolicy,
|
||||
getTargetBranchPolicy,
|
||||
} from './check-target-branch-policy.ts'
|
||||
import { dismissReviews, postReview } from './reviews.ts'
|
||||
import { split } from './supportedBranches.ts'
|
||||
import { dismissReviews, postReview } from './reviews.js'
|
||||
import { split } from './supportedBranches.js'
|
||||
|
||||
// TODO: should this be combined with the branch checks in prepare.js?
|
||||
// They do seem quite similar, but this needs to run after eval,
|
||||
|
||||
@@ -2,8 +2,8 @@ import { execFileSync } from 'node:child_process'
|
||||
import type * as actionsCore from '@actions/core'
|
||||
import type { context as actionsContext } from '@actions/github'
|
||||
import type { GitHub } from '@actions/github/lib/utils'
|
||||
import { dismissReviews, postReview } from './reviews.ts'
|
||||
import { classify } from './supportedBranches.ts'
|
||||
import { dismissReviews, postReview } from './reviews.js'
|
||||
import { classify } from './supportedBranches.js'
|
||||
import withRateLimit from './withRateLimit.js'
|
||||
|
||||
const dirname = import.meta.dirname
|
||||
|
||||
@@ -3,23 +3,26 @@ import { promisify } from 'node:util'
|
||||
|
||||
const execFile = promisify(nodeExecFile)
|
||||
|
||||
export type Commit = {
|
||||
subject: string
|
||||
sha: string
|
||||
author: { name: string; email: string }
|
||||
committer: { name: string; email: string }
|
||||
changedPaths: string[]
|
||||
changedPathSegments: Set<string>
|
||||
}
|
||||
/**
|
||||
* @typedef {{
|
||||
* subject: string,
|
||||
* sha: string,
|
||||
* author: { name: string, email: string },
|
||||
* committer: { name: string, email: string}
|
||||
* changedPaths: string[],
|
||||
* changedPathSegments: Set<string>,
|
||||
* }} Commit
|
||||
*/
|
||||
|
||||
interface RunGitProps {
|
||||
args: string[]
|
||||
core: typeof import('@actions/core')
|
||||
quiet?: boolean
|
||||
repoPath?: string
|
||||
}
|
||||
|
||||
async function runGit({ args, repoPath, core, quiet }: RunGitProps) {
|
||||
/**
|
||||
* @param {{
|
||||
* args: string[]
|
||||
* core: typeof import('@actions/core'),
|
||||
* quiet?: boolean,
|
||||
* repoPath?: string,
|
||||
* }} RunGitProps
|
||||
*/
|
||||
async function runGit({ args, repoPath, core, quiet }) {
|
||||
if (repoPath) {
|
||||
args = ['-C', repoPath, ...args]
|
||||
}
|
||||
@@ -31,29 +34,21 @@ async function runGit({ args, repoPath, core, quiet }: RunGitProps) {
|
||||
return await execFile('git', args)
|
||||
}
|
||||
|
||||
interface GetCommitMessagesForPRProps {
|
||||
core: typeof import('@actions/core')
|
||||
pr: Awaited<
|
||||
ReturnType<
|
||||
InstanceType<
|
||||
typeof import('@actions/github/lib/utils').GitHub
|
||||
>['rest']['pulls']['get']
|
||||
>
|
||||
>['data']
|
||||
repoPath?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SHA, subject and changed files for each commit in the given PR.
|
||||
*
|
||||
* Don't use GitHub API at all: the "list commits on PR" endpoint has a limit
|
||||
* of 250 commits and doesn't return the changed files.
|
||||
*
|
||||
* @param {{
|
||||
* core: typeof import('@actions/core'),
|
||||
* pr: Awaited<ReturnType<InstanceType<typeof import('@actions/github/lib/utils').GitHub>["rest"]["pulls"]["get"]>>["data"]
|
||||
* repoPath?: string,
|
||||
* }} GetCommitMessagesForPRProps
|
||||
*
|
||||
* @returns {Promise<Commit[]>}
|
||||
*/
|
||||
export async function getCommitDetailsForPR({
|
||||
core,
|
||||
pr,
|
||||
repoPath,
|
||||
}: GetCommitMessagesForPRProps): Promise<Commit[]> {
|
||||
export async function getCommitDetailsForPR({ core, pr, repoPath }) {
|
||||
await runGit({
|
||||
args: ['fetch', `--depth=1`, 'origin', pr.base.sha],
|
||||
repoPath,
|
||||
@@ -1,23 +1,17 @@
|
||||
import { type Commit, getCommitDetailsForPR } from './get-pr-commit-details.ts'
|
||||
import { classify } from './supportedBranches.ts'
|
||||
import { getCommitDetailsForPR } from './get-pr-commit-details.js'
|
||||
import { classify } from './supportedBranches.js'
|
||||
|
||||
type GitHub = InstanceType<typeof import('@actions/github/lib/utils').GitHub>
|
||||
type Context = typeof import('@actions/github').context
|
||||
type Core = typeof import('@actions/core')
|
||||
/** @typedef {import('./get-pr-commit-details.js').Commit} Commit */
|
||||
|
||||
interface LintCommitsProps {
|
||||
github: GitHub
|
||||
context: Context
|
||||
core: Core
|
||||
repoPath?: string
|
||||
}
|
||||
|
||||
export default async function lintCommits({
|
||||
github,
|
||||
context,
|
||||
core,
|
||||
repoPath,
|
||||
}: LintCommitsProps) {
|
||||
/**
|
||||
* @param {{
|
||||
* github: InstanceType<typeof import('@actions/github/lib/utils').GitHub>,
|
||||
* context: typeof import('@actions/github').context,
|
||||
* core: typeof import('@actions/core'),
|
||||
* repoPath?: string,
|
||||
* }} LintCommitsProps
|
||||
*/
|
||||
export default async function lintCommits({ github, context, core, repoPath }) {
|
||||
// This check should only be run when we have the pull_request context.
|
||||
const pull_number = context.payload.pull_request?.number
|
||||
if (!pull_number) {
|
||||
@@ -59,15 +53,13 @@ export default async function lintCommits({
|
||||
await checkCommitMetadata({ commits, core })
|
||||
}
|
||||
|
||||
interface CheckCommitMessagesProps {
|
||||
commits: Commit[]
|
||||
core: Core
|
||||
}
|
||||
|
||||
async function checkCommitMessages({
|
||||
commits,
|
||||
core,
|
||||
}: CheckCommitMessagesProps) {
|
||||
/**
|
||||
* @param {{
|
||||
* commits: Commit[],
|
||||
* core: typeof import('@actions/core'),
|
||||
* }} CheckCommitMessagesProps
|
||||
*/
|
||||
async function checkCommitMessages({ commits, core }) {
|
||||
const failures = new Set()
|
||||
|
||||
const conventionalCommitTypes = [
|
||||
@@ -88,13 +80,10 @@ async function checkCommitMessages({
|
||||
]
|
||||
|
||||
/**
|
||||
* @param types e.g. ["fix", "feat"]
|
||||
* @param sha commit hash
|
||||
* @param {string[]} types e.g. ["fix", "feat"]
|
||||
* @param {string?} sha commit hash
|
||||
*/
|
||||
function makeConventionalCommitRegex(
|
||||
types: string[],
|
||||
sha: string | null = null,
|
||||
) {
|
||||
function makeConventionalCommitRegex(types, sha = null) {
|
||||
core.info(
|
||||
`${
|
||||
sha
|
||||
@@ -177,15 +166,17 @@ async function checkCommitMessages({
|
||||
}
|
||||
}
|
||||
|
||||
interface CheckGitFieldsProps {
|
||||
commits: Commit[]
|
||||
core: Core
|
||||
}
|
||||
|
||||
async function checkCommitMetadata({ commits, core }: CheckGitFieldsProps) {
|
||||
/**
|
||||
* @param {{
|
||||
* commits: Commit[],
|
||||
* core: typeof import('@actions/core'),
|
||||
* }} CheckGitFieldsProps
|
||||
*/
|
||||
async function checkCommitMetadata({ commits, core }) {
|
||||
const failures = new Set()
|
||||
|
||||
const isEmail = (s: string) => /^.+@.*$/.test(s)
|
||||
/** @type {(s: string) => boolean} */
|
||||
const isEmail = (s) => /^.+@.*$/.test(s)
|
||||
|
||||
for (const commit of commits) {
|
||||
if (!commit.author.name) {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getCommitDetailsForPR } from './get-pr-commit-details.ts'
|
||||
import { dismissReviews, postReview } from './reviews.ts'
|
||||
import { classify } from './supportedBranches.ts'
|
||||
import { getCommitDetailsForPR } from './get-pr-commit-details.js'
|
||||
import { dismissReviews, postReview } from './reviews.js'
|
||||
import { classify } from './supportedBranches.js'
|
||||
|
||||
export default async function checkManualFileEdits({
|
||||
github,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-nocheck
|
||||
import { classify } from './supportedBranches.ts'
|
||||
import { classify } from './supportedBranches.js'
|
||||
|
||||
function runChecklist({
|
||||
committers,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import { dismissReviews, postReview } from './reviews.ts'
|
||||
import { classify } from './supportedBranches.ts'
|
||||
import supportedSystems from './supportedSystems.ts'
|
||||
import { dismissReviews, postReview } from './reviews.js'
|
||||
import { classify } from './supportedBranches.js'
|
||||
import supportedSystems from './supportedSystems.js'
|
||||
|
||||
const reviewKey = 'prepare'
|
||||
|
||||
@@ -66,7 +66,7 @@ export default async ({ github, context, core, dry }) => {
|
||||
// commits between that base and head is the real base. We can query for this via GitHub's
|
||||
// REST API. There can be multiple candidates for the real base with the same number of
|
||||
// commits. In this case we pick the "best" candidate by a fixed ordering of branches,
|
||||
// as defined in ci/github-script/supportedBranches.ts.
|
||||
// as defined in ci/github-script/supportedBranches.js.
|
||||
//
|
||||
// These requests take a while, when comparing against the wrong release - they need
|
||||
// to look at way more than 10k commits in that case. Thus, we try to minimize the
|
||||
|
||||
@@ -3,9 +3,9 @@ import path from 'node:path'
|
||||
import type * as actionsCore from '@actions/core'
|
||||
import type { context as actionsContext } from '@actions/github'
|
||||
import type { GitHub } from '@actions/github/lib/utils'
|
||||
import { getCommitDetailsForPR } from './get-pr-commit-details.ts'
|
||||
import { dismissReviews, postReview } from './reviews.ts'
|
||||
import { classify } from './supportedBranches.ts'
|
||||
import { getCommitDetailsForPR } from './get-pr-commit-details.js'
|
||||
import { dismissReviews, postReview } from './reviews.js'
|
||||
import { classify } from './supportedBranches.js'
|
||||
|
||||
/**
|
||||
* Reminders to post as a non-blocking review when a pull request touches
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Thanks for contributing to the documentation
|
||||
|
||||
Make sure you follow the [documentation style guide](https://github.com/NixOS/nixpkgs/blob/master/doc/styleguide.md), most notably:
|
||||
Make sure you follow the [documentation styleguide](https://github.com/NixOS/nixpkgs/blob/master/doc/styleguide.md), most notably:
|
||||
|
||||
- Show, don't tell: lead with a minimal working example; explanation follows the code.
|
||||
- No meta-commentary: don't write "This section explains how to…", just do it.
|
||||
|
||||
@@ -13,28 +13,30 @@ const reviewUsers = [
|
||||
'manual-edit',
|
||||
]
|
||||
|
||||
type GitHub = InstanceType<typeof import('@actions/github/lib/utils').GitHub>
|
||||
type Context = typeof import('@actions/github').context
|
||||
type Review = Awaited<
|
||||
ReturnType<GitHub['rest']['pulls']['listReviews']>
|
||||
>['data'][number]
|
||||
type ReviewWithNonNullUser = Review & { user: NonNullable<Review['user']> }
|
||||
|
||||
interface DismissReviewsProps {
|
||||
github: GitHub
|
||||
context: Context
|
||||
core: typeof import('@actions/core')
|
||||
dry: boolean
|
||||
reviewKey?: string
|
||||
}
|
||||
/**
|
||||
* @typedef {InstanceType<typeof import('@actions/github/lib/utils').GitHub>} GitHub
|
||||
* @typedef {typeof import('@actions/github').context} Context
|
||||
*
|
||||
* @typedef {Awaited<ReturnType<GitHub['rest']['pulls']['listReviews']>>['data'][number]} Review
|
||||
* @typedef {Review & { user: NonNullable<Review['user']> }} ReviewWithNonNullUser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* github: GitHub,
|
||||
* context: Context,
|
||||
* core: typeof import('@actions/core'),
|
||||
* dry: boolean,
|
||||
* reviewKey?: string,
|
||||
* }} DismissReviewsProps
|
||||
*/
|
||||
export async function dismissReviews({
|
||||
github,
|
||||
context,
|
||||
core,
|
||||
dry,
|
||||
reviewKey,
|
||||
}: DismissReviewsProps) {
|
||||
}) {
|
||||
const pull_number = context.payload.pull_request?.number
|
||||
if (!pull_number) {
|
||||
core.warning('dismissReviews called outside of pull_request context')
|
||||
@@ -45,29 +47,23 @@ export async function dismissReviews({
|
||||
return
|
||||
}
|
||||
|
||||
const allReviews: Review[] = await github.paginate(
|
||||
github.rest.pulls.listReviews,
|
||||
{
|
||||
...context.repo,
|
||||
pull_number,
|
||||
},
|
||||
)
|
||||
const allReviews = await github.paginate(github.rest.pulls.listReviews, {
|
||||
...context.repo,
|
||||
pull_number,
|
||||
})
|
||||
|
||||
const reviews = allReviews
|
||||
.filter((review): review is ReviewWithNonNullUser => !!review.user)
|
||||
.filter(
|
||||
const reviews = /** @type {ReviewWithNonNullUser[]} */ (
|
||||
allReviews.filter(
|
||||
(review) =>
|
||||
review.user &&
|
||||
review.state !== 'DISMISSED' &&
|
||||
review.user.login.endsWith('[bot]') &&
|
||||
reviewUsers.some((substr) => review.user?.login.includes(substr)),
|
||||
)
|
||||
)
|
||||
|
||||
const reviewsByUser = reviews.reduce(
|
||||
(prev, curr) => {
|
||||
if (!curr.user) {
|
||||
return prev
|
||||
}
|
||||
|
||||
if (!(curr.user.login in prev)) {
|
||||
prev[curr.user.login] = []
|
||||
}
|
||||
@@ -76,7 +72,7 @@ export async function dismissReviews({
|
||||
|
||||
return prev
|
||||
},
|
||||
{} as Record<string, ReviewWithNonNullUser[]>,
|
||||
/** @type {Record<string, ReviewWithNonNullUser[]> } */ ({}),
|
||||
)
|
||||
|
||||
const commentRegex = new RegExp(
|
||||
@@ -90,8 +86,8 @@ export async function dismissReviews({
|
||||
)
|
||||
|
||||
let reviewsToMinimize = reviews
|
||||
const reviewsToDismiss: ReviewWithNonNullUser[] = []
|
||||
const reviewsToResolve: ReviewWithNonNullUser[] = []
|
||||
const /** @type {ReviewWithNonNullUser[]} */ reviewsToDismiss = []
|
||||
const /** @type {ReviewWithNonNullUser[]} */ reviewsToResolve = []
|
||||
|
||||
if (reviewKey && reviews.every((review) => commentRegex.test(review.body))) {
|
||||
reviewsToMinimize = reviews.filter((review) =>
|
||||
@@ -169,16 +165,17 @@ export async function dismissReviews({
|
||||
])
|
||||
}
|
||||
|
||||
interface PostReviewProps {
|
||||
github: GitHub
|
||||
context: Context
|
||||
core: typeof import('@actions/core')
|
||||
dry: boolean
|
||||
body: string
|
||||
event: keyof typeof eventToState
|
||||
reviewKey: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* github: GitHub,
|
||||
* context: Context,
|
||||
* core: typeof import('@actions/core'),
|
||||
* dry: boolean,
|
||||
* body: string,
|
||||
* event: keyof typeof eventToState,
|
||||
* reviewKey: string,
|
||||
* }} PostReviewProps
|
||||
*/
|
||||
export async function postReview({
|
||||
github,
|
||||
context,
|
||||
@@ -187,7 +184,7 @@ export async function postReview({
|
||||
body,
|
||||
event = 'REQUEST_CHANGES',
|
||||
reviewKey,
|
||||
}: PostReviewProps) {
|
||||
}) {
|
||||
const pull_number = context.payload.pull_request?.number
|
||||
if (!pull_number) {
|
||||
core.warning('postReview called outside of pull_request context')
|
||||
@@ -213,7 +210,8 @@ export async function postReview({
|
||||
reviewUsers.some((substr) => review.user?.login.includes(substr)),
|
||||
)
|
||||
|
||||
let pendingReview: null | Review
|
||||
/** @type {null | Review} */
|
||||
let pendingReview
|
||||
const matchingReviews = reviews.filter((review) =>
|
||||
reviewKeyRegex.test(review.body),
|
||||
)
|
||||
@@ -101,7 +101,7 @@ program
|
||||
.argument('<repo>', 'Name of the GitHub repository to run on (Example: nixpkgs)')
|
||||
.argument('<pr>', 'Number of the Pull Request to run on')
|
||||
.action(async (owner, repo, pr, options) => {
|
||||
const checkCommitMessages = (await import('./lint-commits.ts')).default
|
||||
const checkCommitMessages = (await import('./lint-commits.js')).default
|
||||
await run(checkCommitMessages, owner, repo, pr, options)
|
||||
})
|
||||
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
/*
|
||||
#!nix-shell -i node -p nodejs
|
||||
*/
|
||||
// @ts-nocheck
|
||||
import { resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
type BranchType = 'channel' | 'development' | 'primary' | 'secondary'
|
||||
|
||||
const typeConfig: Record<string, BranchType[]> = {
|
||||
const typeConfig = {
|
||||
master: ['development', 'primary'],
|
||||
release: ['development', 'primary'],
|
||||
staging: ['development', 'secondary'],
|
||||
@@ -20,7 +19,7 @@ const typeConfig: Record<string, BranchType[]> = {
|
||||
|
||||
// "order" ranks the development branches by how likely they are the intended base branch
|
||||
// when they are an otherwise equally good fit according to ci/github-script/prepare.js.
|
||||
const orderConfig: Record<string, number> = {
|
||||
const orderConfig = {
|
||||
master: 0,
|
||||
release: 1,
|
||||
staging: 2,
|
||||
@@ -29,30 +28,15 @@ const orderConfig: Record<string, number> = {
|
||||
'staging-next': 4,
|
||||
}
|
||||
|
||||
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
|
||||
type Version = `${Digit}${Digit}.${Digit}${Digit}` | 'unstable'
|
||||
interface SplitResult {
|
||||
prefix: string
|
||||
version: Version
|
||||
suffix?: string
|
||||
function split(branch) {
|
||||
return {
|
||||
...branch.match(
|
||||
/(?<prefix>.+?)(-(?<version>\d{2}\.\d{2}|unstable)(?:-(?<suffix>.*))?)?$/,
|
||||
).groups,
|
||||
}
|
||||
}
|
||||
|
||||
function split(branch: string) {
|
||||
const groups = branch.match(
|
||||
/(?<prefix>.+?)(-(?<version>\d{2}\.\d{2}|unstable)(?:-(?<suffix>.*))?)?$/,
|
||||
)!.groups!
|
||||
return groups as unknown as SplitResult
|
||||
}
|
||||
|
||||
interface BranchClassification {
|
||||
branch: string
|
||||
order: number
|
||||
stable: boolean
|
||||
type: BranchType[]
|
||||
version: Version
|
||||
}
|
||||
|
||||
function classify(branch: string): BranchClassification {
|
||||
function classify(branch) {
|
||||
const { prefix, version } = split(branch)
|
||||
return {
|
||||
branch,
|
||||
@@ -71,7 +55,7 @@ if (
|
||||
fileURLToPath(import.meta.url) === resolve(process.argv[1])
|
||||
) {
|
||||
console.log('split(branch)')
|
||||
function testSplit(branch: string) {
|
||||
function testSplit(branch) {
|
||||
console.log(branch, split(branch))
|
||||
}
|
||||
testSplit('master')
|
||||
@@ -88,7 +72,7 @@ if (
|
||||
console.log('')
|
||||
|
||||
console.log('classify(branch)')
|
||||
function testClassify(branch: string) {
|
||||
function testClassify(branch) {
|
||||
console.log(branch, classify(branch))
|
||||
}
|
||||
testClassify('master')
|
||||
11
ci/github-script/supportedSystems.js
Normal file
11
ci/github-script/supportedSystems.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// @ts-nocheck
|
||||
export default async ({ github, context, targetSha }) => {
|
||||
const { content, encoding } = (
|
||||
await github.rest.repos.getContent({
|
||||
...context.repo,
|
||||
path: 'pkgs/top-level/release-supported-systems.json',
|
||||
ref: targetSha,
|
||||
})
|
||||
).data
|
||||
return JSON.parse(Buffer.from(content, encoding).toString())
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
interface SupportedSystemsProps {
|
||||
github: InstanceType<typeof import('@actions/github/lib/utils').GitHub>
|
||||
context: typeof import('@actions/github').context
|
||||
targetSha: string
|
||||
}
|
||||
|
||||
export default async ({
|
||||
github,
|
||||
context,
|
||||
targetSha,
|
||||
}: SupportedSystemsProps) => {
|
||||
const contentObject = (
|
||||
await github.rest.repos.getContent({
|
||||
...context.repo,
|
||||
path: 'pkgs/top-level/release-supported-systems.json',
|
||||
ref: targetSha,
|
||||
})
|
||||
).data
|
||||
|
||||
if ('type' in contentObject && contentObject.type === 'file') {
|
||||
const { content, encoding } = contentObject
|
||||
return JSON.parse(
|
||||
Buffer.from(content, encoding as BufferEncoding).toString(),
|
||||
)
|
||||
} else {
|
||||
throw new Error(
|
||||
'Fetched pkgs/top-level/release-supported-systems.json is not a file',
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -7,26 +7,19 @@ This directory houses the source files for the Nixpkgs manual.
|
||||
> We are actively restructuring our documentation to be more beginner friendly.
|
||||
>
|
||||
|
||||
When writing new docs use **Progressive Disclosure:**
|
||||
When writing new docs use **Progressive Disclosure**
|
||||
|
||||
- Start simple, pick up beginners.
|
||||
- Use **examples** first to show how to get something done.
|
||||
- Keep **explanation** lean.
|
||||
Start simple, pick up beginners.
|
||||
Use **examples** first to show how to get something done. Keep **Explanation** lean.
|
||||
|
||||
Use our [style guide](./styleguide.md) for more in depth guidance on writing good documentation.
|
||||
Use our [styleguide](./styleguide.md) for more in depth guidance on writing good documentation.
|
||||
|
||||
Documentation about Nixpkgs belongs here.
|
||||
This includes getting started guides and onboarding guides for *using* Nixpkgs and the language frameworks it ships.
|
||||
Documentation about Nixpkgs belongs here, this includes 'getting-started'-guides and 'onboarding-guides' for *using* Nixpkgs and the language frameworks it ships.
|
||||
|
||||
Write **guides** task-first: lead with a working example, then explain in prose.
|
||||
Write **reference** as the specification of functions and attributes.
|
||||
|
||||
We are actively working to generate reference documentation from the [doc-comments](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md) present in code, which also lets you view it locally with the `:doc` command in `nix repl`, e.g.:
|
||||
|
||||
```
|
||||
nix-repl> :l <nixpkgs>
|
||||
nix-repl> :doc lib.mapAttrsToList
|
||||
```
|
||||
We are actively working to generate reference documentation from the [doc-comments](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md) present in code, which also lets you view it locally with `:doc` in `nix repl`.
|
||||
|
||||
See [Document structure](#document-structure) for a structural template.
|
||||
|
||||
@@ -49,23 +42,23 @@ If the build succeeds, the manual will be in `./result/share/doc/nixpkgs/manual.
|
||||
|
||||
### Development environment
|
||||
|
||||
To reduce repetition, consider using tools from the documentation development environment:
|
||||
To reduce repetition, consider using tools from the provided development environment:
|
||||
|
||||
Load it from the Nixpkgs documentation directory with
|
||||
|
||||
```ShellSession
|
||||
$ cd /path/to/nixpkgs/doc
|
||||
$ nix-shell
|
||||
```
|
||||
|
||||
To load the documentation development environment automatically when entering that directory:
|
||||
To load the development utilities automatically when entering that directory, [set up `nix-direnv`](https://nix.dev/guides/recipes/direnv).
|
||||
|
||||
1. Install [`nix-direnv`](https://search.nixos.org/packages?channel=unstable&query=nix-direnv#show=nix-direnv)
|
||||
1. Set up direnv in the documentation directory:
|
||||
Make sure that your local files aren't added to Git history by adding the following lines to `.git/info/exclude` at the root of the Nixpkgs repository:
|
||||
|
||||
```ShellSession
|
||||
$ cd doc
|
||||
$ echo "use nix" > .envrc
|
||||
$ direnv allow
|
||||
```
|
||||
```
|
||||
/**/.envrc
|
||||
/**/.direnv
|
||||
```
|
||||
|
||||
#### Live preview
|
||||
|
||||
@@ -140,12 +133,14 @@ A few markups for other kinds of literals are also available:
|
||||
- `` {env}`XDG_DATA_DIRS` ``
|
||||
- `` {file}`/etc/passwd` ``
|
||||
- `` {option}`networking.useDHCP` ``
|
||||
- `` {var}`pkgs` ``
|
||||
|
||||
The values will be formatted as inline `<code>` elements.
|
||||
- `` {var}`/etc/passwd` ``
|
||||
|
||||
These literal kinds are used mostly in NixOS option documentation.
|
||||
|
||||
This syntax is taken from [MyST](https://myst-parser.readthedocs.io/en/latest/syntax/syntax.html#roles-an-in-line-extension-point).
|
||||
Though, the feature originates from [reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-manpage) with slightly different syntax.
|
||||
They are handled by `myst_role` defined per renderer. <!-- reverse references in code -->
|
||||
|
||||
#### Admonitions
|
||||
|
||||
Set off from the text to bring attention to something.
|
||||
@@ -168,7 +163,7 @@ The following are supported:
|
||||
- `example`
|
||||
|
||||
Example admonitions require a title to work.
|
||||
If you don't provide one, the manual won't build.
|
||||
If you don't provide one, the manual won't be built.
|
||||
|
||||
```markdown
|
||||
::: {.example #ex-showing-an-example}
|
||||
@@ -184,11 +179,11 @@ Text for the example.
|
||||
For defining a group of terms:
|
||||
|
||||
```markdown
|
||||
Pear
|
||||
: Green or yellow bulbous fruit
|
||||
pear
|
||||
: green or yellow bulbous fruit
|
||||
|
||||
Watermelon
|
||||
: Green fruit with red flesh
|
||||
watermelon
|
||||
: green fruit with red flesh
|
||||
```
|
||||
|
||||
## Commit conventions
|
||||
@@ -220,7 +215,7 @@ When needed, each convention explains why it exists, so you can make a decision
|
||||
Note that these conventions are about the **structure** of the manual (and its source files), not about the content that goes in it.
|
||||
You, as the writer of documentation, are still in charge of its content.
|
||||
|
||||
**For prose style, see the [documentation style guide](./styleguide.md).**
|
||||
**For prose style, see the [documentation styleguide](./styleguide.md).**
|
||||
|
||||
### Document structure
|
||||
|
||||
@@ -290,7 +285,7 @@ When changing existing content, update formatting if possible, but avoid excessi
|
||||
|
||||
### Examples first
|
||||
|
||||
Put examples before detailed explanations (see the [style guide](./styleguide.md) for the rationale).
|
||||
Put examples before detailed explanations (see the [styleguide](./styleguide.md) for the rationale).
|
||||
|
||||
Use this structure for each documented item:
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Create a `shell.nix` with the following:
|
||||
```nix
|
||||
# shell.nix
|
||||
let
|
||||
nixpkgs = fetchTarball "https://channels.nixos.org/nixos-unstable/nixexprs.tar.zst";
|
||||
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz";
|
||||
pkgs = import nixpkgs { };
|
||||
in
|
||||
pkgs.mkShell {
|
||||
@@ -25,7 +25,7 @@ nix-shell
|
||||
This activates your `shell.nix` and you should see:
|
||||
|
||||
```sh
|
||||
unpacking 'https://channels.nixos.org/nixos-unstable/nixexprs.tar.zst' into the Git cache...
|
||||
unpacking 'https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz' into the Git cache...
|
||||
Welcome in your nix shell
|
||||
```
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ Pin Nixpkgs and call the package from `default.nix`:
|
||||
```nix
|
||||
# default.nix
|
||||
let
|
||||
nixpkgs = fetchTarball "https://channels.nixos.org/nixos-unstable/nixexprs.tar.zst";
|
||||
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz";
|
||||
pkgs = import nixpkgs { };
|
||||
in
|
||||
pkgs.callPackage ./package.nix { }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
This hook defaults a variety of environment variables known
|
||||
to control thread counts to 1. Many of these otherwise default
|
||||
to `$(nproc)`, which causes massive overloads on build machines
|
||||
if nix build jobs and build cores are already tuned to fully use
|
||||
if nix build jobs and build cores are already tuned to fully utilize
|
||||
compute capacity of a builder without additional parallelism.
|
||||
|
||||
Currently sets the following environment variables:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
COSMIC (Computer Operating System Main Interface Components) is a desktop environment developed by
|
||||
System76, primarily for the Pop!_OS Linux distribution. Applications in the COSMIC ecosystem are
|
||||
written in Rust and use libcosmic, which builds on the Iced GUI framework. This section explains
|
||||
how to package and integrate COSMIC applications within Nix.
|
||||
how to properly package and integrate COSMIC applications within Nix.
|
||||
|
||||
### libcosmicAppHook {#ssec-cosmic-libcosmic-app-hook}
|
||||
|
||||
@@ -17,7 +17,7 @@ and wrapping applications based on libcosmic. It handles many common requirement
|
||||
- Managing Vergen environment variables for build-time information
|
||||
- Setting up Rust linker flags for specific libraries
|
||||
|
||||
Add the hook to your package's `nativeBuildInputs`:
|
||||
To use the hook, simply add it to your package's `nativeBuildInputs`:
|
||||
|
||||
```nix
|
||||
{
|
||||
@@ -61,9 +61,8 @@ rustPlatform.buildRustPackage {
|
||||
}
|
||||
```
|
||||
|
||||
> [!Note]
|
||||
> `cosmic-settings` is a separate application and not a part of the libcosmic settings
|
||||
> system itself. It's included by default in `libcosmicAppHook` only to provide these fallback theme
|
||||
Note that `cosmic-settings` is a separate application and not a part of the libcosmic settings
|
||||
system itself. It's included by default in `libcosmicAppHook` only to provide these fallback theme
|
||||
settings.
|
||||
|
||||
### Icons {#ssec-cosmic-icons}
|
||||
|
||||
@@ -63,7 +63,7 @@ For instance, `sqlite-lua` needs `g:sqlite_clib_path` to be set to work. Nixpkgs
|
||||
- `wrapperArgs`: Extra arguments forwarded to the `makeWrapper` call.
|
||||
- `wrapRc`: Nix, not being able to write in your `$HOME`, loads the
|
||||
generated Neovim configuration via the `$VIMINIT` environment variable, i.e. : `export VIMINIT='lua dofile("/nix/store/…-init.lua")'`. This has side effects like preventing Neovim from sourcing your `init.lua` in `$XDG_CONFIG_HOME/nvim` (see bullet 7 of [`:help startup`](https://neovim.io/doc/user/starting.html#startup) in Neovim). Disable it if you want to generate your own wrapper. You can still reuse the generated vimscript init code via `neovim.passthru.initRc`.
|
||||
- `plugins`: A list of plugins to add to the wrapper. If a plugin is not available in nixpkgs, you can [package it yourself](#what-if-your-favourite-vim-plugin-isnt-already-packaged).
|
||||
- `plugins`: A list of plugins to add to the wrapper.
|
||||
- `extraLuaPackages`: A function passed on to `lua.withPackages`.
|
||||
- `extraPython3Packages`: A function passed on to `python3.withPackages`.
|
||||
- `withPython3`, `withNodeJs`, `withRuby`, `withPerl` control when to enable neovim
|
||||
|
||||
@@ -11,86 +11,47 @@ nix-shell -p swift --run 'swiftc -' <<< 'print("Hello world!")'
|
||||
|
||||
The `swift` package also provides the `swift` command, with some caveats:
|
||||
|
||||
- Swift Package Manager (SwiftPM) is packaged separately as `swiftpm`.
|
||||
If you need functionality like `swift build`, `swift run`, `swift test`, you must also add the `swiftpm` package to your closure.
|
||||
- On Darwin, the `swift repl` command requires an Xcode installation.
|
||||
This is because it uses the system LLDB debugserver, which has special entitlements.
|
||||
- Swift Package Manager (SwiftPM) is packaged separately as `swiftpm`. If you
|
||||
need functionality like `swift build`, `swift run`, `swift test`, you must
|
||||
also add the `swiftpm` package to your closure.
|
||||
- On Darwin, the `swift repl` command requires an Xcode installation. This is
|
||||
because it uses the system LLDB debugserver, which has special entitlements.
|
||||
|
||||
## Module search paths {#ssec-swift-module-search-paths}
|
||||
|
||||
The Swift compiler executables are patched to find the C and C++ standard libraries associated with its target platform, but they are not wrapped.
|
||||
They will not find your application’s dependencies automatically in the Nix store.
|
||||
Your build system is expected to handle this for you.
|
||||
Like other toolchains in Nixpkgs, the Swift compiler executables are wrapped
|
||||
to help Swift find your application's dependencies in the Nix store. These
|
||||
wrappers scan the `buildInputs` of your package derivation for specific
|
||||
directories where Swift modules are placed by convention, and automatically
|
||||
add those directories to the Swift compiler search paths.
|
||||
|
||||
SwiftPM provides a hook that scans the `buildInputs` of your package derivation for specific directories where the Swift modules are placed by convention.
|
||||
These directories are added automatically to `swiftpmFlags` when the hook runs.
|
||||
Swift in Nixpkgs follows a few conventions when installing dependencies:
|
||||
Swift follows different conventions depending on the platform. The wrappers
|
||||
look for the following directories:
|
||||
|
||||
- Libraries (both shared and static) are installed to `lib`.
|
||||
This differs from upstream packaging, but it matches how other langauges are packaged in Nixpkgs.
|
||||
This allows Swift packages to take advantage of existing tooling that expects libraries to be installed in this standard location.
|
||||
- Modules are installed to `lib/swift/<platform>` where `<platform>` is the Swift platform for your host platform (e.g., `lib/swift/macosx` or `lib/swift/linux`).
|
||||
Note that Linux modules may be installed in a directory specific to the target architecture(e.g., `lib/swift/linux/x86_64`), but this is uncommon.
|
||||
Upstream Swift appears to be moving away from this convention.
|
||||
- On Darwin platforms: `lib/swift/macosx`
|
||||
(If not targeting macOS, replace `macosx` with the Xcode platform name.)
|
||||
- On other platforms: `lib/swift/linux/x86_64`
|
||||
(Where `linux` and `x86_64` are from lowercase `uname -sm`.)
|
||||
- For convenience, Nixpkgs also adds `lib/swift` to the search path.
|
||||
This can save a bit of work packaging Swift modules, because many Nix builds
|
||||
will produce output for just one target anyway.
|
||||
|
||||
## Core libraries {#ssec-swift-core-libraries}
|
||||
|
||||
The `swift` package contains a complete toolchain with the Swift stdlib, Dispatch, Foundation, XCTest, and Swift Testing.
|
||||
These packages do not need to be added to `buildInputs` when packaging applications.
|
||||
The Swift compiler will find them automatically in the `swift` toolchain.
|
||||
In addition to the standard library, the Swift toolchain contains some
|
||||
additional 'core libraries' that, on Apple platforms, are normally distributed
|
||||
as part of the OS or Xcode. These are packaged separately in Nixpkgs and can
|
||||
be found (for use in `buildInputs`) as:
|
||||
|
||||
If you do need to use these packages outside of the Swift toolchain, they are available in the following packages:
|
||||
|
||||
- `swiftPackages.stdlib` contains the Swift stdlib and backdeployment dylibs.
|
||||
- `swiftPackages.swift-corelibs-libdispatch` contains the Dispatch framework.
|
||||
- `swiftPackages.swift-corelibs-foundation` contains the Foundation framework.
|
||||
- `swiftPackages.swift-corelibs-xctest` and `swiftPackages.swift-testing` contain the XCTest and Swift Testing frameworks respectively.
|
||||
|
||||
Note: On Darwin, the Swift stdlib has been removed from the SDK.
|
||||
The Swift toolchain contains the stubs and modules required to build Swift applications with the following exceptions:
|
||||
|
||||
- Swift Differentiation is shipped as a dylib in Nixpkgs because it is no longer shipped with the OS (as of macOS 26.4).
|
||||
This allows packages using Swift Differentiation to work regardless of OS version.
|
||||
- The Span back-deployment dylib is shipped with the stdlib.
|
||||
- This is expected because back-deployment dylibs are normally shipped with the toolchain.
|
||||
- FoundationMacros is built and shipped as a dylib in `swiftPackages.swift-foundation` and included in the toolchain.
|
||||
Macros are actually compiler plugins executed at build time.
|
||||
Without this, FoundationMacros would not work on Darwin.
|
||||
- `swiftPackages.Dispatch`
|
||||
- `swiftPackages.Foundation`
|
||||
- `swiftPackages.XCTest`
|
||||
|
||||
## Packaging with SwiftPM {#ssec-swift-packaging-with-swiftpm}
|
||||
|
||||
Nixpkgs includes two ways to package dependencies for Swift applications: `fetchSwiftPMDeps` and `swiftpm2nix`.
|
||||
While `swiftpm2nix` is not deprecated, using `fetchSwiftPMDeps` is preferred because it is easier to use and does not (usually) require shipping extra files with your package.
|
||||
|
||||
### Packaging with `fetchSwiftPMDeps` {#ssec-swift-packaging-with-fetch-swiftpm-deps}
|
||||
|
||||
Swift provides a fetcher that will download all of your dependencies based on the `Package.resolved` shipped by your package.
|
||||
If your package does not ship one, you will have to generate it yourself and provide it with your package.
|
||||
Otherwise, set `swiftpmDeps` as follows:
|
||||
|
||||
```nix
|
||||
{
|
||||
swiftpmDeps = fetchSwiftPMDeps {
|
||||
inherit src;
|
||||
hash = "sha256-1KfyrQXE1HaO9WsuskzgiiEZxM/oelp40Jwzr8xJEL4=";
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The `src` attribute is required as is the `hash`.
|
||||
The first time you build your package, you will need to set `hash` to an empty value by using `lib.fakeHash` to get the hash for your dependencies.
|
||||
The following optional attributes can also be used:
|
||||
|
||||
- `name`: Sets the name of the vendored dependencies fixed-output derivation.
|
||||
You can also use `pname` and `version` to set the `name`.
|
||||
This is often easier because you can inherit them from `finalAttrs`.
|
||||
- `sourceRoot`: Sets the path where `Package.swift` and `Package.resolved` can be found if they are not in their default, top-level location.
|
||||
- `patches`: Can be used to apply patches to your project before the dependencies are vendored.
|
||||
This is useful to update `Package.swift` or `Package.resolved`.
|
||||
- `postPatch`: Can be used to perform extra steps after patching.
|
||||
You can copy a custom `Package.resolved` in `postPatch`.
|
||||
|
||||
### Packaging with `swiftpm2nix` {#ssec-swift-packaging-with-swiftpm2nix}
|
||||
Nixpkgs includes a small helper `swiftpm2nix` that can fetch your SwiftPM
|
||||
dependencies for you, when you need to write a Nix expression to package your
|
||||
application.
|
||||
|
||||
The first step is to run the generator:
|
||||
|
||||
@@ -104,8 +65,8 @@ swift package resolve
|
||||
swiftpm2nix
|
||||
```
|
||||
|
||||
This produces some files in a directory `nix`, which will be part of your Nix expression.
|
||||
The next step is to write that expression:
|
||||
This produces some files in a directory `nix`, which will be part of your Nix
|
||||
expression. The next step is to write that expression:
|
||||
|
||||
```nix
|
||||
{
|
||||
@@ -165,13 +126,45 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
})
|
||||
```
|
||||
|
||||
#### Patching dependencies {#ssec-swiftpm-patching-dependencies}
|
||||
### Custom build flags {#ssec-swiftpm-custom-build-flags}
|
||||
|
||||
In some cases, it may be necessary to patch a SwiftPM dependency.
|
||||
SwiftPM dependencies are located in `.build/checkouts`, but the `swiftpm2nix` helper provides these as symlinks to read-only `/nix/store` paths.
|
||||
To patch them, we need to make them writable.
|
||||
If you'd like to build a different configuration than `release`:
|
||||
|
||||
A special function `swiftpmMakeMutable` is available to replace the symlink with a writable copy:
|
||||
```nix
|
||||
{ swiftpmBuildConfig = "debug"; }
|
||||
```
|
||||
|
||||
It is also possible to provide additional flags to `swift build`:
|
||||
|
||||
```nix
|
||||
{ swiftpmFlags = [ "--disable-dead-strip" ]; }
|
||||
```
|
||||
|
||||
The default `buildPhase` already passes `-j` for parallel building.
|
||||
|
||||
If these two customization options are insufficient, provide your own
|
||||
`buildPhase` that invokes `swift build`.
|
||||
|
||||
### Running tests {#ssec-swiftpm-running-tests}
|
||||
|
||||
Including `swiftpm` in your `nativeBuildInputs` also provides a default
|
||||
`checkPhase`, but it must be enabled with:
|
||||
|
||||
```nix
|
||||
{ doCheck = true; }
|
||||
```
|
||||
|
||||
This essentially runs: `swift test -c release`
|
||||
|
||||
### Patching dependencies {#ssec-swiftpm-patching-dependencies}
|
||||
|
||||
In some cases, it may be necessary to patch a SwiftPM dependency. SwiftPM
|
||||
dependencies are located in `.build/checkouts`, but the `swiftpm2nix` helper
|
||||
provides these as symlinks to read-only `/nix/store` paths. To patch
|
||||
them, we need to make them writable.
|
||||
|
||||
A special function `swiftpmMakeMutable` is available to replace the symlink
|
||||
with a writable copy:
|
||||
|
||||
```nix
|
||||
{
|
||||
@@ -190,76 +183,21 @@ A special function `swiftpmMakeMutable` is available to replace the symlink with
|
||||
}
|
||||
```
|
||||
|
||||
### Custom build flags {#ssec-swiftpm-custom-build-flags}
|
||||
|
||||
If you'd like to build a different configuration than `release`:
|
||||
|
||||
```nix
|
||||
{ swiftpmBuildConfig = "debug"; }
|
||||
```
|
||||
|
||||
It is also possible to provide additional flags to `swift build`:
|
||||
|
||||
```nix
|
||||
{ swiftpmFlags = [ "--disable-dead-strip" ]; }
|
||||
```
|
||||
|
||||
The default `buildPhase` already passes `-j` for parallel building.
|
||||
|
||||
If these two customization options are insufficient, provide your own `buildPhase` that invokes `swift build`.
|
||||
|
||||
### Running tests {#ssec-swiftpm-running-tests}
|
||||
|
||||
Including `swiftpm` in your `nativeBuildInputs` also provides a default `checkPhase`, but it must be enabled with:
|
||||
|
||||
```nix
|
||||
{ doCheck = true; }
|
||||
```
|
||||
|
||||
This essentially runs: `swift test -c release`
|
||||
|
||||
### Installing packages {#ssec-swiftpm-install-phase}
|
||||
|
||||
SwiftPM provides a default install phase that installs any products specified in your package’s `Package.swift`.
|
||||
If your package does not specify any products, which is not uncommon, you will have to manually install them to `out`.
|
||||
To disable the SwiftPM install phase, include the following in your derivation:
|
||||
|
||||
```nix
|
||||
{ dontUseSwiftpmInstall = true; }
|
||||
```
|
||||
|
||||
## Hooks {#ssec-swift-hooks}
|
||||
|
||||
Swift provides the following hooks to automate builds and unpack dependencies:
|
||||
|
||||
- `swiftpmHook`: Propagated by `swiftpm`.
|
||||
Also propagates `swiftpmUnpackHook`.
|
||||
Provides build, install, and check phases. It also adds any dependencies found in `buildInputs` to `swiftpmFlags`.
|
||||
- `swiftpmUnpackHook`: Sets up `workspace-state.json` and links vendored dependencies to the top-level `Packages` directory in the build environment.
|
||||
|
||||
Swift also provides a hook with the toolchain to replace rpath references to the toolchain with references to the stdlib package.
|
||||
This hook is used automatically by the `swift` package.
|
||||
This avoids pulling the entire toolchain into the closure of your package.
|
||||
|
||||
## Considerations for custom build tools {#ssec-swift-considerations-for-custom-build-tools}
|
||||
|
||||
### Linking the standard library {#ssec-swift-linking-the-standard-library}
|
||||
|
||||
The Swift stdlib is packaged separately as `swiftPackages.stdlib`.
|
||||
The shared and static libraries are installed to `lib`.
|
||||
Most tooling in Nixpkgs should find them automatically when linking.
|
||||
The stdlib provides a hook to change any rpaths pointing to the toolchain to point to the stdlib instead.
|
||||
The `swift` package has a separate `lib` output containing just the Swift
|
||||
standard library, to prevent Swift applications needing a dependency on the
|
||||
full Swift compiler at runtime. Linking with the Nixpkgs Swift toolchain
|
||||
already ensures binaries correctly reference the `lib` output.
|
||||
|
||||
The stdlib modules are installed to `lib/swift/<platform>` in the `dev` output of the stdlib package.
|
||||
These are symlinked together into the `swift` toolchain.
|
||||
If your build tools locate the modules relative to the `swift` compiler executable, it should do the right thing automatically.
|
||||
Sometimes, Swift is used only to compile part of a mixed codebase, and the
|
||||
link step is manual. Custom build tools often locate the standard library
|
||||
relative to the `swift` compiler executable, and while the result will work,
|
||||
when this path ends up in the binary, it will have the Swift compiler as an
|
||||
unintended dependency.
|
||||
|
||||
### Accessing properties of the Swift platform {#ssec-swift-platform-properties}
|
||||
|
||||
The architecture, platform, and triple used by Swift is available as attributes on the build/host/targetPlatform for the `stdenv`.
|
||||
|
||||
- `stdenv.<platform>.swift.platform`: The Swift platform (e.g., `macosx` for macOS, `linux` for Linux, etc).
|
||||
- `stdenv.<platform>.swift.arch`: The Swift architecture (e.g., `arm64` for Darwin or `aarch64` for Linux, `x86_64`, etc).
|
||||
- `stdenv.<platform>.swift.triple`: The triple used by Swift.
|
||||
This is the same as `stdenv.<platform>.config` except on Darwin.
|
||||
On Darwin, it uses the OS name instead of `darwin` and includes the deployment target (e.g., `arm64-apple-macosx14.0`).
|
||||
In this case, you should investigate how your build process discovers the
|
||||
standard library, and override the path. The correct path will be something
|
||||
like: `"${swift.swift.lib}/${swift.swiftModuleSubdir}"`
|
||||
|
||||
@@ -1981,9 +1981,6 @@
|
||||
"sec-darwin-troubleshooting-xcodebuild-absolute-paths": [
|
||||
"index.html#sec-darwin-troubleshooting-xcodebuild-absolute-paths"
|
||||
],
|
||||
"sec-darwin-missing-macros": [
|
||||
"index.html#sec-darwin-missing-macros"
|
||||
],
|
||||
"sec-darwin-troubleshooting-libiconv": [
|
||||
"index.html#sec-darwin-troubleshooting-libiconv"
|
||||
],
|
||||
@@ -4596,26 +4593,14 @@
|
||||
"ssec-swift-packaging-with-swiftpm": [
|
||||
"index.html#ssec-swift-packaging-with-swiftpm"
|
||||
],
|
||||
"ssec-swift-packaging-with-fetch-swiftpm-deps": [
|
||||
"index.html#ssec-swift-packaging-with-fetch-swiftpm-deps"
|
||||
],
|
||||
"ssec-swift-packaging-with-swiftpm2nix": [
|
||||
"index.html#ssec-swift-packaging-with-swiftpm2nix"
|
||||
],
|
||||
"ssec-swiftpm-patching-dependencies": [
|
||||
"index.html#ssec-swiftpm-patching-dependencies"
|
||||
],
|
||||
"ssec-swiftpm-custom-build-flags": [
|
||||
"index.html#ssec-swiftpm-custom-build-flags"
|
||||
],
|
||||
"ssec-swiftpm-running-tests": [
|
||||
"index.html#ssec-swiftpm-running-tests"
|
||||
],
|
||||
"ssec-swiftpm-install-phase": [
|
||||
"index.html#ssec-swiftpm-install-phase"
|
||||
],
|
||||
"ssec-swift-hooks": [
|
||||
"index.html#ssec-swift-hooks"
|
||||
"ssec-swiftpm-patching-dependencies": [
|
||||
"index.html#ssec-swiftpm-patching-dependencies"
|
||||
],
|
||||
"ssec-swift-considerations-for-custom-build-tools": [
|
||||
"index.html#ssec-swift-considerations-for-custom-build-tools"
|
||||
@@ -4623,9 +4608,6 @@
|
||||
"ssec-swift-linking-the-standard-library": [
|
||||
"index.html#ssec-swift-linking-the-standard-library"
|
||||
],
|
||||
"ssec-swift-platform-properties": [
|
||||
"index.html#ssec-swift-platform-properties"
|
||||
],
|
||||
"sec-language-tcl": [
|
||||
"index.html#sec-language-tcl"
|
||||
],
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
+nixpkgs.url = "https://channels.nixos.org/nixos-26.05/nixexprs.tar.zst";
|
||||
```
|
||||
|
||||
- GCC has been updated from GCC 15 to GCC 16. This introduces some backwards-incompatible changes. Refer to the [upstream porting guide](https://gcc.gnu.org/gcc-16/porting_to.html) for details.
|
||||
|
||||
- Emacs has been updated to 31.
|
||||
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.
|
||||
@@ -38,10 +36,6 @@
|
||||
- `zabbix.<package>` now defaults to version 7.4. If you want to keep using Zabbix 6.0, use `pkgs.zabbix60.<package>`.
|
||||
Note that Zabbix 6.0 is in limited support, and will be deprecated on February 28, 2027. Consider upgrading.
|
||||
|
||||
- `zabbix-agent2-plugin-postgresql` is now moved to `zabbix{60,70,74}.plugins.postgresql`.
|
||||
|
||||
- Official Zabbix plugins (ember-plus, mongodb, and mssql) have been added under `zabbix{60,70,74}.plugins.<plugin>`.
|
||||
|
||||
- `perlPackages.NetOAuth` has been updated from 0.28 to 0.33.
|
||||
Callers that verify messages must now set `allowed_signature_methods` per message or configure `@Net::OAuth::ALLOWED_SIGNATURE_METHODS`; `verify` otherwise throws an exception.
|
||||
See the [upstream changelog](https://metacpan.org/dist/Net-OAuth/changes) for details.
|
||||
@@ -123,9 +117,6 @@
|
||||
|
||||
- `himalaya` has been updated from `v1.2.0` to `v2.0.0`, which introduces breaking changes. See the [release notes](https://github.com/pimalaya/himalaya/releases/tag/v2.0.0) and the [migration guide](https://github.com/pimalaya/himalaya/blob/master/MIGRATION.md).
|
||||
|
||||
- `proton-cli` has been updated from `2.2.3` to `3.4.0`, and installs its command as `proton`, with `proton-cli` kept beside it as a symlink.
|
||||
`3.0.0` reworked the command line - `--output` is now the response format, secrets are no longer accepted as flag values, and several subcommands moved - so scripts need a review against the [upstream changelog](https://github.com/roman-16/proton-cli/blob/main/CHANGELOG.md).
|
||||
|
||||
- `tengine` has been removed as it has seen seriously delayed responses to security vulnerabilities.
|
||||
|
||||
- `jellyfin` has been upgraded to major version 12, which contains breaking changes. See the [upstream blog post](https://jellyfin.org/posts/jellyfin-release-12.0) for more information on how to safely upgrade.
|
||||
@@ -180,8 +171,6 @@
|
||||
- `replaceVarsWith` now enables `strictDeps` and `__structuredAttrs` and passing these attributes to the function is no longer allowed.
|
||||
By extension, `replaceVars` now also enables `strictDeps` and `__structuredAttrs`.
|
||||
|
||||
- `nginx` / `nginxStable` is now built without the `rtmp` nginx module by default. You can enable it again using `nginx.override { modules = [ pkgs.nginxModules.rtmp ]; }`
|
||||
|
||||
- `buildFHSEnvChroot` has been removed after deprecation in 23.05.
|
||||
|
||||
- `leafnode` has been removed, as it was an unmaintained alpha-release of leafnode 2 and has a dependency on the EOL PRCE-library. Consider using `leafnode1` instead, which is still maintained.
|
||||
@@ -204,11 +193,6 @@
|
||||
|
||||
- `librest` providing 0.7 ABI was removed. `librest_1_0` providing 1.0 ABI was renamed to `librest` and `librest_1_0` was kept as an alias.
|
||||
|
||||
- `secretspec-ffi` has been renamed to `libsecretspec` and updated to 0.21.0.
|
||||
The old package attribute remains an alias, but native consumers must rebuild
|
||||
against the new `libsecretspec` library and pkg-config module. The separate
|
||||
`libsecretspec-resolver` package provides a C client for `secretspec serve`.
|
||||
|
||||
- `luaPackages.lrexlib-pcre` has been removed as part of the process to fully migrate from the end-of-life PRCE library to PCRE2. `luaPackages.lrexlib-pcre2` and multiple other versions of lrexlib can be used instead.
|
||||
|
||||
- `hostapd` was upgraded to version 2.12+, which moves move supported, basic, and beacon transmission rate configuration to be at BSS level instead of per-radio for all BSSs. Refer to the [upstream example config](https://git.w1.fi/cgit/hostap/plain/hostapd/hostapd.conf) for details.
|
||||
@@ -241,16 +225,6 @@
|
||||
- `nim-2_0` & `nim-2_2` and respective aliases have been removed; please migrate to `nim` or `nim-unwrapped` (nim 2.2.10).
|
||||
- `domoticz` has been updated from `2024.7` to `2026.x`, breaking third party applications and scripts using the old RType calls. Review the [release notes](https://github.com/domoticz/domoticz/blob/2026.2/History.txt#L398) for more information.
|
||||
|
||||
- `swift` is no longer wrapped.
|
||||
The `NIX_SWIFTFLAGS_COMPILE` variable is no longer supported.
|
||||
If you need to pass custom flags to the Swift compiler, you must add them via your package’s build system.
|
||||
The default target version used by `swiftc` on Darwin is the operating system major version.
|
||||
This value may be overridden by the build system (e.g., SwiftPM defaults to 10.13 instead).
|
||||
See the Swift documentation in Nixpkgs for details.
|
||||
|
||||
- `swiftpm` is no longer wrapped to include Git to fetch dependencies.
|
||||
Users with Git-based dependencies will need to add `git` to their dev shells or include it in their environment if they weren’t already.
|
||||
|
||||
- `vimacs` has been removed, as it has not been maintained in 10 years and was built for an old version of vim (6.0).
|
||||
|
||||
- The deprecated `appimageTools.extractType1`, `appimageTools.extractType2`, and `appimageTools.wrapType1` aliases now emit warnings. Use `appimageTools.extract` and `appimageTools.wrapType2` instead.
|
||||
@@ -287,27 +261,18 @@
|
||||
|
||||
- `nextpnr` introduced support for the nexus and gatemate architectures. Building support for each individual architecture can be configured using the package parameters.
|
||||
|
||||
- `mastodon` has been updated to 4.7. The [4.7.0 release notes](https://github.com/mastodon/mastodon/releases/tag/v4.7.0) mention some unusually long running migrations.
|
||||
|
||||
- Emacs loads the `early-default` library after `early-init.el`.
|
||||
Users can add `early-init.el` via `emacs.pkgs.withPackages`
|
||||
by packaging `early-init.el` into a library named `early-default`.
|
||||
To prevent loading the `early-default` library,
|
||||
set `inhibit-early-default-init` in `early-init.el`.
|
||||
|
||||
- Ceph has a vulnerability in old generated CephX keys.
|
||||
The project recommends to rotate old keys.
|
||||
This is a manual process, see https://ceph.io/en/news/blog/2026/v20-2-4-v19-2-6-combo-released
|
||||
|
||||
- `services.ceph` enabled the generation of Ceph log files at `/var/log/ceph/`.
|
||||
They were missing before because Ceph omitted logs when this directory was missing.
|
||||
Ceph logs can grow large, so you may want to configure rotation of these logs.
|
||||
|
||||
- Firefox wrapper now accepts an optional `appDataDir` argument, which sets `MOZ_APP_DATA` to relocate Firefox application data. This is especially useful on macOS 27 and later, where wrapped Firefox applications may be denied access to profiles in traditional application data directory.
|
||||
|
||||
- Swift has been upgraded to Swift 6.2.4 from Swift 5.10.1.
|
||||
The Swift packaging has been rewritten.
|
||||
|
||||
## Nixpkgs Library {#sec-nixpkgs-release-26.11-lib}
|
||||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
@@ -318,9 +283,6 @@
|
||||
|
||||
- `typescript` 7.0.2 now uses the Golang implementation. The [announcement document](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/) has information on what was changed.
|
||||
|
||||
- `macaulay2` no longer installs Emacs files.
|
||||
Users can now get the files from an Emacs lisp package, like `emacs.pkgs.withPackages (epkgs: [ epkgs.m2 ])`.
|
||||
|
||||
- `navidrome`'s plugin infrastructure has significantly changed. `buildNavidromePlugin` is renamed to `buildNavidromeGoPlugin` to allow for other language types. Plugins must now be sourced from `pkgsCross.wasi32.navidromePlugins.<name>`.
|
||||
|
||||
- `navidromePlugins.apple-music` now uses a `bundleName` attribute which sets the plugin's name to match the plugin's documentation for easier use. You will need to update your Agent from `apple-music-plugin` to `apple-music` as noted in [their docs](https://github.com/navidrome/apple-music-plugin#installation).
|
||||
|
||||
@@ -121,8 +121,7 @@ Generally, only the last SDK release for a major version is packaged.
|
||||
|---------------|-------------|------------------------------|
|
||||
| 15.0–15.4 | 14.4 | `apple-sdk_14` / `apple-sdk` |
|
||||
| 16.0 | 15.0 | `apple-sdk_15` |
|
||||
| 26.0 | 26.0 | `apple-sdk_26` |
|
||||
| 27.0+ | 27.0+ | `apple-sdk_27`, etc |
|
||||
| 26.0+ | 26.0+ | `apple-sdk_26`, etc |
|
||||
|
||||
|
||||
#### Darwin Default SDK versions {#sec-darwin-troubleshooting-darwin-defaults}
|
||||
@@ -193,13 +192,6 @@ stdenv.mkDerivation {
|
||||
}
|
||||
```
|
||||
|
||||
### Macro library not available {#sec-darwin-missing-macros}
|
||||
|
||||
Some frameworks provide macros that are only shipped with Xcode.
|
||||
For example, the AppleIntelligence framework, Swift Data, and SwiftUI (as of the 27.0 SDK).
|
||||
A non-free package making these available will be added at a later date.
|
||||
Until then, they are unfortunately not available in Nixpkgs.
|
||||
|
||||
#### How to use libiconv on Darwin {#sec-darwin-troubleshooting-libiconv}
|
||||
|
||||
The libiconv package is included in the SDK by default along with libresolv and libsbuf.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Style guide
|
||||
# Styleguide
|
||||
|
||||
Use this page as a reference and style guide for our internal and external documentation.
|
||||
|
||||
@@ -22,7 +22,7 @@ Write for someone who knows a great deal — up to but not including this projec
|
||||
|
||||
If specific knowledge is required, mention it at the start of the page.
|
||||
|
||||
### Show, don't tell
|
||||
### Show, Don't Tell
|
||||
|
||||
The fastest path to understanding is a working example.
|
||||
People learn by doing, not by reading about doing.
|
||||
@@ -34,7 +34,7 @@ People learn by doing, not by reading about doing.
|
||||
- Cover edge cases or variations
|
||||
- Link to further information instead of including it
|
||||
|
||||
### Grammar and style
|
||||
### Grammar and Style
|
||||
|
||||
**Sentence structure:**
|
||||
|
||||
@@ -54,7 +54,7 @@ Users care about *detecting hardware*, not *the tool that does it*.
|
||||
|
||||
> This command detects your hardware and saves the configuration.
|
||||
|
||||
### Content organization
|
||||
### Content Organization
|
||||
|
||||
Lead with value. State what the reader will accomplish before explaining how.
|
||||
|
||||
@@ -83,23 +83,21 @@ Use **progressive disclosure**. Introduce concepts only when needed.
|
||||
3. Explain concepts if needed
|
||||
4. Provide advanced options separately or link to the reference
|
||||
|
||||
### No meta-commentary
|
||||
### No Meta-commentary
|
||||
|
||||
Don't describe what the documentation does. Just do it.
|
||||
|
||||
**Don't:**
|
||||
|
||||
> This section explains how to configure networking.
|
||||
|
||||
> The following guide walks you through setting up a web server.
|
||||
|
||||
**Do:**
|
||||
|
||||
> Configure networking by setting:
|
||||
|
||||
> Set up a web server:
|
||||
|
||||
### Code examples
|
||||
### Code Examples
|
||||
|
||||
**Keep examples focused:**
|
||||
|
||||
@@ -132,7 +130,7 @@ Paste code examples directly and without further alteration.
|
||||
}
|
||||
```
|
||||
|
||||
### Lead with practical examples
|
||||
### Lead with Practical Examples
|
||||
|
||||
Don't front-load theory. Readers want to accomplish something first, then understand why it works.
|
||||
|
||||
@@ -168,7 +166,7 @@ Users learn the NixOS module system by seeing patterns first.
|
||||
- Link deeper concepts instead of inlining them
|
||||
- Link to `nix.dev` for optional learning
|
||||
|
||||
### General rules
|
||||
### General Rules
|
||||
|
||||
- Abbreviate keys like `ssh-ed25519 AAAAC3NzaC…`
|
||||
- Abbreviate IP addresses like `192.168.XXX.XXX`
|
||||
@@ -202,7 +200,7 @@ Use sentence case. A reader scanning only headings should understand the page.
|
||||
> Configure networking
|
||||
> Add a user to the system
|
||||
|
||||
### Imperative mood, voice, and person
|
||||
### Imperative Mood, Voice, and Person
|
||||
|
||||
Use imperative mood for instructions. Address the reader as "you", not "the user". Use active voice; in other words, make the subject do the action.
|
||||
|
||||
@@ -232,7 +230,7 @@ Use present tense for descriptions. Future tense makes documentation feel tentat
|
||||
> This creates a new folder.
|
||||
> Running this command installs the package.
|
||||
|
||||
### Be confident
|
||||
### Be Confident
|
||||
|
||||
State facts. Don't hedge with "should," "might," "typically," or "usually" unless the behavior genuinely varies.
|
||||
|
||||
@@ -246,7 +244,7 @@ State facts. Don't hedge with "should," "might," "typically," or "usually" unles
|
||||
> This creates the configuration file.
|
||||
> The service starts automatically.
|
||||
|
||||
### Avoid nominalizations
|
||||
### Avoid Nominalizations
|
||||
|
||||
A nominalization is a verb turned into a noun, often by adding *-tion*, *-meant*, or *-ance* (e.g. "explanation", "selection"). The fix: find the hidden verb and use it directly.
|
||||
|
||||
@@ -260,7 +258,7 @@ A nominalization is a verb turned into a noun, often by adding *-tion*, *-meant*
|
||||
> Select from the list.
|
||||
> Explain the error.
|
||||
|
||||
### Plain words
|
||||
### Plain Words
|
||||
|
||||
Technical precision for technical terms; plain language for everything else.
|
||||
|
||||
@@ -272,7 +270,7 @@ Technical precision for technical terms; plain language for everything else.
|
||||
- "set up" not "establish"
|
||||
- "find out" not "ascertain"
|
||||
|
||||
### Filler words and weak phrases
|
||||
### Filler Words and Weak Phrases
|
||||
|
||||
Cut words and phrases that add length without meaning.
|
||||
|
||||
@@ -298,7 +296,7 @@ Delete on sight:
|
||||
|
||||
Every word must earn its place.
|
||||
|
||||
### Writing procedures
|
||||
### Writing Procedures
|
||||
|
||||
One instruction per sentence. Don't pack multiple actions into one sentence.
|
||||
|
||||
@@ -322,7 +320,7 @@ Don't bury the negative. Key limitations should be prominent, not a footnote aft
|
||||
|
||||
> This service does not support multiple instances.
|
||||
|
||||
### Consistent terminology
|
||||
### Consistent Terminology
|
||||
|
||||
Pick a term and stick to it. Don't swap synonyms to avoid repetition. In technical documentation, repetition is clarity.
|
||||
|
||||
@@ -361,7 +359,7 @@ Only link when the destination is directly relevant, not for generic background
|
||||
|
||||
> See `[database schema](url)` for the full table structure.
|
||||
|
||||
### UI language
|
||||
### UI Language
|
||||
|
||||
Match UI element names exactly: wording, casing, and spacing (even if a label seems oddly worded).
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import <nixpkgs> {
|
||||
}
|
||||
```
|
||||
|
||||
Note that we set `linker` to `lld`. This is because LLVM has its own linker, called "lld". By setting it, we use Clang and lld within this new instance of Nixpkgs. There is a shorthand method for building everything with LLVM: `pkgsLLVM`. This is easier to use with `nix-build` (or `nix build`):
|
||||
Note that we set `linker` to `lld`. This is because LLVM has its own linker, called "lld". By setting it, we utilize Clang and lld within this new instance of Nixpkgs. There is a shorthand method for building everything with LLVM: `pkgsLLVM`. This is easier to use with `nix-build` (or `nix build`):
|
||||
|
||||
```bash
|
||||
nix-build -A pkgsLLVM.hello
|
||||
|
||||
@@ -105,48 +105,27 @@ There are several ways to tweak how Nix handles a package which has been marked
|
||||
$ export NIXPKGS_ALLOW_UNFREE=1
|
||||
```
|
||||
|
||||
- To allow specific unfree packages, add their names to your Nixpkgs configuration file:
|
||||
- It is possible to permanently allow individual unfree packages, while still blocking unfree packages by default using the `allowUnfreePredicate` configuration option in the user configuration file.
|
||||
|
||||
This option is a function which accepts a package as a parameter, and returns a boolean. The following example configuration accepts a package and always returns false:
|
||||
|
||||
```nix
|
||||
{ allowUnfreePredicate = (pkg: false); }
|
||||
```
|
||||
|
||||
For a more useful example, try the following. This configuration only allows unfree packages named roon-server and Visual Studio Code:
|
||||
|
||||
```nix
|
||||
{
|
||||
allowUnfreePackages = [
|
||||
"fence"
|
||||
"roon-server"
|
||||
"vscode"
|
||||
];
|
||||
allowUnfreePredicate =
|
||||
pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"roon-server"
|
||||
"vscode"
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
`allowUnfreePackages` permits the listed unfree packages.
|
||||
|
||||
In NixOS modules, lists set through `nixpkgs.config.allowUnfreePackages` merge additively across modules. This allows you to declare your unfree exceptions in the same modules that triggered them.
|
||||
|
||||
To allow unfree packages programmatically:
|
||||
|
||||
```nix
|
||||
{ lib, ... }:
|
||||
{
|
||||
allowUnfreePredicate = pkg: lib.hasPrefix "roon" (lib.getName pkg);
|
||||
}
|
||||
```
|
||||
|
||||
This permits packages such as `roon-bridge` and `roon-server`.
|
||||
|
||||
To combine the list and predicate, set both options:
|
||||
|
||||
```nix
|
||||
{ lib, ... }:
|
||||
{
|
||||
allowUnfreePackages = [
|
||||
"fence"
|
||||
"vscode"
|
||||
];
|
||||
allowUnfreePredicate = pkg: lib.hasPrefix "roon" (lib.getName pkg);
|
||||
}
|
||||
```
|
||||
|
||||
This permits unfree packages that match either option.
|
||||
|
||||
- It is also possible to allow and block licenses that are specifically acceptable or not acceptable, using `allowlistedLicenses` and `blocklistedLicenses`, respectively.
|
||||
|
||||
The following example configuration allowlists the licenses `amd` and `wtfpl`:
|
||||
|
||||
@@ -699,7 +699,20 @@ rec {
|
||||
*/
|
||||
filterAttrsRecursive =
|
||||
pred: set:
|
||||
mapAttrs (_: v: if isAttrs v then filterAttrsRecursive pred v else v) (filterAttrs pred set);
|
||||
listToAttrs (
|
||||
concatMap (
|
||||
name:
|
||||
let
|
||||
v = set.${name};
|
||||
in
|
||||
if pred name v then
|
||||
[
|
||||
(nameValuePair name (if isAttrs v then filterAttrsRecursive pred v else v))
|
||||
]
|
||||
else
|
||||
[ ]
|
||||
) (attrNames set)
|
||||
);
|
||||
|
||||
/**
|
||||
Like [`lib.lists.foldl'`](#function-library-lib.lists.foldl-prime) but for attribute sets.
|
||||
@@ -1515,7 +1528,12 @@ rec {
|
||||
*/
|
||||
zipAttrsWithNames =
|
||||
names: f: sets:
|
||||
genAttrs names (name: f name (catAttrs name sets));
|
||||
listToAttrs (
|
||||
map (name: {
|
||||
inherit name;
|
||||
value = f name (catAttrs name sets);
|
||||
}) names
|
||||
);
|
||||
|
||||
/**
|
||||
Merge sets of attributes and use the function `f` to merge attribute values.
|
||||
|
||||
@@ -341,8 +341,9 @@ rec {
|
||||
f: g: final: prev:
|
||||
let
|
||||
fApplied = f final prev;
|
||||
prev' = prev // fApplied;
|
||||
in
|
||||
fApplied // g final (prev // fApplied);
|
||||
fApplied // g final prev';
|
||||
|
||||
/**
|
||||
Composes a list of [`overlays`](#chap-overlays) and returns a single overlay function that combines them.
|
||||
@@ -408,7 +409,7 @@ rec {
|
||||
```
|
||||
:::
|
||||
*/
|
||||
composeManyExtensions = lib.foldr composeExtensions (final: prev: { });
|
||||
composeManyExtensions = lib.foldr (x: y: composeExtensions x y) (final: prev: { });
|
||||
|
||||
/**
|
||||
Create an overridable, recursive attribute set. For example:
|
||||
@@ -509,16 +510,13 @@ rec {
|
||||
:::
|
||||
*/
|
||||
toExtension =
|
||||
let
|
||||
inherit (lib) isFunction;
|
||||
in
|
||||
f:
|
||||
if isFunction f then
|
||||
if lib.isFunction f then
|
||||
final: prev:
|
||||
let
|
||||
fPrev = f prev;
|
||||
in
|
||||
if isFunction fPrev then
|
||||
if lib.isFunction fPrev then
|
||||
# f is (final: prev: { ... })
|
||||
f final prev
|
||||
else
|
||||
|
||||
@@ -703,6 +703,11 @@ lib.mapAttrs mkLicense (
|
||||
url = "https://www.schristiancollins.com/generaluser.php"; # license included in sources
|
||||
};
|
||||
|
||||
gfl = {
|
||||
fullName = "GUST Font License";
|
||||
url = "https://www.gust.org.pl/projects/e-foundry/licenses/GUST-FONT-LICENSE.txt";
|
||||
};
|
||||
|
||||
gfsl = {
|
||||
fullName = "GUST Font Source License";
|
||||
url = "https://www.gust.org.pl/projects/e-foundry/licenses/GUST-FONT-SOURCE-LICENSE.txt";
|
||||
|
||||
@@ -1595,76 +1595,17 @@ let
|
||||
*/
|
||||
mkDefinition = args@{ file, value, ... }: args // { _type = "definition"; };
|
||||
|
||||
/**
|
||||
Labels a definition with a priority.
|
||||
See the documentation of `filterOverrides` for the interpretation of the priority value.
|
||||
Nesting this function usually leads to an invalid definition.
|
||||
`mkDefault`, `mkOptionDefault`, and `mkForce` partially apply `mkOverride` with common priorities used in the NixOS module system.
|
||||
|
||||
# Inputs
|
||||
|
||||
`priority`
|
||||
|
||||
: A numeric value representing the precedence.
|
||||
See the documentation of `filterOverrides` for the interpretation of this value.
|
||||
|
||||
`content`
|
||||
|
||||
: The definition to be labeled with a given priority.
|
||||
|
||||
# Examples
|
||||
:::{.example}
|
||||
## `lib.modules.mkOverride` usage example
|
||||
|
||||
```nix
|
||||
mkOverride 1000 "hello, world!"
|
||||
=> { _type = "override"; content = "hello, world!"; priority = 1000; }
|
||||
```
|
||||
|
||||
```nix
|
||||
(lib.evalModules {
|
||||
modules = [
|
||||
{ options.foo = lib.mkOption { }; }
|
||||
{ config.foo = lib.mkOverride 20 1; }
|
||||
{ config.foo = lib.mkOverride 10 2; }
|
||||
];
|
||||
}).config
|
||||
=> { foo = 2; }
|
||||
```
|
||||
:::
|
||||
*/
|
||||
mkOverride = priority: content: {
|
||||
_type = "override";
|
||||
inherit priority content;
|
||||
};
|
||||
|
||||
/**
|
||||
Labels a definition with the priority of option declaration defaults.
|
||||
*/
|
||||
mkOptionDefault = mkOverride 1500;
|
||||
|
||||
/**
|
||||
Labels a definition with the priority used in config sections of non-user modules to set a default.
|
||||
*/
|
||||
mkDefault = mkOverride 1000;
|
||||
|
||||
mkOptionDefault = mkOverride 1500; # priority of option defaults
|
||||
mkDefault = mkOverride 1000; # used in config sections of non-user modules to set a default
|
||||
defaultOverridePriority = 100;
|
||||
|
||||
/**
|
||||
Labels a definition with the priority used in image media profiles.
|
||||
Image media profiles can be derived by inclusion into host config, hence needing to override host config, but do allow users to `mkForce`.
|
||||
*/
|
||||
mkImageMediaOverride = mkOverride 60;
|
||||
|
||||
/**
|
||||
Labels a definition with a high priority (low value).
|
||||
*/
|
||||
mkImageMediaOverride = mkOverride 60; # image media profiles can be derived by inclusion into host config, hence needing to override host config, but do allow user to mkForce
|
||||
mkForce = mkOverride 50;
|
||||
|
||||
/**
|
||||
Labels a definition with used by {command}`nixos-rebuild build-vm`.
|
||||
*/
|
||||
mkVMOverride = mkOverride 10;
|
||||
mkVMOverride = mkOverride 10; # used by ‘nixos-rebuild build-vm’
|
||||
|
||||
mkFixStrictness = warn "lib.mkFixStrictness has no effect and will be removed. It returns its argument unmodified, so you can just remove any calls." id;
|
||||
|
||||
|
||||
@@ -719,26 +719,6 @@ let
|
||||
else
|
||||
null;
|
||||
};
|
||||
swift = {
|
||||
arch = final.uname.processor;
|
||||
platform =
|
||||
if final.isMacOS then
|
||||
"macosx"
|
||||
else if final.isiOS then
|
||||
"iphoneos"
|
||||
else if final.isLinux then
|
||||
"linux"
|
||||
else if final.isWindows then
|
||||
"windows"
|
||||
else
|
||||
null;
|
||||
triple =
|
||||
if final.isDarwin then
|
||||
# FIXME: Can this be done a better way?
|
||||
"${final.swift.arch}-${final.parsed.vendor.name}-${final.swift.platform}${final.darwinMinVersion}"
|
||||
else
|
||||
final.config;
|
||||
};
|
||||
};
|
||||
in
|
||||
# Platforms elaborated by pre-26.11 Nixpkgs will include the `linux-kernel` attr,
|
||||
|
||||
@@ -445,7 +445,8 @@
|
||||
"id": 4020424,
|
||||
"maintainers": {
|
||||
"Mic92": 96200,
|
||||
"kalbasit": 87115
|
||||
"kalbasit": 87115,
|
||||
"katexochen": 49727155
|
||||
},
|
||||
"members": {
|
||||
"mfrw": 4929861,
|
||||
|
||||
@@ -251,7 +251,7 @@
|
||||
};
|
||||
_365tuwe = {
|
||||
name = "Uwe Schlifkowitz";
|
||||
email = "uwe.schlifkowitz@secunet.com";
|
||||
email = "supertuwe@gmail.com";
|
||||
github = "365tuwe";
|
||||
githubId = 10263091;
|
||||
};
|
||||
@@ -460,7 +460,6 @@
|
||||
name = "aaravrav";
|
||||
github = "aaravrav";
|
||||
githubId = 37036762;
|
||||
matrix = "@hepara:matrix.org";
|
||||
};
|
||||
aarnphm = {
|
||||
email = "contact@aarnphm.xyz";
|
||||
@@ -5091,12 +5090,6 @@
|
||||
githubId = 1689801;
|
||||
name = "Mikhail Chekan";
|
||||
};
|
||||
chemonke = {
|
||||
email = "nixpkgs@chemonke.ch";
|
||||
github = "chemonke";
|
||||
githubId = 183837749;
|
||||
name = "Curdin Bosshart";
|
||||
};
|
||||
chen = {
|
||||
email = "i@cuichen.cc";
|
||||
github = "cu1ch3n";
|
||||
@@ -9520,12 +9513,6 @@
|
||||
github = "fkautz";
|
||||
githubId = 135706;
|
||||
};
|
||||
fkokosinski = {
|
||||
name = "Filip Kokosiński";
|
||||
email = "filip@kokosinski.me";
|
||||
github = "fkokosinski";
|
||||
githubId = 19800410;
|
||||
};
|
||||
fkomarek = {
|
||||
name = "Filip Komárek";
|
||||
github = "filip2cz";
|
||||
@@ -10818,12 +10805,6 @@
|
||||
githubId = 273582;
|
||||
name = "greg";
|
||||
};
|
||||
gregl83 = {
|
||||
email = "general+nixpkgs@gregorylanglais.com";
|
||||
github = "gregl83";
|
||||
githubId = 1258023;
|
||||
name = "gregory langlais";
|
||||
};
|
||||
gregshuflin = {
|
||||
email = "greg@everdayimshuflin.com";
|
||||
github = "neunenak";
|
||||
@@ -11532,13 +11513,6 @@
|
||||
githubId = 58676303;
|
||||
name = "hhydraa";
|
||||
};
|
||||
hideyosh1 = {
|
||||
email = "penelope.zhong@proton.me";
|
||||
keys = [ { fingerprint = "01E9 0D3E 815F 84CA 1003 E7D7 2F75 2D18 C2C1 7AF8"; } ];
|
||||
name = "Penelope Zhong";
|
||||
github = "hideyosh1";
|
||||
githubId = 64223175;
|
||||
};
|
||||
higebu = {
|
||||
name = "Yuya Kusakabe";
|
||||
email = "yuya.kusakabe@gmail.com";
|
||||
@@ -12169,12 +12143,6 @@
|
||||
githubId = 71074737;
|
||||
name = "Simon Wick";
|
||||
};
|
||||
ilovelinux = {
|
||||
email = "nix+nixpkgs@ilovelinux.dev";
|
||||
github = "ilovelinux";
|
||||
githubId = 9268789;
|
||||
name = "Antonio Spadaro";
|
||||
};
|
||||
ilya-epifanov = {
|
||||
email = "mail@ilya.network";
|
||||
github = "ilya-epifanov";
|
||||
@@ -12669,12 +12637,6 @@
|
||||
github = "j0hax";
|
||||
githubId = 3802620;
|
||||
};
|
||||
j0schu = {
|
||||
name = "Jonas";
|
||||
email = "Joschu2015@t-online.de";
|
||||
github = "J0schu";
|
||||
githubId = 56407950;
|
||||
};
|
||||
j0xaf = {
|
||||
email = "j0xaf@j0xaf.de";
|
||||
name = "Jörn Gersdorf";
|
||||
@@ -13191,13 +13153,6 @@
|
||||
githubId = 2377;
|
||||
name = "Jonathan del Strother";
|
||||
};
|
||||
jderrac = {
|
||||
email = "jeremy@derrac.fr";
|
||||
github = "jderrac";
|
||||
githubId = 1788613;
|
||||
name = "Jérémy Derrac";
|
||||
keys = [ { fingerprint = "7B18 DA58 169F AEB8 6826 D1D6 BED4 91C6 40AB 31DD"; } ];
|
||||
};
|
||||
jdev082 = {
|
||||
email = "jdev0894@gmail.com";
|
||||
github = "jdev082";
|
||||
@@ -13668,12 +13623,6 @@
|
||||
githubId = 474643;
|
||||
name = "José Miguel Martínez Carrasco";
|
||||
};
|
||||
jm5905938 = {
|
||||
email = "jm5905938@gmail.com";
|
||||
github = "jm5905938";
|
||||
githubId = 187073435;
|
||||
name = "Aveline Noir";
|
||||
};
|
||||
jmagnusj = {
|
||||
email = "jmagnusj@gmail.com";
|
||||
github = "magnusjonsson";
|
||||
@@ -14138,12 +14087,6 @@
|
||||
github = "jooooscha";
|
||||
githubId = 57965027;
|
||||
};
|
||||
joseg313 = {
|
||||
name = "Jose Garcia";
|
||||
email = "501jag3@gmail.com";
|
||||
github = "joseg313";
|
||||
githubId = 215610619;
|
||||
};
|
||||
josephschmitt = {
|
||||
name = "Joseph Schmitt";
|
||||
email = "dev@joe.sh";
|
||||
@@ -15364,13 +15307,6 @@
|
||||
githubId = 231780064;
|
||||
name = "Klea";
|
||||
};
|
||||
kleiner3 = {
|
||||
name = "kleiner3";
|
||||
email = "nixos@dasriley.de";
|
||||
github = "kleiner3";
|
||||
githubId = 49880817;
|
||||
matrix = "@riley:catgirl.industries";
|
||||
};
|
||||
klntsky = {
|
||||
email = "klntsky@gmail.com";
|
||||
name = "Vladimir Kalnitsky";
|
||||
@@ -17645,12 +17581,6 @@
|
||||
githubId = 85435692;
|
||||
name = "Maxwell Berg";
|
||||
};
|
||||
Mahdi-zarei = {
|
||||
email = "mahdi.zrei@gmail.com";
|
||||
github = "Mahdi-zarei";
|
||||
githubId = 80265960;
|
||||
name = "Mahdi";
|
||||
};
|
||||
mahe = {
|
||||
email = "matthias.mh.herrmann@gmail.com";
|
||||
github = "2chilled";
|
||||
@@ -18798,13 +18728,6 @@
|
||||
github = "mfairley";
|
||||
githubId = 4374785;
|
||||
};
|
||||
mfocko = {
|
||||
name = "Matej Focko";
|
||||
github = "mfocko";
|
||||
githubId = 8149784;
|
||||
email = "me@mfocko.xyz";
|
||||
matrix = "@mfocko:fedora.im";
|
||||
};
|
||||
mfossen = {
|
||||
email = "msfossen@gmail.com";
|
||||
github = "mfossen";
|
||||
@@ -20171,12 +20094,6 @@
|
||||
githubId = 52401682;
|
||||
name = "myul";
|
||||
};
|
||||
Myxogastria0808 = {
|
||||
email = "r.rstudio.c@gmail.com";
|
||||
github = "Myxogastria0808";
|
||||
githubId = 78744619;
|
||||
name = "Yuki Osada";
|
||||
};
|
||||
myypo = {
|
||||
email = "nikirsmcgl@gmail.com";
|
||||
github = "myypo";
|
||||
@@ -26049,12 +25966,6 @@
|
||||
githubId = 11632726;
|
||||
name = "Arijit Basu";
|
||||
};
|
||||
saylesss88 = {
|
||||
email = "saylesss87@proton.me";
|
||||
github = "saylesss88";
|
||||
githubId = 209646716;
|
||||
name = "T. Sawyer";
|
||||
};
|
||||
sb0 = {
|
||||
email = "sb@m-labs.hk";
|
||||
github = "sbourdeauducq";
|
||||
@@ -26770,11 +26681,6 @@
|
||||
github = "shimunn";
|
||||
githubId = 41011289;
|
||||
};
|
||||
shinbunbun = {
|
||||
name = "shinbunbun";
|
||||
github = "shinbunbun";
|
||||
githubId = 34409044;
|
||||
};
|
||||
shiphan = {
|
||||
email = "timlin940511@gmail.com";
|
||||
name = "Shiphan";
|
||||
@@ -29072,13 +28978,6 @@
|
||||
github = "thelissimus";
|
||||
githubId = 70096720;
|
||||
};
|
||||
thelolcoder2007 = {
|
||||
name = "thelolcoder2007";
|
||||
github = "thelolcoder2007";
|
||||
githubId = 52106896;
|
||||
matrix = "@erents:dapperepoging.nl";
|
||||
keys = [ { fingerprint = "E374 815F C754 462B 1C34 3562 FDC3 99DE 8F7E 200B"; } ];
|
||||
};
|
||||
themadbit = {
|
||||
name = "Mark Tanui";
|
||||
email = "marktanui75@gmail.com";
|
||||
@@ -31672,10 +31571,10 @@
|
||||
];
|
||||
};
|
||||
wrench-exile-legacy = {
|
||||
email = "hello@wrenchd.dev";
|
||||
email = "user@wrench-exile-legacy.site";
|
||||
github = "wrench-exile-legacy";
|
||||
githubId = 280737824;
|
||||
name = "wrenchd";
|
||||
name = "wrench";
|
||||
};
|
||||
wrmilling = {
|
||||
name = "Winston R. Milling";
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash
|
||||
#!nix-shell -p jq git
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# Usage: eval-pkg-sets.sh [extra flags for nix-* commands ...]
|
||||
#
|
||||
# Must be executed in a git checkout of Nixpkgs.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NIXPKGS="$(git rev-parse --show-toplevel)"
|
||||
PKGSETS="$(nix-env --readonly-mode --json --drv-path -f "$NIXPKGS" -qaP -A haskell.compiler "$@" \
|
||||
| jq -r 'to_entries | unique_by(.value.drvPath) .[] .key | sub("^haskell.compiler";"haskell.packages")')"
|
||||
|
||||
trap 'exit 1' SIGINT SIGTERM
|
||||
|
||||
set +e
|
||||
|
||||
badsets=""
|
||||
for set in $PKGSETS; do
|
||||
# Confirm an equivalent package set to haskell.compiler.$entry exists and is usable
|
||||
if ! nix-instantiate --readonly-mode -A "$set.ghc" "$@" > /dev/null 2>&1; then
|
||||
echo "Skipping $set... ($set.ghc does not evaluate)"
|
||||
else
|
||||
echo "Evaluating $set..."
|
||||
|
||||
if ! nix-env --readonly-mode -f "$NIXPKGS" -qaP --drv-path -A "$set" "$@" > /dev/null; then
|
||||
badsets+="$set "
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$badsets" ]; then
|
||||
echo "Found potential eval issues in the following sets:" >&2
|
||||
# shellcheck disable=SC2086
|
||||
printf '%s\n' $badsets
|
||||
exit 1
|
||||
fi
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
# Attention: For unknown reasons, the script can't be easily cancelled and needs to be killed manually if it shouldn't run to completion.
|
||||
|
||||
use std/log
|
||||
use std log
|
||||
|
||||
let broken_config = "pkgs/development/haskell-modules/configuration-hackage2nix/broken.yaml"
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ fi
|
||||
|
||||
# Stackage solver to use, LTS or Nightly
|
||||
# (should be capitalized like the display name)
|
||||
SOLVER=Nightly
|
||||
SOLVER=LTS
|
||||
# Stackage solver version, if any. Use latest if empty
|
||||
VERSION=
|
||||
TMP_TEMPLATE=update-stackage.XXXXXXX
|
||||
@@ -105,7 +105,6 @@ sed -r \
|
||||
-e '/ hledger-ui /d' \
|
||||
-e '/ hledger-web /d' \
|
||||
-e '/ spacecookie /d' \
|
||||
-e '/ hnix-store-core /d' \
|
||||
< "${tmpfile_new}" >> $stackage_config
|
||||
# Explanations:
|
||||
# cabal2nix, distribution-nixpkgs, jailbreak-cabal, language-nix: These are our packages and we know what we are doing.
|
||||
|
||||
@@ -108,6 +108,7 @@ with lib.maintainers;
|
||||
members = [
|
||||
lopsided98
|
||||
mic92
|
||||
zowoq
|
||||
];
|
||||
scope = "Maintain Buildbot CI framework";
|
||||
shortName = "Buildbot";
|
||||
@@ -750,7 +751,6 @@ with lib.maintainers;
|
||||
|
||||
swift = {
|
||||
members = [
|
||||
reckenrode
|
||||
samasaur
|
||||
stephank
|
||||
];
|
||||
|
||||
@@ -207,8 +207,6 @@
|
||||
|
||||
- The `jetty_11` package has been removed as it reached end of life. Use `jetty_12` instead.
|
||||
|
||||
- The postsrsd module now supports integrating with Postfix as a milter. The [](#opt-services.postsrsd.configurePostfix) option has become an enum to reflect the different integration options. Boolean values are deprecated and will be removed in NixOS 27.05. The previous default `true` is equivalent to `socketmap`.
|
||||
|
||||
- The Mullvad VPN service now has a separate toggle to enable the Mullvad VPN graphical user interface. If you have previously used Mullvad on a desktop by setting `services.mullvad-vpn.package` to `pkgs.mullvad-vpn`, you should now **unset that option**, and enable `services.mullvad-vpn.gui.enable`. The VPN will not work if `services.mullvad-vpn.package` is set to `pkgs.mullvad-vpn`, as `pkgs.mullvad-vpn` no longer contains the Mullvad Daemon; please ensure that `services.mullvad-vpn.package` is set to `pkgs.mullvad`, regardless if you plan to enable the graphical user interface or not.
|
||||
|
||||
- TUI command of `tracexec` now allocates a pseudo terminal by default. Use `--no-tty` to run without one and redirect the tracee's stdin, stdout, and
|
||||
@@ -304,11 +302,9 @@
|
||||
|
||||
- `chatgpt` has been retargeted to OpenAI's new ChatGPT desktop app, while the previous app has been renamed to `chatgpt-classic`.
|
||||
|
||||
- NetBox was updated to `>= 4.7.0`. Have a look at the breaking changes
|
||||
of the [4.6 release](https://github.com/netbox-community/netbox/releases/tag/v4.6.0)
|
||||
and the [4.7 release](https://github.com/netbox-community/netbox/releases/tag/v4.7.0),
|
||||
make the required changes to your configuration and database, if needed,
|
||||
before you upgrade to NixOS 26.11.
|
||||
- NetBox was updated to `>= 4.6.8`. Have a look at the breaking changes
|
||||
of the [4.6 release](https://github.com/netbox-community/netbox/releases/tag/v4.6.0),
|
||||
make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_6;` in your configuration.
|
||||
|
||||
- The COSMIC desktop module now enables by default `system76-power` and `system76-scheduler` following upstream recommended packages. The previous power managment service can be enabled back by setting `services.power-profiles-daemon.enable = true`.
|
||||
|
||||
@@ -334,41 +330,6 @@
|
||||
|
||||
- The `shell_interact()` function on interactive runs of NixOS VM tests has been deprecated. Use the SSH backdoor instead.
|
||||
|
||||
- The {option}`programs.fish.shellFunctions` option can now be used to create custom fish functions in a structured manner, as opposed to concatenating strings with {option}`program.fish.interactiveShellInit`.
|
||||
:::{.example}
|
||||
# Migrating fish functions to `programs.fish.shellFunctions`
|
||||
|
||||
Custom fish functions have historically been defined like so:
|
||||
|
||||
```nix
|
||||
{
|
||||
programs.fish.interactiveShellInit = ''
|
||||
function backup --argument filename --description "Creates a backup copy of a file in the current directory."
|
||||
cp $filename $filename.bak
|
||||
end
|
||||
'';
|
||||
}
|
||||
```
|
||||
|
||||
The above example can be migrated via the following structured code block:
|
||||
|
||||
```nix
|
||||
{
|
||||
programs.fish.shellFunctions = {
|
||||
backup = {
|
||||
modifiers = {
|
||||
description = "Creates a backup copy of a file in the current directory.";
|
||||
argument = "filename";
|
||||
};
|
||||
body = ''
|
||||
cp $filename $filename.bak
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
:::
|
||||
|
||||
- NixOS VM tests now prefer to express durations and timeouts as `datetime.timedelta` values instead of bare numbers. Methods such as `machine.wait_until_succeeds`, `machine.sleep`, `retry`, and `polling_condition` now accept a `timedelta` (e.g., `machine.wait_for_unit("sshd.service", timeout=datetime.timedelta(minutes=1))`). Passing an `int`/`float` as seconds still works but now emits a deprecation warning. Argument names that explicitly defined units were preserved but have had `timedelta` equivalents introduced (`timeout_seconds` → `timeout`, `secs` → `duration`, `seconds_interval` → `interval`).
|
||||
|
||||
- `darwin.linux-builder-vz` has been added: a variant of `darwin.linux-builder` that runs the builder guest on Apple's Virtualization.framework via the new `vzvm` package, translating `x86_64-linux` builds with Rosetta instead of emulating them. Apple silicon hosts only. As part of this, the `nixos/modules/profiles/nix-builder-vm.nix` profile has been split into the backend-neutral `nixos/modules/profiles/nix-builder.nix` and a QEMU-specific part. Existing imports of `nix-builder-vm.nix` keep working unchanged.
|
||||
@@ -395,8 +356,6 @@ The above example can be migrated via the following structured code block:
|
||||
|
||||
- `boot.loader.systemd-boot` gained support for [Automatic Boot Assessment](https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/) via the new [`boot.loader.systemd-boot.bootCounting`](#opt-boot.loader.systemd-boot.bootCounting.enable) options, allowing automatic detection of and recovery from bad NixOS generations. As part of this change, boot loader entries on the ESP/XBOOTLDR partition are now named `nixos-<content-hash>.conf` instead of `nixos-generation-<n>.conf`; existing entries are migrated automatically on the next `nixos-rebuild boot`/`switch`.
|
||||
|
||||
- The Wordpress module now supports auto database migrations using wp-cli, which gets triggered after every package version update.
|
||||
|
||||
- `services.nginx` gained a [`lua`](#opt-services.nginx.lua.enable) option to enable Lua scripting via OpenResty's lua-nginx-module on a stock nginx, configuring `lua_package_path`/`lua_package_cpath` from the packages listed in [`services.nginx.lua.extraPackages`](#opt-services.nginx.lua.extraPackages). Use this to add Lua to a regular nginx; for the full OpenResty platform (libraries that rely on its bundled lualib, such as `lua-resty-openidc`), set `services.nginx.package` to `pkgs.openresty` instead — the option configures the Lua search path for it too.
|
||||
|
||||
- `services.nginx.virtualHosts.<name>.locations.<name>` gained a new `useGrpcErrorPages` option. If enabled, it sets up error pages that are valid gRPC messages. This is useful if you proxy gRPC and want to emit errors from nginx, for example when adding authentication on top.
|
||||
|
||||
@@ -335,7 +335,7 @@ class BaseMachine(ABC):
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def wait_for_shutdown(self, timeout: dt.timedelta | None = None) -> None:
|
||||
def wait_for_shutdown(self) -> None:
|
||||
"""Wait for the machine to power off. This does *not* initiate a shutdown;
|
||||
that's usually done via `shutdown()`.
|
||||
"""
|
||||
@@ -1061,7 +1061,7 @@ class QemuMachine(BaseMachine):
|
||||
break
|
||||
self.send_console(char.decode())
|
||||
|
||||
def wait_for_shutdown(self, timeout: dt.timedelta | None = None) -> None:
|
||||
def wait_for_shutdown(self) -> None:
|
||||
"""
|
||||
Wait for the VM to power off. This does *not* initiate a shutdown;
|
||||
that's usually done via `shutdown()`.
|
||||
@@ -1072,9 +1072,7 @@ class QemuMachine(BaseMachine):
|
||||
with self.nested("waiting for the VM to power off"):
|
||||
sys.stdout.flush()
|
||||
assert self.process
|
||||
self.process.wait(
|
||||
timeout=timeout.total_seconds() if timeout is not None else None
|
||||
)
|
||||
self.process.wait()
|
||||
|
||||
self.pid = None
|
||||
self.booted = False
|
||||
@@ -1905,7 +1903,7 @@ class NspawnMachine(BaseMachine):
|
||||
self.systemctl("poweroff")
|
||||
self.wait_for_shutdown()
|
||||
|
||||
def wait_for_shutdown(self, timeout: dt.timedelta | None = None) -> None:
|
||||
def wait_for_shutdown(self) -> None:
|
||||
"""
|
||||
Wait for the container to power off. This does *not* initiate a shutdown;
|
||||
that's usually done via `shutdown()`.
|
||||
@@ -1914,9 +1912,7 @@ class NspawnMachine(BaseMachine):
|
||||
return
|
||||
|
||||
with self.nested("waiting for the container to power off"):
|
||||
self.process.wait(
|
||||
timeout=timeout.total_seconds() if timeout is not None else None
|
||||
)
|
||||
self.process.wait()
|
||||
self.process = None
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
settings.nix-path = mkOption {
|
||||
nixPath = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default =
|
||||
if cfg.channel.enable then
|
||||
@@ -80,11 +80,8 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule [ "nix" "nixPath" ] [ "nix" "settings" "nix-path" ])
|
||||
];
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
environment.extraInit = mkIf cfg.channel.enable ''
|
||||
if [ -e "$HOME/.nix-defexpr/channels" ]; then
|
||||
export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
|
||||
@@ -98,7 +95,7 @@ in
|
||||
# NIX_PATH has a non-empty default according to Nix docs, so we don't unset
|
||||
# it when empty.
|
||||
environment.sessionVariables = {
|
||||
NIX_PATH = cfg.settings.nix-path;
|
||||
NIX_PATH = cfg.nixPath;
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = lib.mkIf cfg.channel.enable [
|
||||
|
||||
@@ -63,7 +63,7 @@ in
|
||||
default = false;
|
||||
description = ''
|
||||
Use the Wayland input method frontend.
|
||||
This doesn't set `GTK_IM_MODULE` and `QT_IM_MODULE` environment variables.
|
||||
This doesn't set `QT_IM_MODULE` environment variable.
|
||||
See [Using Fcitx 5 on Wayland](https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland#GTK_IM_MODULE).
|
||||
'';
|
||||
};
|
||||
@@ -90,7 +90,6 @@ in
|
||||
XMODIFIERS = "@im=ibus";
|
||||
}
|
||||
// lib.optionalAttrs (!cfg.waylandFrontend) {
|
||||
GTK_IM_MODULE = "ibus";
|
||||
QT_IM_MODULE = "ibus";
|
||||
};
|
||||
|
||||
|
||||
@@ -72,20 +72,6 @@ $ nixos-version --configuration-revision
|
||||
aa314ebd1592f6cdd53cb5bba8bcae97d9323de8
|
||||
.Ed
|
||||
.
|
||||
.It Fl -kernel-version
|
||||
Show the kernel version, e.g.
|
||||
.Bd -literal -offset indent
|
||||
$ nixos-version --kernel-version
|
||||
7.2.5
|
||||
.Ed
|
||||
.
|
||||
.It Fl -specialisations
|
||||
Show specialisations, separated by spaces, if available, e.g.
|
||||
.Bd -literal -offset indent
|
||||
$ nixos-version --specialisations
|
||||
foo bar
|
||||
.Ed
|
||||
.
|
||||
.It Fl -json
|
||||
Print a JSON representation of the versions of NixOS and the top-level
|
||||
configuration flake.
|
||||
|
||||
@@ -20,23 +20,8 @@ case "$1" in
|
||||
fi
|
||||
echo "@configurationRevision@"
|
||||
;;
|
||||
--kernel-version)
|
||||
if [[ "@kernelVersion@" =~ "@" ]]; then
|
||||
echo "$0: kernel version is unknown" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "@kernelVersion@"
|
||||
;;
|
||||
--specialisations)
|
||||
specialisations=@specialisations@
|
||||
if [[ -z "$specialisations" ]]; then
|
||||
echo "$0: no specialisations found" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' "$specialisations"
|
||||
;;
|
||||
--json)
|
||||
cat <<'EOF'
|
||||
cat <<EOF
|
||||
@json@
|
||||
EOF
|
||||
;;
|
||||
|
||||
@@ -53,27 +53,13 @@ let
|
||||
nixos-version = makeProg {
|
||||
name = "nixos-version";
|
||||
src = ./nixos-version.sh;
|
||||
replacements = rec {
|
||||
replacements = {
|
||||
inherit (pkgs) runtimeShell;
|
||||
inherit (config.system.nixos) version codeName revision;
|
||||
inherit (config.system) configurationRevision;
|
||||
kernelVersion =
|
||||
if config.boot.kernel.enable then
|
||||
# modDirVersion returns 6.18.54-xanmod1 instead of 6.18.54
|
||||
config.boot.kernelPackages.kernel.modDirVersion or config.boot.kernelPackages.kernel.version
|
||||
else
|
||||
null;
|
||||
specialisations = lib.escapeShellArg (
|
||||
lib.concatStringsSep " " (lib.attrNames config.specialisation)
|
||||
);
|
||||
|
||||
json = builtins.toJSON (
|
||||
{
|
||||
nixosVersion = config.system.nixos.version;
|
||||
specialisations = lib.attrNames config.specialisation;
|
||||
}
|
||||
// lib.optionalAttrs (kernelVersion != null) {
|
||||
inherit kernelVersion;
|
||||
}
|
||||
// lib.optionalAttrs (config.system.nixos.revision != null) {
|
||||
nixpkgsRevision = config.system.nixos.revision;
|
||||
@@ -306,7 +292,7 @@ in
|
||||
{
|
||||
options.system.tools.${name}.enable = lib.mkEnableOption "${name} script" // {
|
||||
default = config.nix.enable && !config.system.disableInstallerTools;
|
||||
defaultText = lib.literalExpression "config.nix.enable && !config.system.disableInstallerTools";
|
||||
defaultText = "config.nix.enable && !config.system.disableInstallerTools";
|
||||
};
|
||||
|
||||
config = lib.mkIf config.system.tools.${name}.enable {
|
||||
|
||||
@@ -102,7 +102,7 @@ in
|
||||
# because we would need some kind of evil shim taking the *calling* flake's self path,
|
||||
# perhaps, to ever make that work (in order to know where the Nix expr for the system came
|
||||
# from and how to call it).
|
||||
nix.settings.nix-path = lib.mkDefault (
|
||||
nix.nixPath = lib.mkDefault (
|
||||
[ "nixpkgs=flake:nixpkgs" ]
|
||||
++ lib.optional config.nix.channel.enable "/nix/var/nix/profiles/per-user/root/channels"
|
||||
);
|
||||
|
||||
@@ -1258,6 +1258,7 @@
|
||||
./services/networking/gnunet.nix
|
||||
./services/networking/go-autoconfig.nix
|
||||
./services/networking/go-camo.nix
|
||||
./services/networking/go-neb.nix
|
||||
./services/networking/go-shadowsocks2.nix
|
||||
./services/networking/gobgpd.nix
|
||||
./services/networking/godns.nix
|
||||
|
||||
@@ -61,10 +61,11 @@ in
|
||||
#!${pkgs.runtimeShell}
|
||||
# Import environment variables
|
||||
${cfg.extraSessionCommands}
|
||||
# Start dwl, then set up the systemd user environment once dwl
|
||||
# has actually set WAYLAND_DISPLAY (see dwl(1) -s), instead of
|
||||
# importing it before dwl exists.
|
||||
exec ${lib.getExe cfg.package} -s "systemctl --user import-environment DISPLAY WAYLAND_DISPLAY; systemctl --user start dwl-session.target"
|
||||
# Setup systemd user environment
|
||||
systemctl --user import-environment DISPLAY WAYLAND_DISPLAY
|
||||
systemctl --user start dwl-session.target
|
||||
# Start dwl
|
||||
exec ${lib.getExe cfg.package}
|
||||
'';
|
||||
mode = "0755"; # Make it executable
|
||||
};
|
||||
|
||||
@@ -33,13 +33,7 @@ in
|
||||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
environment.systemPackages = [
|
||||
cfg.package
|
||||
];
|
||||
|
||||
# Needed to add the freedesktop sound theme
|
||||
# It's only a runtime dependency for noctalia, so it's not made a package dependency.
|
||||
xdg.sounds.enable = true;
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
systemd.user.services.noctalia = lib.mkIf cfg.systemd.enable {
|
||||
description = "Noctalia Wayland desktop shell";
|
||||
|
||||
@@ -486,10 +486,6 @@ in
|
||||
See https://www.isc.org/blogs/isc-dhcp-eol/ for details.
|
||||
Please switch to a different implementation like kea or dnsmasq.
|
||||
'')
|
||||
(lib.mkRemovedOptionModule [ "services" "go-neb" ] ''
|
||||
The Go-NEB project was discontinued by Matrix.org and archived in June
|
||||
2023. Use matrix-hookshot or another maintained Matrix bot instead.
|
||||
'')
|
||||
(mkRemovedOptionModule [ "services" "gsignond" ] ''
|
||||
The corresponding package was unmaintained, abandoned upstream, used outdated library and thus removed from nixpkgs.
|
||||
'')
|
||||
|
||||
@@ -51,10 +51,7 @@ in
|
||||
sockets.pwupdd.wantedBy = lib.optional config.users.mutableUsers "sockets.target"; # immutable users do not need password updating
|
||||
sockets.newidmapd.wantedBy = [ "sockets.target" ];
|
||||
services."pwupdd@".environment.PWUPDD_OPTS = lib.escapeShellArgs cfg.extraArgs;
|
||||
services."pwaccessd".environment = {
|
||||
LD_LIBRARY_PATH = config.system.nssModules.path;
|
||||
PWACCESSD_OPTS = lib.escapeShellArgs cfg.extraArgs;
|
||||
};
|
||||
services."pwaccessd".environment.PWACCESSD_OPTS = lib.escapeShellArgs cfg.extraArgs;
|
||||
};
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
|
||||
# Accounts daemon looks for dbus interfaces in $XDG_DATA_DIRS/accountsservice
|
||||
environment.XDG_DATA_DIRS = "${config.system.path}/share";
|
||||
environment.LD_LIBRARY_PATH = config.system.nssModules.path;
|
||||
|
||||
}
|
||||
(
|
||||
|
||||
@@ -90,6 +90,8 @@ let
|
||||
}) cfg.sieve.pipeBins
|
||||
);
|
||||
|
||||
yesOrNo = v: if v then "yes" else "no";
|
||||
|
||||
toOption =
|
||||
i: n: v:
|
||||
"${i}${toString n} = ${v}";
|
||||
@@ -101,7 +103,7 @@ let
|
||||
if isInt v then
|
||||
toString v
|
||||
else if isBool v then
|
||||
lib.boolToYesNo v
|
||||
yesOrNo v
|
||||
else if isString v then
|
||||
v
|
||||
else if isPath v || isDerivation v then
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
}:
|
||||
let
|
||||
|
||||
concatMapLines = f: l: lib.concatStringsSep "\n" (map f l);
|
||||
|
||||
cfg = config.services.mlmmj;
|
||||
stateDir = "/var/lib/mlmmj";
|
||||
spoolDir = "/var/spool/mlmmj";
|
||||
@@ -139,10 +141,10 @@ in
|
||||
];
|
||||
};
|
||||
|
||||
extraAliases = lib.concatMapStringsSep "\n" (alias cfg.listDomain) cfg.mailLists;
|
||||
extraAliases = concatMapLines (alias cfg.listDomain) cfg.mailLists;
|
||||
|
||||
virtual = lib.concatMapStringsSep "\n" (virtual cfg.listDomain) cfg.mailLists;
|
||||
transport = lib.concatMapStringsSep "\n" (transport cfg.listDomain) cfg.mailLists;
|
||||
virtual = concatMapLines (virtual cfg.listDomain) cfg.mailLists;
|
||||
transport = concatMapLines (transport cfg.listDomain) cfg.mailLists;
|
||||
};
|
||||
|
||||
environment.systemPackages = [ pkgs.mlmmj ];
|
||||
@@ -163,7 +165,7 @@ in
|
||||
ExecStart = "${pkgs.mlmmj}/bin/mlmmj-maintd -F -d ${spoolDir}/${cfg.listDomain}";
|
||||
};
|
||||
preStart = ''
|
||||
${lib.concatMapStringsSep "\n" (createList cfg.listDomain) cfg.mailLists}
|
||||
${concatMapLines (createList cfg.listDomain) cfg.mailLists}
|
||||
${lib.getExe' config.services.postfix.package "postmap"} /etc/postfix/virtual
|
||||
${lib.getExe' config.services.postfix.package "postmap"} /etc/postfix/transport
|
||||
'';
|
||||
|
||||
@@ -244,6 +244,8 @@ in
|
||||
"noroot"
|
||||
"noroot-locked"
|
||||
];
|
||||
RuntimeDirectory = "postfix-tlspol";
|
||||
RuntimeDirectoryMode = "1750";
|
||||
WorkingDirectory = "/var/cache/postfix-tlspol";
|
||||
UMask = "0077";
|
||||
};
|
||||
|
||||
@@ -46,14 +46,6 @@ let
|
||||
configFile = pkgs.writeText "postsrsd.conf" (
|
||||
renderAttr (lib.filterAttrsRecursive (_: v: v != null) cfg.settings)
|
||||
);
|
||||
|
||||
postfixIntegration =
|
||||
if cfg.configurePostfix == true then
|
||||
"socketmap"
|
||||
else if cfg.configurePostfix == false then
|
||||
"none"
|
||||
else
|
||||
cfg.configurePostfix;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
@@ -130,15 +122,6 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
milter = lib.mkOption {
|
||||
type = with lib.types; nullOr (strMatching "^(unix|inet):.+");
|
||||
default = "unix:/run/postsrsd/milter";
|
||||
example = "inet:localhost:9997";
|
||||
description = ''
|
||||
Milter listener configuration in `unix:/path/to/socket` or `inet:host:port` format.
|
||||
'';
|
||||
};
|
||||
|
||||
secrets-file = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "\${CREDENTIALS_DIRECTORY}/secrets-file";
|
||||
@@ -185,11 +168,11 @@ in
|
||||
};
|
||||
|
||||
socketmap = lib.mkOption {
|
||||
type = with lib.types; nullOr (strMatching "^(unix|inet):.+");
|
||||
default = "unix:/run/postsrsd/socketmap";
|
||||
type = lib.types.strMatching "^(unix|inet):.+";
|
||||
default = "unix:/run/postsrsd/socket";
|
||||
example = "inet:localhost:10003";
|
||||
description = ''
|
||||
Socketmap listener configuration in `unix:/path/to/socket` or `inet:host:port` format.
|
||||
Listener configuration in socket map format native to Postfix configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -229,23 +212,10 @@ in
|
||||
};
|
||||
|
||||
configurePostfix = lib.mkOption {
|
||||
type = lib.types.enum [
|
||||
true
|
||||
false
|
||||
"none"
|
||||
"socketmap"
|
||||
"milter"
|
||||
];
|
||||
default = "socketmap";
|
||||
example = "milter";
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether and how to integrate postsrsd into the local Postfix instance.
|
||||
|
||||
::: {.caution}
|
||||
Boolean values are deprecated and retained for backwards
|
||||
compatibility. `true` is equivalent to `socketmap`, and `false` is
|
||||
equivalent to `none`.
|
||||
:::
|
||||
Whether to configure the required settings to use postsrsd in the local Postfix instance.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -264,41 +234,17 @@ in
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
warnings = lib.optionals (cfg.enable && isBool cfg.configurePostfix) [
|
||||
''
|
||||
Boolean values are deprecated for `services.postsrsd.configurePostfix` and will be rejected in NixOS 27.05.
|
||||
Use `none`, `socketmap`, or `milter` instead. `true` is equivalent to `socketmap` and `false` is equivalent to `none`.
|
||||
''
|
||||
];
|
||||
}
|
||||
(lib.mkIf (cfg.enable && postfixIntegration != "none" && config.services.postfix.enable) {
|
||||
assertions = [
|
||||
{
|
||||
assertion = postfixIntegration == "milter" -> cfg.settings.milter != null;
|
||||
message = "Configuring Postfix `smtpd_milters` requires `services.postsrsd.settings.milter` to be set.";
|
||||
}
|
||||
{
|
||||
assertion = postfixIntegration == "socketmap" -> cfg.settings.socketmap != null;
|
||||
message = "Configuring Postfix canonical maps requires `services.postsrsd.settings.socketmap` to be set.";
|
||||
}
|
||||
];
|
||||
|
||||
services.postfix.settings.main =
|
||||
lib.optionalAttrs (postfixIntegration == "socketmap") {
|
||||
# https://github.com/roehling/postsrsd#configuration
|
||||
sender_canonical_maps = "socketmap:${cfg.settings.socketmap}:forward";
|
||||
sender_canonical_classes = "envelope_sender";
|
||||
recipient_canonical_maps = "socketmap:${cfg.settings.socketmap}:reverse";
|
||||
recipient_canonical_classes = [
|
||||
"envelope_recipient"
|
||||
"header_recipient"
|
||||
];
|
||||
}
|
||||
// lib.optionalAttrs (postfixIntegration == "milter") {
|
||||
# https://github.com/roehling/postsrsd/tree/main#milter-support
|
||||
smtpd_milters = [ cfg.settings.milter ];
|
||||
};
|
||||
(lib.mkIf (cfg.enable && cfg.configurePostfix && config.services.postfix.enable) {
|
||||
services.postfix.settings.main = {
|
||||
# https://github.com/roehling/postsrsd#configuration
|
||||
sender_canonical_maps = "socketmap:${cfg.settings.socketmap}:forward";
|
||||
sender_canonical_classes = "envelope_sender";
|
||||
recipient_canonical_maps = "socketmap:${cfg.settings.socketmap}:reverse";
|
||||
recipient_canonical_classes = [
|
||||
"envelope_recipient"
|
||||
"header_recipient"
|
||||
];
|
||||
};
|
||||
|
||||
users.users.postfix.extraGroups = [ cfg.group ];
|
||||
})
|
||||
|
||||
@@ -20,16 +20,11 @@ let
|
||||
rawHomeserverUrl = cfg.homeserverUrl;
|
||||
|
||||
pantalaimon = {
|
||||
use = cfg.pantalaimon.enable;
|
||||
}
|
||||
// lib.optionalAttrs cfg.pantalaimon.enable {
|
||||
inherit (cfg.pantalaimon) username;
|
||||
|
||||
use = cfg.pantalaimon.enable;
|
||||
password = "@PANTALAIMON_PASSWORD@"; # will be replaced in "generateConfig"
|
||||
};
|
||||
encryption = {
|
||||
inherit (cfg.settings.encryption) username;
|
||||
password = "@ENCRYPTION_PASSWORD@"; # will be replaced in "generateConfig"
|
||||
};
|
||||
};
|
||||
|
||||
moduleConfigFile = pkgs.writeText "module-config.yaml" (
|
||||
@@ -77,9 +72,6 @@ let
|
||||
${lib.optionalString (cfg.pantalaimon.passwordFile != null) ''
|
||||
${pkgs.replace-secret}/bin/replace-secret '@PANTALAIMON_PASSWORD@' '${cfg.pantalaimon.passwordFile}' ${cfg.dataPath}/config/default.yaml
|
||||
''}
|
||||
${lib.optionalString (cfg.encryption.passwordFile != null) ''
|
||||
${pkgs.replace-secret}/bin/replace-secret '@ENCRYPTION_PASSWORD@' '${cfg.encryption.passwordFile}' ${cfg.dataPath}/config/default.yaml
|
||||
''}
|
||||
''
|
||||
);
|
||||
in
|
||||
@@ -106,14 +98,6 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
encryption.passwordFile = lib.mkOption {
|
||||
type = with lib.types; nullOr path;
|
||||
default = null;
|
||||
description = ''
|
||||
File containing the matrix password for the `mjolnir` user.
|
||||
'';
|
||||
};
|
||||
|
||||
pantalaimon = lib.mkOption {
|
||||
description = ''
|
||||
`pantalaimon` options (enables E2E Encryption support).
|
||||
@@ -202,22 +186,17 @@ in
|
||||
|
||||
config = lib.mkIf config.services.mjolnir.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = !(cfg.settings.encryption.use && cfg.encryption.passwordFile == null);
|
||||
message = "encryption.passwordFile must be specified when native encryption is used.";
|
||||
}
|
||||
{
|
||||
assertion = !(cfg.pantalaimon.enable && cfg.pantalaimon.passwordFile == null);
|
||||
message = "pantalaimon.passwordFile must be specified when pantalaimon is enabled.";
|
||||
message = "Specify pantalaimon.passwordFile";
|
||||
}
|
||||
{
|
||||
assertion = cfg.accessTokenFile == null -> cfg.pantalaimon.enable || cfg.settings.encryption.use;
|
||||
message = "Do not specify accessTokenFile when using native encryption or pantalaimon";
|
||||
assertion = !(cfg.pantalaimon.enable && cfg.accessTokenFile != null);
|
||||
message = "Do not specify accessTokenFile when using pantalaimon";
|
||||
}
|
||||
{
|
||||
assertion =
|
||||
!(!cfg.pantalaimon.enable && !cfg.settings.encryption.use && cfg.accessTokenFile == null);
|
||||
message = "Specify accessTokenFile when not using pantalaimon or native encryption.";
|
||||
assertion = !(!cfg.pantalaimon.enable && cfg.accessTokenFile == null);
|
||||
message = "Specify accessTokenFile when not using pantalaimon";
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# CLIProxyAPI {#module-services-cliproxyapi}
|
||||
|
||||
[CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) exposes OAuth-based subscription CLIs (Claude Code, Codex, Grok, Antigravity, Kimi, Devin, Meta) behind OpenAI/Gemini/Anthropic-compatible HTTP APIs.
|
||||
[CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) exposes OAuth-based subscription CLIs (Claude Code, Codex, Gemini, Qwen, Grok, Antigravity) behind OpenAI/Gemini/Anthropic-compatible HTTP APIs.
|
||||
|
||||
Enable it with:
|
||||
|
||||
@@ -10,11 +10,11 @@ Enable it with:
|
||||
}
|
||||
```
|
||||
|
||||
The service runs as a dedicated `cliproxyapi` user and keeps its configuration and OAuth tokens under `/var/lib/cliproxyapi`. The configuration file is regenerated from [](#opt-services.cliproxyapi.settings) at startup, which overwrites any changes made through the management API.
|
||||
The service runs as a dedicated `cliproxyapi` user and keeps its configuration and OAuth tokens under `/var/lib/cliproxyapi`.
|
||||
|
||||
## Authentication {#module-services-cliproxyapi-authentication}
|
||||
|
||||
Provider logins use OAuth and must land in the service's `oauth.auth-dir` (`/var/lib/cliproxyapi`), which is owned by the `cliproxyapi` user. Either of the approaches below writes the token with the correct ownership, and the running service picks it up without a restart.
|
||||
Provider logins use OAuth and must land in the service's `auth-dir` (`/var/lib/cliproxyapi`), which is owned by the `cliproxyapi` user. Either of the approaches below writes the token with the correct ownership, and the running service picks it up without a restart.
|
||||
|
||||
### Management API {#module-services-cliproxyapi-authentication-management-api}
|
||||
|
||||
@@ -22,31 +22,19 @@ Set a management key in [](#opt-services.cliproxyapi.settings):
|
||||
|
||||
```nix
|
||||
{
|
||||
services.cliproxyapi.settings.management.secret-key._secret = "/run/secrets/cliproxyapi-mgmt-key";
|
||||
services.cliproxyapi.settings.remote-management.secret-key._secret =
|
||||
"/run/secrets/cliproxyapi-mgmt-key";
|
||||
}
|
||||
```
|
||||
|
||||
Request a login URL and open it in a browser:
|
||||
Then request an authentication URL for the desired provider and open it in a browser:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <management-key>" \
|
||||
"http://127.0.0.1:8317/v8/management/oauth/auth-url?provider=claude"
|
||||
http://127.0.0.1:8317/v0/management/anthropic-auth-url
|
||||
```
|
||||
|
||||
Other values for `provider` are `codex`, `antigravity`, `kimi`, `kimi-ai`, `xai`, `devin` and `meta`. `kimi`, `kimi-ai`, `xai` and `meta` use a device code, so the login finishes once it is approved in the browser.
|
||||
|
||||
For `claude`, `codex` and `antigravity`, the browser ends up on a `localhost` page that fails to load. Send that URL to the daemon to finish the login:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <management-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"redirect_url": "<url>"}' \
|
||||
http://127.0.0.1:8317/v8/management/oauth/callback
|
||||
```
|
||||
|
||||
Alternatively, add `is_webui=true` to the login URL request, and the daemon will listen on the callback port and finish the login itself.
|
||||
|
||||
To check on a login, query `/v8/management/oauth/status?state=<state>` with the `state` from the login URL response. It returns `wait` while the login is pending, `ok` once the token is saved and `error` if it failed.
|
||||
The daemon completes the OAuth flow itself and stores the token in its `auth-dir`. Authentication endpoints are available for the `anthropic`, `codex`, `xai`, `antigravity`, and `kimi` providers.
|
||||
|
||||
### Command-line login {#module-services-cliproxyapi-authentication-cli}
|
||||
|
||||
@@ -64,4 +52,4 @@ Then run the login as the service user, pointing at the managed configuration:
|
||||
sudo -u cliproxyapi cliproxyapi -config /var/lib/cliproxyapi/config.yaml --claude-login
|
||||
```
|
||||
|
||||
Other providers have their own flags, such as `--codex-login` or `--xai-login`; see `cliproxyapi -help`. On a headless host, add `-no-browser` to print the login URL. The Claude, Codex, Antigravity and Devin logins then ask you to paste the `localhost` URL you were redirected to.
|
||||
Other providers use their matching flags, for example `--codex-login` or `--xai-login`. On a headless host, pass `-no-browser` to print the OAuth URL instead of launching a browser.
|
||||
|
||||
@@ -10,9 +10,14 @@ let
|
||||
format = pkgs.formats.yaml { };
|
||||
stateDir = "/var/lib/cliproxyapi";
|
||||
configPath = "${stateDir}/config.yaml";
|
||||
settings = {
|
||||
auth-dir = stateDir;
|
||||
}
|
||||
// cfg.settings;
|
||||
secretsReplacement = utils.genJqSecretsReplacement {
|
||||
loadCredential = true;
|
||||
} cfg.settings configPath;
|
||||
} settings configPath;
|
||||
port = cfg.settings.port or 8317;
|
||||
in
|
||||
{
|
||||
options.services.cliproxyapi = {
|
||||
@@ -21,30 +26,14 @@ in
|
||||
package = lib.mkPackageOption pkgs "cliproxyapi" { };
|
||||
|
||||
settings = lib.mkOption {
|
||||
type = lib.types.submodule {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
server.port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8317;
|
||||
description = "Port on which CLIProxyAPI listens.";
|
||||
};
|
||||
oauth.auth-dir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = stateDir;
|
||||
description = "Directory where OAuth tokens are stored.";
|
||||
};
|
||||
};
|
||||
};
|
||||
type = format.type;
|
||||
default = { };
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
server = {
|
||||
host = "127.0.0.1";
|
||||
port = 8317;
|
||||
};
|
||||
access.api-keys = [ { _secret = "/run/secrets/cliproxyapi-api-key"; } ];
|
||||
management.secret-key._secret = "/run/secrets/cliproxyapi-management-key";
|
||||
host = "127.0.0.1";
|
||||
port = 8317;
|
||||
api-keys = [ { _secret = "/run/secrets/cliproxyapi-api-key"; } ];
|
||||
remote-management.secret-key._secret = "/run/secrets/cliproxyapi-management-key";
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
@@ -65,7 +54,7 @@ in
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Whether to open the firewall for {option}`services.cliproxyapi.settings.server.port`.";
|
||||
description = "Whether to open the firewall for the specified port.";
|
||||
};
|
||||
|
||||
user = lib.mkOption {
|
||||
@@ -153,7 +142,7 @@ in
|
||||
};
|
||||
|
||||
networking.firewall = lib.mkIf cfg.openFirewall {
|
||||
allowedTCPPorts = [ cfg.settings.server.port ];
|
||||
allowedTCPPorts = [ port ];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -38,8 +38,12 @@ let
|
||||
PAPERLESS_REDIS = "unix://${redisServer.unixSocket}";
|
||||
}
|
||||
// lib.optionalAttrs (cfg.settings.PAPERLESS_AI_ENABLED or true) {
|
||||
NLTK_DATA = cfg.package.nltkDataDir;
|
||||
TIKTOKEN_CACHE_DIR = cfg.package.tiktokenCacheDir;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.settings.PAPERLESS_ENABLE_NLTK or true) {
|
||||
PAPERLESS_NLTK_DIR = cfg.package.nltkDataDir;
|
||||
}
|
||||
// lib.optionalAttrs (cfg.openMPThreadingWorkaround) {
|
||||
OMP_NUM_THREADS = "1";
|
||||
}
|
||||
@@ -713,9 +717,7 @@ in
|
||||
"d '${cfg.exporter.directory}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
|
||||
];
|
||||
|
||||
services.paperless.exporter.settings = lib.mapAttrs (
|
||||
_: v: lib.mkDefault v
|
||||
) options.services.paperless.exporter.settings.default;
|
||||
services.paperless.exporter.settings = options.services.paperless.exporter.settings.default;
|
||||
|
||||
systemd.services.paperless-exporter = {
|
||||
startAt = lib.defaultTo [ ] cfg.exporter.onCalendar;
|
||||
|
||||
@@ -6,76 +6,6 @@
|
||||
}:
|
||||
let
|
||||
cfg = config.services.beszel.agent;
|
||||
|
||||
hasVideoDriver = driver: builtins.elem driver config.services.xserver.videoDrivers;
|
||||
|
||||
# Collector names must match `isValidCollectorSource` in upstream's agent/gpu.go.
|
||||
# macmon and powermetrics are macOS-only and omitted here.
|
||||
gpuCollectors = {
|
||||
# read sysfs directly, need no package or device access
|
||||
"amd_sysfs" = { };
|
||||
"intel_sysfs" = { };
|
||||
"intel_gpu_top" = {
|
||||
package = lib.getBin pkgs.intel-gpu-tools;
|
||||
deviceAllow = [ "char-drm rw" ];
|
||||
capabilities = [ "CAP_PERFMON" ];
|
||||
# perf_event_open is in @debug, not @system-service
|
||||
systemCalls = [ "perf_event_open" ];
|
||||
};
|
||||
"nvidia-smi" = {
|
||||
package = lib.getBin config.hardware.nvidia.package;
|
||||
deviceAllow = [ "char-nvidia* rw" ];
|
||||
};
|
||||
"nvml" = {
|
||||
deviceAllow = [ "char-nvidia* rw" ];
|
||||
};
|
||||
"nvtop" = {
|
||||
package = lib.getBin pkgs.nvtopPackages.full;
|
||||
deviceAllow = [
|
||||
"char-nvidia* rw"
|
||||
"char-drm rw"
|
||||
];
|
||||
};
|
||||
"rocm-smi" = {
|
||||
package = lib.getBin pkgs.rocmPackages.rocm-smi;
|
||||
deviceAllow = [
|
||||
"char-drm rw"
|
||||
"char-kfd rw"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
activeCollectors = lib.optionals (!cfg.environment.SKIP_GPU) cfg.environment.GPU_COLLECTOR;
|
||||
|
||||
collectorAttrs =
|
||||
attr: lib.unique (lib.concatMap (name: gpuCollectors.${name}.${attr} or [ ]) activeCollectors);
|
||||
|
||||
gpuPackages = map (name: gpuCollectors.${name}.package) (
|
||||
lib.filter (name: gpuCollectors.${name} ? package) activeCollectors
|
||||
);
|
||||
|
||||
gpuNeedsDevices = collectorAttrs "deviceAllow" != [ ];
|
||||
|
||||
# capabilities granted under PrivateUsers are void on the host, see
|
||||
# systemd.exec(5), so these collectors also need the user namespace disabled
|
||||
gpuNeedsCapabilities = collectorAttrs "capabilities" != [ ];
|
||||
|
||||
# Any explicit DeviceAllow turns DevicePolicy=auto into an allow-list, so the GPU
|
||||
# devices are omitted when smartmon relies on full /dev access.
|
||||
deviceAllowList =
|
||||
lib.optionals (cfg.smartmon.enable && cfg.smartmon.deviceAllow != [ ]) (
|
||||
map (device: "${device} r") cfg.smartmon.deviceAllow
|
||||
)
|
||||
++ lib.optionals (!cfg.smartmon.enable || cfg.smartmon.deviceAllow != [ ]) (
|
||||
collectorAttrs "deviceAllow" ++ lib.optionals config.boot.zfs.enabled [ "/dev/zfs rw" ]
|
||||
);
|
||||
|
||||
serviceCapabilities =
|
||||
lib.optionals cfg.smartmon.enable [
|
||||
"CAP_SYS_RAWIO"
|
||||
"CAP_SYS_ADMIN"
|
||||
]
|
||||
++ collectorAttrs "capabilities";
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
@@ -130,45 +60,6 @@ in
|
||||
Enabling this option will skip systemd tracking and its setup in NixOS.
|
||||
'';
|
||||
};
|
||||
SKIP_GPU = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to disable GPU monitoring.
|
||||
Enabling this option will skip GPU tracking.
|
||||
'';
|
||||
};
|
||||
GPU_COLLECTOR = lib.mkOption {
|
||||
# upstream takes a comma-separated string, which used to be passed through as is
|
||||
type =
|
||||
with lib.types;
|
||||
coercedTo str (value: map lib.trim (lib.splitString "," value)) (
|
||||
listOf (enum (lib.attrNames gpuCollectors))
|
||||
);
|
||||
default =
|
||||
lib.optionals (hasVideoDriver "nvidia") [ "nvidia-smi" ]
|
||||
++ lib.optionals (hasVideoDriver "amdgpu") [ "amd_sysfs" ]
|
||||
++ lib.optionals (hasVideoDriver "intel") [ "intel_sysfs" ];
|
||||
defaultText = lib.literalMD ''
|
||||
derived from {option}`services.xserver.videoDrivers`
|
||||
'';
|
||||
example = [
|
||||
"nvidia-smi"
|
||||
"intel_gpu_top"
|
||||
];
|
||||
description = ''
|
||||
GPU collectors to use, in priority order. Overrides the agent's
|
||||
auto-detection; the packages needed by the selected collectors are added
|
||||
to the service path. If empty, the agent auto-detects available
|
||||
collectors. `rocm-smi` is deprecated upstream in favour of `amd_sysfs`,
|
||||
and `intel_gpu_top` is not used on the xe driver, where `intel_sysfs` is
|
||||
preferred.
|
||||
|
||||
Access to GPU device nodes is only granted for the collectors listed
|
||||
here, so a collector provided through
|
||||
{option}`services.beszel.agent.extraPath` has to be listed as well.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
@@ -238,22 +129,22 @@ in
|
||||
wants = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
|
||||
# drop empty lists so an unset GPU_COLLECTOR keeps upstream auto-detection
|
||||
environment = lib.mapAttrs (
|
||||
_: value:
|
||||
if lib.isBool value then
|
||||
(lib.boolToString value)
|
||||
else if lib.isList value then
|
||||
lib.concatStringsSep "," value
|
||||
else
|
||||
value
|
||||
) (lib.filterAttrs (_: value: value != [ ]) (cfg.environment // { DATA_DIR = cfg.dataDir; }));
|
||||
_: value: if lib.isBool value then (lib.boolToString value) else value
|
||||
) (cfg.environment // { DATA_DIR = cfg.dataDir; });
|
||||
|
||||
path =
|
||||
cfg.extraPath
|
||||
++ lib.optionals cfg.smartmon.enable [ cfg.smartmon.package ]
|
||||
++ lib.optionals config.boot.zfs.enabled [ config.boot.zfs.package ]
|
||||
++ gpuPackages;
|
||||
++ lib.optionals (builtins.elem "nvidia" config.services.xserver.videoDrivers) [
|
||||
(lib.getBin config.hardware.nvidia.package)
|
||||
]
|
||||
++ lib.optionals (builtins.elem "amdgpu" config.services.xserver.videoDrivers) [
|
||||
(lib.getBin pkgs.rocmPackages.rocm-smi)
|
||||
]
|
||||
++ lib.optionals (builtins.elem "intel" config.services.xserver.videoDrivers) [
|
||||
(lib.getBin pkgs.intel-gpu-tools)
|
||||
];
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = ''
|
||||
@@ -274,22 +165,26 @@ in
|
||||
DynamicUser = true;
|
||||
User = "beszel-agent";
|
||||
|
||||
# Capabilities needed for SMART monitoring and GPU performance counters
|
||||
AmbientCapabilities = serviceCapabilities;
|
||||
CapabilityBoundingSet = serviceCapabilities;
|
||||
# Capabilities needed for SMART monitoring
|
||||
AmbientCapabilities = lib.mkIf cfg.smartmon.enable [
|
||||
"CAP_SYS_RAWIO"
|
||||
"CAP_SYS_ADMIN"
|
||||
];
|
||||
CapabilityBoundingSet = lib.mkIf cfg.smartmon.enable [
|
||||
"CAP_SYS_RAWIO"
|
||||
"CAP_SYS_ADMIN"
|
||||
];
|
||||
|
||||
DeviceAllow = lib.mkIf (deviceAllowList != [ ]) deviceAllowList;
|
||||
# Device access for SMART monitoring
|
||||
DeviceAllow = lib.mkIf (cfg.smartmon.enable && cfg.smartmon.deviceAllow != [ ]) (
|
||||
map (device: "${device} r") cfg.smartmon.deviceAllow
|
||||
);
|
||||
|
||||
LockPersonality = true;
|
||||
NoNewPrivileges = !cfg.smartmon.enable;
|
||||
PrivateDevices = !cfg.smartmon.enable && !gpuNeedsDevices;
|
||||
PrivateDevices = !cfg.smartmon.enable;
|
||||
PrivateTmp = true;
|
||||
# zfs commands fail inside a user namespace since zfs 2.2, see syncoid.nix
|
||||
PrivateUsers =
|
||||
!cfg.smartmon.enable
|
||||
&& !config.boot.zfs.enabled
|
||||
&& !cfg.environment.SKIP_SYSTEMD
|
||||
&& !gpuNeedsCapabilities;
|
||||
PrivateUsers = !cfg.smartmon.enable && !cfg.environment.SKIP_SYSTEMD;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = "strict";
|
||||
ProtectHome = "read-only";
|
||||
@@ -304,7 +199,7 @@ in
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallErrorNumber = "EPERM";
|
||||
SystemCallFilter = [ "@system-service" ] ++ collectorAttrs "systemCalls";
|
||||
SystemCallFilter = [ "@system-service" ];
|
||||
Type = "simple";
|
||||
UMask = 27;
|
||||
};
|
||||
|
||||
@@ -95,8 +95,8 @@ in
|
||||
|
||||
DynamicUser = true;
|
||||
StateDirectory = "glpi-agent";
|
||||
CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
|
||||
AmbientCapabilities = [ "CAP_DAC_READ_SEARCH" ];
|
||||
CapabilityBoundingSet = [ "CAP_SYS_ADMIN" ];
|
||||
AmbientCapabilities = [ "CAP_SYS_ADMIN" ];
|
||||
|
||||
LimitCORE = 0;
|
||||
LimitNOFILE = 65535;
|
||||
@@ -104,7 +104,7 @@ in
|
||||
MemorySwapMax = 0;
|
||||
MemoryZSwapMax = 0;
|
||||
PrivateTmp = true;
|
||||
ProcSubset = "all";
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
|
||||
@@ -32,14 +32,10 @@ let
|
||||
inherit (package) phpPackage;
|
||||
phpOptions = toKeyValue cfg.phpOptions;
|
||||
preferLocalBuild = true;
|
||||
strictDeps = true;
|
||||
__structuredAttrs = true;
|
||||
passAsFile = [ "phpOptions" ];
|
||||
}
|
||||
''
|
||||
(
|
||||
cat $phpPackage/etc/php.ini
|
||||
printf "%s" "$phpOptions"
|
||||
) > $out
|
||||
cat $phpPackage/etc/php.ini $phpOptionsPath > $out
|
||||
'';
|
||||
|
||||
artisanWrapper = pkgs.writeShellScriptBin "librenms-artisan" ''
|
||||
|
||||
@@ -113,7 +113,20 @@ let
|
||||
filterAttrsListRecursive =
|
||||
pred: x:
|
||||
if isAttrs x then
|
||||
mapAttrs (_: filterAttrsListRecursive pred) (filterAttrs pred x)
|
||||
listToAttrs (
|
||||
concatMap (
|
||||
name:
|
||||
let
|
||||
v = x.${name};
|
||||
in
|
||||
if pred name v then
|
||||
[
|
||||
(nameValuePair name (filterAttrsListRecursive pred v))
|
||||
]
|
||||
else
|
||||
[ ]
|
||||
) (attrNames x)
|
||||
)
|
||||
else if isList x then
|
||||
map (filterAttrsListRecursive pred) x
|
||||
else
|
||||
|
||||
@@ -11,10 +11,6 @@ let
|
||||
configFile = settingsFormat.generate "config.toml" cfg.extraConfig;
|
||||
in
|
||||
{
|
||||
meta = {
|
||||
inherit (pkgs.telegraf.meta) maintainers;
|
||||
};
|
||||
|
||||
###### interface
|
||||
options = {
|
||||
services.telegraf = {
|
||||
|
||||
@@ -240,8 +240,6 @@ in
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
]
|
||||
# AF_UNIX to be able to connect to e.g. /dev/log
|
||||
++ lib.optionals (cfg.settings.log.file or "" == "syslog") [ "AF_UNIX" ]
|
||||
++ lib.optionals cfg.allowDHCP [ "AF_PACKET" ];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
|
||||
@@ -21,6 +21,8 @@ let
|
||||
(listOf settingType)
|
||||
];
|
||||
|
||||
genAttrs' = names: f: lib.listToAttrs (map f names);
|
||||
|
||||
regexEscape =
|
||||
let
|
||||
# taken from https://github.com/python/cpython/blob/05cb728d68a278d11466f9a6c8258d914135c96c/Lib/re.py#L251-L266
|
||||
@@ -298,7 +300,7 @@ in
|
||||
lib.mapAttrsToList (name: cfg: {
|
||||
${cfg.nginx.virtualHost} = {
|
||||
locations =
|
||||
(lib.genAttrs' [ "cgit.css" "cgit.js" "cgit.png" "favicon.ico" "robots.txt" ] (
|
||||
(genAttrs' [ "cgit.css" "cgit.js" "cgit.png" "favicon.ico" "robots.txt" ] (
|
||||
fileName:
|
||||
lib.nameValuePair "= ${stripLocation cfg}/${fileName}" {
|
||||
alias = lib.mkDefault "${cfg.package}/cgit/${fileName}";
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
let
|
||||
cfg = config.services.cloudflare-ddns;
|
||||
|
||||
boolToString = b: if b then "true" else "false";
|
||||
formatList = l: lib.concatStringsSep "," l;
|
||||
in
|
||||
{
|
||||
@@ -264,7 +265,7 @@ in
|
||||
let
|
||||
toEnv = name: value: "${name}=\"${toString value}\"";
|
||||
toEnvList = name: value: "${name}=\"${formatList value}\"";
|
||||
toEnvBool = name: value: "${name}=\"${lib.boolToString value}\"";
|
||||
toEnvBool = name: value: "${name}=\"${boolToString value}\"";
|
||||
toEnvMaybe =
|
||||
pred: name: value:
|
||||
lib.optionalString pred (toEnv name value);
|
||||
|
||||
@@ -13,6 +13,7 @@ let
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
mkOverride
|
||||
mkPackageOption
|
||||
nameValuePair
|
||||
recursiveUpdate
|
||||
@@ -350,11 +351,13 @@ in
|
||||
fedimintdName: cfg:
|
||||
(nameValuePair cfg.nginx.fqdn (
|
||||
lib.mkMerge [
|
||||
(lib.mapAttrsRecursive (_: lib.mkDefault) cfg.nginx.config)
|
||||
cfg.nginx.config
|
||||
|
||||
{
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
# Note: we want by default to enable OpenSSL, but it seems anything 100 and above is
|
||||
# overridden by default value from vhost-options.nix
|
||||
enableACME = mkOverride 99 true;
|
||||
forceSSL = mkOverride 99 true;
|
||||
locations.${cfg.nginx.path_ws} = {
|
||||
proxyPass = "http://127.0.0.1:${toString cfg.api_ws.port}/";
|
||||
proxyWebsockets = true;
|
||||
|
||||
10
nixos/modules/services/networking/go-neb.nix
Normal file
10
nixos/modules/services/networking/go-neb.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRemovedOptionModule [ "services" "go-neb" ] ''
|
||||
The Go-NEB project was discontinued by Matrix.org and archived in June
|
||||
2023. Use matrix-hookshot or another maintained Matrix bot instead.
|
||||
'')
|
||||
];
|
||||
}
|
||||
@@ -20,24 +20,6 @@ in
|
||||
default = null;
|
||||
description = "Portal to discover targets on";
|
||||
};
|
||||
|
||||
discoverType = mkOption {
|
||||
description = ''
|
||||
Target discovery type.
|
||||
Change this if you want to discover your targes via an iSNS server
|
||||
or use the targets provided via firmware settings.
|
||||
See {manpage}`iscsiadm(8)`.
|
||||
'';
|
||||
default = "sendtargets";
|
||||
example = "sendtargets";
|
||||
type = enum [
|
||||
"st"
|
||||
"sendtargets"
|
||||
"isns"
|
||||
"fw"
|
||||
];
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = str;
|
||||
description = "Name of this iscsi initiator";
|
||||
@@ -99,7 +81,7 @@ in
|
||||
wantedBy = [ "remote-fs.target" ];
|
||||
serviceConfig.ExecStartPre =
|
||||
mkIf (cfg.discoverPortal != null)
|
||||
"${cfg.package}/bin/iscsiadm --mode discoverydb --type ${cfg.discoverType} --portal ${escapeShellArg cfg.discoverPortal} --discover";
|
||||
"${cfg.package}/bin/iscsiadm --mode discoverydb --type sendtargets --portal ${escapeShellArg cfg.discoverPortal} --discover";
|
||||
};
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
@@ -43,23 +43,6 @@ in
|
||||
type = nullOr str;
|
||||
};
|
||||
|
||||
discoverType = mkOption {
|
||||
description = ''
|
||||
Target discovery type.
|
||||
Change this if you want to discover your targes via an iSNS server
|
||||
or use the targets provided via firmware settings.
|
||||
See {manpage}`iscsiadm(8)`.
|
||||
'';
|
||||
default = "sendtargets";
|
||||
example = "sendtargets";
|
||||
type = enum [
|
||||
"st"
|
||||
"sendtargets"
|
||||
"isns"
|
||||
"fw"
|
||||
];
|
||||
};
|
||||
|
||||
target = mkOption {
|
||||
description = ''
|
||||
Name of the iSCSI target to boot from.
|
||||
@@ -185,7 +168,7 @@ in
|
||||
|
||||
iscsid --foreground --no-pid-file --debug ${toString cfg.logLevel} &
|
||||
iscsiadm --mode discoverydb \
|
||||
--type ${cfg.discoverType} \
|
||||
--type sendtargets \
|
||||
--discover \
|
||||
--portal ${escapeShellArg cfg.discoverPortal} \
|
||||
--debug ${toString cfg.logLevel}
|
||||
|
||||
@@ -292,7 +292,7 @@ in
|
||||
assertions = lib.mapAttrsToList (netName: netCfg: {
|
||||
# IFNAMSIZ caps network device names to 16 chars (including NULL terminator).
|
||||
# Without this check, users might end up with a truncated interface name.
|
||||
assertion = netCfg.tun.disable || builtins.stringLength netCfg.tun.device <= 15;
|
||||
assertion = !netCfg.tun.disable && builtins.stringLength netCfg.tun.device <= 15;
|
||||
message = ''
|
||||
Network device names can't be longer than 15 chars.
|
||||
`config.services.nebula.networks.${netName}.tun.device` is set to "${netCfg.tun.device}" which is above the limit.
|
||||
|
||||
@@ -85,10 +85,12 @@ rec {
|
||||
else
|
||||
f (path ++ [ name ]) name value;
|
||||
in
|
||||
concatMapAttrs g set;
|
||||
mapAttrs'' g set;
|
||||
in
|
||||
recurse [ ] set;
|
||||
|
||||
mapAttrs'' = f: set: foldl' (a: b: a // b) { } (mapAttrsToList f set);
|
||||
|
||||
# Extract the options from the given set of parameters.
|
||||
paramsToOptions = ps: mapParamsRecursive (_path: name: param: { ${name} = param.option; }) ps;
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ let
|
||||
}:
|
||||
attrsOfAttrs:
|
||||
let
|
||||
# map function to string for each key val
|
||||
mapAttrsToStringsSep =
|
||||
sep: mapFn: attrs:
|
||||
lib.concatStringsSep sep (lib.mapAttrsToList mapFn attrs);
|
||||
mkSection =
|
||||
sectName: sectValues:
|
||||
''
|
||||
@@ -25,7 +29,7 @@ let
|
||||
+ "}";
|
||||
in
|
||||
# map input to ini sections
|
||||
lib.concatMapAttrsStringSep "\n" mkSection attrsOfAttrs;
|
||||
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
|
||||
|
||||
configFile = pkgs.writeText "manticore.conf" (
|
||||
toSphinx {
|
||||
|
||||
@@ -405,9 +405,7 @@ in
|
||||
extraConfig = nginxAuthRequest + ''
|
||||
types {
|
||||
video/mp4 mp4;
|
||||
image/jpeg jpg jpeg;
|
||||
image/png png;
|
||||
image/webp webp;
|
||||
image/jpeg jpg;
|
||||
}
|
||||
|
||||
expires 7d;
|
||||
@@ -495,6 +493,19 @@ in
|
||||
}
|
||||
'';
|
||||
};
|
||||
# frontend uses this to fetch the version
|
||||
"/api/go2rtc/api" = {
|
||||
proxyPass = "http://frigate-go2rtc/api";
|
||||
recommendedProxySettings = true;
|
||||
extraConfig =
|
||||
nginxAuthRequest
|
||||
+ nginxProxySettings
|
||||
+ ''
|
||||
limit_except GET {
|
||||
deny all;
|
||||
}
|
||||
'';
|
||||
};
|
||||
# integrationn uses this to add webrtc candidate
|
||||
"/api/go2rtc/webrtc" = {
|
||||
proxyPass = "http://frigate-go2rtc/api/webrtc";
|
||||
@@ -530,7 +541,6 @@ in
|
||||
expires off;
|
||||
|
||||
proxy_cache frigate_api_cache;
|
||||
proxy_cache_key "$scheme$proxy_host$request_uri|$role|$groups|$user";
|
||||
proxy_cache_lock on;
|
||||
proxy_cache_use_stale updating;
|
||||
proxy_cache_valid 200 5s;
|
||||
@@ -553,13 +563,6 @@ in
|
||||
${nginxProxySettings}
|
||||
}
|
||||
|
||||
location /api/logout {
|
||||
auth_request off;
|
||||
rewrite ^/api(/.*)$ $1 break;
|
||||
proxy_pass http://frigate-api;
|
||||
${nginxProxySettings}
|
||||
}
|
||||
|
||||
location /api/auth/first_time_login {
|
||||
auth_request off;
|
||||
limit_except GET {
|
||||
@@ -744,6 +747,7 @@ in
|
||||
]
|
||||
++ optionals (!stdenv.hostPlatform.isAarch64) [
|
||||
# not available on aarch64-linux
|
||||
intel-gpu-tools
|
||||
rocmPackages.rocminfo
|
||||
];
|
||||
serviceConfig = {
|
||||
@@ -771,10 +775,11 @@ in
|
||||
Group = "frigate";
|
||||
SupplementaryGroups = [ "render" ] ++ optionals withCoral [ "coral" ];
|
||||
|
||||
# No capabilities
|
||||
CapabilityBoundingSet = [ "" ];
|
||||
AmbientCapabilities = optionals (elem cfg.vaapiDriver [
|
||||
"i965"
|
||||
"iHD"
|
||||
]) [ "CAP_PERFMON" ]; # for intel_gpu_top
|
||||
|
||||
# Allow delegating access
|
||||
UMask = "0027";
|
||||
|
||||
StateDirectory = "frigate";
|
||||
@@ -792,53 +797,9 @@ in
|
||||
|
||||
# Sockets/IPC
|
||||
RuntimeDirectory = "frigate";
|
||||
RemoveIPC = true;
|
||||
|
||||
# Reduce visible process scope to cgroup
|
||||
ProtectProc = "invisible";
|
||||
|
||||
# Allow wide /proc inspection, e.g. for cpuinfo
|
||||
ProcSubset = "all";
|
||||
|
||||
# Protect various system locations/interfaces
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectSystem = "strict";
|
||||
|
||||
# No JIT compilation
|
||||
MemoryDenyWriteExecute = true;
|
||||
|
||||
# No ABI personality changes
|
||||
LockPersonality = true;
|
||||
|
||||
# Only IP/Unix sockets
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_UNIX"
|
||||
];
|
||||
|
||||
# Deny namespace creation
|
||||
RestrictNamespaces = true;
|
||||
|
||||
# No privilege escalation
|
||||
NoNewPrivileges = true;
|
||||
RestrictSUIDSGID = true;
|
||||
|
||||
# No realtime schedulign
|
||||
RestrictRealtime = true;
|
||||
|
||||
# Restrict allowed syscalls
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged"
|
||||
];
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallErrorNumber = "EPERM";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1167,16 +1167,17 @@ in
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
assertions =
|
||||
optionals
|
||||
(
|
||||
cfg.config.":pleroma".":media_proxy".enabled
|
||||
-> cfg.config.":pleroma".":media_proxy".base_url != null;
|
||||
message = ''
|
||||
`services.akkoma.config.":pleroma".":media_proxy".base_url` must be set to a URL with a different host component (domain name) than the web endpoint when the media proxy is enabled.
|
||||
'';
|
||||
}
|
||||
];
|
||||
&& cfg.config.":pleroma".":media_proxy".base_url == null
|
||||
)
|
||||
[
|
||||
''
|
||||
`services.akkoma.config.":pleroma".":media_proxy".base_url` must be set when the media proxy is enabled.
|
||||
''
|
||||
];
|
||||
warnings =
|
||||
optionals (with config.security; cfg.installWrapper && (!sudo.enable) && (!sudo-rs.enable))
|
||||
[
|
||||
|
||||
@@ -202,7 +202,7 @@ in
|
||||
|
||||
(mkIf cfg.playwrightSupport {
|
||||
changedetection-io-playwright = {
|
||||
image = "docker.io/browserless/chrome";
|
||||
image = "browserless/chrome";
|
||||
environment = {
|
||||
SCREEN_WIDTH = "1920";
|
||||
SCREEN_HEIGHT = "1024";
|
||||
|
||||
@@ -116,7 +116,7 @@ in
|
||||
|
||||
services.phpfpm.pools.engelsystem = {
|
||||
user = "engelsystem";
|
||||
settings = lib.mapAttrs (_: v: lib.mkDefault v) {
|
||||
settings = {
|
||||
"listen.owner" = config.services.nginx.user;
|
||||
"pm" = "dynamic";
|
||||
"pm.max_children" = 32;
|
||||
|
||||
@@ -43,7 +43,7 @@ in
|
||||
type = types.submodule { freeformType = types.attrsOf (types.nullOr types.str); };
|
||||
defaultText = lib.literalExpression ''
|
||||
{
|
||||
HBOX_STORAGE_CONN_STRING = "file:///var/lib/homebox?no_tmp_dir=1";
|
||||
HBOX_STORAGE_CONN_STRING = "file:///var/lib/homebox";
|
||||
HBOX_STORAGE_PREFIX_PATH = "data";
|
||||
HBOX_DATABASE_DRIVER = "sqlite3";
|
||||
HBOX_DATABASE_SQLITE_PATH = "/var/lib/homebox/data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1";
|
||||
@@ -51,6 +51,7 @@ in
|
||||
HBOX_OPTIONS_GITHUB_RELEASE_CHECK = "false";
|
||||
HBOX_MODE = "production";
|
||||
HOME = "/var/lib/homebox";
|
||||
TMPDIR = "/var/lib/homebox/tmp";
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
@@ -124,9 +125,7 @@ in
|
||||
|
||||
services.homebox.settings = lib.mkMerge [
|
||||
(lib.mapAttrs (_: mkDefault) {
|
||||
# We cannot use a tempdir as homebox wants to rename the file, which does not work across filesystem boundaries
|
||||
# also see: https://github.com/google/go-cloud/issues/3294 and https://pkg.go.dev/gocloud.dev/blob/fileblob#URLOpener
|
||||
HBOX_STORAGE_CONN_STRING = "file:///var/lib/homebox?no_tmp_dir=1";
|
||||
HBOX_STORAGE_CONN_STRING = "file:///var/lib/homebox";
|
||||
HBOX_STORAGE_PREFIX_PATH = "data";
|
||||
HBOX_DATABASE_DRIVER = "sqlite3";
|
||||
HBOX_DATABASE_SQLITE_PATH = "/var/lib/homebox/data/homebox.db?_pragma=busy_timeout=999&_pragma=journal_mode=WAL&_fk=1";
|
||||
@@ -135,8 +134,10 @@ in
|
||||
HBOX_MODE = "production";
|
||||
# Fix this startup issue:
|
||||
# failed to create modcache index dir: mkdir /var/empty/.cache: read-only file system
|
||||
# TODO: remove once https://github.com/golang/tools/commit/03cb4551c662c0e078502fe5f317ca4114b89cd8 is available
|
||||
HOME = "/var/lib/homebox";
|
||||
# Fix uploading/saving attachments/images:
|
||||
# [...] rename /tmp/ced4804c80b1ed1f6e88060f6d829db421e6dbf3a189715265900b5d6b0243ed.1889b3d16ab36e22.tmp /var/lib/homebox/data/5f42f81b-e9ad-4495-b6a6-9e9f704db30e/documents/ced4804c80b1ed1f6e88060f6d829db421e6dbf3a189715265900b5d6b0243ed: invalid cross-device link" [...]
|
||||
TMPDIR = "/var/lib/homebox/tmp";
|
||||
})
|
||||
|
||||
(mkIf cfg.database.createLocally {
|
||||
|
||||
@@ -434,10 +434,10 @@ in
|
||||
package = lib.mkOption {
|
||||
type = types.package;
|
||||
default =
|
||||
if lib.versionAtLeast config.system.stateVersion "26.11" then pkgs.netbox_4_7 else pkgs.netbox_4_5;
|
||||
if lib.versionAtLeast config.system.stateVersion "26.11" then pkgs.netbox_4_6 else pkgs.netbox_4_5;
|
||||
defaultText = lib.literalExpression ''
|
||||
if lib.versionAtLeast config.system.stateVersion "26.11" then
|
||||
pkgs.netbox_4_7
|
||||
pkgs.netbox_4_6
|
||||
else
|
||||
pkgs.netbox_4_5;
|
||||
'';
|
||||
@@ -563,15 +563,6 @@ in
|
||||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
cfg.postgresql.createLocally
|
||||
-> lib.versionAtLeast config.services.postgresql.finalPackage.version "15";
|
||||
message = "NetBox requires PostgreSQL >= 15. Please read the NixOS manual to upgrade your PostgreSQL version.";
|
||||
}
|
||||
];
|
||||
|
||||
services.netbox.plugins = lib.mkIf enableLDAP (ps: [ ps.django-auth-ldap ]);
|
||||
|
||||
services.redis.servers.netbox.enable = cfg.redis.createLocally;
|
||||
@@ -742,6 +733,26 @@ in
|
||||
PrivateTmp = true;
|
||||
};
|
||||
};
|
||||
|
||||
netbox-housekeeping = defaultUnitConfig // {
|
||||
description = "NetBox housekeeping job";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
after = [
|
||||
"network-online.target"
|
||||
"netbox.service"
|
||||
];
|
||||
wants = [ "network-online.target" ];
|
||||
|
||||
serviceConfig = defaultServiceConfig // {
|
||||
Type = "oneshot";
|
||||
ExecStart = toString [
|
||||
(lib.getExe finalPackage)
|
||||
"housekeeping"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.netbox-housekeeping = {
|
||||
|
||||
@@ -12,13 +12,6 @@ let
|
||||
scheme = if cfg.ssl.enable then "https" else "http";
|
||||
|
||||
configFile = settingsFormat.generate "rundeck-config.properties" cfg.settings;
|
||||
|
||||
jaasLoginModuleClass =
|
||||
if lib.versionAtLeast cfg.package.version "6" then
|
||||
"org.rundeck.jaas.PropertyFileLoginModule"
|
||||
else
|
||||
"org.eclipse.jetty.jaas.spi.PropertyFileLoginModule";
|
||||
|
||||
frameworkFile = settingsFormat.generate "framework.properties" cfg.frameworkSettings;
|
||||
|
||||
realmFile = pkgs.writeText "realm.properties" ''
|
||||
@@ -140,43 +133,7 @@ in
|
||||
|
||||
aclPolicies = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
default = {
|
||||
"admin.aclpolicy" = ''
|
||||
description: Admin, all access.
|
||||
context:
|
||||
project: '.*'
|
||||
for:
|
||||
resource:
|
||||
- allow: '*'
|
||||
adhoc:
|
||||
- allow: '*'
|
||||
job:
|
||||
- allow: '*'
|
||||
node:
|
||||
- allow: '*'
|
||||
runner:
|
||||
- allow: '*'
|
||||
by:
|
||||
group: admin
|
||||
|
||||
---
|
||||
|
||||
description: Admin, all access.
|
||||
context:
|
||||
application: 'rundeck'
|
||||
for:
|
||||
resource:
|
||||
- allow: '*'
|
||||
project:
|
||||
- allow: '*'
|
||||
project_acl:
|
||||
- allow: '*'
|
||||
storage:
|
||||
- allow: '*'
|
||||
by:
|
||||
group: admin
|
||||
'';
|
||||
};
|
||||
default = { };
|
||||
description = "ACL policies for Rundeck, where the attribute name is the filename and the value is the policy content";
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
@@ -536,9 +493,9 @@ in
|
||||
group = cfg.group;
|
||||
text = ''
|
||||
RDpropertyfilelogin {
|
||||
${jaasLoginModuleClass} required
|
||||
org.eclipse.jetty.jaas.spi.PropertyFileLoginModule required
|
||||
debug="true"
|
||||
file="${cfg.configDir}/realm.properties";
|
||||
file="/etc/rundeck/realm.properties";
|
||||
};
|
||||
'';
|
||||
};
|
||||
@@ -674,7 +631,9 @@ in
|
||||
replaceSecret "@SERVER_UUID@" "${cfg.dataDir}/.uuid" "${cfg.configDir}/framework.properties"
|
||||
)}
|
||||
|
||||
install -C -m 0640 ${cfg.configDir}/framework.properties ${cfg.dataDir}/etc/framework.properties
|
||||
if [ -f ${cfg.dataDir}/etc/framework.properties ]; then
|
||||
install -m 0640 ${cfg.configDir}/framework.properties ${cfg.dataDir}/etc/framework.properties
|
||||
fi
|
||||
|
||||
${lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (
|
||||
|
||||
@@ -555,33 +555,7 @@ in
|
||||
before = [ "phpfpm-wordpress-${hostName}.service" ];
|
||||
after = optional cfg.database.createLocally "mysql.service";
|
||||
script = secretsScript (stateDir hostName);
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = user;
|
||||
Group = webserver.group;
|
||||
};
|
||||
})
|
||||
) eachSite)
|
||||
|
||||
(mapAttrs' (
|
||||
hostName: cfg:
|
||||
(nameValuePair "wordpress-migrate-database-${hostName}" {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [
|
||||
"phpfpm-wordpress-${hostName}.service"
|
||||
]
|
||||
++ optional cfg.database.createLocally "mysql.service";
|
||||
script = ''
|
||||
# Auto migrate database after version update
|
||||
versionFile="${stateDir hostName}/src-version"
|
||||
version=$(cat "$versionFile" 2>/dev/null || echo 0)
|
||||
if [[ $version != 0 && $version != ${cfg.package.version} ]]; then
|
||||
echo "Executing database migration"
|
||||
${lib.getExe pkgs.wp-cli} --path="${cfg.finalPackage}/share/wordpress" \
|
||||
--skip-plugins --skip-themes core update-db
|
||||
fi
|
||||
echo ${cfg.package.version} > "$versionFile"
|
||||
'';
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = user;
|
||||
|
||||
@@ -9,10 +9,6 @@ let
|
||||
cfg = config.boot.kexec;
|
||||
in
|
||||
{
|
||||
meta = {
|
||||
inherit (pkgs.kexec-tools.meta) maintainers;
|
||||
};
|
||||
|
||||
options.boot.kexec = {
|
||||
enable = lib.mkEnableOption "kexec" // {
|
||||
default = lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.kexec-tools;
|
||||
|
||||
@@ -34,7 +34,6 @@ in
|
||||
thin_repair = "${pkgs."thin-provisioning-tools"}/bin/thin_repair";
|
||||
thin_metadata_size = "${pkgs."thin-provisioning-tools"}/bin/thin_metadata_size";
|
||||
stratis-min = "${pkgs.stratisd}/bin/stratis-min";
|
||||
cryptsetup = "${pkgs.cryptsetup}/bin/cryptsetup";
|
||||
};
|
||||
services = lib.attrsets.mapAttrs' (mountPoint: fileSystem: {
|
||||
name = "stratis-setup-${fileSystem.stratis.poolUuid}";
|
||||
|
||||
@@ -22,14 +22,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
syslogConf = {
|
||||
services.adguardhome = {
|
||||
enable = true;
|
||||
|
||||
settings.log.file = "syslog";
|
||||
};
|
||||
};
|
||||
|
||||
declarativeConf = {
|
||||
services.adguardhome = {
|
||||
enable = true;
|
||||
@@ -135,12 +127,6 @@
|
||||
schemaVersionBefore23.wait_for_unit("adguardhome.service")
|
||||
schemaVersionBefore23.wait_for_open_port(3000)
|
||||
|
||||
with subtest("Logging to syslog test"):
|
||||
# AdGuard is expected to fail when it cannot connect to syslog
|
||||
# hence its sufficient to look whether the service starts at all
|
||||
syslogConf.wait_for_unit("adguardhome.service")
|
||||
syslogConf.wait_for_open_port(3000)
|
||||
|
||||
with subtest("Declarative config test, DNS will be reachable"):
|
||||
declarativeConf.wait_for_unit("adguardhome.service")
|
||||
declarativeConf.wait_for_open_port(53)
|
||||
|
||||
@@ -402,10 +402,22 @@ in
|
||||
ceph-multi-node-bluestore-cephfs = runTestOn [ "aarch64-linux" "x86_64-linux" ] (
|
||||
import ./ceph-multi-node-bluestore.nix { withCephfs = true; }
|
||||
);
|
||||
ceph-multi-node-deprecated-filestore = runTestOn [
|
||||
"aarch64-linux"
|
||||
"x86_64-linux"
|
||||
] ./ceph-multi-node-deprecated-filestore.nix;
|
||||
ceph-single-node-bluestore = runTestOn [
|
||||
"aarch64-linux"
|
||||
"x86_64-linux"
|
||||
] ./ceph-single-node-bluestore.nix;
|
||||
ceph-single-node-bluestore-dmcrypt = runTestOn [
|
||||
"aarch64-linux"
|
||||
"x86_64-linux"
|
||||
] ./ceph-single-node-bluestore-dmcrypt.nix;
|
||||
ceph-single-node-deprecated-filestore = runTestOn [
|
||||
"aarch64-linux"
|
||||
"x86_64-linux"
|
||||
] ./ceph-single-node-deprecated-filestore.nix;
|
||||
certmgr = import ./certmgr.nix { inherit pkgs runTest; };
|
||||
cfssl = runTestOn [ "aarch64-linux" "x86_64-linux" ] ./cfssl.nix;
|
||||
cgit = runTest ./cgit.nix;
|
||||
@@ -647,6 +659,10 @@ in
|
||||
imports = [ ./firefox.nix ];
|
||||
_module.args.firefoxPackage = pkgs.firefox;
|
||||
};
|
||||
firefox-beta = runTest {
|
||||
imports = [ ./firefox.nix ];
|
||||
_module.args.firefoxPackage = pkgs.firefox-beta;
|
||||
};
|
||||
firefox-devedition = runTest {
|
||||
imports = [ ./firefox.nix ];
|
||||
_module.args.firefoxPackage = pkgs.firefox-devedition;
|
||||
@@ -656,6 +672,10 @@ in
|
||||
imports = [ ./firefox.nix ];
|
||||
_module.args.firefoxPackage = pkgs.firefox-esr;
|
||||
};
|
||||
firefox-esr-140 = runTest {
|
||||
imports = [ ./firefox.nix ];
|
||||
_module.args.firefoxPackage = pkgs.firefox-esr-140;
|
||||
};
|
||||
firefox-esr-153 = runTest {
|
||||
imports = [ ./firefox.nix ];
|
||||
_module.args.firefoxPackage = pkgs.firefox-esr-153;
|
||||
@@ -1262,7 +1282,6 @@ in
|
||||
nginx-modsecurity = runTest ./nginx-modsecurity.nix;
|
||||
nginx-moreheaders = runTest ./nginx-moreheaders.nix;
|
||||
nginx-njs = runTest ./nginx-njs.nix;
|
||||
nginx-otel = runTest ./nginx-otel.nix;
|
||||
nginx-proxyprotocol = runTest ./nginx-proxyprotocol/default.nix;
|
||||
nginx-pubhtml = runTest ./nginx-pubhtml.nix;
|
||||
nginx-redirectcode = runTest ./nginx-redirectcode.nix;
|
||||
@@ -1303,9 +1322,6 @@ in
|
||||
nixos-rebuild-target-host = runTest {
|
||||
imports = [ ./nixos-rebuild-target-host.nix ];
|
||||
};
|
||||
nixos-rebuild-target-host-interrupted = runTest {
|
||||
imports = [ ./nixos-rebuild-target-host-interrupted.nix ];
|
||||
};
|
||||
nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; };
|
||||
nixpkgs-config-allow-unfree =
|
||||
pkgs.callPackage ../modules/misc/nixpkgs/test-nixpkgs-config-allow-unfree.nix
|
||||
|
||||
@@ -59,50 +59,6 @@
|
||||
openFirewall = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Only inspected by the test script, never activated: the VM has no GPU,
|
||||
# but the generated units can still be checked.
|
||||
specialisation."gpu-sysfs".configuration = {
|
||||
services.beszel.agent = {
|
||||
enable = true;
|
||||
environment.GPU_COLLECTOR = [ "amd_sysfs" ];
|
||||
};
|
||||
};
|
||||
|
||||
specialisation."gpu-devices".configuration = {
|
||||
services.beszel.agent = {
|
||||
enable = true;
|
||||
environment.GPU_COLLECTOR = [ "intel_gpu_top" ];
|
||||
};
|
||||
};
|
||||
|
||||
specialisation."gpu-smartmon".configuration = {
|
||||
services.beszel.agent = {
|
||||
enable = true;
|
||||
# upstream's comma-separated form is accepted as well
|
||||
environment.GPU_COLLECTOR = "intel_gpu_top";
|
||||
smartmon = {
|
||||
enable = true;
|
||||
deviceAllow = [ "/dev/nvme0" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
specialisation."zfs".configuration = {
|
||||
networking.hostId = "8425e349";
|
||||
boot.supportedFilesystems = [ "zfs" ];
|
||||
services.beszel.agent.enable = true;
|
||||
};
|
||||
|
||||
specialisation."gpu-skipped".configuration = {
|
||||
services.beszel.agent = {
|
||||
enable = true;
|
||||
environment = {
|
||||
SKIP_GPU = true;
|
||||
GPU_COLLECTOR = [ "intel_gpu_top" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -111,13 +67,6 @@
|
||||
let
|
||||
hubCfg = nodes.hubHost.services.beszel.hub;
|
||||
agentCfg = nodes.agentHost.specialisation."agent".configuration.services.beszel.agent;
|
||||
# /run/current-system points at the "agent" specialisation after the switch,
|
||||
# so the units are read from the store directly.
|
||||
gpuUnit =
|
||||
name:
|
||||
"${
|
||||
nodes.agentHost.specialisation.${name}.configuration.system.build.toplevel
|
||||
}/etc/systemd/system/beszel-agent.service";
|
||||
in
|
||||
''
|
||||
import json
|
||||
@@ -166,41 +115,5 @@
|
||||
agentHost.wait_for_unit("beszel-agent.service")
|
||||
agentHost.wait_until_succeeds("journalctl -eu beszel-agent --grep 'SSH connection established'")
|
||||
agentHost.wait_until_succeeds(f'curl -H \'Authorization: {user["token"]}\' -f ${agentCfg.environment.HUB_URL}/api/collections/systems/records | jq -e \'.items[].status == "up"\' ')
|
||||
|
||||
with subtest("Agent stays sandboxed without a GPU"):
|
||||
agentHost.succeed("systemctl show beszel-agent -p PrivateDevices --value | grep -qx yes")
|
||||
agentHost.succeed("systemctl show beszel-agent -p PrivateUsers --value | grep -qx yes")
|
||||
|
||||
with subtest("GPU collectors shape the unit"):
|
||||
# sysfs-only collector keeps the sandbox
|
||||
sysfs = agentHost.succeed("cat ${gpuUnit "gpu-sysfs"}")
|
||||
assert "PrivateDevices=true" in sysfs, sysfs
|
||||
assert "PrivateUsers=true" in sysfs, sysfs
|
||||
assert "intel-gpu-tools" not in sysfs, sysfs
|
||||
|
||||
# device-based collector gets its devices as an allow-list, and
|
||||
# CAP_PERFMON/perf_event_open with the user namespace disabled
|
||||
devices = agentHost.succeed("cat ${gpuUnit "gpu-devices"}")
|
||||
assert "PrivateDevices=false" in devices, devices
|
||||
assert "PrivateUsers=false" in devices, devices
|
||||
assert "DeviceAllow=char-drm rw" in devices, devices
|
||||
assert "CAP_PERFMON" in devices, devices
|
||||
assert "perf_event_open" in devices, devices
|
||||
|
||||
# GPU devices must survive smartmon's DeviceAllow list
|
||||
smartmon = agentHost.succeed("cat ${gpuUnit "gpu-smartmon"}")
|
||||
assert "DeviceAllow=/dev/nvme0 r" in smartmon, smartmon
|
||||
assert "DeviceAllow=char-drm rw" in smartmon, smartmon
|
||||
|
||||
# zfs only gets /dev/zfs, but needs the host user namespace
|
||||
zfs = agentHost.succeed("cat ${gpuUnit "zfs"}")
|
||||
assert "PrivateDevices=true" in zfs, zfs
|
||||
assert "DeviceAllow=/dev/zfs rw" in zfs, zfs
|
||||
assert "PrivateUsers=false" in zfs, zfs
|
||||
|
||||
# SKIP_GPU wins over an explicitly configured collector
|
||||
skipped = agentHost.succeed("cat ${gpuUnit "gpu-skipped"}")
|
||||
assert "PrivateDevices=true" in skipped, skipped
|
||||
assert "intel-gpu-tools" not in skipped, skipped
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -25,16 +25,19 @@ let
|
||||
osd0 = {
|
||||
name = "0";
|
||||
ip = "192.168.1.2";
|
||||
key = "AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==";
|
||||
uuid = "55ba2294-3e24-478f-bee0-9dca4c231dd9";
|
||||
};
|
||||
osd1 = {
|
||||
name = "1";
|
||||
ip = "192.168.1.3";
|
||||
key = "AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==";
|
||||
uuid = "5e97a838-85b6-43b0-8950-cb56d554d1e5";
|
||||
};
|
||||
osd2 = {
|
||||
name = "2";
|
||||
ip = "192.168.1.4";
|
||||
key = "AQAdyhZeIaUlARAAGRoidDAmS6Vkp546UFEf5w==";
|
||||
uuid = "ea999274-13d0-4dd5-9af9-ad25a324f72f";
|
||||
};
|
||||
# Client that mounts CephFS using the in-kernel client.
|
||||
@@ -55,14 +58,6 @@ let
|
||||
monHost = cfg.monA.ip;
|
||||
monInitialMembers = cfg.monA.name;
|
||||
};
|
||||
extraConfig = {
|
||||
log_to_syslog = "false";
|
||||
log_to_file = "false";
|
||||
log_to_stderr = "true";
|
||||
debug_rocksdb = "1/5";
|
||||
debug_mgr = "1/5";
|
||||
mon_host = "v2:${cfg.monA.ip}:3300 v1:${cfg.monA.ip}:6789";
|
||||
};
|
||||
}
|
||||
// daemonConfig;
|
||||
|
||||
@@ -86,7 +81,6 @@ let
|
||||
bash
|
||||
sudo
|
||||
ceph
|
||||
cryptsetup
|
||||
netcat
|
||||
];
|
||||
|
||||
@@ -151,11 +145,6 @@ let
|
||||
enable = true;
|
||||
daemons = [ cfg.monA.name ];
|
||||
};
|
||||
# TODO: move this to a separate machine
|
||||
rgw = {
|
||||
enable = true;
|
||||
daemons = [ cfg.monA.name ];
|
||||
};
|
||||
}
|
||||
# The MDS daemon (which provides CephFS) is only configured in the CephFS
|
||||
# variant of this test.
|
||||
@@ -220,11 +209,6 @@ let
|
||||
vlans = [ 1 ];
|
||||
};
|
||||
|
||||
# Ceph 20.2.4 introduced the aes256k cipher for authentication.
|
||||
# Linux started supporting these in kernel version 7.0.
|
||||
# Remove this line at the earliest convenience (i.e. when tests are run by 7.0 or higher by default).
|
||||
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||
|
||||
networking = networkConfig;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
@@ -301,8 +285,6 @@ let
|
||||
# Based on the "manual deployment" approach from:
|
||||
# https://docs.ceph.com/en/tentacle/install/manual-deployment/
|
||||
baseScript = ''
|
||||
import json
|
||||
|
||||
start_all()
|
||||
|
||||
monA.wait_for_unit("network.target")
|
||||
@@ -315,15 +297,14 @@ let
|
||||
"sudo -u ceph ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
|
||||
"sudo -u ceph ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
|
||||
"sudo -u ceph ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
|
||||
# Creating the mon with v2 (and a legacy v1) address right away removes the need for running `enable-msgr2` later on.
|
||||
# It is also makes the test more consistent by fixing the address to a known value instead of letting it derive the address.
|
||||
"monmaptool --create --addv ${cfg.monA.name} '[v2:${cfg.monA.ip}:3300,v1:${cfg.monA.ip}:6789]' --auth-allowed-ciphers aes256k --auth-preferred-cipher aes256k --auth-service-cipher aes256k --fsid ${cfg.clusterId} /tmp/monmap",
|
||||
"monmaptool --create --add ${cfg.monA.name} ${cfg.monA.ip} --fsid ${cfg.clusterId} /tmp/monmap",
|
||||
"sudo -u ceph ceph-mon --mkfs -i ${cfg.monA.name} --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring",
|
||||
"sudo -u ceph mkdir -p /var/lib/ceph/mgr/ceph-${cfg.monA.name}/",
|
||||
"sudo -u ceph touch /var/lib/ceph/mon/ceph-${cfg.monA.name}/done",
|
||||
"systemctl start ceph-mon-${cfg.monA.name}",
|
||||
)
|
||||
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
|
||||
monA.succeed("ceph mon enable-msgr2")
|
||||
monA.succeed("ceph config set mon auth_allow_insecure_global_id_reclaim false")
|
||||
|
||||
# Can't check ceph status until a mon is up
|
||||
@@ -339,63 +320,59 @@ let
|
||||
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
|
||||
|
||||
# Send the bootstrap-osd keyring to the OSD machines.
|
||||
monA.succeed("ceph auth get client.bootstrap-osd -o /etc/ceph/ceph.client.bootstrap-osd.keyring")
|
||||
monA.succeed("cp /etc/ceph/ceph.client.bootstrap-osd.keyring /tmp/shared")
|
||||
# Send the admin keyring to the OSD machines.
|
||||
monA.succeed("cp /etc/ceph/ceph.client.admin.keyring /tmp/shared")
|
||||
osd0.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
|
||||
osd1.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
|
||||
osd2.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
|
||||
|
||||
# Bootstrap the BlueStore OSDs.
|
||||
#
|
||||
# The steps for this are roughly the same for all OSDs:
|
||||
# 1. get the bootstrap-osd keyring
|
||||
# 2. prepare the osd via ceph-volume lvm, the second line contains the OSD specific configuration
|
||||
# 3. deactivate it to unmount the tmpfs
|
||||
# 4. activate it without a tmpfs for persistent data
|
||||
# 5. sync, so the osd has at least one consistent state saved
|
||||
# 6. start it
|
||||
|
||||
# osd.0: plain
|
||||
osd0.succeed(
|
||||
"mkdir -p /var/lib/ceph/bootstrap-osd",
|
||||
"cp /tmp/shared/ceph.client.bootstrap-osd.keyring /var/lib/ceph/bootstrap-osd/ceph.keyring",
|
||||
"ceph-volume lvm prepare --objectstore bluestore --no-systemd --osd-id ${cfg.osd0.name} --osd-fsid ${cfg.osd0.uuid} "
|
||||
"--data /dev/vdb",
|
||||
"ceph-volume lvm deactivate ${cfg.osd0.name} ${cfg.osd0.uuid}",
|
||||
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd0.name} ${cfg.osd0.uuid}",
|
||||
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd0.name}",
|
||||
"echo bluestore > /var/lib/ceph/osd/ceph-${cfg.osd0.name}/type",
|
||||
"ln -sf /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd0.name}/block",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd0.name}/keyring --name osd.${cfg.osd0.name} --add-key ${cfg.osd0.key}",
|
||||
'echo \'{"cephx_secret": "${cfg.osd0.key}"}\' | ceph osd new ${cfg.osd0.uuid} -i -',
|
||||
)
|
||||
osd1.succeed(
|
||||
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd1.name}",
|
||||
"echo bluestore > /var/lib/ceph/osd/ceph-${cfg.osd1.name}/type",
|
||||
"ln -sf /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd1.name}/block",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd1.name}/keyring --name osd.${cfg.osd1.name} --add-key ${cfg.osd1.key}",
|
||||
'echo \'{"cephx_secret": "${cfg.osd1.key}"}\' | ceph osd new ${cfg.osd1.uuid} -i -',
|
||||
)
|
||||
osd2.succeed(
|
||||
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
|
||||
"echo bluestore > /var/lib/ceph/osd/ceph-${cfg.osd2.name}/type",
|
||||
"ln -sf /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd2.name}/block",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd2.name}/keyring --name osd.${cfg.osd2.name} --add-key ${cfg.osd2.key}",
|
||||
'echo \'{"cephx_secret": "${cfg.osd2.key}"}\' | ceph osd new ${cfg.osd2.uuid} -i -',
|
||||
)
|
||||
|
||||
# We `sync` so that the config survives the forced crashes below.
|
||||
osd0.succeed(
|
||||
"ceph-osd -i ${cfg.osd0.name} --mkfs --osd-uuid ${cfg.osd0.uuid}",
|
||||
"chown -R ceph:ceph /var/lib/ceph/osd",
|
||||
"sync",
|
||||
"systemctl start ceph-osd-${cfg.osd0.name}",
|
||||
)
|
||||
# osd.1: plain
|
||||
osd1.succeed(
|
||||
"mkdir -p /var/lib/ceph/bootstrap-osd",
|
||||
"cp /tmp/shared/ceph.client.bootstrap-osd.keyring /var/lib/ceph/bootstrap-osd/ceph.keyring",
|
||||
"ceph-volume lvm prepare --objectstore bluestore --no-systemd --osd-id ${cfg.osd1.name} --osd-fsid ${cfg.osd1.uuid} "
|
||||
"--data /dev/vdb --dmcrypt",
|
||||
"ceph-volume lvm deactivate ${cfg.osd1.name} ${cfg.osd1.uuid}",
|
||||
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd1.name} ${cfg.osd1.uuid}",
|
||||
"ceph-osd -i ${cfg.osd1.name} --mkfs --osd-uuid ${cfg.osd1.uuid}",
|
||||
"chown -R ceph:ceph /var/lib/ceph/osd",
|
||||
"sync",
|
||||
"systemctl start ceph-osd-${cfg.osd1.name}",
|
||||
)
|
||||
# osd.2: plain
|
||||
osd2.succeed(
|
||||
"mkdir -p /var/lib/ceph/bootstrap-osd",
|
||||
"cp /tmp/shared/ceph.client.bootstrap-osd.keyring /var/lib/ceph/bootstrap-osd/ceph.keyring",
|
||||
"ceph-volume lvm prepare --objectstore bluestore --no-systemd --osd-fsid ${cfg.osd2.uuid} --osd-id ${cfg.osd2.name} "
|
||||
"--data /dev/vdb",
|
||||
"ceph-volume lvm deactivate ${cfg.osd2.name} ${cfg.osd2.uuid}",
|
||||
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd2.name} ${cfg.osd2.uuid}",
|
||||
"ceph-osd -i ${cfg.osd2.name} --mkfs --osd-uuid ${cfg.osd2.uuid}",
|
||||
"chown -R ceph:ceph /var/lib/ceph/osd",
|
||||
"sync",
|
||||
"systemctl start ceph-osd-${cfg.osd2.name}",
|
||||
)
|
||||
|
||||
|
||||
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
|
||||
monA.succeed(
|
||||
# Autoscaling will cause PGs to be peering, causing the tests to become flakey.
|
||||
"ceph osd pool set noautoscale",
|
||||
|
||||
"ceph osd pool create multi-node-test 32 32",
|
||||
"ceph osd pool ls | grep 'multi-node-test'",
|
||||
|
||||
@@ -412,7 +389,6 @@ let
|
||||
"ceph osd pool ls | grep 'multi-node-other-test'",
|
||||
)
|
||||
monA.succeed("ceph osd pool set multi-node-other-test size 2")
|
||||
# TODO: actually write to the pool using rados directly
|
||||
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
monA.wait_until_succeeds("! ceph -s | grep -e 'unknown' -e 'pgs inactive'")
|
||||
monA.fail(
|
||||
@@ -420,100 +396,23 @@ let
|
||||
"ceph osd pool delete multi-node-other-test multi-node-other-test --yes-i-really-really-mean-it",
|
||||
)
|
||||
|
||||
# Bootstrap RGW
|
||||
monA.succeed(
|
||||
"sudo -u ceph mkdir -p /var/lib/ceph/radosgw/ceph-${cfg.monA.name}",
|
||||
"ceph auth get-or-create client.${cfg.monA.name} osd 'allow rwx' mon 'allow rw' > /var/lib/ceph/radosgw/ceph-${cfg.monA.name}/keyring",
|
||||
"chown ceph:ceph /var/lib/ceph/radosgw/ceph-${cfg.monA.name}/keyring",
|
||||
"systemctl start ceph-rgw-${cfg.monA.name}",
|
||||
)
|
||||
monA.wait_for_unit("ceph-rgw-${cfg.monA.name}")
|
||||
monA.wait_for_open_port(7480)
|
||||
|
||||
# Enable the dashboard and recheck health
|
||||
monA.succeed(
|
||||
"ceph mgr module enable dashboard",
|
||||
"ceph config set mgr mgr/dashboard/ssl false",
|
||||
# default is 8080 but it's better to be explicit
|
||||
"ceph config set mgr mgr/dashboard/server_port 8080",
|
||||
)
|
||||
|
||||
# The dashboard does not listen on localhost:
|
||||
# `server_addr` defaults to the wildcard address, but the dashboard module
|
||||
# resolves that to the active mgr's own IP and binds only to it,
|
||||
# so loopback is never bound.
|
||||
# See https://github.com/ceph/ceph/blob/v20.2.2/src/pybind/mgr/dashboard/module.py#L213-L214
|
||||
# Therefore address the dashboard via the mgr's IP instead of localhost.
|
||||
dashboard = "http://${cfg.monA.ip}:8080"
|
||||
|
||||
monA.wait_for_open_port(8080, addr="${cfg.monA.ip}")
|
||||
monA.wait_until_succeeds(f"curl -s --fail {dashboard}")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
|
||||
# Initialize dashboard creds.
|
||||
# In a the query below, we test the Dashboard's `/api/rgw/daemon`,
|
||||
# which needs that the dashboard can talk to RGW.
|
||||
# `set-rgw-credentials` needs a running RGW daemon.
|
||||
monA.succeed(
|
||||
"echo 'foo bar baz qux' > /tmp/dashboard_pw",
|
||||
"ceph dashboard ac-user-create admin -i /tmp/dashboard_pw administrator",
|
||||
"ceph dashboard set-rgw-credentials",
|
||||
"sync",
|
||||
)
|
||||
|
||||
# Get dashboard auth token
|
||||
auth_payload = json.dumps({"username": "admin", "password": "foo bar baz qux"})
|
||||
auth_response = json.loads(monA.succeed(
|
||||
f"curl --fail -s -X POST -H 'Accept: application/vnd.ceph.api.v1.0+json' -H 'Content-Type: application/json' -d '{auth_payload}' {dashboard}/api/auth",
|
||||
))
|
||||
token = auth_response["token"]
|
||||
|
||||
# Check cluster health via dashboard API
|
||||
health = json.loads(monA.succeed(
|
||||
f"curl --fail -s -H 'Accept: application/vnd.ceph.api.v1.0+json' -H 'Authorization: Bearer {token}' {dashboard}/api/health/minimal",
|
||||
))
|
||||
assert health["health"]["status"] == "HEALTH_OK"
|
||||
|
||||
# List daemons via REST API.
|
||||
# This also requires a running RGW daemon, as it asserts on the first one.
|
||||
rgw_daemons = json.loads(monA.succeed(
|
||||
f"curl --fail -s -H 'Accept: application/vnd.ceph.api.v1.0+json' -H 'Authorization: Bearer {token}' {dashboard}/api/rgw/daemon",
|
||||
))
|
||||
assert rgw_daemons[0]["id"] == "${cfg.monA.name}"
|
||||
|
||||
# Shut down ceph on all machines in a very unpolite way
|
||||
monA.crash()
|
||||
osd0.crash()
|
||||
osd1.crash()
|
||||
osd2.crash()
|
||||
|
||||
# Start the mon first and mark the OSDs as down.
|
||||
# Since the heartbeats are pretty high by default, the OSDs would otherwise be marked as up still.
|
||||
# However we do not want to lower the heartbeats since this might cause flakey tests.
|
||||
monA.start()
|
||||
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
|
||||
monA.wait_until_succeeds("ceph osd down all")
|
||||
# Then start the OSDs as normal.
|
||||
# Start it up
|
||||
osd0.start()
|
||||
osd1.start()
|
||||
osd2.start()
|
||||
# Ensure they are all up.
|
||||
osd0.wait_for_unit("network.target")
|
||||
osd1.wait_for_unit("network.target")
|
||||
osd2.wait_for_unit("network.target")
|
||||
monA.start()
|
||||
|
||||
# FIXME: dmcrypt OSDs currently do not work out of the box.
|
||||
# For a potential long-term fix see: https://github.com/NixOS/nixpkgs/pull/512912#discussion_r3140295546
|
||||
osd1.succeed(
|
||||
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd1.name} ${cfg.osd1.uuid}",
|
||||
"systemctl start ceph-osd-${cfg.osd1.name}",
|
||||
)
|
||||
|
||||
# Test the cluster state thoroughly.
|
||||
# Ensure the cluster comes back up again.
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mon: 1 daemons'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
|
||||
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
|
||||
# Verify the recovery.
|
||||
@@ -545,50 +444,45 @@ let
|
||||
|
||||
# Create a CephFS.
|
||||
monA.succeed(
|
||||
"ceph fs volume create testing",
|
||||
"ceph osd pool set cephfs.testing.data pg_num 32",
|
||||
"ceph osd pool set cephfs.testing.meta pg_num 32",
|
||||
"ceph osd pool create cephfs-data 32 32",
|
||||
"ceph osd pool create cephfs-metadata 32 32",
|
||||
"ceph fs new cephfs cephfs-metadata cephfs-data",
|
||||
)
|
||||
# Wait for the MDS to claim the filesystem and become active.
|
||||
monA.wait_until_succeeds("ceph fs status testing | grep -e 'active'", timeout=60)
|
||||
monA.wait_until_succeeds("ceph fs status cephfs | grep -e 'active'", timeout=60)
|
||||
|
||||
# Create a subvolume, issue credentials, then distribute those credentials.
|
||||
# Distribute the admin keyring (and a plain secret file for the kernel
|
||||
# client) to both client machines, so that they can authenticate.
|
||||
monA.succeed(
|
||||
"ceph fs subvolumegroup create testing group",
|
||||
"ceph fs subvolume create testing subvolume --group_name group",
|
||||
"ceph fs subvolume authorize testing subvolume kclient group",
|
||||
"ceph fs subvolume authorize testing subvolume fuseclient group",
|
||||
"ceph auth get client.kclient -o /tmp/shared/ceph.client.kclient.keyring",
|
||||
"ceph auth get client.fuseclient -o /tmp/shared/ceph.client.fuseclient.keyring",
|
||||
"cp /etc/ceph/ceph.client.admin.keyring /tmp/shared",
|
||||
"ceph-authtool -p /etc/ceph/ceph.client.admin.keyring > /tmp/shared/admin.secret",
|
||||
)
|
||||
kclient.succeed("cp /tmp/shared/ceph.client.kclient.keyring /etc/ceph")
|
||||
fuseclient.succeed("cp /tmp/shared/ceph.client.fuseclient.keyring /etc/ceph")
|
||||
|
||||
# Get the volume path generated by Ceph.
|
||||
volume_path = monA.succeed("ceph fs subvolume getpath testing subvolume group | tee /dev/stderr").strip()
|
||||
kclient.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
|
||||
fuseclient.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
|
||||
kclient.succeed("cp /tmp/shared/admin.secret /etc/ceph/admin.secret")
|
||||
|
||||
# Mount CephFS on the kernel client.
|
||||
# We force the messenger v2 protocol via "ms_mode=secure"; the cluster
|
||||
# has msgr2 enabled (the monmap is created with a v2 address above) and the legacy v1
|
||||
# has msgr2 enabled (see "ceph mon enable-msgr2" above) and the legacy v1
|
||||
# protocol apparently does not reconnect reliably after the servers are restarted.
|
||||
# The msgr2 monitor listens on port 3300 (instead of legacy v1 port 6789),
|
||||
# so we have to point the device string at that port explicitly.
|
||||
# `recover_session=clean` makes the kernel client automatically reconnect
|
||||
# (discarding its stale session) after the whole cluster has been down,
|
||||
# which would otherwise leave the mount blocklisted and hanging.
|
||||
# which would otherwise leave the mount blocklisted and hanging forever.
|
||||
# Real CephFS use may not prefer hanging `recover_session=clean`, and
|
||||
# prefer manual de-blocklisting to avoid any failed OS syscalls,
|
||||
# but for this test, discarding stale sessions is good enough.
|
||||
kclient.succeed("mkdir -p /mnt/cephfs")
|
||||
kclient.wait_until_succeeds(
|
||||
f"mount -t ceph kclient@.testing={volume_path} /mnt/cephfs -o ms_mode=secure,recover_session=clean"
|
||||
"mount -t ceph ${cfg.monA.ip}:3300:/ /mnt/cephfs -o name=admin,secretfile=/etc/ceph/admin.secret,ms_mode=secure,recover_session=clean"
|
||||
)
|
||||
kclient.succeed("mountpoint /mnt/cephfs")
|
||||
|
||||
# Mount CephFS on the FUSE client using ceph-fuse.
|
||||
fuseclient.succeed("mkdir -p /mnt/cephfs")
|
||||
fuseclient.wait_until_succeeds(
|
||||
f"ceph-fuse --id fuseclient -m ${cfg.monA.ip}:3300 -r {volume_path} /mnt/cephfs"
|
||||
"ceph-fuse --id admin -m ${cfg.monA.ip}:6789 /mnt/cephfs"
|
||||
)
|
||||
fuseclient.succeed("mountpoint /mnt/cephfs")
|
||||
|
||||
@@ -616,40 +510,24 @@ let
|
||||
osd1.crash()
|
||||
osd2.crash()
|
||||
|
||||
# Start the mon first and mark the OSDs as down.
|
||||
# Since the heartbeats are pretty high by default, the OSDs would otherwise be marked as up still.
|
||||
# However we do not want to lower the heartbeats since this might cause flakey tests.
|
||||
monA.start()
|
||||
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
|
||||
monA.wait_until_succeeds("ceph osd down all")
|
||||
# Then start the OSDs as normal.
|
||||
# Start it up
|
||||
osd0.start()
|
||||
osd1.start()
|
||||
osd2.start()
|
||||
# Ensure they are all up.
|
||||
osd0.wait_for_unit("network.target")
|
||||
osd1.wait_for_unit("network.target")
|
||||
osd2.wait_for_unit("network.target")
|
||||
|
||||
# FIXME: dmcrypt OSDs currently do not work out of the box.
|
||||
# For a potential long-term fix see: https://github.com/NixOS/nixpkgs/pull/512912#discussion_r3140295546
|
||||
osd1.succeed(
|
||||
"ceph-volume lvm activate --no-tmpfs --no-systemd ${cfg.osd1.name} ${cfg.osd1.uuid}",
|
||||
"systemctl start ceph-osd-${cfg.osd1.name}",
|
||||
)
|
||||
monA.start()
|
||||
|
||||
# Ensure the cluster comes back up again.
|
||||
# See the note above on why this uses `wait_until_succeeds`.
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mon: 1 daemons'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
|
||||
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
|
||||
|
||||
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'", timeout=60)
|
||||
|
||||
# Ensure the MDS/CephFS comes back up again, too.
|
||||
monA.wait_for_unit("ceph-mds-${cfg.monA.name}")
|
||||
monA.wait_until_succeeds("ceph fs status testing | grep -e 'active'", timeout=60)
|
||||
monA.wait_until_succeeds("ceph fs status cephfs | grep -e 'active'", timeout=60)
|
||||
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
|
||||
# The clients kept running across the outage, so their CephFS mounts
|
||||
|
||||
291
nixos/tests/ceph-multi-node-deprecated-filestore.nix
Normal file
291
nixos/tests/ceph-multi-node-deprecated-filestore.nix
Normal file
@@ -0,0 +1,291 @@
|
||||
# Tests the legacy FileStore OSD backend.
|
||||
{ lib, ... }:
|
||||
let
|
||||
cfg = {
|
||||
clusterId = "066ae264-2a5d-4729-8001-6ad265f50b03";
|
||||
monA = {
|
||||
name = "a";
|
||||
ip = "192.168.1.1";
|
||||
};
|
||||
osd0 = {
|
||||
name = "0";
|
||||
ip = "192.168.1.2";
|
||||
key = "AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==";
|
||||
uuid = "55ba2294-3e24-478f-bee0-9dca4c231dd9";
|
||||
};
|
||||
osd1 = {
|
||||
name = "1";
|
||||
ip = "192.168.1.3";
|
||||
key = "AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==";
|
||||
uuid = "5e97a838-85b6-43b0-8950-cb56d554d1e5";
|
||||
};
|
||||
osd2 = {
|
||||
name = "2";
|
||||
ip = "192.168.1.4";
|
||||
key = "AQAdyhZeIaUlARAAGRoidDAmS6Vkp546UFEf5w==";
|
||||
uuid = "ea999274-13d0-4dd5-9af9-ad25a324f72f";
|
||||
};
|
||||
};
|
||||
generateCephConfig =
|
||||
{ daemonConfig }:
|
||||
{
|
||||
enable = true;
|
||||
global = {
|
||||
fsid = cfg.clusterId;
|
||||
monHost = cfg.monA.ip;
|
||||
monInitialMembers = cfg.monA.name;
|
||||
};
|
||||
}
|
||||
// daemonConfig;
|
||||
|
||||
generateHost =
|
||||
{ cephConfig, networkConfig }:
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
virtualisation = {
|
||||
emptyDiskImages = [ 20480 ];
|
||||
vlans = [ 1 ];
|
||||
};
|
||||
|
||||
networking = networkConfig;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
bash
|
||||
sudo
|
||||
ceph
|
||||
xfsprogs
|
||||
netcat
|
||||
];
|
||||
|
||||
boot.kernelModules = [ "xfs" ];
|
||||
|
||||
services.ceph = cephConfig;
|
||||
};
|
||||
|
||||
networkMonA = {
|
||||
dhcpcd.enable = false;
|
||||
interfaces.eth1.ipv4.addresses = lib.mkOverride 0 [
|
||||
{
|
||||
address = cfg.monA.ip;
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
firewall = {
|
||||
allowedTCPPorts = [
|
||||
6789
|
||||
3300
|
||||
];
|
||||
allowedTCPPortRanges = [
|
||||
{
|
||||
from = 6800;
|
||||
to = 7300;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
cephConfigMonA = generateCephConfig {
|
||||
daemonConfig = {
|
||||
mon = {
|
||||
enable = true;
|
||||
daemons = [ cfg.monA.name ];
|
||||
};
|
||||
mgr = {
|
||||
enable = true;
|
||||
daemons = [ cfg.monA.name ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
networkOsd = osd: {
|
||||
dhcpcd.enable = false;
|
||||
interfaces.eth1.ipv4.addresses = lib.mkOverride 0 [
|
||||
{
|
||||
address = osd.ip;
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
firewall = {
|
||||
allowedTCPPortRanges = [
|
||||
{
|
||||
from = 6800;
|
||||
to = 7300;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
cephConfigOsd =
|
||||
osd:
|
||||
generateCephConfig {
|
||||
daemonConfig = {
|
||||
osd = {
|
||||
enable = true;
|
||||
daemons = [ osd.name ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Following deployment is based on the manual deployment described here:
|
||||
# https://docs.ceph.com/docs/master/install/manual-deployment/
|
||||
# For other ways to deploy a ceph cluster, look at the documentation at
|
||||
# https://docs.ceph.com/docs/master/
|
||||
testscript =
|
||||
{ ... }:
|
||||
''
|
||||
start_all()
|
||||
|
||||
monA.wait_for_unit("network.target")
|
||||
osd0.wait_for_unit("network.target")
|
||||
osd1.wait_for_unit("network.target")
|
||||
osd2.wait_for_unit("network.target")
|
||||
|
||||
# Bootstrap ceph-mon daemon
|
||||
monA.succeed(
|
||||
"sudo -u ceph ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
|
||||
"sudo -u ceph ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
|
||||
"sudo -u ceph ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
|
||||
"monmaptool --create --add ${cfg.monA.name} ${cfg.monA.ip} --fsid ${cfg.clusterId} /tmp/monmap",
|
||||
"sudo -u ceph ceph-mon --mkfs -i ${cfg.monA.name} --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring",
|
||||
"sudo -u ceph mkdir -p /var/lib/ceph/mgr/ceph-${cfg.monA.name}/",
|
||||
"sudo -u ceph touch /var/lib/ceph/mon/ceph-${cfg.monA.name}/done",
|
||||
"systemctl start ceph-mon-${cfg.monA.name}",
|
||||
)
|
||||
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
|
||||
monA.succeed("ceph mon enable-msgr2")
|
||||
monA.succeed("ceph config set mon auth_allow_insecure_global_id_reclaim false")
|
||||
|
||||
# Can't check ceph status until a mon is up
|
||||
monA.succeed("ceph -s | grep 'mon: 1 daemons'")
|
||||
|
||||
# Start the ceph-mgr daemon, it has no deps and hardly any setup
|
||||
monA.succeed(
|
||||
"ceph auth get-or-create mgr.${cfg.monA.name} mon 'allow profile mgr' osd 'allow *' mds 'allow *' > /var/lib/ceph/mgr/ceph-${cfg.monA.name}/keyring",
|
||||
"sync", # to ensure shell redirection above is durable
|
||||
"systemctl start ceph-mgr-${cfg.monA.name}",
|
||||
)
|
||||
monA.wait_for_unit("ceph-mgr-a")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
|
||||
|
||||
# Send the admin keyring to the OSD machines
|
||||
monA.succeed("cp /etc/ceph/ceph.client.admin.keyring /tmp/shared")
|
||||
osd0.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
|
||||
osd1.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
|
||||
osd2.succeed("cp /tmp/shared/ceph.client.admin.keyring /etc/ceph")
|
||||
|
||||
# Bootstrap OSDs
|
||||
osd0.succeed(
|
||||
"mkfs.xfs /dev/vdb",
|
||||
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd0.name}",
|
||||
"mount /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd0.name}",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd0.name}/keyring --name osd.${cfg.osd0.name} --add-key ${cfg.osd0.key}",
|
||||
'echo \'{"cephx_secret": "${cfg.osd0.key}"}\' | ceph osd new ${cfg.osd0.uuid} -i -',
|
||||
)
|
||||
osd1.succeed(
|
||||
"mkfs.xfs /dev/vdb",
|
||||
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd1.name}",
|
||||
"mount /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd1.name}",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd1.name}/keyring --name osd.${cfg.osd1.name} --add-key ${cfg.osd1.key}",
|
||||
'echo \'{"cephx_secret": "${cfg.osd1.key}"}\' | ceph osd new ${cfg.osd1.uuid} -i -',
|
||||
)
|
||||
osd2.succeed(
|
||||
"mkfs.xfs /dev/vdb",
|
||||
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
|
||||
"mount /dev/vdb /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd2.name}/keyring --name osd.${cfg.osd2.name} --add-key ${cfg.osd2.key}",
|
||||
'echo \'{"cephx_secret": "${cfg.osd2.key}"}\' | ceph osd new ${cfg.osd2.uuid} -i -',
|
||||
)
|
||||
|
||||
# We `sync` so that the config survives the forced crashes below.
|
||||
osd0.succeed(
|
||||
"ceph-osd -i ${cfg.osd0.name} --mkfs --osd-uuid ${cfg.osd0.uuid}",
|
||||
"chown -R ceph:ceph /var/lib/ceph/osd",
|
||||
"sync",
|
||||
"systemctl start ceph-osd-${cfg.osd0.name}",
|
||||
)
|
||||
osd1.succeed(
|
||||
"ceph-osd -i ${cfg.osd1.name} --mkfs --osd-uuid ${cfg.osd1.uuid}",
|
||||
"chown -R ceph:ceph /var/lib/ceph/osd",
|
||||
"sync",
|
||||
"systemctl start ceph-osd-${cfg.osd1.name}",
|
||||
)
|
||||
osd2.succeed(
|
||||
"ceph-osd -i ${cfg.osd2.name} --mkfs --osd-uuid ${cfg.osd2.uuid}",
|
||||
"chown -R ceph:ceph /var/lib/ceph/osd",
|
||||
"sync",
|
||||
"systemctl start ceph-osd-${cfg.osd2.name}",
|
||||
)
|
||||
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
|
||||
monA.succeed(
|
||||
"ceph osd pool create multi-node-test 32 32",
|
||||
"ceph osd pool ls | grep 'multi-node-test'",
|
||||
|
||||
# We need to enable an application on the pool, otherwise it will
|
||||
# stay unhealthy in state POOL_APP_NOT_ENABLED.
|
||||
# Creating a CephFS would do this automatically, but we haven't done that here.
|
||||
# See: https://docs.ceph.com/en/reef/rados/operations/pools/#associating-a-pool-with-an-application
|
||||
# We use the custom application name "nixos-test" for this.
|
||||
"ceph osd pool application enable multi-node-test nixos-test",
|
||||
|
||||
"ceph osd pool rename multi-node-test multi-node-other-test",
|
||||
"ceph osd pool ls | grep 'multi-node-other-test'",
|
||||
)
|
||||
monA.wait_until_succeeds("ceph -s | grep '2 pools, 33 pgs'")
|
||||
monA.succeed("ceph osd pool set multi-node-other-test size 2")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
monA.wait_until_succeeds("ceph -s | grep '33 active+clean'")
|
||||
monA.fail(
|
||||
"ceph osd pool ls | grep 'multi-node-test'",
|
||||
"ceph osd pool delete multi-node-other-test multi-node-other-test --yes-i-really-really-mean-it",
|
||||
)
|
||||
|
||||
# Shut down ceph on all machines in a very unpolite way
|
||||
monA.crash()
|
||||
osd0.crash()
|
||||
osd1.crash()
|
||||
osd2.crash()
|
||||
|
||||
# Start it up
|
||||
osd0.start()
|
||||
osd1.start()
|
||||
osd2.start()
|
||||
monA.start()
|
||||
|
||||
# Ensure the cluster comes back up again
|
||||
monA.succeed("ceph -s | grep 'mon: 1 daemons'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'quorum ${cfg.monA.name}'")
|
||||
monA.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
|
||||
monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
'';
|
||||
in
|
||||
{
|
||||
name = "basic-multi-node-ceph-cluster-deprecated-filestore";
|
||||
meta = with lib.maintainers; {
|
||||
maintainers = [ lejonet ];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
monA = generateHost {
|
||||
cephConfig = cephConfigMonA;
|
||||
networkConfig = networkMonA;
|
||||
};
|
||||
osd0 = generateHost {
|
||||
cephConfig = cephConfigOsd cfg.osd0;
|
||||
networkConfig = networkOsd cfg.osd0;
|
||||
};
|
||||
osd1 = generateHost {
|
||||
cephConfig = cephConfigOsd cfg.osd1;
|
||||
networkConfig = networkOsd cfg.osd1;
|
||||
};
|
||||
osd2 = generateHost {
|
||||
cephConfig = cephConfigOsd cfg.osd2;
|
||||
networkConfig = networkOsd cfg.osd2;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = testscript;
|
||||
}
|
||||
269
nixos/tests/ceph-single-node-bluestore-dmcrypt.nix
Normal file
269
nixos/tests/ceph-single-node-bluestore-dmcrypt.nix
Normal file
@@ -0,0 +1,269 @@
|
||||
{ lib, ... }:
|
||||
|
||||
let
|
||||
# the single node ipv6 address
|
||||
ip = "2001:db8:ffff::";
|
||||
# the global ceph cluster id
|
||||
cluster = "54465b37-b9d8-4539-a1f9-dd33c75ee45a";
|
||||
# the fsids of OSDs
|
||||
osd-fsid-map = {
|
||||
"0" = "1c1b7ea9-06bf-4d30-9a01-37ac3a0254aa";
|
||||
"1" = "bd5a6f49-69d5-428c-ac25-a99f0c44375c";
|
||||
"2" = "c90de6c7-86c6-41da-9694-e794096dfc5c";
|
||||
};
|
||||
in
|
||||
{
|
||||
name = "basic-single-node-ceph-cluster-bluestore-dmcrypt";
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
benaryorg
|
||||
nh2
|
||||
];
|
||||
|
||||
nodes.ceph =
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# disks for bluestore
|
||||
virtualisation.emptyDiskImages = [
|
||||
20480
|
||||
20480
|
||||
20480
|
||||
];
|
||||
|
||||
# networking setup (no external connectivity required, only local IPv6)
|
||||
networking.useDHCP = false;
|
||||
systemd.network = {
|
||||
enable = true;
|
||||
wait-online.extraArgs = [
|
||||
"-i"
|
||||
"lo"
|
||||
];
|
||||
networks = {
|
||||
"40-loopback" = {
|
||||
enable = true;
|
||||
name = "lo";
|
||||
DHCP = "no";
|
||||
addresses = [ { Address = "${ip}/128"; } ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# do not start the ceph target by default so we can format the disks first
|
||||
systemd.targets.ceph.wantedBy = lib.mkForce [ ];
|
||||
|
||||
# add the packages to systemPackages so the testscript doesn't run into any unexpected issues
|
||||
# this shouldn't be required on production systems which have their required packages in the unit paths only
|
||||
# but it helps in case one needs to actually run the tooling anyway
|
||||
environment.systemPackages = with pkgs; [
|
||||
ceph
|
||||
cryptsetup
|
||||
lvm2
|
||||
];
|
||||
|
||||
services.ceph = {
|
||||
enable = true;
|
||||
client.enable = true;
|
||||
extraConfig = {
|
||||
public_addr = ip;
|
||||
cluster_addr = ip;
|
||||
# ipv6
|
||||
ms_bind_ipv4 = "false";
|
||||
ms_bind_ipv6 = "true";
|
||||
# msgr2 settings
|
||||
ms_cluster_mode = "secure";
|
||||
ms_service_mode = "secure";
|
||||
ms_client_mode = "secure";
|
||||
ms_mon_cluster_mode = "secure";
|
||||
ms_mon_service_mode = "secure";
|
||||
ms_mon_client_mode = "secure";
|
||||
# less default modules, cuts down on memory and startup time in the tests
|
||||
mgr_initial_modules = "";
|
||||
# distribute by OSD, not by host, as per https://docs.ceph.com/en/reef/cephadm/install/#single-host
|
||||
osd_crush_chooseleaf_type = "0";
|
||||
};
|
||||
client.extraConfig."mon.0" = {
|
||||
host = "ceph";
|
||||
mon_addr = "v2:[${ip}]:3300";
|
||||
public_addr = "v2:[${ip}]:3300";
|
||||
};
|
||||
global = {
|
||||
fsid = cluster;
|
||||
clusterNetwork = "${ip}/64";
|
||||
publicNetwork = "${ip}/64";
|
||||
monInitialMembers = "0";
|
||||
};
|
||||
|
||||
mon = {
|
||||
enable = true;
|
||||
daemons = [ "0" ];
|
||||
};
|
||||
|
||||
osd = {
|
||||
enable = true;
|
||||
daemons = builtins.attrNames osd-fsid-map;
|
||||
};
|
||||
|
||||
mgr = {
|
||||
enable = true;
|
||||
daemons = [ "ceph" ];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services =
|
||||
let
|
||||
osd-name = id: "ceph-osd-${id}";
|
||||
osd-pre-start = id: [
|
||||
"!${config.services.ceph.osd.package.out}/bin/ceph-volume lvm activate --bluestore ${id} ${osd-fsid-map.${id}} --no-systemd"
|
||||
"${config.services.ceph.osd.package.lib}/libexec/ceph/ceph-osd-prestart.sh --id ${id} --cluster ${config.services.ceph.global.clusterName}"
|
||||
];
|
||||
osd-post-stop = id: [
|
||||
"!${config.services.ceph.osd.package.out}/bin/ceph-volume lvm deactivate ${id}"
|
||||
];
|
||||
map-osd = id: {
|
||||
name = osd-name id;
|
||||
value = {
|
||||
serviceConfig.ExecStartPre = lib.mkForce (osd-pre-start id);
|
||||
serviceConfig.ExecStopPost = osd-post-stop id;
|
||||
unitConfig.ConditionPathExists = lib.mkForce [ ];
|
||||
unitConfig.StartLimitBurst = lib.mkForce 4;
|
||||
path = with pkgs; [
|
||||
util-linux
|
||||
lvm2
|
||||
cryptsetup
|
||||
];
|
||||
};
|
||||
};
|
||||
in
|
||||
lib.pipe config.services.ceph.osd.daemons [
|
||||
(map map-osd)
|
||||
builtins.listToAttrs
|
||||
];
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
ceph.wait_for_unit("default.target")
|
||||
|
||||
# Bootstrap ceph-mon daemon
|
||||
ceph.succeed(
|
||||
"mkdir -p /var/lib/ceph/bootstrap-osd",
|
||||
"ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
|
||||
"ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/bootstrap-osd/ceph.keyring --gen-key -n client.bootstrap-osd --cap mon 'profile bootstrap-osd' --cap mgr 'allow r'",
|
||||
"ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
|
||||
"ceph-authtool /tmp/ceph.mon.keyring --import-keyring /var/lib/ceph/bootstrap-osd/ceph.keyring",
|
||||
"monmaptool --create --fsid ${cluster} --addv 0 'v2:[${ip}]:3300/0' --clobber /tmp/ceph.initial-monmap",
|
||||
"mkdir -p /var/lib/ceph/mon/ceph-0",
|
||||
"ceph-mon --mkfs -i 0 --monmap /tmp/ceph.initial-monmap --keyring /tmp/ceph.mon.keyring",
|
||||
"chown ceph:ceph -R /tmp/ceph.mon.keyring /var/lib/ceph",
|
||||
"systemctl start ceph-mon-0.service",
|
||||
)
|
||||
|
||||
ceph.wait_for_unit("ceph-mon-0.service")
|
||||
# should the mon not start or bind for some reason this gives us a better error message than the config commands running into a timeout
|
||||
ceph.wait_for_open_port(3300, "${ip}")
|
||||
ceph.succeed(
|
||||
# required for HEALTH_OK
|
||||
"ceph config set mon auth_allow_insecure_global_id_reclaim false",
|
||||
# IPv6
|
||||
"ceph config set global ms_bind_ipv4 false",
|
||||
"ceph config set global ms_bind_ipv6 true",
|
||||
# the new (secure) protocol
|
||||
"ceph config set global ms_bind_msgr1 false",
|
||||
"ceph config set global ms_bind_msgr2 true",
|
||||
# just a small little thing
|
||||
"ceph config set mon mon_compact_on_start true",
|
||||
)
|
||||
|
||||
# Can't check ceph status until a mon is up
|
||||
ceph.succeed("ceph -s | grep 'mon: 1 daemons'")
|
||||
|
||||
# Bootstrap OSDs (do this before starting the mgr because cryptsetup and the mgr both eat a lot of memory)
|
||||
ceph.succeed(
|
||||
# this will automatically do what's required for LVM, cryptsetup, and stores all the data in Ceph's internal databases
|
||||
"ceph-volume lvm prepare --bluestore --data /dev/vdb --dmcrypt --no-systemd --osd-id 0 --osd-fsid ${osd-fsid-map."0"}",
|
||||
"ceph-volume lvm prepare --bluestore --data /dev/vdc --dmcrypt --no-systemd --osd-id 1 --osd-fsid ${osd-fsid-map."1"}",
|
||||
"ceph-volume lvm prepare --bluestore --data /dev/vdd --dmcrypt --no-systemd --osd-id 2 --osd-fsid ${osd-fsid-map."2"}",
|
||||
"sudo ceph-volume lvm deactivate 0",
|
||||
"sudo ceph-volume lvm deactivate 1",
|
||||
"sudo ceph-volume lvm deactivate 2",
|
||||
"chown -R ceph:ceph /var/lib/ceph",
|
||||
)
|
||||
|
||||
# Start OSDs (again, argon2id eats memory, so this happens before starting the mgr)
|
||||
ceph.succeed(
|
||||
"systemctl start ceph-osd-0.service",
|
||||
"systemctl start ceph-osd-1.service",
|
||||
"systemctl start ceph-osd-2.service",
|
||||
)
|
||||
ceph.wait_until_succeeds("ceph -s | grep 'quorum 0'")
|
||||
ceph.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
|
||||
|
||||
# Start the ceph-mgr daemon, after copying in the keyring
|
||||
ceph.succeed(
|
||||
"mkdir -p /var/lib/ceph/mgr/ceph-ceph/",
|
||||
"ceph auth get-or-create -o /var/lib/ceph/mgr/ceph-ceph/keyring mgr.ceph mon 'allow profile mgr' osd 'allow *' mds 'allow *'",
|
||||
"chown -R ceph:ceph /var/lib/ceph/mgr/ceph-ceph/",
|
||||
"systemctl start ceph-mgr-ceph.service",
|
||||
)
|
||||
ceph.wait_for_unit("ceph-mgr-ceph")
|
||||
ceph.wait_until_succeeds("ceph -s | grep 'quorum 0'")
|
||||
ceph.wait_until_succeeds("ceph -s | grep 'mgr: ceph(active,'")
|
||||
ceph.wait_until_succeeds("ceph osd stat | grep -e '3 osds: 3 up[^,]*, 3 in'")
|
||||
ceph.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
|
||||
# test the actual storage
|
||||
ceph.succeed(
|
||||
"ceph osd pool create single-node-test 32 32",
|
||||
"ceph osd pool ls | grep 'single-node-test'",
|
||||
|
||||
# We need to enable an application on the pool, otherwise it will
|
||||
# stay unhealthy in state POOL_APP_NOT_ENABLED.
|
||||
# Creating a CephFS would do this automatically, but we haven't done that here.
|
||||
# See: https://docs.ceph.com/en/reef/rados/operations/pools/#associating-a-pool-with-an-application
|
||||
# We use the custom application name "nixos-test" for this.
|
||||
"ceph osd pool application enable single-node-test nixos-test",
|
||||
|
||||
"ceph osd pool rename single-node-test single-node-other-test",
|
||||
"ceph osd pool ls | grep 'single-node-other-test'",
|
||||
)
|
||||
ceph.wait_until_succeeds("ceph -s | grep '2 pools, 33 pgs'")
|
||||
ceph.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
ceph.wait_until_succeeds("ceph -s | grep '33 active+clean'")
|
||||
ceph.fail(
|
||||
# the old pool should be gone
|
||||
"ceph osd pool ls | grep 'multi-node-test'",
|
||||
# deleting the pool should fail without setting mon_allow_pool_delete
|
||||
"ceph osd pool delete single-node-other-test single-node-other-test --yes-i-really-really-mean-it",
|
||||
)
|
||||
|
||||
# rebooting gets rid of any potential tmpfs mounts or device-mapper devices
|
||||
ceph.shutdown()
|
||||
ceph.start()
|
||||
ceph.wait_for_unit("default.target")
|
||||
|
||||
# Start it up (again OSDs first due to memory constraints of cryptsetup and mgr)
|
||||
ceph.systemctl("start ceph-mon-0.service")
|
||||
ceph.wait_for_unit("ceph-mon-0")
|
||||
ceph.systemctl("start ceph-osd-0.service")
|
||||
ceph.wait_for_unit("ceph-osd-0")
|
||||
ceph.systemctl("start ceph-osd-1.service")
|
||||
ceph.wait_for_unit("ceph-osd-1")
|
||||
ceph.systemctl("start ceph-osd-2.service")
|
||||
ceph.wait_for_unit("ceph-osd-2")
|
||||
ceph.systemctl("start ceph-mgr-ceph.service")
|
||||
ceph.wait_for_unit("ceph-mgr-ceph")
|
||||
|
||||
# Ensure the cluster comes back up again
|
||||
ceph.succeed("ceph -s | grep 'mon: 1 daemons'")
|
||||
ceph.wait_until_succeeds("ceph -s | grep 'quorum 0'")
|
||||
ceph.wait_until_succeeds("ceph osd stat | grep -E '3 osds: 3 up[^,]*, 3 in'")
|
||||
ceph.wait_until_succeeds("ceph -s | grep 'mgr: ceph(active,'")
|
||||
ceph.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
|
||||
'';
|
||||
}
|
||||
@@ -9,14 +9,17 @@ let
|
||||
};
|
||||
osd0 = {
|
||||
name = "0";
|
||||
key = "AQBCEJNa3s8nHRAANvdsr93KqzBznuIWm2gOGg==";
|
||||
uuid = "55ba2294-3e24-478f-bee0-9dca4c231dd9";
|
||||
};
|
||||
osd1 = {
|
||||
name = "1";
|
||||
key = "AQBEEJNac00kExAAXEgy943BGyOpVH1LLlHafQ==";
|
||||
uuid = "5e97a838-85b6-43b0-8950-cb56d554d1e5";
|
||||
};
|
||||
osd2 = {
|
||||
name = "2";
|
||||
key = "AQAdyhZeIaUlARAAGRoidDAmS6Vkp546UFEf5w==";
|
||||
uuid = "ea999274-13d0-4dd5-9af9-ad25a324f72f";
|
||||
};
|
||||
};
|
||||
@@ -48,11 +51,6 @@ let
|
||||
vlans = [ 1 ];
|
||||
};
|
||||
|
||||
# Ceph 20.2.4 introduced the aes256k cipher for authentication.
|
||||
# Linux started supporting these in kernel version 7.0.
|
||||
# Remove this line at the earliest convenience (i.e. when tests are run by 7.0 or higher by default).
|
||||
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||
|
||||
networking = networkConfig;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
@@ -117,18 +115,13 @@ let
|
||||
"sudo -u ceph ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'",
|
||||
"sudo -u ceph ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'",
|
||||
"sudo -u ceph ceph-authtool /tmp/ceph.mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring",
|
||||
# Create the monmap with both a msgr2 (v2) and a legacy (v1) address.
|
||||
# Using plain `--add` yields a v1-only monmap, which leaves the cluster
|
||||
# in HEALTH_WARN with MON_MSGR2_NOT_ENABLED. Running `ceph mon
|
||||
# enable-msgr2` afterwards is not enough: it rewrites the monmap (a
|
||||
# subsequent `ceph mon dump` does show the v2 address), but the health
|
||||
# check keeps reporting the mon as v1-only indefinitely.
|
||||
"monmaptool --create --addv ${cfg.monA.name} '[v2:${cfg.monA.ip}:3300,v1:${cfg.monA.ip}:6789]' --fsid ${cfg.clusterId} /tmp/monmap",
|
||||
"monmaptool --create --add ${cfg.monA.name} ${cfg.monA.ip} --fsid ${cfg.clusterId} /tmp/monmap",
|
||||
"sudo -u ceph ceph-mon --mkfs -i ${cfg.monA.name} --monmap /tmp/monmap --keyring /tmp/ceph.mon.keyring",
|
||||
"sudo -u ceph touch /var/lib/ceph/mon/ceph-${cfg.monA.name}/done",
|
||||
"systemctl start ceph-mon-${cfg.monA.name}",
|
||||
)
|
||||
monA.wait_for_unit("ceph-mon-${cfg.monA.name}")
|
||||
monA.succeed("ceph mon enable-msgr2")
|
||||
monA.succeed("ceph config set mon auth_allow_insecure_global_id_reclaim false")
|
||||
|
||||
# Can't check ceph status until a mon is up
|
||||
@@ -155,24 +148,14 @@ let
|
||||
"mkdir -p /var/lib/ceph/osd/ceph-${cfg.osd2.name}",
|
||||
"echo bluestore > /var/lib/ceph/osd/ceph-${cfg.osd2.name}/type",
|
||||
"ln -sf /dev/vdd /var/lib/ceph/osd/ceph-${cfg.osd2.name}/block",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd0.name}/keyring --name osd.${cfg.osd0.name} --gen-key",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd1.name}/keyring --name osd.${cfg.osd1.name} --gen-key",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd2.name}/keyring --name osd.${cfg.osd2.name} --gen-key",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd0.name}/keyring --name osd.${cfg.osd0.name} --add-key ${cfg.osd0.key}",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd1.name}/keyring --name osd.${cfg.osd1.name} --add-key ${cfg.osd1.key}",
|
||||
"ceph-authtool --create-keyring /var/lib/ceph/osd/ceph-${cfg.osd2.name}/keyring --name osd.${cfg.osd2.name} --add-key ${cfg.osd2.key}",
|
||||
'echo \'{"cephx_secret": "${cfg.osd0.key}"}\' | ceph osd new ${cfg.osd0.uuid} -i -',
|
||||
'echo \'{"cephx_secret": "${cfg.osd1.key}"}\' | ceph osd new ${cfg.osd1.uuid} -i -',
|
||||
'echo \'{"cephx_secret": "${cfg.osd2.key}"}\' | ceph osd new ${cfg.osd2.uuid} -i -',
|
||||
)
|
||||
|
||||
# Register the OSDs with the generated keys read back from their keyrings.
|
||||
for osd_name, osd_uuid in [
|
||||
("${cfg.osd0.name}", "${cfg.osd0.uuid}"),
|
||||
("${cfg.osd1.name}", "${cfg.osd1.uuid}"),
|
||||
("${cfg.osd2.name}", "${cfg.osd2.uuid}"),
|
||||
]:
|
||||
key = monA.succeed(
|
||||
f"ceph-authtool --print-key /var/lib/ceph/osd/ceph-{osd_name}/keyring --name osd.{osd_name}"
|
||||
).strip()
|
||||
monA.succeed(
|
||||
f"echo '{{\"cephx_secret\": \"{key}\"}}' | ceph osd new {osd_uuid} -i -"
|
||||
)
|
||||
|
||||
# Initialize the OSDs with regular filestore
|
||||
monA.succeed(
|
||||
"ceph-osd -i ${cfg.osd0.name} --mkfs --osd-uuid ${cfg.osd0.uuid}",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user