Merge branch 'staging' into sbcl/2.5.10

This commit is contained in:
7c6f434c
2025-11-08 22:01:02 +00:00
committed by GitHub
7385 changed files with 172477 additions and 121795 deletions
+7
View File
@@ -89,6 +89,13 @@ trim_trailing_whitespace = unset
end_of_line = unset
insert_final_newline = unset
# see https://manual.jule.dev/project/code-style.html#indentions
[*.jule]
indent_style = tab
[jule.mod]
insert_final_newline = unset
# Keep this hint at the bottom:
# Please don't add entries for subfolders here.
# Create <subfolder>/.editorconfig instead.
+7
View File
@@ -193,6 +193,10 @@ cffc27daf06c77c0d76bc35d24b929cb9d68c3c9
# nixos/kanidm: inherit lib, nixfmt
8f18393d380079904d072007fb19dc64baef0a3a
# fetchgit, fetchurl, fetchzip:
# format after refactoring with lib.extendMkDerivation (#455994)
aeddd850c6d3485fc1af2edfb111e58141d18dc1
# fetchhg: format after refactoring with lib.extendMkDerivation and make overridable (#423539)
34a5b1eb23129f8fb62c677e3760903f6d43228f
@@ -300,3 +304,6 @@ f7611cad5154a9096faa26d156a4079577bfae17
90e7159c559021ac4f4cc1222000f08a91feff69 # !autorebase nix-shell --run treefmt
c283f32d296564fd649ef3ed268c1f1f7b199c49 # !autorebase nix-shell --run treefmt
91a8fee3aaf79348aa2dc1552a29fc1b786c5133 # !autorebase nix-shell --run treefmt
# aliases: keep-sorted
48ce0739044bd6eba83c3a43bd4ad1046399cdad # !autorebase nix-shell --run treefmt
+1 -1
View File
@@ -1,4 +1,4 @@
# This file is used by .github/workflows/labels.yml
# This file is used by .github/workflows/bot.yml
# This version is only run for Pull Requests from development branches like staging-next, haskell-updates or python-updates.
"4.workflow: package set update":
+2 -1
View File
@@ -1,4 +1,4 @@
# This file is used by .github/workflows/labels.yml
# This file is used by .github/workflows/bot.yml
# This version uses `sync-labels: false`, meaning that a non-match will NOT remove the label
# keep-sorted start case=no numeric=yes newline_separated=yes skip_lines=1
@@ -29,5 +29,6 @@
- .github/actions/*
- .github/workflows/*
- ci/**/*.*
- maintainers/github-teams.json
# keep-sorted end
+1 -1
View File
@@ -1,4 +1,4 @@
# This file is used by .github/workflows/labels.yml
# This file is used by .github/workflows/bot.yml
# This version uses `sync-labels: true`, meaning that a non-match will remove the label
# keep-sorted start case=no numeric=yes newline_separated=yes skip_lines=1
+1 -1
View File
@@ -64,7 +64,7 @@ This results in a key with the following semantics:
## Required Status Checks
The "Required Status Checks" branch ruleset is implemented in two top-level workflows: `pr.yml` and `merge-group.yml`.
The "Required Status Checks" branch ruleset is implemented in two top-level workflows: `pull-request-target.yml` and `merge-group.yml`.
The PR workflow defines all checks that need to succeed to add a Pull Request to the Merge Queue.
If no Merge Queue is set up for a branch, the PR workflow defines the checks required to merge into the target branch.
+5 -3
View File
@@ -40,6 +40,7 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha }}
token: ${{ steps.app-token.outputs.token }}
persist-credentials: true
- name: Log current API rate limits
env:
@@ -48,7 +49,7 @@ jobs:
- name: Create backport PRs
id: backport
uses: korthout/backport-action@ca4972adce8039ff995e618f5fc02d1b7961f27a # v3.3.0
uses: korthout/backport-action@d07416681cab29bf2661702f925f020aaa962997 # v3.4.1
with:
# Config README: https://github.com/korthout/backport-action#backport-action
copy_labels_pattern: 'severity:\ssecurity'
@@ -56,8 +57,9 @@ jobs:
pull_description: |-
Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}.
* [ ] Before merging, ensure that this backport is [acceptable for the release](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#changes-acceptable-for-releases).
* Even as a non-committer, if you find that it is not acceptable, leave a comment.
**Before merging, ensure that this backport is [acceptable for the release](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#changes-acceptable-for-releases).**
Even as a non-committer, if you find that it is not acceptable, leave a comment.
- name: Log current API rate limits
env:
@@ -3,11 +3,13 @@
# access to the GitHub API. This means that it should not evaluate user input in
# a way that allows code injection.
name: Labels
name: Bot
on:
schedule:
- cron: '07,17,27,37,47,57 * * * *'
# Run every 10m
# i.e., at each of the listed minutes, every hour
- cron: '05,15,25,35,45,55 * * * *'
workflow_call:
inputs:
headBranch:
@@ -21,7 +23,7 @@ on:
concurrency:
# This explicitly avoids using `run_id` for the concurrency key to make sure that only
# *one* scheduled run can run at a time.
group: labels-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number }}
group: bot-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number }}
# PR-triggered runs will be cancelled, but scheduled runs will be queued.
cancel-in-progress: ${{ github.event_name != 'schedule' }}
@@ -36,7 +38,7 @@ defaults:
shell: bash
jobs:
update:
run:
runs-on: ubuntu-24.04-arm
if: github.event_name != 'schedule' || github.repository_owner == 'NixOS'
env:
@@ -46,6 +48,7 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: |
ci/github-script
@@ -59,7 +62,10 @@ jobs:
with:
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
permission-administration: read
permission-contents: write
permission-issues: write
permission-members: read
permission-pull-requests: write
- name: Log current API rate limits
@@ -67,13 +73,13 @@ jobs:
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
run: gh api /rate_limit | jq
- name: Labels from API data and Eval results
- name: Run bot
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ steps.app-token.outputs.token || github.token }}
retries: 3
script: |
require('./ci/github-script/labels.js')({
require('./ci/github-script/bot.js')({
github,
context,
core,
+7 -3
View File
@@ -3,6 +3,9 @@ name: Build
on:
workflow_call:
inputs:
artifact-prefix:
required: true
type: string
baseBranch:
required: true
type: string
@@ -46,13 +49,14 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: .github/actions
- name: Checkout the merge commit
uses: ./.github/actions/checkout
with:
merged-as-untrusted-at: ${{ inputs.mergedSha }}
- uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
- uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31
with:
# Sandbox is disabled on MacOS by default.
extra_nix_config: sandbox = true
@@ -97,7 +101,7 @@ jobs:
if: |
contains(matrix.builds, 'manual-nixos') && !cancelled() &&
contains(fromJSON(inputs.baseBranch).type, 'primary')
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: nixos-manual-${{ matrix.name }}
name: ${{ inputs.artifact-prefix }}nixos-manual-${{ matrix.name }}
path: nixos-manual
+5 -2
View File
@@ -34,6 +34,7 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
path: trusted
sparse-checkout: |
ci/github-script
@@ -73,13 +74,15 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: .github/actions
- name: Checkout merge and target commits
uses: ./.github/actions/checkout
with:
merged-as-untrusted-at: ${{ inputs.mergedSha }}
target-as-trusted-at: ${{ inputs.targetSha }}
- uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
- uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
@@ -90,7 +93,7 @@ jobs:
pushFilter: -source$
- name: Build codeowners validator
run: nix-build nixpkgs/untrusted/ci --arg nixpkgs ./nixpkgs/untrusted-pinned -A codeownersValidator
run: nix-build nixpkgs/trusted/ci --arg nixpkgs ./nixpkgs/trusted-pinned -A codeownersValidator
- name: Validate codeowners
env:
+54
View File
@@ -0,0 +1,54 @@
name: Comment
on:
issue_comment:
types: [created]
# This is used as fallback without app only.
# This happens when testing in forks without setting up that app.
permissions:
pull-requests: write
defaults:
run:
shell: bash
jobs:
# The `bot` workflow reacts to comments with @NixOS/nixpkgs-merge-bot references, but might only
# pick up a comment after up to 10 minutes. To give the user instant feedback, this job adds
# a reaction to these comments.
react:
name: React with eyes
runs-on: ubuntu-24.04-arm
timeout-minutes: 2
if: contains(github.event.comment.body, '@NixOS/nixpkgs-merge-bot merge')
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: |
ci/github-script
# Use the GitHub App to make sure the reaction happens with the same user who will later merge.
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_APP_ID
id: app-token
with:
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
permission-pull-requests: write
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ steps.app-token.outputs.token || github.token }}
retries: 3
script: |
const { handleMergeComment } = require('./ci/github-script/merge.js')
const { body, node_id } = context.payload.comment
await handleMergeComment({
github,
body,
node_id,
reaction: 'EYES',
})
-66
View File
@@ -1,66 +0,0 @@
name: Dismissed review
on:
workflow_run:
workflows:
- Review dismissed
types: [completed]
concurrency:
group: dismissed-review-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
pull-requests: write
defaults:
run:
shell: bash
jobs:
# The `check-cherry-picks` workflow creates review comments which reviewers
# are encouraged to manually dismiss if they're not relevant.
# When a CI-generated review is dismissed, this job automatically minimizes
# it, preventing it from cluttering the PR.
minimize:
name: Minimize as resolved
runs-on: ubuntu-24.04-arm
timeout-minutes: 2
steps:
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
// PRs from forks don't have any PRs associated by default.
// Thus, we request the PR number with an API call *to* the fork's repo.
// Multiple pull requests can be open from the same head commit, either via
// different base branches or head branches.
const { head_repository, head_sha, repository } = context.payload.workflow_run
await Promise.all(
(await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit, {
owner: head_repository.owner.login,
repo: head_repository.name,
commit_sha: head_sha
}))
.filter(pull_request => pull_request.base.repo.id == repository.id)
.map(async (pull_request) =>
Promise.all(
(await github.paginate(github.rest.pulls.listReviews, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pull_request.number
})).filter(review =>
review.user?.login == 'github-actions[bot]' &&
review.state == 'DISMISSED'
).map(review => github.graphql(`
mutation($node_id:ID!) {
minimizeComment(input: {
classifier: RESOLVED,
subjectId: $node_id
})
{ clientMutationId }
}`,
{ node_id: review.node_id }
))
)
)
)
+54 -40
View File
@@ -3,6 +3,9 @@ name: Eval
on:
workflow_call:
inputs:
artifact-prefix:
required: true
type: string
mergedSha:
required: true
type: string
@@ -11,9 +14,6 @@ on:
systems:
required: true
type: string
defaultVersion:
required: true
type: string
testVersions:
required: false
default: false
@@ -37,6 +37,7 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
path: trusted
sparse-checkout: |
ci/supportedVersions.nix
@@ -44,13 +45,14 @@ jobs:
- name: Check out the PR at the test merge commit
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
ref: ${{ inputs.mergedSha }}
path: untrusted
sparse-checkout: |
ci/pinned.json
- name: Install Nix
uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31
- name: Load supported versions
id: versions
@@ -87,6 +89,7 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: .github/actions
- name: Check out the PR at merged and target commits
uses: ./.github/actions/checkout
@@ -95,7 +98,7 @@ jobs:
target-as-trusted-at: ${{ inputs.targetSha }}
- name: Install Nix
uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
@@ -108,9 +111,9 @@ jobs:
- name: Evaluate the ${{ matrix.system }} output paths at the merge commit
env:
MATRIX_SYSTEM: ${{ matrix.system }}
MATRIX_VERSION: ${{ matrix.version || inputs.defaultVersion }}
MATRIX_VERSION: ${{ matrix.version || 'nixVersions.latest' }}
run: |
nix-build nixpkgs/untrusted/ci --arg nixpkgs ./nixpkgs/untrusted-pinned -A eval.singleSystem \
nix-build nixpkgs/untrusted/ci --arg nixpkgs ./nixpkgs/untrusted-pinned -A eval.singleSystem \
--argstr evalSystem "$MATRIX_SYSTEM" \
--arg chunkSize 8000 \
--argstr nixPath "$MATRIX_VERSION" \
@@ -119,21 +122,29 @@ jobs:
# Note: Keep the same further down in sync!
- name: Evaluate the ${{ matrix.system }} output paths at the target commit
if: inputs.targetSha
env:
MATRIX_SYSTEM: ${{ matrix.system }}
# This must match the default version set in the Merge Queue.
VERSION: lixPackageSets.latest.lix
# This is very quick, because it pulls the eval results from Cachix.
run: |
nix-build nixpkgs/trusted/ci --arg nixpkgs ./nixpkgs/trusted-pinned -A eval.singleSystem \
TARGET_DRV=$(nix-instantiate nixpkgs/trusted/ci --arg nixpkgs ./nixpkgs/trusted-pinned -A eval.singleSystem \
--argstr evalSystem "$MATRIX_SYSTEM" \
--arg chunkSize 8000 \
--argstr nixPath "$VERSION" \
--out-link target
--argstr nixPath "nixVersions.latest")
# Try to fetch this from Cachix a few times, for up to 30 seconds. This avoids running Eval
# twice in the Merge Queue, when a later item finishes Eval at the merge commit earlier.
for _i in {1..6}; do
# Using --max-jobs 0 will cause nix-build to fail if this can't be substituted from cachix.
if nix-build "$TARGET_DRV" --max-jobs 0; then
break
fi
sleep 5
done
# Either fetches from Cachix or runs Eval itself. The fallback is required
# for pull requests into wip-branches without merge queue.
nix-build "$TARGET_DRV" --out-link target
- name: Compare outpaths against the target branch
if: inputs.targetSha
env:
MATRIX_SYSTEM: ${{ matrix.system }}
run: |
@@ -144,22 +155,22 @@ jobs:
--out-link diff
- name: Upload outpaths diff and stats
if: inputs.targetSha
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ matrix.version && format('{0}-', matrix.version) || '' }}diff-${{ matrix.system }}
name: ${{ inputs.artifact-prefix }}${{ matrix.version && format('{0}-', matrix.version) || '' }}diff-${{ matrix.system }}
path: diff/*
compare:
runs-on: ubuntu-24.04-arm
needs: [eval]
if: inputs.targetSha && !cancelled() && !failure()
if: ${{ !cancelled() && !failure() }}
permissions:
statuses: write
timeout-minutes: 5
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: .github/actions
- name: Check out the PR at the target commit
uses: ./.github/actions/checkout
@@ -168,14 +179,14 @@ jobs:
target-as-trusted-at: ${{ inputs.targetSha }}
- name: Download output paths and eval stats for all systems
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
pattern: diff-*
pattern: ${{ inputs.artifact-prefix }}diff-*
path: diff
merge-multiple: true
- name: Install Nix
uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31
- name: Combine all output paths and eval stats
run: |
@@ -183,26 +194,31 @@ jobs:
--arg diffDir ./diff \
--out-link combined
- name: Upload the maintainer list
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifact-prefix }}maintainers
path: combined/maintainers.json
- name: Compare against the target branch
env:
AUTHOR_ID: ${{ github.event.pull_request.user.id }}
TARGET_SHA: ${{ inputs.mergedSha }}
run: |
git -C nixpkgs/trusted diff --name-only ${{ inputs.mergedSha }} \
git -C nixpkgs/trusted diff --name-only "$TARGET_SHA" \
| jq --raw-input --slurp 'split("\n")[:-1]' > touched-files.json
# Use the target branch to get accurate maintainer info
nix-build nixpkgs/trusted/ci --arg nixpkgs ./nixpkgs/trusted-pinned -A eval.compare \
--arg combinedDir ./combined \
--arg touchedFilesJson ./touched-files.json \
--argstr githubAuthorId "$AUTHOR_ID" \
--out-link comparison
cat comparison/step-summary.md >> "$GITHUB_STEP_SUMMARY"
- name: Upload the comparison results
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: comparison
name: ${{ inputs.artifact-prefix }}comparison
path: comparison/*
- name: Add eval summary to commit statuses
@@ -242,7 +258,7 @@ jobs:
needs: [versions, eval]
steps:
- name: Download output paths and eval stats for all versions
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
pattern: "*-diff-*"
path: versions
@@ -250,6 +266,7 @@ jobs:
- name: Add version comparison table to job summary
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
ARTIFACT_PREFIX: ${{ inputs.artifact-prefix }}
SYSTEMS: ${{ inputs.systems }}
VERSIONS: ${{ needs.versions.outputs.versions }}
with:
@@ -257,6 +274,7 @@ jobs:
const { readFileSync } = require('node:fs')
const path = require('node:path')
const prefix = process.env.ARTIFACT_PREFIX
const systems = JSON.parse(process.env.SYSTEMS)
const versions = JSON.parse(process.env.VERSIONS)
@@ -272,7 +290,7 @@ jobs:
[{ data: version }].concat(
systems.map((system) => {
try {
const artifact = path.join('versions', `${version}-diff-${system}`)
const artifact = path.join('versions', `${prefix}${version}-diff-${system}`)
const time = Math.round(
parseFloat(
readFileSync(
@@ -284,16 +302,11 @@ jobs:
const diff = JSON.parse(
readFileSync(path.join(artifact, system, 'diff.json'), 'utf-8'),
)
const attrs = [].concat(
diff.added,
diff.removed,
diff.changed,
diff.rebuilds
).filter(attr =>
// Exceptions related to dev shells, which changed at some time between 2.18 and 2.24.
!attr.startsWith('tests.devShellTools.nixos.') &&
!attr.startsWith('tests.devShellTools.unstructuredDerivationInputEnv.')
)
const attrs = []
.concat(diff.added, diff.removed, diff.changed, diff.rebuilds)
// There are some special attributes, which are ignored for rebuilds.
// These only have a single path component, because they lack the `.<system>` suffix.
.filter((attr) => attr.split('.').length > 1)
if (attrs.length > 0) {
core.setFailed(
`${version} on ${system} has changed outpaths!\nNote: Please make sure to update ci/pinned.json separately from changes to other packages.`,
@@ -328,6 +341,7 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: .github/actions
- name: Checkout the merge commit
uses: ./.github/actions/checkout
@@ -335,7 +349,7 @@ jobs:
merged-as-untrusted-at: ${{ inputs.mergedSha }}
- name: Install Nix
uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31
- name: Run misc eval tasks in parallel
run: |
+6 -3
View File
@@ -26,13 +26,14 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: .github/actions
- name: Checkout the merge commit
uses: ./.github/actions/checkout
with:
merged-as-untrusted-at: ${{ inputs.mergedSha }}
- uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
- uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31
# TODO: Figure out how to best enable caching for the treefmt job. Cachix won't work well,
# because the cache would be invalidated on every commit - treefmt checks every file.
@@ -60,13 +61,14 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: .github/actions
- name: Checkout the merge commit
uses: ./.github/actions/checkout
with:
merged-as-untrusted-at: ${{ inputs.mergedSha }}
- uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
- uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
@@ -87,6 +89,7 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: .github/actions
- name: Checkout merge and target commits
uses: ./.github/actions/checkout
@@ -94,7 +97,7 @@ jobs:
merged-as-untrusted-at: ${{ inputs.mergedSha }}
target-as-trusted-at: ${{ inputs.targetSha }}
- uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
- uses: cachix/install-nix-action@456688f15bc354bef6d396e4a35f4f89d40bf2b7 # v31
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
with:
+6 -2
View File
@@ -4,6 +4,9 @@ on:
merge_group:
workflow_call:
inputs:
artifact-prefix:
required: true
type: string
mergedSha:
required: true
type: string
@@ -24,6 +27,7 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: |
ci/supportedSystems.json
@@ -53,10 +57,10 @@ jobs:
secrets:
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
with:
artifact-prefix: ${{ inputs.artifact-prefix }}
mergedSha: ${{ inputs.mergedSha || github.event.merge_group.head_sha }}
targetSha: ${{ inputs.targetSha || github.event.merge_group.base_sha }}
systems: ${{ needs.prepare.outputs.systems }}
# This must match the version in Eval's target step.
defaultVersion: lixPackageSets.latest.lix
# This job's only purpose is to create the target for the "Required Status Checks" branch ruleset.
# It "needs" all the jobs that should block the Merge Queue.
+2 -1
View File
@@ -43,4 +43,5 @@ jobs:
from: ${{ matrix.pairs.from }}
into: ${{ matrix.pairs.into }}
name: ${{ matrix.pairs.name || format('{0} → {1}', matrix.pairs.from, matrix.pairs.into) }}
secrets: inherit
secrets:
NIXPKGS_CI_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
+2 -1
View File
@@ -42,4 +42,5 @@ jobs:
from: ${{ matrix.pairs.from }}
into: ${{ matrix.pairs.into }}
name: ${{ format('{0} → {1}', matrix.pairs.from, matrix.pairs.into) }}
secrets: inherit
secrets:
NIXPKGS_CI_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
+5
View File
@@ -11,6 +11,9 @@ on:
description: Target branch to merge into.
required: true
type: string
secrets:
NIXPKGS_CI_APP_PRIVATE_KEY:
required: true
defaults:
run:
@@ -32,6 +35,8 @@ jobs:
permission-pull-requests: write
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Find merge base between two branches
if: contains(inputs.from, ' ')
@@ -3,14 +3,15 @@ name: PR
on:
pull_request_target:
workflow_call:
inputs:
artifact-prefix:
required: true
type: string
secrets:
CACHIX_AUTH_TOKEN:
required: true
NIXPKGS_CI_APP_PRIVATE_KEY:
required: true
OWNER_APP_PRIVATE_KEY:
# The Test workflow should not actually request reviews from owners.
required: false
concurrency:
group: pr-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
@@ -34,12 +35,19 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout-cone-mode: true # default, for clarity
sparse-checkout: |
ci/github-script
- id: prepare
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
retries: 10
# The default for this includes code 422, which happens regularly for us when comparing commits:
# 422 - Server Error: Sorry, this diff is taking too long to generate.
# Listing all other values from here to effectively remove 422:
# https://github.com/octokit/plugin-retry.js/blob/9a2443746c350b3beedec35cf26e197ea318a261/src/index.ts#L14
retry-exempt-status-codes: 400,401,403,404
script: |
require('./ci/github-script/prepare.js')({
github,
@@ -83,16 +91,16 @@ jobs:
secrets:
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
with:
artifact-prefix: ${{ inputs.artifact-prefix }}
mergedSha: ${{ needs.prepare.outputs.mergedSha }}
targetSha: ${{ needs.prepare.outputs.targetSha }}
systems: ${{ needs.prepare.outputs.systems }}
defaultVersion: nixVersions.latest
testVersions: ${{ contains(fromJSON(needs.prepare.outputs.touched), 'pinned') && !contains(fromJSON(needs.prepare.outputs.headBranch).type, 'development') }}
labels:
name: Labels
bot:
name: Bot
needs: [prepare, eval]
uses: ./.github/workflows/labels.yml
uses: ./.github/workflows/bot.yml
permissions:
issues: write
pull-requests: write
@@ -101,16 +109,6 @@ jobs:
with:
headBranch: ${{ needs.prepare.outputs.headBranch }}
reviewers:
name: Reviewers
needs: [prepare, eval]
if: |
needs.prepare.outputs.targetSha &&
!contains(fromJSON(needs.prepare.outputs.headBranch).type, 'development')
uses: ./.github/workflows/reviewers.yml
secrets:
OWNER_APP_PRIVATE_KEY: ${{ secrets.OWNER_APP_PRIVATE_KEY }}
build:
name: Build
needs: [prepare]
@@ -118,6 +116,7 @@ jobs:
secrets:
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
with:
artifact-prefix: ${{ inputs.artifact-prefix }}
baseBranch: ${{ needs.prepare.outputs.baseBranch }}
mergedSha: ${{ needs.prepare.outputs.mergedSha }}
+92
View File
@@ -0,0 +1,92 @@
name: Review
on:
workflow_run:
workflows:
- Reviewed
types: [completed]
# This is used as fallback without app only.
# This happens when testing in forks without setting up that app.
permissions:
pull-requests: write
defaults:
run:
shell: bash
jobs:
process:
runs-on: ubuntu-24.04-arm
timeout-minutes: 2
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: |
ci/github-script
# Use the GitHub App to make sure the reaction happens with the same user who will later merge.
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
if: github.event_name != 'pull_request' && vars.NIXPKGS_CI_APP_ID
id: app-token
with:
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
permission-pull-requests: write
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ steps.app-token.outputs.token || github.token }}
retries: 3
script: |
const { handleMergeComment } = require('./ci/github-script/merge.js')
// PRs from forks don't have any PRs associated by default.
// Thus, we request the PR number with an API call *to* the fork's repo.
// Multiple pull requests can be open from the same head commit, either via
// different base branches or head branches.
const { head_repository, head_sha, repository } = context.payload.workflow_run
await Promise.all(
(await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit, {
owner: head_repository.owner.login,
repo: head_repository.name,
commit_sha: head_sha
}))
.filter(pull_request => pull_request.base.repo.id == repository.id)
.map(async (pull_request) =>
Promise.all(
(await github.paginate(github.rest.pulls.listReviews, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pull_request.number
})).map(review => {
// The `check` workflow creates review comments which reviewers
// are encouraged to manually dismiss if they're not relevant.
// When a CI-generated review is dismissed, this job automatically minimizes
// it, preventing it from cluttering the PR.
if (review.user?.login == 'github-actions[bot]' && review.state == 'DISMISSED')
return github.graphql(`
mutation($node_id:ID!) {
minimizeComment(input: {
classifier: RESOLVED,
subjectId: $node_id
})
{ clientMutationId }
}`,
{ node_id: review.node_id }
)
// The `bot` workflow reacts to comments with @NixOS/nixpkgs-merge-bot references, but might only
// pick up a comment after up to 10 minutes. To give the user instant feedback, this job adds
// a reaction to these comments.
return handleMergeComment({
github,
body: review.body,
node_id: review.node_id,
reaction: 'EYES',
})
})
)
)
)
@@ -1,8 +1,8 @@
name: Review dismissed
name: Reviewed
on:
pull_request_review:
types: [dismissed]
types: [submitted, dismissed]
permissions: {}
-170
View File
@@ -1,170 +0,0 @@
# This workflow will request reviews from the maintainers of each package
# listed in the PR's most recent eval comparison artifact.
name: Reviewers
on:
pull_request_target:
types: [ready_for_review]
workflow_call:
secrets:
OWNER_APP_PRIVATE_KEY:
required: true
concurrency:
group: reviewers-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions: {}
defaults:
run:
shell: bash
jobs:
request:
runs-on: ubuntu-24.04-arm
timeout-minutes: 20
steps:
- name: Check out the PR at the base commit
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
path: trusted
sparse-checkout: ci
- name: Install Nix
uses: cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31
- name: Build the requestReviews derivation
run: nix-build trusted/ci -A requestReviews
# For requesting reviewers, this job depends on a GitHub App with the following permissions:
# - Permissions:
# - Repository > Administration: read-only
# - Organization > Members: read-only
# - Repository > Pull Requests: read-write
# - Install App on this repository, setting these variables:
# - OWNER_APP_ID (variable)
# - OWNER_APP_PRIVATE_KEY (secret)
#
# Can't use the token received from permissions above, because it can't get enough permissions.
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
if: github.event_name == 'pull_request_target' && vars.OWNER_APP_ID
id: app-token
with:
app-id: ${{ vars.OWNER_APP_ID }}
private-key: ${{ secrets.OWNER_APP_PRIVATE_KEY }}
permission-administration: read
permission-members: read
permission-pull-requests: write
- name: Log current API rate limits (app-token)
if: ${{ steps.app-token.outputs.token }}
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh api /rate_limit | jq
- name: Requesting code owner reviews
if: steps.app-token.outputs.token
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
REPOSITORY: ${{ github.repository }}
NUMBER: ${{ github.event.number }}
# Don't do anything on draft PRs
DRY_MODE: ${{ github.event.pull_request.draft && '1' || '' }}
run: result/bin/request-code-owner-reviews.sh "$REPOSITORY" "$NUMBER" ci/OWNERS
- name: Log current API rate limits (app-token)
if: ${{ steps.app-token.outputs.token }}
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh api /rate_limit | jq
- name: Log current API rate limits (github.token)
env:
GH_TOKEN: ${{ github.token }}
run: gh api /rate_limit | jq
# In the regular case, this workflow is called via workflow_call from the eval workflow directly.
# In the more special case, when a PR is undrafted an eval run will have started already.
- name: Wait for comparison to be done
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
id: eval
with:
script: |
const run_id = (await github.rest.actions.listWorkflowRuns({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: context.eventName === 'pull_request' ? 'test.yml' : 'pr.yml',
event: context.eventName,
head_sha: context.payload.pull_request.head.sha
})).data.workflow_runs[0].id
core.setOutput('run-id', run_id)
// Waiting 120 * 5 sec = 10 min. max.
// The extreme case is an Eval run that just started when the PR is undrafted.
// Eval takes max 5-6 minutes, normally.
for (let i = 0; i < 120; i++) {
const result = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id,
name: 'comparison'
})
if (result.data.total_count > 0) return
await new Promise(resolve => setTimeout(resolve, 5000))
}
throw new Error("No comparison artifact found.")
- name: Log current API rate limits (github.token)
env:
GH_TOKEN: ${{ github.token }}
run: gh api /rate_limit | jq
- name: Download the comparison results
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
run-id: ${{ steps.eval.outputs.run-id }}
github-token: ${{ github.token }}
pattern: comparison
path: comparison
merge-multiple: true
- name: Log current API rate limits (app-token)
if: ${{ steps.app-token.outputs.token }}
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh api /rate_limit | jq
- name: Log current API rate limits (github.token)
env:
GH_TOKEN: ${{ github.token }}
run: gh api /rate_limit | jq
- name: Requesting maintainer reviews
if: ${{ steps.app-token.outputs.token }}
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
NUMBER: ${{ github.event.number }}
AUTHOR: ${{ github.event.pull_request.user.login }}
# Don't request reviewers on draft PRs
DRY_MODE: ${{ github.event.pull_request.draft && '1' || '' }}
run: |
# maintainers.json contains GitHub IDs. Look up handles to request reviews from.
# There appears to be no API to request reviews based on GitHub IDs
jq -r 'keys[]' comparison/maintainers.json \
| while read -r id; do gh api /user/"$id" --jq .login; done \
| GH_TOKEN=${{ steps.app-token.outputs.token }} result/bin/request-reviewers.sh "$REPOSITORY" "$NUMBER" "$AUTHOR"
- name: Log current API rate limits (app-token)
if: ${{ steps.app-token.outputs.token }}
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh api /rate_limit | jq
- name: Log current API rate limits (github.token)
env:
GH_TOKEN: ${{ github.token }}
run: gh api /rate_limit | jq
+79
View File
@@ -0,0 +1,79 @@
name: Teams
on:
schedule:
# Every Tuesday at 19:42 (randomly chosen)
- cron: '42 19 * * 1'
workflow_dispatch:
permissions: {}
defaults:
run:
shell: bash
jobs:
sync:
runs-on: ubuntu-24.04-arm
steps:
# Use a GitHub App to create the PR so that CI gets triggered and to
# request team member lists.
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
id: app-token
with:
app-id: ${{ vars.NIXPKGS_CI_APP_ID }}
private-key: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
permission-administration: read
permission-contents: write
permission-members: read
permission-pull-requests: write
- name: Fetch source
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout: |
ci/github-script
maintainers/github-teams.json
- name: Install dependencies
run: npm install bottleneck
- name: Synchronise teams
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
require('./ci/github-script/get-teams.js')({
github,
context,
core,
outFile: "maintainers/github-teams.json"
})
- name: Get GitHub App User Git String
id: user
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
run: |
name="${APP_SLUG}[bot]"
userId=$(gh api "/users/$name" --jq .id)
email="$userId+$name@users.noreply.github.com"
echo "git-string=$name <$email>" >> "$GITHUB_OUTPUT"
- name: Create Pull Request
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with:
token: ${{ steps.app-token.outputs.token }}
add-paths: maintainers/github-teams.json
author: ${{ steps.user.outputs.git-string }}
committer: ${{ steps.user.outputs.git-string }}
commit-message: "maintainers/github-teams.json: Automated sync"
branch: pr/github-team-sync
title: "maintainers/github-teams.json: Automated sync"
body: |
This is an automated PR to sync the GitHub teams with access to this repository to the `lib.teams` list.
This PR can be merged without taking any further action.
+16 -4
View File
@@ -21,12 +21,19 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
sparse-checkout-cone-mode: true # default, for clarity
sparse-checkout: |
ci/github-script
- id: prepare
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
retries: 10
# The default for this includes code 422, which happens regularly for us when comparing commits:
# 422 - Server Error: Sorry, this diff is taking too long to generate.
# Listing all other values from here to effectively remove 422:
# https://github.com/octokit/plugin-retry.js/blob/9a2443746c350b3beedec35cf26e197ea318a261/src/index.ts#L14
retry-exempt-status-codes: 400,401,403,404
script: |
require('./ci/github-script/prepare.js')({
github,
@@ -56,14 +63,16 @@ jobs:
if (files.some(file => [
'.github/actions/checkout/action.yml',
'.github/workflows/bot.yml',
'.github/workflows/build.yml',
'.github/workflows/check.yml',
'.github/workflows/eval.yml',
'.github/workflows/labels.yml',
'.github/workflows/lint.yml',
'.github/workflows/pr.yml',
'.github/workflows/reviewers.yml',
'.github/workflows/pull-request-target.yml',
'.github/workflows/test.yml',
'ci/github-script/bot.js',
'ci/github-script/merge.js',
'ci/github-script/withRateLimit.js',
].includes(file))) core.setOutput('pr', true)
merge-group:
@@ -77,6 +86,7 @@ jobs:
secrets:
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
with:
artifact-prefix: mg-
mergedSha: ${{ needs.prepare.outputs.mergedSha }}
targetSha: ${{ needs.prepare.outputs.targetSha }}
@@ -84,7 +94,7 @@ jobs:
if: needs.prepare.outputs.pr
name: PR
needs: [prepare]
uses: ./.github/workflows/pr.yml
uses: ./.github/workflows/pull-request-target.yml
# Those are actually only used on the pull_request_target event, but will throw an error if not set.
permissions:
issues: write
@@ -93,3 +103,5 @@ jobs:
secrets:
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
NIXPKGS_CI_APP_PRIVATE_KEY: ${{ secrets.NIXPKGS_CI_APP_PRIVATE_KEY }}
with:
artifact-prefix: pr-
+12
View File
@@ -0,0 +1,12 @@
# This file defines the ignore rules for zizmor.
#
# For rules that contain a high number of false positives, prefer listing them here
# instead of adding ignore comments. Note that zizmor cannot ignore by line-within-a-string, so
# there are some ignore items that encompass multiple problems within one `run` block. An issue
# tracking this is at https://github.com/woodruffw/zizmor/issues/648.
#
# For more info, see the documentation: https://woodruffw.github.io/zizmor/usage/#ignoring-results
rules:
dangerous-triggers:
disable: true
+2 -2
View File
@@ -430,7 +430,7 @@ gitGraph
Here's an overview of the different branches:
| branch | `master` | `staging-next` | `staging` | [`staging-nixos`](#test-driver-rebuild) |
| branch | `master` | `staging-next` | `staging` | [`staging-nixos`][test-driver-rebuild] |
| --- | --- | --- | --- | --- |
| Used for development | ✔️ | ❌ | ✔️ | ✔️ |
| Built by Hydra | ✔️ | ✔️ | ❌ | ❌ |
@@ -501,7 +501,7 @@ Please note that changes to the Linux kernel are an exception to this rule.
These PRs go to `staging-nixos`, see [the next section for more context](#changes-rebuilding-all-tests).
### Changes rebuilding all NixOS tests
[test-driver-rebuild]: #changes-rebuilding-all-tests
[test-driver-rebuild]: #changes-rebuilding-all-nixos-tests
Changes causing a rebuild of all NixOS tests get a special [`10.rebuild-nixos-tests`](https://github.com/NixOS/nixpkgs/issues?q=state%3Aopen%20label%3A10.rebuild-nixos-tests) label.
These changes pose a significant impact on the build infrastructure.
+10 -7
View File
@@ -32,6 +32,8 @@
/lib/asserts.nix @infinisil @hsjobeki @Profpatsch
/lib/path/* @infinisil @hsjobeki
/lib/fileset @infinisil @hsjobeki
/maintainers/github-teams.json @infinisil
/maintainers/computed-team-list.nix @infinisil
## Standard environmentrelated libraries
/lib/customisation.nix @alyssais @NixOS/stdenv
/lib/derivations.nix @alyssais @NixOS/stdenv
@@ -56,7 +58,8 @@
/pkgs/top-level/by-name-overlay.nix @infinisil @philiptaron
/pkgs/stdenv @philiptaron @NixOS/stdenv
/pkgs/stdenv/generic @Ericson2314 @NixOS/stdenv
/pkgs/stdenv/generic/check-meta.nix @Ericson2314 @NixOS/stdenv
/pkgs/stdenv/generic/check-meta.nix @Ericson2314 @adisbladis @NixOS/stdenv
/pkgs/stdenv/generic/meta-types.nix @adisbladis @NixOS/stdenv
/pkgs/stdenv/cross @Ericson2314 @NixOS/stdenv
/pkgs/build-support @philiptaron
/pkgs/build-support/cc-wrapper @Ericson2314
@@ -212,10 +215,10 @@ nixos/modules/installer/tools/nix-fallback-paths.nix @NixOS/nix-team @raitobeza
/pkgs/development/r-modules @jbedo
# Rust
/pkgs/development/compilers/rust @alyssais @Mic92 @zowoq @winterqt @figsoda
/pkgs/build-support/rust @zowoq @winterqt @figsoda
/pkgs/development/compilers/rust @alyssais @Mic92 @zowoq @winterqt
/pkgs/build-support/rust @zowoq @winterqt
/pkgs/build-support/rust/fetch-cargo-vendor* @TomaSajt
/doc/languages-frameworks/rust.section.md @zowoq @winterqt @figsoda
/doc/languages-frameworks/rust.section.md @zowoq @winterqt
# Tcl
/pkgs/development/interpreters/tcl @fgaz
@@ -341,7 +344,7 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt
/pkgs/top-level/agda-packages.nix @NixOS/agda
/pkgs/development/libraries/agda @NixOS/agda
/doc/languages-frameworks/agda.section.md @NixOS/agda
/nixos/tests/agda.nix @NixOS/agda
/nixos/tests/agda @NixOS/agda
# Idris
/pkgs/development/idris-modules @Infinisil
@@ -444,8 +447,8 @@ pkgs/by-name/fo/forgejo/ @adamcstephens @bendlas @emilylange
/pkgs/os-specific/linux/zfs @adamcstephens @amarshall
# Zig
/pkgs/development/compilers/zig @figsoda @RossComputerGuy
/doc/hooks/zig.section.md @figsoda @RossComputerGuy
/pkgs/development/compilers/zig @RossComputerGuy
/doc/hooks/zig.section.md @RossComputerGuy
# Buildbot
nixos/modules/services/continuous-integration/buildbot @Mic92 @zowoq
+54
View File
@@ -10,6 +10,53 @@ In order to ensure that the needed packages are generally available without buil
Run [`update-pinned.sh`](./update-pinned.sh) to update it.
## GitHub specific code
Some of the code is specific to GitHub.
This code is currently spread out over multiple places and written in both Bash and JavaScript.
The goal is to eventually have all GitHub specific code in `ci/github-script` and written in JavaScript via `actions/github-script`.
A lot of code has already been migrated, but some Bash code still remains.
New CI features need to be introduced in JavaScript, not Bash.
## Nixpkgs merge bot
The Nixpkgs merge bot empowers package maintainers by enabling them to merge PRs related to their own packages.
It serves as a bridge for maintainers to quickly respond to user feedback, facilitating a more self-reliant approach.
Especially when considering there are roughly 20 maintainers for every committer, this bot is a game-changer.
Following [RFC 172] the merge bot was originally implemented as a [python webapp](https://github.com/NixOS/nixpkgs-merge-bot), which has now been integrated into [`ci/github-script/bot.js`](./github-script/bot.js) and [`ci/github-script/merge.js`](./github-script/merge.js).
### Using the merge bot
To merge a PR, maintainers can simply comment:
```gfm
@NixOS/nixpkgs-merge-bot merge
```
The next time the bot runs it will verify the below constraints, then (if satisfied) merge the PR.
The merge bot will reference [#306934](https://github.com/NixOS/nixpkgs/issues/306934) on PRs it merges successfully, [#305350](https://github.com/NixOS/nixpkgs/issues/305350) for unsuccessful attempts, or [#371492](https://github.com/NixOS/nixpkgs/issues/371492) if an error occurs.
These issues effectively list PRs the merge bot has interacted with.
### Merge bot constraints
To ensure security and a focused utility, the bot adheres to specific limitations:
- The PR targets one of the [development branches](#branch-classification).
- The PR only touches files of packages located under `pkgs/by-name/*`.
- The PR is either:
- approved by a [committer][@NixOS/nixpkgs-committers].
- backported via label.
- opened by a [committer][@NixOS/nixpkgs-committers].
- opened by [@r-ryantm](https://nix-community.github.io/nixpkgs-update/r-ryantm/).
- The user attempting to merge is a member of [@NixOS/nixpkgs-maintainers].
- The user attempting to merge is a maintainer of all packages touched by the PR.
### Approving merge bot changes
Changes to the bot can usually be approved by the [@NixOS/nixpkgs-ci] team, as with other CI changes.
However, additional acknowledgement from the [@NixOS/nixpkgs-core] team is required for changes to what the merge bot will merge, who is eligible to use the merge bot, or similar changes in scope.
## `ci/nixpkgs-vet.sh BASE_BRANCH [REPOSITORY]`
Runs the [`nixpkgs-vet` tool](https://github.com/NixOS/nixpkgs-vet) on the HEAD commit, closely matching what CI does.
@@ -50,3 +97,10 @@ Some branches also have a version component, which is either `unstable` or `YY.M
`ci/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.
[@NixOS/nixpkgs-maintainers]: https://github.com/orgs/NixOS/teams/nixpkgs-maintainers
[@NixOS/nixpkgs-committers]: https://github.com/orgs/NixOS/teams/nixpkgs-committers
[@NixOS/nixpkgs-ci]: https://github.com/orgs/NixOS/teams/nixpkgs-ci
[@NixOS/nixpkgs-core]: https://github.com/orgs/NixOS/teams/nixpkgs-core
[RFC 172]: https://github.com/NixOS/rfcs/pull/172
+4 -1
View File
@@ -49,6 +49,8 @@ let
programs.biome = {
enable = true;
# Disable settings validation because its inputs are liable to hash mismatch
validate.enable = false;
settings.formatter = {
useEditorconfig = true;
};
@@ -136,6 +138,8 @@ let
[ "--config=${config}" ];
includes = [ "*.md" ];
};
programs.zizmor.enable = true;
};
fs = pkgs.lib.fileset;
nixFilesSrc = fs.toSource {
@@ -152,7 +156,6 @@ let
in
rec {
inherit pkgs fmt;
requestReviews = pkgs.callPackage ./request-reviews { };
codeownersValidator = pkgs.callPackage ./codeowners-validator { };
# FIXME(lf-): it might be useful to test other Nix implementations
+2
View File
@@ -18,6 +18,7 @@
lib ? import (path + "/lib"),
trace ? false,
path ? ./../..,
extraNixpkgsConfigJson ? "{}",
}:
let
@@ -46,6 +47,7 @@ let
outpaths = import ./outpaths.nix {
inherit path;
extraNixpkgsConfig = builtins.fromJSON extraNixpkgsConfigJson;
attrNamesOnly = true;
};
+2
View File
@@ -8,6 +8,7 @@
myChunk,
includeBroken,
systems,
extraNixpkgsConfigJson,
}:
let
@@ -17,6 +18,7 @@ let
unfiltered = import ./outpaths.nix {
inherit path;
inherit includeBroken systems;
extraNixpkgsConfig = builtins.fromJSON extraNixpkgsConfigJson;
};
# Turns the unfiltered recursive attribute set into one that is limited to myAttrpaths
+55 -16
View File
@@ -7,6 +7,7 @@
python3,
stdenvNoCC,
makeWrapper,
codeowners,
}:
let
python = python3.withPackages (ps: [
@@ -48,8 +49,7 @@ in
{
combinedDir,
touchedFilesJson,
githubAuthorId,
byName ? false,
ownersFile ? ../../OWNERS,
}:
let
# Usually we expect a derivation, but when evaluating in multiple separate steps, we pass
@@ -156,23 +156,19 @@ let
# Only set this label when no other label with indication for staging has been set.
# This avoids confusion whether to target staging or batch this with kernel updates.
lib.last (lib.sort lib.lessThan (lib.attrValues rebuildCountByKernel)) <= 500;
# Set the "11.by: package-maintainer" label to whether all packages directly
# changed are maintained by the PR's author.
"11.by: package-maintainer" =
maintainers ? ${githubAuthorId}
&& lib.all (lib.flip lib.elem maintainers.${githubAuthorId}) (
lib.flatten (lib.attrValues maintainers)
);
};
}
);
maintainers = callPackage ./maintainers.nix { } {
changedattrs = lib.attrNames (lib.groupBy (a: a.name) changedPackagePlatformAttrs);
changedpathsjson = touchedFilesJson;
removedattrs = lib.attrNames (lib.groupBy (a: a.name) removedPackagePlatformAttrs);
inherit byName;
};
inherit
(callPackage ./maintainers.nix { } {
changedattrs = lib.attrNames (lib.groupBy (a: a.name) changedPackagePlatformAttrs);
changedpathsjson = touchedFilesJson;
removedattrs = lib.attrNames (lib.groupBy (a: a.name) removedPackagePlatformAttrs);
})
maintainers
packages
;
in
runCommand "compare"
{
@@ -180,9 +176,14 @@ runCommand "compare"
nativeBuildInputs = map lib.getBin [
jq
cmp-stats
codeowners
];
maintainers = builtins.toJSON maintainers;
passAsFile = [ "maintainers" ];
packages = builtins.toJSON packages;
passAsFile = [
"maintainers"
"packages"
];
}
''
mkdir $out
@@ -224,5 +225,43 @@ runCommand "compare"
} >> $out/step-summary.md
fi
jq -r '.[]' "${touchedFilesJson}" > ./touched-files
readarray -t touchedFiles < ./touched-files
echo "This PR touches ''${#touchedFiles[@]} files"
# TODO: Move ci/OWNERS to Nix and produce owners.json instead of owners.txt.
touch "$out/owners.txt"
for file in "''${touchedFiles[@]}"; do
result=$(codeowners --file "${ownersFile}" "$file")
# Remove the file prefix and trim the surrounding spaces
read -r owners <<< "''${result#"$file"}"
if [[ "$owners" == "(unowned)" ]]; then
echo "File $file is unowned"
continue
fi
echo "File $file is owned by $owners"
# Split up multiple owners, separated by arbitrary amounts of spaces
IFS=" " read -r -a entries <<< "$owners"
for entry in "''${entries[@]}"; do
# GitHub technically also supports Emails as code owners,
# but we can't easily support that, so let's not
if [[ ! "$entry" =~ @(.*) ]]; then
echo -e "\e[33mCodeowner \"$entry\" for file $file is not valid: Must start with \"@\"\e[0m"
# Don't fail, because the PR for which this script runs can't fix it,
# it has to be fixed in the base branch
continue
fi
# The first regex match is everything after the @
entry=''${BASH_REMATCH[1]}
echo "$entry" >> "$out/owners.txt"
done
done
cp "$maintainersPath" "$out/maintainers.json"
cp "$packagesPath" "$out/packages.json"
''
+48 -33
View File
@@ -5,33 +5,52 @@
changedattrs,
changedpathsjson,
removedattrs,
byName ? false,
}:
let
pkgs = import ../../.. {
system = "x86_64-linux";
config = { };
overlays = [ ];
# We should never try to ping maintainers through package aliases, this can only lead to errors.
# One example case is, where an attribute is a throw alias, but then re-introduced in a PR.
# This would trigger the throw. By disabling aliases, we can fallback gracefully below.
config.allowAliases = false;
};
changedpaths = builtins.fromJSON (builtins.readFile changedpathsjson);
changedpaths = lib.importJSON changedpathsjson;
anyMatchingFile =
filename: builtins.any (changed: lib.strings.hasSuffix changed filename) changedpaths;
# Extract attributes that changed from by-name paths.
# This allows pinging reviewers for pure refactors.
touchedattrs = lib.pipe changedpaths [
(lib.filter (changed: lib.hasPrefix "pkgs/by-name/" changed))
(map (lib.splitString "/"))
(map (path: lib.elemAt path 3))
lib.unique
];
anyMatchingFiles = files: builtins.any anyMatchingFile files;
anyMatchingFile = filename: lib.any (lib.hasPrefix filename) changedpaths;
attrsWithMaintainers = lib.pipe (changedattrs ++ removedattrs) [
anyMatchingFiles = files: lib.any anyMatchingFile files;
sharded = name: "${lib.substring 0 2 name}/${name}";
attrsWithMaintainers = lib.pipe (changedattrs ++ removedattrs ++ touchedattrs) [
# An attribute can appear in changed/removed *and* touched
lib.unique
(map (
name:
let
path = lib.splitString "." name;
# Some packages might be reported as changed on a different platform, but
# not even have an attribute on the platform the maintainers are requested on.
# Fallback to `null` for these to filter them out below.
package = lib.attrByPath (lib.splitString "." name) null pkgs;
package = lib.attrByPath path null pkgs;
in
{
inherit name package;
# Adds all files in by-name to each package, no matter whether they are discoverable
# via meta attributes below. For example, this allows pinging maintainers for
# updates to .json files.
# TODO: Support by-name package sets.
filenames = lib.optional (lib.length path == 1) "pkgs/by-name/${sharded (lib.head path)}/";
# TODO: Refactor this so we can ping entire teams instead of the individual members.
# Note that this will require keeping track of GH team IDs in "maintainers/teams.nix".
maintainers = package.meta.maintainers or [ ];
@@ -40,25 +59,21 @@ let
# No need to match up packages without maintainers with their files.
# This also filters out attributes where `packge = null`, which is the
# case for libintl, for example.
(builtins.filter (pkg: pkg.maintainers != [ ]))
(lib.filter (pkg: pkg.maintainers != [ ]))
];
relevantFilenames =
drv:
(lib.lists.unique (
map (pos: lib.strings.removePrefix (toString ../..) pos.file) (
builtins.filter (x: x != null) [
((drv.meta or { }).maintainersPosition or null)
((drv.meta or { }).teamsPosition or null)
(builtins.unsafeGetAttrPos "src" drv)
# broken because name is always set by stdenv:
# # A hack to make `nix-env -qa` and `nix search` ignore broken packages.
# # TODO(@oxij): remove this assert when something like NixOS/nix#1771 gets merged into nix.
# name = assert validity.handled; name + lib.optionalString
#(builtins.unsafeGetAttrPos "name" drv)
(builtins.unsafeGetAttrPos "pname" drv)
(builtins.unsafeGetAttrPos "version" drv)
(lib.unique (
map (pos: lib.removePrefix "${toString ../../..}/" pos.file) (
lib.filter (x: x != null) [
(drv.meta.maintainersPosition or null)
(drv.meta.teamsPosition or null)
(lib.unsafeGetAttrPos "src" drv)
(lib.unsafeGetAttrPos "pname" drv)
(lib.unsafeGetAttrPos "version" drv)
]
++ lib.optionals (drv ? meta.position) [
# Use ".meta.position" for cases when most of the package is
# defined in a "common" section and the only place where
# reference to the file with a derivation the "pos"
@@ -68,16 +83,16 @@ let
# "pkgs/tools/package-management/nix/default.nix:155"
# We transform it to the following:
# { file = "pkgs/tools/package-management/nix/default.nix"; }
{ file = lib.head (lib.splitString ":" (drv.meta.position or "")); }
{ file = lib.head (lib.splitString ":" drv.meta.position); }
]
)
));
attrsWithFilenames = map (
pkg: pkg // { filenames = relevantFilenames pkg.package; }
pkg: pkg // { filenames = pkg.filenames ++ relevantFilenames pkg.package; }
) attrsWithMaintainers;
attrsWithModifiedFiles = builtins.filter (pkg: anyMatchingFiles pkg.filenames) attrsWithFilenames;
attrsWithModifiedFiles = lib.filter (pkg: anyMatchingFiles pkg.filenames) attrsWithFilenames;
listToPing = lib.concatMap (
pkg:
@@ -89,10 +104,10 @@ let
}) pkg.maintainers
) attrsWithModifiedFiles;
byMaintainer = lib.groupBy (ping: toString ping.${if byName then "github" else "id"}) listToPing;
packagesPerMaintainer = lib.attrsets.mapAttrs (
maintainer: packages: map (pkg: pkg.packageName) packages
) byMaintainer;
byMaintainer = lib.groupBy (ping: toString ping.id) listToPing;
in
packagesPerMaintainer
{
maintainers = lib.mapAttrs (_: lib.catAttrs "packageName") byMaintainer;
packages = lib.catAttrs "packageName" listToPing;
}
+1 -1
View File
@@ -22,7 +22,7 @@ rec {
splittedPath = lib.splitString "." packagePlatformPath;
# ["python312Packages" "numpy" "aarch64-linux"] -> ["python312Packages" "numpy"]
packagePath = lib.sublist 0 (lib.length splittedPath - 1) splittedPath;
packagePath = lib.init splittedPath;
# "python312Packages.numpy"
name = lib.concatStringsSep "." packagePath;
+30 -19
View File
@@ -19,6 +19,17 @@
nix,
}:
{
# The number of attributes per chunk, see ./README.md for more info.
chunkSize ? 5000,
# Whether to just evaluate a single chunk for quick testing
quickTest ? false,
# Don't try to eval packages marked as broken.
includeBroken ? false,
# Customize the config used to evaluate nixpkgs
extraNixpkgsConfig ? { },
}:
let
nixpkgs =
with lib.fileset;
@@ -66,6 +77,7 @@ let
"$src/ci/eval/attrpaths.nix" \
-A paths \
-I "$src" \
--argstr extraNixpkgsConfigJson ${lib.escapeShellArg (builtins.toJSON extraNixpkgsConfig)} \
--option restrict-eval true \
--option allow-import-from-derivation false \
--option eval-system "${evalSystem}" > $out/paths.json
@@ -79,13 +91,6 @@ let
evalSystem ? builtins.currentSystem,
# The path to the `paths.json` file from `attrpathsSuperset`
attrpathFile ? "${attrpathsSuperset { inherit evalSystem; }}/paths.json",
# The number of attributes per chunk, see ./README.md for more info.
chunkSize ? 5000,
# Don't try to eval packages marked as broken.
includeBroken ? false,
# Whether to just evaluate a single chunk for quick testing
quickTest ? false,
}:
let
singleChunk = writeShellScript "single-chunk" ''
@@ -95,6 +100,10 @@ let
system=$3
outputDir=$4
# Default is 5, higher values effectively disable the warning.
# This randomly breaks Eval.
export GC_LARGE_ALLOC_WARN_INTERVAL=1000
export NIX_SHOW_STATS=1
export NIX_SHOW_STATS_PATH="$outputDir/stats/$myChunk"
echo "Chunk $myChunk on $system start"
@@ -107,12 +116,14 @@ let
--option allow-import-from-derivation false \
--query --available \
--out-path --json \
--meta \
--show-trace \
--arg chunkSize "$chunkSize" \
--arg myChunk "$myChunk" \
--arg attrpathFile "${attrpathFile}" \
--arg systems "[ \"$system\" ]" \
--arg includeBroken ${lib.boolToString includeBroken} \
--argstr extraNixpkgsConfigJson ${lib.escapeShellArg (builtins.toJSON extraNixpkgsConfig)} \
-I ${nixpkgs} \
-I ${attrpathFile} \
> "$outputDir/result/$myChunk" \
@@ -200,6 +211,7 @@ let
fi
cat "$chunkOutputDir"/result/* | jq -s 'add | map_values(.outputs)' > $out/${evalSystem}/paths.json
cat "$chunkOutputDir"/result/* | jq -s 'add | map_values(.meta)' > $out/${evalSystem}/meta.json
'';
diff = callPackage ./diff.nix { };
@@ -228,6 +240,14 @@ let
})
' > $out/combined-diff.json
# Combine maintainers from all systems
cat ${diffDir}/*/maintainers.json | jq -s '
add | group_by(.package) | map({
key: .[0].package,
value: map(.maintainers) | flatten | unique
}) | from_entries
' > $out/maintainers.json
mkdir -p $out/before/stats
for d in ${diffDir}/before/*; do
cp -r "$d"/stats-by-chunk $out/before/stats/$(basename "$d")
@@ -245,16 +265,13 @@ let
{
# Whether to evaluate on a specific set of systems, by default all are evaluated
evalSystems ? if quickTest then [ "x86_64-linux" ] else supportedSystems,
# The number of attributes per chunk, see ./README.md for more info.
chunkSize ? 5000,
quickTest ? false,
}:
symlinkJoin {
name = "nixpkgs-eval-baseline";
paths = map (
evalSystem:
singleSystem {
inherit quickTest evalSystem chunkSize;
inherit evalSystem;
}
) evalSystems;
};
@@ -263,13 +280,7 @@ let
{
# Whether to evaluate on a specific set of systems, by default all are evaluated
evalSystems ? if quickTest then [ "x86_64-linux" ] else supportedSystems,
# The number of attributes per chunk, see ./README.md for more info.
chunkSize ? 5000,
quickTest ? false,
baseline,
# Which maintainer should be considered the author?
# Defaults to nixpkgs-ci which is not a maintainer and skips the check.
githubAuthorId ? "nixpkgs-ci",
# What files have been touched? Defaults to none; use the expression below to calculate it.
# ```
# git diff --name-only --merge-base master HEAD \
@@ -286,14 +297,14 @@ let
inherit evalSystem;
beforeDir = baseline;
afterDir = singleSystem {
inherit quickTest evalSystem chunkSize;
inherit evalSystem;
};
}
) evalSystems;
};
comparisonReport = compare {
combinedDir = combine { diffDir = diffs; };
inherit touchedFilesJson githubAuthorId;
inherit touchedFilesJson;
};
in
comparisonReport;
+27 -2
View File
@@ -76,11 +76,36 @@ let
afterAttrs = getAttrs after;
diffAttrs = diff beforeAttrs afterAttrs;
diffJson = writeText "diff.json" (builtins.toJSON diffAttrs);
# The maintainer list is not diffed, but just taken as is, to provide a map
# of maintainers on the target branch. A list of GitHub IDs is sufficient for
# all our purposes and reduces size massively.
meta = lib.importJSON "${after}/${evalSystem}/meta.json";
maintainers = lib.pipe meta [
(lib.mapAttrsToList (
k: v: {
# splits off the platform suffix
package = lib.pipe k [
(lib.splitString ".")
lib.init
(lib.concatStringsSep ".")
];
maintainers = map (m: m.githubId) v.maintainers or [ ];
}
))
# Some paths don't have a platform suffix, those will appear with an empty package here.
(lib.filter ({ package, maintainers }: package != "" && maintainers != [ ]))
];
maintainersJson = writeText "maintainers.json" (builtins.toJSON maintainers);
in
runCommand "diff" { } ''
mkdir -p $out/${evalSystem}
cp -r ${before} $out/before
cp -r ${after} $out/after
cp -r --no-preserve=mode ${before} $out/before
cp -r --no-preserve=mode ${after} $out/after
# JSON files will be processed above explicitly, so avoid copying over
# the source files to keep the artifacts smaller.
find $out/before $out/after -iname '*.json' -delete
cp ${diffJson} $out/${evalSystem}/diff.json
cp ${maintainersJson} $out/${evalSystem}/maintainers.json
''
+5 -1
View File
@@ -11,6 +11,9 @@
# Set this to `null` to build for builtins.currentSystem only
systems ? builtins.fromJSON (builtins.readFile ../supportedSystems.json),
# Customize the config used to evaluate nixpkgs
extraNixpkgsConfig ? { },
}:
let
lib = import (path + "/lib");
@@ -55,7 +58,8 @@ let
true;
inHydra = true;
};
}
// extraNixpkgsConfig;
__allowFileset = false;
};
@@ -3,10 +3,142 @@ module.exports = async ({ github, context, core, dry }) => {
const { DefaultArtifactClient } = require('@actions/artifact')
const { readFile, writeFile } = require('node:fs/promises')
const withRateLimit = require('./withRateLimit.js')
const { classify } = require('../supportedBranches.js')
const { handleMerge } = require('./merge.js')
const { handleReviewers } = require('./reviewers.js')
const artifactClient = new DefaultArtifactClient()
async function handlePullRequest({ item, stats }) {
async function downloadMaintainerMap(branch) {
let run
const commits = (
await github.rest.repos.listCommits({
...context.repo,
sha: branch,
// We look at 10 commits to find a maintainer map, but this is an arbitrary number. The
// head commit might not have a map, if the queue was bypassed to merge it. This happens
// frequently on staging-esque branches. The branch with the highest chance of getting
// 10 consecutive bypassing commits is the stable staging-next branch. Luckily, this
// also means that the number of PRs open towards that branch is very low, so falling
// back to slightly imprecise maintainer data from master only has a marginal effect.
per_page: 10,
})
).data
for (const commit of commits) {
const run = (
await github.rest.actions.listWorkflowRuns({
...context.repo,
workflow_id: 'merge-group.yml',
status: 'success',
exclude_pull_requests: true,
per_page: 1,
head_sha: commit.sha,
})
).data.workflow_runs[0]
if (!run) continue
const artifact = (
await github.rest.actions.listWorkflowRunArtifacts({
...context.repo,
run_id: run.id,
name: 'maintainers',
})
).data.artifacts[0]
if (!artifact) continue
await artifactClient.downloadArtifact(artifact.id, {
findBy: {
repositoryName: context.repo.repo,
repositoryOwner: context.repo.owner,
token: core.getInput('github-token'),
},
path: path.resolve(path.join('branches', branch)),
expectedHash: artifact.digest,
})
return JSON.parse(
await readFile(
path.resolve(path.join('branches', branch, 'maintainers.json')),
'utf-8',
),
)
}
// We get here when none of the 10 commits we looked at contained a maintainer map.
// For the master branch, we don't have any fallback options, so we error out.
// For other branches, we select a suitable fallback below.
if (branch === 'master') throw new Error('No maintainer map found.')
const { stable, version } = classify(branch)
const release = `release-${version}`
if (stable && branch !== release) {
// Only fallback to the release branch from *other* stable branches.
// Explicitly avoids infinite recursion.
return await getMaintainerMap(release)
} else {
// Falling back to master as last resort.
// This can either be the case for unstable staging-esque or wip branches,
// or for the primary stable branch (release-XX.YY).
return await getMaintainerMap('master')
}
}
// Simple cache for maintainer maps to avoid downloading the same artifacts
// over and over again. Ultimately returns a promise, so the result must be
// awaited for.
const maintainerMaps = {}
function getMaintainerMap(branch) {
if (!maintainerMaps[branch]) {
maintainerMaps[branch] = downloadMaintainerMap(branch)
}
return maintainerMaps[branch]
}
// Caching the list of team members saves API requests when running the bot on the schedule and
// processing many PRs at once.
const members = {}
function getTeamMembers(team_slug) {
if (context.eventName === 'pull_request') {
// We have no chance of getting a token in the pull_request context with the right
// permissions to access the members endpoint below. Thus, we're pretending to have
// no members. This is OK; because this is only for the Test workflow, not for
// real use.
return []
}
if (!members[team_slug]) {
members[team_slug] = github.paginate(github.rest.teams.listMembersInOrg, {
org: context.repo.owner,
team_slug,
per_page: 100,
})
}
return members[team_slug]
}
// Caching users saves API requests when running the bot on the schedule and processing
// many PRs at once. It also helps to encapsulate the special logic we need, because
// actions/github doesn't support that endpoint fully, yet.
const users = {}
function getUser(id) {
if (!users[id]) {
users[id] = github
.request({
method: 'GET',
url: '/user/{id}',
id,
})
.then((resp) => resp.data)
}
return users[id]
}
async function handlePullRequest({ item, stats, events }) {
const log = (k, v) => core.info(`PR #${item.number} - ${k}: ${v}`)
const pull_number = item.number
@@ -20,11 +152,75 @@ module.exports = async ({ github, context, core, dry }) => {
})
).data
const reviews = await github.paginate(github.rest.pulls.listReviews, {
...context.repo,
pull_number,
const maintainers = await getMaintainerMap(pull_request.base.ref)
const merge_bot_eligible = await handleMerge({
github,
context,
core,
log,
dry,
pull_request,
events,
maintainers,
getTeamMembers,
getUser,
})
// When the same change has already been merged to the target branch, a PR will still be
// open and display the same changes - but will not actually have any effect. This causes
// strange CI behavior, because the diff of the merge-commit is empty, no rebuilds will
// be detected, no maintainers pinged.
// We can just check the temporary merge commit, and if it's empty the PR can safely be
// closed - there are no further changes.
// We only do this for PRs, which are non-empty to start with. This avoids closing PRs
// which have been created with an empty commit for notification purposes, for example
// the yearly election notification for voters.
if (pull_request.merge_commit_sha && pull_request.changed_files > 0) {
const commit = (
await github.rest.repos.getCommit({
...context.repo,
ref: pull_request.merge_commit_sha,
})
).data
if (commit.files.length === 0) {
const body = [
`The diff for the temporary merge commit ${pull_request.merge_commit_sha} is empty.`,
'The changes in this PR have almost certainly already been merged to the target branch.',
].join('\n')
core.info(`PR #${item.number}: closed`)
if (!dry) {
await github.rest.issues.createComment({
...context.repo,
issue_number: pull_number,
body,
})
await github.rest.pulls.update({
...context.repo,
pull_number,
state: 'closed',
})
}
return {}
}
}
// Check for any human reviews other than GitHub actions and other GitHub apps.
// Accounts could be deleted as well, so don't count them.
const reviews = (
await github.paginate(github.rest.pulls.listReviews, {
...context.repo,
pull_number,
})
).filter(
(r) => r.user && !r.user.login.endsWith('[bot]') && r.user.type !== 'Bot',
)
const approvals = new Set(
reviews
.filter((review) => review.state === 'APPROVED')
@@ -53,6 +249,7 @@ module.exports = async ({ github, context, core, dry }) => {
// The second pass will then read the result from the first pass and set the label.
'2.status: merge conflict':
merge_commit_sha_valid && !pull_request.merge_commit_sha,
'2.status: merge-bot eligible': merge_bot_eligible,
'12.approvals: 1': approvals.size === 1,
'12.approvals: 2': approvals.size === 2,
'12.approvals: 3+': approvals.size >= 3,
@@ -67,12 +264,24 @@ module.exports = async ({ github, context, core, dry }) => {
(
await github.rest.actions.listWorkflowRuns({
...context.repo,
workflow_id: 'pull-request-target.yml',
event: 'pull_request_target',
exclude_pull_requests: true,
head_sha: pull_request.head.sha,
})
).data.workflow_runs[0] ??
// TODO: Remove this after 2026-02-01, at which point all pr.yml artifacts will have expired.
(
await github.rest.actions.listWorkflowRuns({
...context.repo,
// In older PRs, we need pr.yml instead of pull-request-target.yml.
workflow_id: 'pr.yml',
event: 'pull_request_target',
exclude_pull_requests: true,
head_sha: pull_request.head.sha,
})
).data.workflow_runs[0] ?? {}
).data.workflow_runs[0] ??
{}
// Newer PRs might not have run Eval to completion, yet.
// Older PRs might not have an eval.yml workflow, yet.
@@ -80,13 +289,6 @@ module.exports = async ({ github, context, core, dry }) => {
log('Last eval run', run_id ?? '<n/a>')
if (conclusion === 'success') {
// Check for any human reviews other than GitHub actions and other GitHub apps.
// Accounts could be deleted as well, so don't count them.
const humanReviews = reviews.filter(
(r) =>
r.user && !r.user.login.endsWith('[bot]') && r.user.type !== 'Bot',
)
Object.assign(prLabels, {
// We only set this label if the latest eval run was successful, because if it was not, it
// *could* have requested reviewers. We will let the PR author fix CI first, before "escalating"
@@ -99,7 +301,7 @@ module.exports = async ({ github, context, core, dry }) => {
'9.needs: reviewer':
!pull_request.draft &&
pull_request.requested_reviewers.length === 0 &&
humanReviews.length === 0,
reviews.length === 0,
})
}
@@ -134,22 +336,78 @@ module.exports = async ({ github, context, core, dry }) => {
expectedHash: artifact.digest,
})
const maintainers = new Set(
Object.keys(
JSON.parse(
await readFile(`${pull_number}/maintainers.json`, 'utf-8'),
),
).map((m) => Number.parseInt(m, 10)),
)
const evalLabels = JSON.parse(
await readFile(`${pull_number}/changed-paths.json`, 'utf-8'),
).labels
// TODO: Get "changed packages" information from list of changed by-name files
// in addition to just the Eval results, to make this work for these packages
// when Eval results have expired as well.
let packages
try {
packages = JSON.parse(
await readFile(`${pull_number}/packages.json`, 'utf-8'),
)
} catch (e) {
if (e.code !== 'ENOENT') throw e
// TODO: Remove this fallback code once all old artifacts without packages.json
// have expired. This should be the case in ~ February 2026.
packages = Array.from(
new Set(
Object.values(
JSON.parse(
await readFile(`${pull_number}/maintainers.json`, 'utf-8'),
),
).flat(1),
),
)
}
Object.assign(prLabels, evalLabels, {
'12.approved-by: package-maintainer':
maintainers.intersection(approvals).size > 0,
'11.by: package-maintainer':
Boolean(packages.length) &&
packages.every((pkg) =>
maintainers[pkg]?.includes(pull_request.user.id),
),
'12.approved-by: package-maintainer': packages.some((pkg) =>
maintainers[pkg]?.some((m) => approvals.has(m)),
),
})
if (!pull_request.draft) {
let owners = []
try {
// TODO: Create owner map similar to maintainer map.
owners = (await readFile(`${pull_number}/owners.txt`, 'utf-8')).split(
'\n',
)
} catch (e) {
// Older artifacts don't have the owners.txt, yet.
if (e.code !== 'ENOENT') throw e
}
// We set this label earlier already, but the current PR state can be very different
// after handleReviewers has requested reviews, so update it in this case to prevent
// this label from flip-flopping.
prLabels['9.needs: reviewer'] = await handleReviewers({
github,
context,
core,
log,
dry,
pull_request,
reviews,
// TODO: Use maintainer map instead of the artifact.
maintainers: Object.keys(
JSON.parse(
await readFile(`${pull_number}/maintainers.json`, 'utf-8'),
),
).map((id) => parseInt(id)),
owners,
getTeamMembers,
getUser,
})
}
}
return prLabels
@@ -210,9 +468,21 @@ module.exports = async ({ github, context, core, dry }) => {
const itemLabels = {}
const events = await github.paginate(
github.rest.issues.listEventsForTimeline,
{
...context.repo,
issue_number,
per_page: 100,
},
)
if (item.pull_request || context.payload.pull_request) {
stats.prs++
Object.assign(itemLabels, await handlePullRequest({ item, stats }))
Object.assign(
itemLabels,
await handlePullRequest({ item, stats, events }),
)
} else {
stats.issues++
if (item.labels.some(({ name }) => name === '4.workflow: auto-close')) {
@@ -224,13 +494,7 @@ module.exports = async ({ github, context, core, dry }) => {
}
const latest_event_at = new Date(
(
await github.paginate(github.rest.issues.listEventsForTimeline, {
...context.repo,
issue_number,
per_page: 100,
})
)
events
.filter(({ event }) =>
[
// These events are hand-picked from:
@@ -292,7 +556,7 @@ module.exports = async ({ github, context, core, dry }) => {
const hasChanges = Object.keys(after).some(
(name) => (before[name] ?? false) !== after[name],
)
if (log('Has changes', hasChanges, !hasChanges)) return
if (log('Has label changes', hasChanges, !hasChanges)) return
// Skipping labeling on a pull_request event, because we have no privileges.
const labels = Object.entries(after)
@@ -310,14 +574,21 @@ module.exports = async ({ github, context, core, dry }) => {
}
}
await withRateLimit({ github, core }, async (stats) => {
// Controls level of parallelism. Applies to both the number of concurrent requests
// as well as the number of concurrent workers going through the list of PRs.
// We'll only boost concurrency when we're running many PRs in parallel on a schedule,
// but not for single PRs. This avoids things going wild, when we accidentally make
// too many API requests on treewides.
const maxConcurrent = context.payload.pull_request ? 1 : 20
await withRateLimit({ github, core, maxConcurrent }, async (stats) => {
if (context.payload.pull_request) {
await handle({ item: context.payload.pull_request, stats })
} else {
const lastRun = (
await github.rest.actions.listWorkflowRuns({
...context.repo,
workflow_id: 'labels.yml',
workflow_id: 'bot.yml',
event: 'schedule',
status: 'success',
exclude_pull_requests: true,
@@ -439,11 +710,23 @@ module.exports = async ({ github, context, core, dry }) => {
arr.findIndex((firstItem) => firstItem.number === thisItem.number),
)
;(await Promise.allSettled(items.map((item) => handle({ item, stats }))))
.filter(({ status }) => status === 'rejected')
.map(({ reason }) =>
core.setFailed(`${reason.message}\n${reason.cause.stack}`),
)
// Instead of handling all items in parallel we set up some workers to handle the queue
// with more controlled parallelism. This avoids problems with `pull_request` fetched at
// the beginning getting out of date towards the end, because it took the whole job 20
// minutes or more to go through 100's of PRs.
await Promise.all(
Array.from({ length: maxConcurrent }, async () => {
while (true) {
const item = items.pop()
if (!item) break
try {
await handle({ item, stats })
} catch (e) {
core.setFailed(`${e.message}\n${e.cause.stack}`)
}
}
}),
)
}
})
}
+85
View File
@@ -0,0 +1,85 @@
const excludeTeams = [
/^voters.*$/,
/^nixpkgs-maintainers$/,
/^nixpkgs-committers$/,
]
module.exports = async ({ github, context, core, outFile }) => {
const withRateLimit = require('./withRateLimit.js')
const { writeFileSync } = require('node:fs')
const org = context.repo.owner
const result = {}
await withRateLimit({ github, core }, async () => {
// Turn an Array of users into an Object, mapping user.login -> user.id
function makeUserSet(users) {
// Sort in-place and build result by mutation
users.sort((a, b) => (a.login > b.login ? 1 : -1))
return users.reduce((acc, user) => {
acc[user.login] = user.id
return acc
}, {})
}
// Process a list of teams and append to the result variable
async function processTeams(teams) {
for (const team of teams) {
core.notice(`Processing team ${team.slug}`)
if (!excludeTeams.some((regex) => team.slug.match(regex))) {
const members = makeUserSet(
await github.paginate(github.rest.teams.listMembersInOrg, {
org,
team_slug: team.slug,
role: 'member',
}),
)
const maintainers = makeUserSet(
await github.paginate(github.rest.teams.listMembersInOrg, {
org,
team_slug: team.slug,
role: 'maintainer',
}),
)
result[team.slug] = {
description: team.description,
id: team.id,
maintainers,
members,
name: team.name,
}
}
await processTeams(
await github.paginate(github.rest.teams.listChildInOrg, {
org,
team_slug: team.slug,
}),
)
}
}
const teams = await github.paginate(github.rest.repos.listTeams, {
...context.repo,
})
await processTeams(teams)
})
// Sort the teams by team name
const sorted = Object.keys(result)
.sort()
.reduce((acc, key) => {
acc[key] = result[key]
return acc
}, {})
const json = `${JSON.stringify(sorted, null, 2)}\n`
if (outFile) {
writeFileSync(outFile, json)
} else {
console.log(json)
}
}
+347
View File
@@ -0,0 +1,347 @@
const { classify } = require('../supportedBranches.js')
function runChecklist({
committers,
events,
files,
pull_request,
log,
maintainers,
user,
userIsMaintainer,
}) {
const allByName = files.every(
({ filename }) =>
filename.startsWith('pkgs/by-name/') && filename.split('/').length > 4,
)
const packages = files
.filter(({ filename }) => filename.startsWith('pkgs/by-name/'))
.map(({ filename }) => filename.split('/')[3])
.filter(Boolean)
const eligible = !packages.length
? new Set()
: packages
.map((pkg) => new Set(maintainers[pkg]))
.reduce((acc, cur) => acc?.intersection(cur) ?? cur)
const approvals = new Set(
events
.filter(
({ event, state, commit_id }) =>
event === 'reviewed' &&
state === 'approved' &&
// Only approvals for the current head SHA count, otherwise authors could push
// bad code between the approval and the merge.
commit_id === pull_request.head.sha,
)
.map(({ user }) => user?.id)
// Some users have been deleted, so filter these out.
.filter(Boolean),
)
const checklist = {
'PR targets a [development branch](https://github.com/NixOS/nixpkgs/blob/-/ci/README.md#branch-classification).':
classify(pull_request.base.ref).type.includes('development'),
'PR touches only files of packages in `pkgs/by-name/`.': allByName,
'PR is at least one of:': {
'Approved by a committer.': committers.intersection(approvals).size > 0,
'Backported via label.':
pull_request.user.login === 'nixpkgs-ci[bot]' &&
pull_request.head.ref.startsWith('backport-'),
'Opened by a committer.': committers.has(pull_request.user.id),
'Opened by r-ryantm.': pull_request.user.login === 'r-ryantm',
},
}
if (user) {
checklist[
`${user.login} is a member of [@NixOS/nixpkgs-maintainers](https://github.com/orgs/NixOS/teams/nixpkgs-maintainers).`
] = userIsMaintainer
if (allByName) {
// We can only determine the below, if all packages are in by-name, since
// we can't reliably relate changed files to packages outside by-name.
checklist[`${user.login} is a maintainer of all touched packages.`] =
eligible.has(user.id)
}
} else {
// This is only used when no user is passed, i.e. for labeling.
checklist['PR has maintainers eligible to merge.'] = eligible.size > 0
}
const result = Object.values(checklist).every((v) =>
typeof v === 'boolean' ? v : Object.values(v).some(Boolean),
)
log('checklist', JSON.stringify(checklist))
log('eligible', JSON.stringify(Array.from(eligible)))
log('result', result)
return {
checklist,
eligible,
result,
}
}
// The merge command must be on a separate line and not within codeblocks or html comments.
// Codeblocks can have any number of ` larger than 3 to open/close. We only look at code
// blocks that are not indented, because the later regex wouldn't match those anyway.
function hasMergeCommand(body) {
return (body ?? '')
.replace(/<!--.*?-->/gms, '')
.replace(/(^`{3,})[^`].*?\1/gms, '')
.match(/^@NixOS\/nixpkgs-merge-bot merge\s*$/m)
}
async function handleMergeComment({ github, body, node_id, reaction }) {
if (!hasMergeCommand(body)) return
await github.graphql(
`mutation($node_id: ID!, $reaction: ReactionContent!) {
addReaction(input: {
content: $reaction,
subjectId: $node_id
})
{ clientMutationId }
}`,
{ node_id, reaction },
)
}
async function handleMerge({
github,
context,
core,
log,
dry,
pull_request,
events,
maintainers,
getTeamMembers,
getUser,
}) {
const pull_number = pull_request.number
const committers = new Set(
(await getTeamMembers('nixpkgs-committers')).map(({ id }) => id),
)
const files = (
await github.rest.pulls.listFiles({
...context.repo,
pull_number,
per_page: 100,
})
).data
// Early exit to prevent treewides from using up a lot of API requests (and time!) to list
// all the files in the pull request. For now, the merge-bot will not work when 100 or more
// files are touched in a PR - which should be more than fine.
// TODO: Find a more efficient way of downloading all the *names* of the touched files,
// including an early exit when the first non-by-name file is found.
if (files.length >= 100) return false
// Only look through comments *after* the latest (force) push.
const lastPush = events.findLastIndex(
({ event, sha, commit_id }) =>
['committed', 'head_ref_force_pushed'].includes(event) &&
(sha ?? commit_id) === pull_request.head.sha,
)
const comments = events.slice(lastPush + 1).filter(
({ event, body, user, node_id }) =>
['commented', 'reviewed'].includes(event) &&
hasMergeCommand(body) &&
// Ignore comments where the user has been deleted already.
user &&
// Ignore comments which had already been responded to by the bot.
(dry ||
!events.some(
({ event, body }) =>
['commented'].includes(event) &&
// We're only testing this hidden reference, but not the author of the comment.
// We'll just assume that nobody creates comments with this marker on purpose.
// Additionally checking the author is quite annoying for local debugging.
body.match(new RegExp(`^<!-- comment: ${node_id} -->$`, 'm')),
)),
)
async function merge() {
if (dry) {
core.info(`Merging #${pull_number}... (dry)`)
return 'Merge completed (dry)'
}
// Using GraphQL mutations instead of the REST /merge endpoint, because the latter
// doesn't work with Merge Queues. We now have merge queues enabled on all development
// branches, so we don't need a fallback for regular merges.
try {
const resp = await github.graphql(
`mutation($node_id: ID!, $sha: GitObjectID) {
enqueuePullRequest(input: {
expectedHeadOid: $sha,
pullRequestId: $node_id
})
{
clientMutationId,
mergeQueueEntry { mergeQueue { url } }
}
}`,
{ node_id: pull_request.node_id, sha: pull_request.head.sha },
)
return [
`:heavy_check_mark: [Queued](${resp.enqueuePullRequest.mergeQueueEntry.mergeQueue.url}) for merge (#306934)`,
]
} catch (e) {
log('Enqueing failed', e.response.errors[0].message)
}
// If required status checks are not satisfied, yet, the above will fail. In this case
// we can enable auto-merge. We could also only use auto-merge, but this often gets
// stuck for no apparent reason.
try {
await github.graphql(
`mutation($node_id: ID!, $sha: GitObjectID) {
enablePullRequestAutoMerge(input: {
expectedHeadOid: $sha,
pullRequestId: $node_id
})
{ clientMutationId }
}`,
{ node_id: pull_request.node_id, sha: pull_request.head.sha },
)
return [
`:heavy_check_mark: Enabled Auto Merge (#306934)`,
'',
'> [!TIP]',
'> Sometimes GitHub gets stuck after enabling Auto Merge. In this case, leaving another approval should trigger the merge.',
]
} catch (e) {
log('Auto Merge failed', e.response.errors[0].message)
throw new Error(e.response.errors[0].message)
}
}
for (const comment of comments) {
log('comment', comment.node_id)
async function react(reaction) {
if (dry) {
core.info(`Reaction ${reaction} on ${comment.node_id} (dry)`)
return
}
await handleMergeComment({
github,
body: comment.body,
node_id: comment.node_id,
reaction,
})
}
async function isMaintainer(username) {
try {
return (
(
await github.rest.teams.getMembershipForUserInOrg({
org: context.repo.owner,
team_slug: 'nixpkgs-maintainers',
username,
})
).data.state === 'active'
)
} catch (e) {
if (e.status === 404) return false
else throw e
}
}
const { result, eligible, checklist } = runChecklist({
committers,
events,
files,
pull_request,
log,
maintainers,
user: comment.user,
userIsMaintainer: await isMaintainer(comment.user.login),
})
const body = [
`<!-- comment: ${comment.node_id} -->`,
`@${comment.user.login} wants to merge this PR.`,
'',
'Requirements to merge this PR with `@NixOS/nixpkgs-merge-bot merge`:',
...Object.entries(checklist).flatMap(([msg, res]) =>
typeof res === 'boolean'
? `- :${res ? 'white_check_mark' : 'x'}: ${msg}`
: [
`- :${Object.values(res).some(Boolean) ? 'white_check_mark' : 'x'}: ${msg}`,
...Object.entries(res).map(
([msg, res]) =>
` - ${res ? ':white_check_mark:' : ':white_large_square:'} ${msg}`,
),
],
),
'',
]
if (eligible.size > 0 && !eligible.has(comment.user.id)) {
const users = await Promise.all(
Array.from(eligible, async (id) => (await getUser(id)).login),
)
body.push(
'> [!TIP]',
'> Maintainers eligible to merge are:',
...users.map((login) => `> - ${login}`),
'',
)
}
if (result) {
await react('ROCKET')
try {
body.push(...(await merge()))
} catch (e) {
// Remove the HTML comment with node_id reference to allow retrying this merge on the next run.
body.shift()
body.push(`:x: Merge failed with: ${e} (#371492)`)
}
} else {
await react('THUMBS_DOWN')
body.push(':x: Pull Request could not be merged (#305350)')
}
if (dry) {
core.info(body.join('\n'))
} else {
await github.rest.issues.createComment({
...context.repo,
issue_number: pull_number,
body: body.join('\n'),
})
}
if (result) break
}
const { result } = runChecklist({
committers,
events,
files,
pull_request,
log,
maintainers,
})
// Returns a boolean, which indicates whether the PR is merge-bot eligible in principle.
// This is used to set the respective label in bot.js.
return result
}
module.exports = {
handleMerge,
handleMergeComment,
}
+143
View File
@@ -0,0 +1,143 @@
async function handleReviewers({
github,
context,
core,
log,
dry,
pull_request,
reviews,
maintainers,
owners,
getTeamMembers,
getUser,
}) {
const pull_number = pull_request.number
const requested_reviewers = new Set(
pull_request.requested_reviewers.map(({ login }) => login),
)
log(
'reviewers - requested_reviewers',
Array.from(requested_reviewers).join(', '),
)
const existing_reviewers = new Set(
reviews.map(({ user }) => user?.login).filter(Boolean),
)
log(
'reviewers - existing_reviewers',
Array.from(existing_reviewers).join(', '),
)
// Early sanity check, before we start making any API requests. The list of maintainers
// does not have duplicates so the only user to filter out from this list would be the
// PR author. Therefore, we check for a limit of 15+1, where 15 is the limit we check
// further down again.
// This is to protect against huge treewides consuming all our API requests for no
// reason.
if (maintainers.length > 16) {
core.warning('Too many potential reviewers, skipping review requests.')
// Return a boolean on whether the "needs: reviewers" label should be set.
return existing_reviewers.size === 0 && requested_reviewers.size === 0
}
const users = new Set([
...(await Promise.all(
maintainers.map(async (id) => (await getUser(id)).login),
)),
...owners.filter((handle) => handle && !handle.includes('/')),
])
log('reviewers - users', Array.from(users).join(', '))
const teams = new Set(
owners
.map((handle) => handle.split('/'))
.filter(([org, slug]) => org === context.repo.owner && slug)
.map(([, slug]) => slug),
)
log('reviewers - teams', Array.from(teams).join(', '))
const team_members = new Set(
(await Promise.all(Array.from(teams, getTeamMembers)))
.flat(1)
.map(({ login }) => login),
)
log('reviewers - team_members', Array.from(team_members).join(', '))
const new_reviewers = users
.union(team_members)
// We can't request a review from the author.
.difference(new Set([pull_request.user?.login]))
log('reviewers - new_reviewers', Array.from(new_reviewers).join(', '))
// Filter users to repository collaborators. If they're not, they can't be requested
// for review. In that case, they probably missed their invite to the maintainers team.
const reviewers = (
await Promise.all(
Array.from(new_reviewers, async (username) => {
// TODO: Restructure this file to only do the collaborator check for those users
// who were not already part of a team. Being a member of a team makes them
// collaborators by definition.
try {
await github.rest.repos.checkCollaborator({
...context.repo,
username,
})
return username
} catch (e) {
if (e.status !== 404) throw e
core.warning(
`PR #${pull_number}: User ${username} cannot be requested for review because they don't exist or are not a repository collaborator, ignoring. They probably missed the automated invite to the maintainers team (see <https://github.com/NixOS/nixpkgs/issues/234293>).`,
)
}
}),
)
).filter(Boolean)
log('reviewers - reviewers', reviewers.join(', '))
if (reviewers.length > 15) {
core.warning(
`Too many reviewers (${reviewers.join(', ')}), skipping review requests.`,
)
// Return a boolean on whether the "needs: reviewers" label should be set.
return existing_reviewers.size === 0 && requested_reviewers.size === 0
}
const non_requested_reviewers = new Set(reviewers)
.difference(requested_reviewers)
// We don't want to rerequest reviews from people who already reviewed.
.difference(existing_reviewers)
log(
'reviewers - non_requested_reviewers',
Array.from(non_requested_reviewers).join(', '),
)
if (non_requested_reviewers.size === 0) {
log('Has reviewer changes', 'false (skipped)')
} else if (dry) {
core.info(
`Requesting reviewers for #${pull_number}: ${Array.from(non_requested_reviewers).join(', ')} (dry)`,
)
} else {
// We had tried the "request all reviewers at once" thing in the past, but it didn't work out:
// https://github.com/NixOS/nixpkgs/commit/034613f860fcd339bd2c20c8f6bc259a2f9dc034
// If we're hitting API errors here again, we'll need to investigate - and possibly reverse
// course.
await github.rest.pulls.requestReviewers({
...context.repo,
pull_number,
reviewers,
})
}
// Return a boolean on whether the "needs: reviewers" label should be set.
return (
non_requested_reviewers.size === 0 &&
existing_reviewers.size === 0 &&
requested_reviewers.size === 0
)
}
module.exports = {
handleReviewers,
}
+15 -4
View File
@@ -65,22 +65,33 @@ program
})
program
.command('labels')
.description('Manage labels on pull requests.')
.command('bot')
.description('Run automation on pull requests and issues.')
.argument('<owner>', 'Owner of the GitHub repository to label (Example: NixOS)')
.argument('<repo>', 'Name of the GitHub repository to label (Example: nixpkgs)')
.argument('[pr]', 'Number of the Pull Request to label')
.option('--no-dry', 'Make actual modifications')
.action(async (owner, repo, pr, options) => {
const labels = (await import('./labels.js')).default
const bot = (await import('./bot.js')).default
const tmp = mkdtempSync(join(tmpdir(), 'github-script-'))
try {
process.env.GITHUB_WORKSPACE = tmp
process.chdir(tmp)
await run(labels, owner, repo, pr, options)
await run(bot, owner, repo, pr, options)
} finally {
rmSync(tmp, { recursive: true })
}
})
program
.command('get-teams')
.description('Fetch the list of teams with GitHub and output it to a file')
.argument('<owner>', 'Owner of the GitHub repository to label (Example: NixOS)')
.argument('<repo>', 'Name of the GitHub repository to label (Example: nixpkgs)')
.argument('[outFile]', 'Path to the output file (Example: github-teams.json). If not set, prints to stdout')
.action(async (owner, repo, outFile, options) => {
const getTeams = (await import('./get-teams.js')).default
await run(getTeams, owner, repo, undefined, { ...options, outFile })
})
await program.parse()
+2 -2
View File
@@ -1,4 +1,4 @@
module.exports = async ({ github, core }, callback) => {
module.exports = async ({ github, core, maxConcurrent = 1 }, callback) => {
const Bottleneck = require('bottleneck')
const stats = {
@@ -13,7 +13,7 @@ module.exports = async ({ github, core }, callback) => {
// https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api
const allLimits = new Bottleneck({
// Avoid concurrent requests
maxConcurrent: 1,
maxConcurrent,
// Will be updated with first `updateReservoir()` call below.
reservoir: 0,
})
+1
View File
@@ -32,6 +32,7 @@ runCommand "nixpkgs-vet"
}
''
export NIX_STATE_DIR=$(mktemp -d)
$NIXPKGS_VET_NIX_PACKAGE/bin/nix-store --init
nixpkgs-vet --base ${filteredBase} ${filteredHead}
+6 -15
View File
@@ -20,24 +20,15 @@ runCommand "nix-parse-${nix.name}"
''
export NIX_STORE_DIR=$TMPDIR/store
export NIX_STATE_DIR=$TMPDIR/state
nix-store --init
cd "${nixpkgs}"
# Passes all files to nix-instantiate at once.
# Much faster, but will only show first error.
parse-all() {
find . -type f -iname '*.nix' | xargs -P $(nproc) nix-instantiate --parse >/dev/null 2>/dev/null
}
# Passes each file separately to nix-instantiate with -n1.
# Much slower, but will show all errors.
parse-each() {
find . -type f -iname '*.nix' | xargs -n1 -P $(nproc) nix-instantiate --parse >/dev/null
}
if ! parse-all; then
parse-each
fi
# This will only show the first parse error, not all of them. That's fine, because
# the other CI jobs will report in more detail. This job is about checking parsing
# across different implementations / versions, not about providing the best DX.
# Returning all parse errors requires significantly more resources.
find . -type f -iname '*.nix' | xargs -P $(nproc) nix-instantiate --parse >/dev/null
touch $out
''
+6 -6
View File
@@ -9,9 +9,9 @@
},
"branch": "nixpkgs-unstable",
"submodules": false,
"revision": "e57b3b16ad8758fd681511a078f35c416a8cc939",
"url": "https://github.com/NixOS/nixpkgs/archive/e57b3b16ad8758fd681511a078f35c416a8cc939.tar.gz",
"hash": "04zp6jjd4xr6jfps84p8yh5ym5962mii4825fn75lqk14sz4rq56"
"revision": "12c1f0253aa9a54fdf8ec8aecaafada64a111e24",
"url": "https://github.com/NixOS/nixpkgs/archive/12c1f0253aa9a54fdf8ec8aecaafada64a111e24.tar.gz",
"hash": "0zr033ybqjc5spwh7xnzkhbqgc6gh8waw6z76rpvadxckyqlfgiq"
},
"treefmt-nix": {
"type": "Git",
@@ -22,9 +22,9 @@
},
"branch": "main",
"submodules": false,
"revision": "5eda4ee8121f97b218f7cc73f5172098d458f1d1",
"url": "https://github.com/numtide/treefmt-nix/archive/5eda4ee8121f97b218f7cc73f5172098d458f1d1.tar.gz",
"hash": "1vqns9hjhmbnhdq2xvcmdxng11jrmcn9lpk2ncfh1f969z9lj8y9"
"revision": "4ef3dfdbb5ddfb9e39999a2f2b0c2637277859d4",
"url": "https://github.com/numtide/treefmt-nix/archive/4ef3dfdbb5ddfb9e39999a2f2b0c2637277859d4.tar.gz",
"hash": "0dhvpzcknsr2ybi3zz9mjggs93aqkfr24radvlw74y9620dziqw4"
}
},
"version": 5
-41
View File
@@ -1,41 +0,0 @@
{
lib,
stdenvNoCC,
makeWrapper,
coreutils,
codeowners,
jq,
curl,
github-cli,
gitMinimal,
}:
stdenvNoCC.mkDerivation {
name = "request-reviews";
src = lib.fileset.toSource {
root = ./.;
fileset = lib.fileset.unions [
./get-code-owners.sh
./request-reviewers.sh
./request-code-owner-reviews.sh
];
};
nativeBuildInputs = [ makeWrapper ];
dontBuild = true;
installPhase = ''
mkdir -p $out/bin
for bin in *.sh; do
mv "$bin" "$out/bin"
wrapProgram "$out/bin/$bin" \
--set PATH ${
lib.makeBinPath [
coreutils
codeowners
jq
curl
github-cli
gitMinimal
]
}
done
'';
}
-97
View File
@@ -1,97 +0,0 @@
#!/usr/bin/env bash
# Get the code owners of the files changed by a PR, returning one username per line
set -euo pipefail
log() {
echo "$@" >&2
}
if (( "$#" < 4 )); then
log "Usage: $0 GIT_REPO OWNERS_FILE BASE_REF HEAD_REF"
exit 1
fi
gitRepo=$1
ownersFile=$2
baseRef=$3
headRef=$4
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' exit
git -C "$gitRepo" diff --name-only --merge-base "$baseRef" "$headRef" > "$tmp/touched-files"
readarray -t touchedFiles < "$tmp/touched-files"
log "This PR touches ${#touchedFiles[@]} files"
# Get the owners file from the base, because we don't want to allow PRs to
# remove code owners to avoid pinging them
git -C "$gitRepo" show "$baseRef":"$ownersFile" > "$tmp"/codeowners
# Associative array with the user as the key for easy de-duplication
# Make sure to always lowercase keys to avoid duplicates with different casings
declare -A users=()
for file in "${touchedFiles[@]}"; do
result=$(codeowners --file "$tmp"/codeowners "$file")
# Remove the file prefix and trim the surrounding spaces
read -r owners <<< "${result#"$file"}"
if [[ "$owners" == "(unowned)" ]]; then
log "File $file is unowned"
continue
fi
log "File $file is owned by $owners"
# Split up multiple owners, separated by arbitrary amounts of spaces
IFS=" " read -r -a entries <<< "$owners"
for entry in "${entries[@]}"; do
# GitHub technically also supports Emails as code owners,
# but we can't easily support that, so let's not
if [[ ! "$entry" =~ @(.*) ]]; then
warn -e "\e[33mCodeowner \"$entry\" for file $file is not valid: Must start with \"@\"\e[0m" >&2
# Don't fail, because the PR for which this script runs can't fix it,
# it has to be fixed in the base branch
continue
fi
# The first regex match is everything after the @
entry=${BASH_REMATCH[1]}
if [[ "$entry" =~ (.*)/(.*) ]]; then
# Teams look like $org/$team
org=${BASH_REMATCH[1]}
team=${BASH_REMATCH[2]}
# Instead of requesting a review from the team itself,
# we request reviews from the individual users.
# This is because once somebody from a team reviewed the PR,
# the API doesn't expose that the team was already requested for a review,
# so we wouldn't be able to avoid rerequesting reviews
# without saving some some extra state somewhere
# We could also consider implementing a more advanced heuristic
# in the future that e.g. only pings one team member,
# but escalates to somebody else if that member doesn't respond in time.
gh api \
--cache=1h \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/orgs/$org/teams/$team/members" \
--jq '.[].login' > "$tmp/team-members"
readarray -t members < "$tmp/team-members"
log "Team $entry has these members: ${members[*]}"
for user in "${members[@]}"; do
users[${user,,}]=
done
else
# Everything else is a user
users[${entry,,}]=
fi
done
done
printf "%s\n" "${!users[@]}"
@@ -1,60 +0,0 @@
#!/usr/bin/env bash
# Requests reviews for a PR
set -euo pipefail
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' exit
SCRIPT_DIR=$(dirname "$0")
log() {
echo "$@" >&2
}
if (( $# < 3 )); then
log "Usage: $0 GITHUB_REPO PR_NUMBER OWNERS_FILE"
exit 1
fi
baseRepo=$1
prNumber=$2
ownersFile=$3
log "Fetching PR info"
prInfo=$(gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$baseRepo/pulls/$prNumber")
baseBranch=$(jq -r .base.ref <<< "$prInfo")
log "Base branch: $baseBranch"
prRepo=$(jq -r .head.repo.full_name <<< "$prInfo")
log "PR repo: $prRepo"
prBranch=$(jq -r .head.ref <<< "$prInfo")
log "PR branch: $prBranch"
prAuthor=$(jq -r .user.login <<< "$prInfo")
log "PR author: $prAuthor"
extraArgs=()
if pwdRepo=$(git rev-parse --show-toplevel 2>/dev/null); then
# Speedup for local runs
extraArgs+=(--reference-if-able "$pwdRepo")
fi
log "Fetching Nixpkgs commit history"
# We only need the commit history, not the contents, so we can do a tree-less clone using tree:0
# https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/#user-content-quick-summary
git clone --bare --filter=tree:0 --no-tags --origin upstream "${extraArgs[@]}" https://github.com/"$baseRepo".git "$tmp"/nixpkgs.git
log "Fetching the PR commit history"
# Fetch the PR
git -C "$tmp/nixpkgs.git" remote add fork https://github.com/"$prRepo".git
# This remote config is the same as --filter=tree:0 when cloning
git -C "$tmp/nixpkgs.git" config remote.fork.partialclonefilter tree:0
git -C "$tmp/nixpkgs.git" config remote.fork.promisor true
git -C "$tmp/nixpkgs.git" fetch --no-tags fork "$prBranch"
headRef=$(git -C "$tmp/nixpkgs.git" rev-parse refs/remotes/fork/"$prBranch")
log "Requesting reviews from code owners"
"$SCRIPT_DIR"/get-code-owners.sh "$tmp/nixpkgs.git" "$ownersFile" "$baseBranch" "$headRef" | \
"$SCRIPT_DIR"/request-reviewers.sh "$baseRepo" "$prNumber" "$prAuthor"
-88
View File
@@ -1,88 +0,0 @@
#!/usr/bin/env bash
# Request reviewers for a PR, reading line-separated usernames on stdin,
# filtering for valid reviewers before using the API endpoint to request reviews:
# https://docs.github.com/en/rest/pulls/review-requests?apiVersion=2022-11-28#request-reviewers-for-a-pull-request
set -euo pipefail
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' exit
log() {
echo "$@" >&2
}
effect() {
if [[ -n "${DRY_MODE:-}" ]]; then
log "Skipping in dry mode:" "${@@Q}"
else
"$@"
fi
}
if (( "$#" < 3 )); then
log "Usage: $0 BASE_REPO PR_NUMBER PR_AUTHOR"
exit 1
fi
baseRepo=$1
prNumber=$2
prAuthor=$3
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' exit
declare -A users=()
while read -r handle && [[ -n "$handle" ]]; do
users[${handle,,}]=
done
# Cannot request a review from the author
if [[ -v users[${prAuthor,,}] ]]; then
log "One or more files are owned by the PR author, ignoring"
unset 'users[${prAuthor,,}]'
fi
gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$baseRepo/pulls/$prNumber/reviews" \
--jq '.[].user.login' > "$tmp/already-reviewed-by"
# And we don't want to rerequest reviews from people who already reviewed
while read -r user; do
if [[ -v users[${user,,}] ]]; then
log "User $user is a potential reviewer, but has already left a review, ignoring"
unset 'users[${user,,}]'
fi
done < "$tmp/already-reviewed-by"
for user in "${!users[@]}"; do
if ! gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$baseRepo/collaborators/$user" >&2; then
log "User $user is not a repository collaborator, probably missed the automated invite to the maintainers team (see <https://github.com/NixOS/nixpkgs/issues/234293>), ignoring"
unset 'users[$user]'
fi
done
if [[ "${#users[@]}" -gt 10 ]]; then
log "Too many reviewers (${!users[*]}), skipping review requests"
exit 0
fi
for user in "${!users[@]}"; do
log "Requesting review from: $user"
if ! response=$(jq -n --arg user "$user" '{ reviewers: [ $user ] }' | \
effect gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/$baseRepo/pulls/$prNumber/requested_reviewers" \
--input -); then
log "Failed to request review from $user: $response"
fi
done
+1 -1
View File
@@ -9,7 +9,7 @@ if missingFeatures != [ ] then
This version of Nixpkgs requires an implementation of Nix with the following features:
- ${builtins.concatStringsSep "\n- " missingFeatures}
Your are evaluating with Nix ${builtins.nixVersion or "(too old to know)"}, please upgrade:
You are evaluating with Nix ${builtins.nixVersion or "(too old to know)"}, please upgrade:
- If you are running NixOS, `nixos-rebuild' can be used to upgrade your system.
@@ -1,6 +1,6 @@
# pkgs.dockerTools {#sec-pkgs-dockerTools}
`pkgs.dockerTools` is a set of functions for creating and manipulating Docker images according to the [Docker Image Specification v1.3.0](https://github.com/moby/moby/blob/46f7ab808b9504d735d600e259ca0723f76fb164/image/spec/spec.md#image-json-field-descriptions).
`pkgs.dockerTools` is a set of functions for creating and manipulating Docker images according to the [Docker Image Specification v1.3.1](https://github.com/moby/docker-image-spec/blob/v1.3.1/spec.md).
Docker itself is not used to perform any of the operations done by these functions.
## buildImage {#ssec-pkgs-dockerTools-buildImage}
@@ -130,7 +130,7 @@ Similarly, if you encounter errors similar to `Error_Protocol ("certificate has
`config` (Attribute Set or Null; _optional_)
: Used to specify the configuration of the containers that will be started off the generated image.
Must be an attribute set, with each attribute as listed in the [Docker Image Specification v1.3.0](https://github.com/moby/moby/blob/46f7ab808b9504d735d600e259ca0723f76fb164/image/spec/spec.md#image-json-field-descriptions).
Must be an attribute set, with each attribute as listed in the [Docker Image Specification v1.3.1](https://github.com/moby/docker-image-spec/blob/v1.3.1/spec.md#image-json-field-descriptions).
_Default value:_ `null`.
@@ -138,7 +138,7 @@ Similarly, if you encounter errors similar to `Error_Protocol ("certificate has
: Used to specify the image architecture.
This is useful for multi-architecture builds that don't need cross compiling.
If specified, its value should follow the [OCI Image Configuration Specification](https://github.com/opencontainers/image-spec/blob/main/config.md#properties), which should still be compatible with Docker.
If specified, its value should follow the [OCI Image Configuration Specification](https://github.com/opencontainers/image-spec/blob/v1.1.1/config.md#properties), which should still be compatible with Docker.
According to the linked specification, all possible values for `$GOARCH` in [the Go docs](https://go.dev/doc/install/source#environment) should be valid, but will commonly be one of `386`, `amd64`, `arm`, or `arm64`.
_Default value:_ the same value from `pkgs.go.GOARCH`.
@@ -202,6 +202,10 @@ Similarly, if you encounter errors similar to `Error_Protocol ("certificate has
_Default value:_ `false`.
`meta` (Attribute Set)
: The `meta` attribute of the resulting derivation, as in `stdenv.mkDerivation`. Accepts `description`, `maintainers` and any other `meta` attributes.
`contents` **DEPRECATED**
: This attribute is deprecated, and users are encouraged to use `copyToRoot` instead.
@@ -635,6 +639,10 @@ This allows the function to produce reproducible images.
_Default value:_ `false`.
`meta` (Attribute Set)
: The `meta` attribute of the resulting derivation, as in `stdenv.mkDerivation`. Accepts `description`, `maintainers` and any other `meta` attributes.
`passthru` (Attribute Set; _optional_)
: Use this to pass any attributes as [`passthru`](#chap-passthru) for the resulting derivation.
+5
View File
@@ -421,6 +421,11 @@ Check that two paths have the same contents.
: A message that is printed last if the file system object contents at the two paths don't match exactly.
`checkMetadata` (boolean)
: Whether to fail on metadata differences, such as permissions or ownership.
Defaults to `true`.
:::{.example #ex-testEqualContents-toyexample}
# Check that two paths have the same contents
+8
View File
@@ -30,6 +30,14 @@ This setting has no tangible effect when running the build in a sandboxed deriva
The default value is `build`.
#### `cmakeBuildType` {#cmake-build-type}
Build type of cmake output.
Internally populates the `CMAKE_BUILD_TYPE` cmake flag.
The default value is `Release`.
#### `dontUseCmakeConfigure` {#dont-use-cmake-configure}
When set to true, don't use the predefined `cmakeConfigurePhase`.
+1
View File
@@ -20,6 +20,7 @@ ghc.section.md
gnome.section.md
haredo.section.md
installShellFiles.section.md
julec.section.md
just.section.md
libiconv.section.md
libxml2.section.md
+77
View File
@@ -0,0 +1,77 @@
# julec.hook {#julec-hook}
[Jule](https://jule.dev) is an effective programming language designed to build efficient, fast, reliable and safe software while maintaining simplicity.
In Nixpkgs, `jule.hook` overrides the default build, check and install phases.
## Example code snippet {#julec-hook-example-code-snippet}
```nix
{
julec,
clangStdenv,
}:
clangStdenv.mkDerivation (finalAttrs: {
# ...
nativeBuildInputs = [ julec.hook ];
# Customize filenames if needed
JULE_SRC_DIR = "./src";
JULE_OUT_DIR = "./bin";
JULE_OUT_NAME = "hello-jule";
JULE_TEST_DIR = "./tests";
JULE_TEST_OUT_DIR = "./test-bin";
JULE_TEST_OUT_NAME = "hello-jule-test";
# ...
})
```
## Variables controlling julec.hook {#julec-hook-variables}
### `JULE_SRC_DIR` {#julec-hook-variable-jule-src-dir}
Specifies the source directory containing `main.jule`.
Default is `./src`.
### `JULE_OUT_DIR` {#julec-hook-variable-jule-out-dir}
Specifies the output directory for the compiled binary.
Default is `./bin`.
### `JULE_OUT_NAME` {#julec-hook-variable-jule-out-name}
Specifies the name of the compiled binary.
Default is `output`.
### `JULE_TEST_DIR` {#julec-hook-variable-jule-test-dir}
Specifies the directory containing test files.
Default is the value of [`JULE_SRC_DIR`](#julec-hook-variable-jule-src-dir).
### `JULE_TEST_OUT_DIR` {#julec-hook-variable-jule-test-out-dir}
Specifies the output directory for compiled test binaries.
Default is the value of [`JULE_OUT_DIR`](#julec-hook-variable-jule-out-dir).
### `JULE_TEST_OUT_NAME` {#julec-hook-variable-jule-test-out-name}
Specifies the name of the compiled test binary.
Default is the value of [`JULE_OUT_NAME`](#julec-hook-variable-jule-out-name) with `-test` suffix.
### `dontUseJulecBuild` {#julec-hook-variable-dontusejulecbuild}
When set to true, doesn't use the predefined `julecBuildHook`.
Default is false.
### `dontUseJulecCheck` {#julec-hook-variable-dontusejuleccheck}
When set to true, doesn't use the predefined `julecCheckHook`.
Default is false.
### `dontUseJulecInstall` {#julec-hook-variable-dontusejulecinstall}
When set to true, doesn't use the predefined `julecInstallHook`.
Default is false.
+38 -106
View File
@@ -26,23 +26,9 @@ All CUDA package sets include common CUDA packages like `libcublas`, `cudnn`, `t
CUDA support is not enabled by default in Nixpkgs. To enable CUDA support, make sure Nixpkgs is imported with a configuration similar to the following:
```nix
{ pkgs }:
{
allowUnfreePredicate =
let
ensureList = x: if builtins.isList x then x else [ x ];
in
package:
builtins.all (
license:
license.free
|| builtins.elem license.shortName [
"CUDA EULA"
"cuDNN EULA"
"cuSPARSELt EULA"
"cuTENSOR EULA"
"NVidia OptiX EULA"
]
) (ensureList package.meta.license);
allowUnfreePredicate = pkgs._cuda.lib.allowUnfreeCudaPredicate;
cudaCapabilities = [ <target-architectures> ];
cudaForwardCompat = true;
cudaSupport = true;
@@ -63,45 +49,23 @@ The `cudaForwardCompat` boolean configuration option determines whether PTX supp
### Modifying CUDA package sets {#cuda-modifying-cuda-package-sets}
CUDA package sets are created by using `callPackage` on `pkgs/top-level/cuda-packages.nix` with an explicit argument for `cudaMajorMinorVersion`, a string of the form `"<major>.<minor>"` (e.g., `"12.2"`), which informs the CUDA package set tooling which version of CUDA to use. The majority of the CUDA package set tooling is available through the top-level attribute set `_cuda`, a fixed-point defined outside the CUDA package sets.
CUDA package sets are defined in `pkgs/top-level/cuda-packages.nix`. A CUDA package set is created by `callPackage`-ing `pkgs/development/cuda-modules/default.nix` with an attribute set `manifests`, containing NVIDIA manifests for each redistributable. The manifests for supported redistributables are available through `_cuda.manifests` and live in `pkgs/development/cuda-modules/_cuda/manifests`.
::: {.caution}
The `cudaMajorMinorVersion` and `_cuda` attributes are not part of the CUDA package set fixed-point, but are instead provided by `callPackage` from the top-level in the construction of the package set. As such, they must be modified via the package set's `override` attribute.
:::
The majority of the CUDA package set tooling is available through the top-level attribute set `_cuda`, a fixed-point defined outside the CUDA package sets. As a fixed-point, `_cuda` should be modified through its `extend` attribute.
::: {.caution}
As indicated by the underscore prefix, `_cuda` is an implementation detail and no guarantees are provided with respect to its stability or API. The `_cuda` attribute set is exposed only to ease creation or modification of CUDA package sets by expert, out-of-tree users.
:::
Out-of-tree modifications of packages should use `overrideAttrs` to make any necessary modifications to the package expression.
::: {.note}
The `_cuda` attribute set fixed-point should be modified through its `extend` attribute.
The `_cuda` attribute set previously exposed `fixups`, an attribute set mapping from package name (`pname`) to a `callPackage`-compatible expression which provided to `overrideAttrs` on the result of a generic redistributable builder. This functionality has been removed in favor of including full package expressions for each redistributable package to ensure consistent attribute set membership across supported CUDA releases, platforms, and configurations.
:::
The `_cuda.fixups` attribute set is a mapping from package name (`pname`) to a `callPackage`-compatible expression which will be provided to `overrideAttrs` on the result of our generic builder.
::: {.caution}
Fixups are chosen from `_cuda.fixups` by `pname`. As a result, packages with multiple versions (e.g., `cudnn`, `cudnn_8_9`, etc.) all share a single fixup function (i.e., `_cuda.fixups.cudnn`, which is `pkgs/development/cuda-modules/fixups/cudnn.nix`).
:::
As an example, you can change the fixup function used for cuDNN for only the default CUDA package set with this overlay:
```nix
final: prev: {
cudaPackages = prev.cudaPackages.override (prevArgs: {
_cuda = prevArgs._cuda.extend (
_: prevAttrs: {
fixups = prevAttrs.fixups // {
cudnn = <your-fixup-function>;
};
}
);
});
}
```
### Extending CUDA package sets {#cuda-extending-cuda-package-sets}
CUDA package sets are scopes and provide the usual `overrideScope` attribute for overriding package attributes (see the note about `cudaMajorMinorVersion` and `_cuda` in [Configuring CUDA package sets](#cuda-modifying-cuda-package-sets)).
CUDA package sets are scopes and provide the usual `overrideScope` attribute for overriding package attributes (see the note about `_cuda` in [Configuring CUDA package sets](#cuda-modifying-cuda-package-sets)).
Inspired by `pythonPackagesExtensions`, the `_cuda.extensions` attribute is a list of extensions applied to every version of the CUDA package set, allowing modification of all versions of the CUDA package set without needing to know their names or explicitly enumerate and modify them. As an example, disabling `cuda_compat` across all CUDA package sets can be accomplished with this overlay:
@@ -115,6 +79,8 @@ final: prev: {
}
```
Redistributable packages are constructed by the `buildRedist` helper; see `pkgs/development/cuda-modules/buildRedist/default.nix` for the implementation.
### Using `cudaPackages` {#cuda-using-cudapackages}
::: {.caution}
@@ -194,7 +160,7 @@ In `pkgsForCudaArch`, the `cudaForwardCompat` option is set to `false` because e
::: {.caution}
Not every version of CUDA supports every architecture!
To illustrate: support for Blackwell (e.g., `sm_100`) was added in CUDA 12.8. Assume our Nixpkgs' default CUDA package set is to CUDA 12.6. Then the Nixpkgs variant available through `pkgsForCudaArch.sm_100` is useless, since packages like `pkgsForCudaArch.sm_100.opencv` and `pkgsForCudaArch.sm_100.python3Packages.torch` will try to generate code for `sm_100`, an architecture unknown to CUDA 12.6. In that case, you should use `pkgsForCudaArch.sm_100.cudaPackages_12_8.pkgs` instead (see [Using cudaPackages.pkgs](#cuda-using-cudapackages-pkgs) for more details).
To illustrate: support for Blackwell (e.g., `sm_100`) was added in CUDA 12.8. Assume our Nixpkgs' default CUDA package set is to CUDA 12.6. Then the Nixpkgs variant available through `pkgsForCudaArch.sm_100` is useless, since packages like `pkgsForCudaArch.sm_100.opencv` and `pkgsForCudaArch.sm_100.python3Packages.torch` will try to generate code for `sm_100`, an architecture unknown to CUDA 12.6. In that case, you should use `pkgsForCudaArch.sm_100.cudaPackages_12_8.pkgs` instead (see [Using `cudaPackages.pkgs`](#cuda-using-cudapackages-pkgs) for more details).
:::
The `pkgsForCudaArch` attribute set makes it possible to access packages built for a specific architecture without needing to manually call `pkgs.extend` and supply a new `config`. As an example, `pkgsForCudaArch.sm_89.python3Packages.torch` provides PyTorch built for Ada Lovelace GPUs.
@@ -306,82 +272,41 @@ This section of the docs is still very much in progress. Feedback is welcome in
### Package set maintenance {#cuda-package-set-maintenance}
The CUDA Toolkit is a suite of CUDA libraries and software meant to provide a development environment for CUDA-accelerated applications. Until the release of CUDA 11.4, NVIDIA had only made the CUDA Toolkit available as a multi-gigabyte runfile installer, which we provide through the [`cudaPackages.cudatoolkit`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages.cudatoolkit) attribute. From CUDA 11.4 and onwards, NVIDIA has also provided CUDA redistributables (“CUDA-redist”): individually packaged CUDA Toolkit components meant to facilitate redistribution and inclusion in downstream projects. These packages are available in the [`cudaPackages`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages) package set.
The CUDA Toolkit is a suite of CUDA libraries and software meant to provide a development environment for CUDA-accelerated applications. Until the release of CUDA 11.4, NVIDIA had only made the CUDA Toolkit available as a multi-gigabyte runfile installer. From CUDA 11.4 and onwards, NVIDIA has also provided CUDA redistributables (“CUDA-redist”): individually packaged CUDA Toolkit components meant to facilitate redistribution and inclusion in downstream projects. These packages are available in the [`cudaPackages`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages) package set.
All new projects should use the CUDA redistributables available in [`cudaPackages`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages) in place of [`cudaPackages.cudatoolkit`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages.cudatoolkit), as they are much easier to maintain and update.
While the monolithic CUDA Toolkit runfile installer is no longer provided, [`cudaPackages.cudatoolkit`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages.cudatoolkit) provides a `symlinkJoin`-ed approximation which common libraries. The use of [`cudaPackages.cudatoolkit`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages.cudatoolkit) is discouraged: all new projects should use the CUDA redistributables available in [`cudaPackages`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages) instead, as they are much easier to maintain and update.
#### Updating redistributables {#cuda-updating-redistributables}
1. Go to NVIDIA's index of CUDA redistributables: <https://developer.download.nvidia.com/compute/cuda/redist/>
2. Make a note of the new version of CUDA available.
3. Run
Whenever a new version of a redistributable manifest is made available:
```bash
nix run github:connorbaker/cuda-redist-find-features -- \
download-manifests \
--log-level DEBUG \
--version <newest CUDA version> \
https://developer.download.nvidia.com/compute/cuda/redist \
./pkgs/development/cuda-modules/cuda/manifests
```
1. Check the corresponding README.md in `pkgs/development/cuda-modules/_cuda/manifests` for the URL to use when vendoring manifests.
2. Update the manifest version used in construction of each CUDA package set in `pkgs/top-level/cuda-packages.nix`.
3. Update package expressions in `pkgs/development/cuda-modules/packages`.
This will download a copy of the manifest for the new version of CUDA.
4. Run
Updating package expressions amounts to:
```bash
nix run github:connorbaker/cuda-redist-find-features -- \
process-manifests \
--log-level DEBUG \
--version <newest CUDA version> \
https://developer.download.nvidia.com/compute/cuda/redist \
./pkgs/development/cuda-modules/cuda/manifests
```
This will generate a `redistrib_features_<newest CUDA version>.json` file in the same directory as the manifest.
5. Update the `cudaVersionMap` attribute set in `pkgs/development/cuda-modules/cuda/extension.nix`.
#### Updating cuTensor {#cuda-updating-cutensor}
1. Repeat the steps present in [Updating CUDA redistributables](#cuda-updating-redistributables) with the following changes:
- Use the index of cuTensor redistributables: <https://developer.download.nvidia.com/compute/cutensor/redist>
- Use the newest version of cuTensor available instead of the newest version of CUDA.
- Use `pkgs/development/cuda-modules/cutensor/manifests` instead of `pkgs/development/cuda-modules/cuda/manifests`.
- Skip the step of updating `cudaVersionMap` in `pkgs/development/cuda-modules/cuda/extension.nix`.
- adding fixes conditioned on newer releases, like added or removed dependencies
- adding package expressions for new packages
- updating `passthru.brokenConditions` and `passthru.badPlatformsConditions` with various constraints, (e.g., new releases removing support for various architectures)
#### Updating supported compilers and GPUs {#cuda-updating-supported-compilers-and-gpus}
1. Update `nvccCompatibilities` in `pkgs/development/cuda-modules/_cuda/data/nvcc.nix` to include the newest release of NVCC, as well as any newly supported host compilers.
2. Update `cudaCapabilityToInfo` in `pkgs/development/cuda-modules/_cuda/data/cuda.nix` to include any new GPUs supported by the new release of CUDA.
#### Updating the CUDA Toolkit runfile installer {#cuda-updating-the-cuda-toolkit}
::: {.warning}
While the CUDA Toolkit runfile installer is still available in Nixpkgs as the [`cudaPackages.cudatoolkit`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages.cudatoolkit) attribute, its use is not recommended, and it should be considered deprecated. Please migrate to the CUDA redistributables provided by the [`cudaPackages`](https://search.nixos.org/packages?channel=unstable&type=packages&query=cudaPackages) package set.
To ensure packages relying on the CUDA Toolkit runfile installer continue to build, it will continue to be updated until a migration path is available.
:::
1. Go to NVIDIA's CUDA Toolkit runfile installer download page: <https://developer.nvidia.com/cuda-downloads>
2. Select the appropriate OS, architecture, distribution, and version, and installer type.
- For example: Linux, x86_64, Ubuntu, 22.04, runfile (local)
- NOTE: Typically, we use the Ubuntu runfile. It is unclear if the runfile for other distributions will work.
3. Take the link provided by the installer instructions on the webpage after selecting the installer type and get its hash by running:
```bash
nix store prefetch-file --hash-type sha256 <link>
```
4. Update `pkgs/development/cuda-modules/cudatoolkit/releases.nix` to include the release.
1. Update `nvccCompatibilities` in `pkgs/development/cuda-modules/_cuda/db/bootstrap/nvcc.nix` to include the newest release of NVCC, as well as any newly supported host compilers.
2. Update `cudaCapabilityToInfo` in `pkgs/development/cuda-modules/_cuda/db/bootstrap/cuda.nix` to include any new GPUs supported by the new release of CUDA.
#### Updating the CUDA package set {#cuda-updating-the-cuda-package-set}
1. Include a new `cudaPackages_<major>_<minor>` package set in `pkgs/top-level/all-packages.nix`.
::: {.note}
Changing the default CUDA package set should occur in a separate PR, allowing time for additional testing.
:::
- NOTE: Changing the default CUDA package set should occur in a separate PR, allowing time for additional testing.
::: {.warning}
As described in [Using `cudaPackages.pkgs`](#cuda-using-cudapackages-pkgs), the current implementation fix for package set leakage involves creating a new instance for each non-default CUDA package sets. As such, We should limit the number of CUDA package sets which have `recurseForDerivations` set to true: `lib.recurseIntoAttrs` should only be applied to the default CUDA package set.
:::
2. Successfully build the closure of the new package set, updating `pkgs/development/cuda-modules/cuda/overrides.nix` as needed. Below are some common failures:
1. Include a new `cudaPackages_<major>_<minor>` package set in `pkgs/top-level/cuda-packages.nix` and inherit it in `pkgs/top-level/all-packages.nix`.
2. Successfully build the closure of the new package set, updating expressions in `pkgs/development/cuda-modules/packages` as needed. Below are some common failures:
| Unable to ... | During ... | Reason | Solution | Note |
| -------------- | -------------------------------- | ------------------------------------------------ | -------------------------- | ------------------------------------------------------------ |
@@ -389,6 +314,13 @@ To ensure packages relying on the CUDA Toolkit runfile installer continue to bui
| Find libraries | `configurePhase` | Missing dependency on a `dev` output | Add the missing dependency | The `dev` output typically contains CMake configuration files |
| Find libraries | `buildPhase` or `patchelf` | Missing dependency on a `lib` or `static` output | Add the missing dependency | The `lib` or `static` output typically contains the libraries |
::: {.note}
Two utility derivations ease testing updates to the package set:
- `cudaPackages.tests.redists-unpacked`: the `src` of each redistributable package unpacked and `symlinkJoin`-ed
- `cudaPackages.tests.redists-installed`: each output of each redistributable package `symlinkJoin`-ed
:::
Failure to run the resulting binary is typically the most challenging to diagnose, as it may involve a combination of the aforementioned issues. This type of failure typically occurs when a library attempts to load or open a library it depends on that it does not declare in its `DT_NEEDED` section. Try the following debugging steps:
1. First ensure that dependencies are patched with [`autoAddDriverRunpath`](https://search.nixos.org/packages?channel=unstable&type=packages&query=autoAddDriverRunpath).
+1 -1
View File
@@ -129,7 +129,7 @@ The update script does the following:
`fetchDeps` takes the following arguments:
- `attrPath` - the path to the package in nixpkgs (for example,
`"javaPackages.openjfx22"`). Used for update script metadata.
`"javaPackages.openjfx25"`). Used for update script metadata.
- `pname` - an alias for `attrPath` for convenience. This is what you
will generally use instead of `pkg` or `attrPath`.
- `pkg` - the package to be used for fetching the dependencies. Defaults
+10 -2
View File
@@ -260,6 +260,14 @@ the same package with the `previousIntermediates` argument to support
incremental builds. See [“Incremental builds”](#haskell-incremental-builds) for
more information. Defaults to `false`.
`dontConvertCabalFileToUnix`
: By default, `haskellPackages.mkDerivation` converts the `.cabal` file of a
given package to Unix line endings.
This is intended to work around
[Hackage converting revised `.cabal` files to DOS line endings](https://github.com/haskell/hackage-server/issues/316)
which frequently causes patches to stop applying.
You can pass `true` to disable this behavior.
`enableLibraryProfiling`
: Whether to enable [profiling][profiling] for libraries contained in the
package. Enabled by default if supported.
@@ -1249,8 +1257,8 @@ it does for the unstable branches.
### Why is topic X not covered in this section? Why is section Y missing? {#haskell-why-not-covered}
We have been working on [moving the nixpkgs Haskell documentation back into the
nixpkgs manual](https://github.com/NixOS/nixpkgs/issues/121403). Since this
process has not been completed yet, you may find some topics missing here
nixpkgs manual](https://github.com/NixOS/nixpkgs/issues/121403). <!-- krank:ignore-line -->
Since this process has not been completed yet, you may find some topics missing here
covered in the old [haskell4nix docs](https://haskell4nix.readthedocs.io/).
If you feel any important topic is not documented at all, feel free to comment
@@ -1,5 +1,57 @@
# Idris2 {#sec-idris2}
When developing using Idris2, by default the Idris compiler only has the minimal support libraries in its environment. This means that it will not attempt to read any libraries installed
globally, for example in the `$HOME` directory. The recommended way to use Idris2 is to wrap the compiler in an environment that provides these packages per-project, for example in a
devShell.
```nix
{
pkgs ? import <nixpkgs> { },
}:
pkgs.mkShell {
packages = [ (idris2.withPackages (p: [ p.idris2Api ])) ];
}
```
or, alternatively if Nix is used to build the Idris2 project:
```nix
{
pkgs ? import <nixpkgs> { },
}:
pkgs.mkShell {
inputsFrom = [ (pkgs.callPackage ./package.nix { }) ];
}
```
By default, the Idris2 compiler provided by Nixpkgs does not read globally installed packages, nor can install them. Running `idris2 --install` will fail because the Nix store is
a read-only file-system. If globally-installed packages are desired rather than the above strategy, one can set `IDRIS2_PREFIX`, or additional `IDRIS2_PACKAGE_PATH` entries
for the compiler to read from. The following snippet will append `$HOME/.idris2` to `$IDRIS2_PACKAGE_PATH`, and if such a variable does not exist, create it. The Nixpkg's Idris2
compiler append a few required libraries to this path variable, but any paths in the user's environment will be prefixed to those libraries.
```nix
{
pkgs ? import <nixpkgs> { },
}:
pkgs.mkShell {
packages = [ (idris2.withPackages (p: [ p.idris2Api ])) ];
shellHook = ''
IDRIS2_PACKAGE_PATH="''${IDRIS2_PACKAGE_PATH:+$IDRIS2_PACKAGE_PATH}$HOME/.idris2"
'';
}
```
The following snippet will allow the Idris2 to run `idris2 --install` successfully:
```nix
{
pkgs ? import <nixpkgs> { },
}:
pkgs.mkShell {
packages = [ (idris2.withPackages (p: [ p.idris2Api ])) ];
shellHook = ''
IDRIS2_PREFIX="$HOME/.idris2"
'';
}
```
In addition to exposing the Idris2 compiler itself, Nixpkgs exposes an `idris2Packages.buildIdris` helper to make it a bit more ergonomic to build Idris2 executables or libraries.
The `buildIdris` function takes an attribute set that defines at a minimum the `src` and `ipkgName` of the package to be built and any `idrisLibraries` required to build it. The `src` is the same source you're familiar with and the `ipkgName` must be the name of the `ipkg` file for the project (omitting the `.ipkg` extension). The `idrisLibraries` is a list of other library derivations created with `buildIdris`. You can optionally specify other derivation properties as needed but sensible defaults for `configurePhase`, `buildPhase`, and `installPhase` are provided.
@@ -56,3 +108,20 @@ lspPkg.executable
```
The above uses the default value of `withSource = false` for the `idris2Api` but could be modified to include that library's source by passing `(idris2Api { withSource = true; })` to `idrisLibraries` instead. `idris2Api` in the above derivation comes built in with `idris2Packages`. This library exposes many of the otherwise internal APIs of the Idris2 compiler.
The compiler package can be instantiated with packages on its `IDRIS2_PACKAGES` path from the `idris2Packages` set.
```nix
{
idris2,
devShell,
}:
let
myIdris = idris2.withPackages (p: [ p.idris2Api ]);
in
devShell {
packages = [ myIdris ];
}
```
This search path is extended from the path already in the user's environment.
+1 -1
View File
@@ -20,7 +20,7 @@ Each supported language or software ecosystem has its own package set named `<la
$ nix repl '<nixpkgs>' -I nixpkgs=channel:nixpkgs-unstable
nix-repl> javaPackages.<tab>
javaPackages.compiler javaPackages.openjfx15 javaPackages.openjfx21 javaPackages.recurseForDerivations
javaPackages.jogl_2_4_0 javaPackages.openjfx17 javaPackages.openjfx22
javaPackages.jogl_2_4_0 javaPackages.openjfx17 javaPackages.openjfx25
javaPackages.mavenfod javaPackages.openjfx19 javaPackages.override
javaPackages.openjfx11 javaPackages.openjfx20 javaPackages.overrideDerivation
```
+6
View File
@@ -214,6 +214,12 @@ code, while others choose not to.
In Nix, there are multiple approaches to building a Composer-based project.
::: {.warning}
`buildComposerProject2` has a [known bug](https://github.com/NixOS/nixpkgs/issues/451395)
where the `vendorHash` changes every time a Composer release happens that changes the
`autoload.php` or vendored composer code.
:::
One such method is the `php.buildComposerProject2` helper function, which serves
as a wrapper around `mkDerivation`.
@@ -557,6 +557,19 @@ are used in [`buildPythonPackage`](#buildpythonpackage-function).
with the `pipInstallHook`.
- `unittestCheckHook` will run tests with `python -m unittest discover`. See [example usage](#using-unittestcheckhook).
#### Overriding build helpers {#overriding-python-build-helpers}
Like many of the build helpers provided by Nixpkgs, Python build helpers typically provide a `<function>.override` attribute.
It works like [`<pkg>.override`](#sec-pkg-override), and can be used to override the dependencies of each build helper.
This allows specifying the stdenv to be used by `buildPythonPackage` or `buildPythonApplication`. The default (`python.stdenv`) can be overridden as follows:
```nix
buildPythonPackage.override { stdenv = customStdenv; } {
# package attrs...
}
```
## User Guide {#user-guide}
### Using Python {#using-python}
+2
View File
@@ -273,6 +273,8 @@ To test that it works, you can then try using the gem with:
NIX_PATH=nixpkgs=$PWD nix-shell -p "ruby.withPackages (ps: with ps; [ name-of-your-gem ])"
```
To check the gems for any security vulnerabilities, run `./maintainers/scripts/audit-ruby-packages/audit-ruby-packages.bash`.
### Packaging applications {#packaging-applications}
A common task is to add a Ruby executable to Nixpkgs; popular examples would be `chef`, `jekyll`, or `sass`. A good way to do that is to use the `bundlerApp` function, that allows you to make a package that only exposes the listed executables. Otherwise, the package may cause conflicts through common paths like `bin/rake` or `bin/bundler` that aren't meant to be used.
+1 -1
View File
@@ -235,7 +235,7 @@ runCommand "test.pdf" { nativeBuildInputs = [ latex_with_foiltex ]; } ''
The font cache for LuaLaTeX is written to `$HOME`.
Therefore, it is necessary to set `$HOME` to a writable path, e.g. [before using LuaLaTeX in nix derivations](https://github.com/NixOS/nixpkgs/issues/180639):
```nix
runCommandNoCC "lualatex-hello-world" { buildInputs = [ texliveFull ]; } ''
runCommand "lualatex-hello-world" { buildInputs = [ texliveFull ]; } ''
mkdir $out
echo '\documentclass{article} \begin{document} Hello world \end{document}' > main.tex
env HOME=$(mktemp -d) lualatex -interaction=nonstopmode -output-format=pdf -output-directory=$out ./main.tex
-329
View File
@@ -1,329 +0,0 @@
Setting up a cross compiler with Nix
"Cross compilation" means compiling a program on one machine for another
type of machine. A typical use of cross compilation is to compile programs
for embedded devices. These devices often don't have the computing power
and memory to compile programs natively.
For a fully working cross compiler the following are needed:
* cross binutils: assembler, archiver, linker, etcetera that understand
the format of the target system
* cross compiler: a compiler that can generate binary code and object files
for the target platform
* cross C library: a library to link object files with to create fully
functional programs
Cross compilers are difficult to set up. A lot of people report that they
cannot succeed in building a cross toolchain successfully. The answers
usually consist of "download this pre-built toolchain", which is equally
unhelpful.
A toolchain is set up in five steps:
1. build binutils to that can run on the host platform, but generate code
for the target platform
2. build Linux kernel headers for the target platform
3. build a minimal C only version of GCC, that can run on the host platform
and generate code for the target platform
4. build a C library for the target platform. This includes the dynamic
linker, C library, etc.
5. build a full GCC
****
NB:
Keep in mind that many programs are not very well suited for cross
compilation. Either they are not intended to run on other platforms,
because the code is highly platform specific, or the configuration process
is not written with cross compilation in mind.
Nix will not solve these problems for you!
***
This document describes to set up a cross compiler to generate code for
arm-linux with uClibc and runs on i686-linux. The "stdenv" used is the
default from the standard Nix packages collection.
Step 1: build binutils for arm-linux in the stdenv for i686-linux
---
{stdenv, fetchurl, noSysDirs}:
stdenv.mkDerivation {
name = "binutils-2.16.1-arm";
builder = ./builder.sh;
src = fetchurl {
url = "http://ftp.nluug.nl/gnu/binutils/binutils-2.16.1.tar.bz2";
hash = "sha256-14pv+YKrL3NyFwbnv9MoWsZHgEZk5+pHhuZtAfkcVsU=";
};
inherit noSysDirs;
configureFlags = [ "--target=arm-linux" ];
}
---
This will compile binutils that will run on i686-linux, but knows the
format used by arm-linux.
Step 2: build kernel headers for the target architecture
default.nix for kernel-headers-arm:
---
{stdenv, fetchurl}:
assert stdenv.buildPlatform.system == "i686-linux";
stdenv.mkDerivation {
name = "linux-headers-2.6.13.1-arm";
builder = ./builder.sh;
src = fetchurl {
url = "http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.13.1.tar.bz2";
hash = "sha256-qtICDjfiA1HxWBrHqtB5DCv9s9/HyznKV1C6IxCrHYs=";
};
}
---
builder.sh for kernel-headers-arm:
---
source $stdenv/setup
buildPhase() {
make include/linux/version.h
}
buildPhase=buildPhase
installPhase() {
mkdir $out
mkdir $out/include
#cd $out/include
#ln -s asm-arm asm
make include/asm ARCH=arm
cp -prvd include/linux include/asm include/asm-arm include/asm-generic $out/include
echo -n > $out/include/linux/autoconf.h
}
installPhase=installPhase
genericBuild
---
Step 3: build a minimal GCC
Extra/different parameters include the target platform and the kernel
headers argument (this needs a major cleanup, as well as the name, it
needs to be different!). Profiled compilers are disabled. The tarball
used here is just gcc-core. For some reason it doesn't install nicely
if the whole tarball is used (or is this some braino on my side? -- AH).
Only C is used, because for other languages (such as C++) extra libraries
need to be compiled, for which libraries compiled for the target system
are needed.
There is a bit of evilness going on. The cross compiled utilities need
to be either copied to or be linked from the output tree of the compiler.
(Is this really true? Back this up with arguments! -- AH)
Symbolic links are not something we want inside the Nix store.
---
{ stdenv, fetchurl, noSysDirs
, langC ? true, langCC ? true, langF77 ? false
, profiledCompiler ? false
, binutilsArm
, kernelHeadersArm
}:
assert langC;
stdenv.mkDerivation {
name = "gcc-4.0.2-arm";
builder = ./builder.sh;
src = fetchurl {
url = "ftp://ftp.nluug.nl/pub/gnu/gcc/gcc-4.0.2/gcc-core-4.0.2.tar.bz2";
hash = "sha256-LANmXRS7/fN2zF5JUJVd8OjNA5aCDsGLQKhSpxWA3Qk=";
};
# !!! apply only if noSysDirs is set
patches = [./no-sys-dirs.patch ./gcc-inhibit.patch];
inherit noSysDirs langC langCC langF77 profiledCompiler;
buildInputs = [binutilsArm];
inherit kernelHeadersArm binutilsArm;
platform = "arm-linux";
}
---
The builder.sh for a cross-compiler. Note that the binutils are prefixed
with the architecture name, so arm-linux-ld instead of ld, etc. This is
necessary because when we cross-compile a lot of programs look for these
tools with these specific names. The standard gcc-wrapper does not take this
into account yet.
---
source $stdenv/setup
export NIX_FIXINC_DUMMY=$NIX_BUILD_TOP/dummy
mkdir $NIX_FIXINC_DUMMY
if test "$noSysDirs" = "1"; then
if test "$noSysDirs" = "1"; then
# Figure out what extra flags to pass to the gcc compilers
# being generated to make sure that they use our glibc.
if test -e $NIX_CC/nix-support/orig-glibc; then
glibc=$(cat $NIX_CC/nix-support/orig-glibc)
# Ugh. Copied from gcc-wrapper/builder.sh. We can't just
# source in $NIX_CC/nix-support/add-flags, since that
# would cause *this* GCC to be linked against the
# *previous* GCC. Need some more modularity there.
extraCFlags="-B$glibc/lib -isystem $glibc/include"
extraLDFlags="-B$glibc/lib -L$glibc/lib -Wl,-s \
-Wl,-dynamic-linker,$glibc/lib/ld-linux.so.2"
# Oh, what a hack. I should be shot for this.
# In stage 1, we should link against the previous GCC, but
# not afterwards. Otherwise we retain a dependency.
# However, ld-wrapper, which adds the linker flags for the
# previous GCC, is also used in stage 2/3. We can prevent
# it from adding them by NIX_GLIBC_FLAGS_SET, but then
# gcc-wrapper will also not add them, thereby causing
# stage 1 to fail. So we use a trick to only set the
# flags in gcc-wrapper.
hook=$(pwd)/ld-wrapper-hook
echo "NIX_GLIBC_FLAGS_SET=1" > $hook
export NIX_LD_WRAPPER_START_HOOK=$hook
fi
export NIX_EXTRA_CFLAGS=$extraCFlags
export NIX_EXTRA_LDFLAGS=$extraLDFlags
export CFLAGS=$extraCFlags
export CXXFLAGS=$extraCFlags
export LDFLAGS=$extraLDFlags
fi
else
patches=""
fi
preConfigure=preConfigure
preConfigure() {
# Determine the frontends to build.
langs="c"
if test -n "$langCC"; then
langs="$langs,c++"
fi
if test -n "$langF77"; then
langs="$langs,f77"
fi
# Cross compiler evilness
mkdir -p $out
mkdir -p $out/arm-linux
mkdir -p $out/arm-linux/bin
ln -s $binutilsArm/arm-linux/bin/as $out/arm-linux/bin/as
ln -s $binutilsArm/arm-linux/bin/ld $out/arm-linux/bin/ld
ln -s $binutilsArm/arm-linux/bin/ar $out/arm-linux/bin/ar
ln -s $binutilsArm/arm-linux/bin/ranlib $out/arm-linux/bin/ranlib
# Perform the build in a different directory.
mkdir ../build
cd ../build
configureScript=../$sourceRoot/configure
configureFlags="--enable-languages=$langs --target=$platform --disable-threads --disable-libmudflap --disable-shared --with-headers=$kernelHeadersArm/include --disable-multilib"
}
postInstall=postInstall
postInstall() {
# Remove precompiled headers for now. They are very big and
# probably not very useful yet.
find $out/include -name "*.gch" -exec rm -rf {} \; -prune
# Remove `fixincl' to prevent a retained dependency on the
# previous gcc.
rm -rf $out/libexec/gcc/*/*/install-tools
}
#if test -z "$profiledCompiler"; then
#makeFlags="bootstrap"
#else
#makeFlags="profiledbootstrap"
#fi
genericBuild
---
Step 4: build a C library for the target platform.
The previous steps are enough to compile a C library. In our case we take
uClibc. It's intended to be a small sized replacement for glibc. It is widely
used in embedded environments.
...
Step 5: Build a compiler to link with the newly built C library.
...
If we restrict the compiler to just C programs it is relatively easy,
since we only need to wrap the GCC we built in the previous step with all
the right tools and the right C library. Successfully compiled programs with
this compiler and verified to be working on a HP Jornada 820 running Linux
are "patch", "make" and "wget".
If we want to build C++ programs it gets a lot more difficult. GCC has a
three step compilation process. In the first step a simple compiler, called
xgcc, that can compile only C programs is built. With that compiler it
compiles itself two more times: one time to build a full compiler, and another
time to build a full compiler once again with the freshly built compiler from
step 2. In the second and third step support for C++ is compiled, if this
is configured.
One of the libraries that has to be built for C++ support step is libstdc++.
This library uses xgcc, even when cross compiling, since libstdc++ has to be
compiled for arm-linux.
One of the compiler flags that GCC uses for this compiler is called X_CFLAGS.
This is used by the Nix build process to set the dynamic linker, glibc
in the case of i686-linux using the default Nix packages collection.
Obviously, since we need to compile libstc++ for arm-linux with uClibc linking
will not be done correctly: you can't link object files built for arm-linux
with a glibc built for i686-linux.
Setting X_CFLAGS to use the uClibc libraries and dynamic linker will fail
too. Earlier on in the build process these flags are used to compile important
files like libgcc.a by the host system gcc, which does need to be linked
to glibc. To make this work correctly you will need to carefully juggle
with compilation flags. This is still work in progress for Nix.
---
After successfully completing the whole toolchain you can start building
packages with the newly built tools. To make everything build correctly
you will need a stdenv for your target platform. Setting up this platform
will take some effort. Right now there is a very experimental setup for
arm-linux, which needs to be cleaned up before it is production ready.
Please note that many packages are not well suited for cross-compilation.
Even though the package itself might be very well portable often the
buildscripts are not. One thing that we have seen that causes frequent
build failures is the use of the LD variable. This is often set to 'ld'
and not $(CROSS)-ld.
+5 -1
View File
@@ -64,13 +64,15 @@ builders = ssh-ng://builder@linux-builder ${ARCH}-linux /etc/nix/builder_ed25519
builders-use-substitutes = true
```
To allow Nix to connect to a remote builder not running on port 22, you will also need to create a new file at `/etc/ssh/ssh_config.d/100-linux-builder.conf`:
To allow Nix to connect to the default remote builder, which does not run on port 22, you will also need to create a new file at `/etc/ssh/ssh_config.d/100-linux-builder.conf`:
```
Host linux-builder
Hostname localhost
HostKeyAlias linux-builder
Port 31022
User builder
IdentityFile /etc/nix/builder_ed25519
```
… and then restart your Nix daemon to apply the change:
@@ -79,6 +81,8 @@ Host linux-builder
$ sudo launchctl kickstart -k system/org.nixos.nix-daemon
```
Note that if the builder is running and you have created the above ssh conf file, you can ssh into the builder with `sudo ssh builder@linux-builder`.
## Example flake usage {#sec-darwin-builder-example-flake}
```nix
+1
View File
@@ -22,6 +22,7 @@ lhapdf.section.md
locales.section.md
etc-files.section.md
nginx.section.md
nrfutil.section.md
opengl.section.md
shell-helpers.section.md
python-tree-sitter.section.md
+2 -2
View File
@@ -5,7 +5,7 @@ The Nix expressions to build the Linux kernel are in [`pkgs/os-specific/linux/ke
The function [`pkgs.buildLinux`](https://github.com/NixOS/nixpkgs/blob/d77bda728d5041c1294a68fb25c79e2d161f62b9/pkgs/os-specific/linux/kernel/generic.nix) builds a kernel with [common configuration values](https://github.com/NixOS/nixpkgs/blob/d77bda728d5041c1294a68fb25c79e2d161f62b9/pkgs/os-specific/linux/kernel/common-config.nix).
This is the preferred option unless you have a very specific use case.
Most kernels packaged in Nixpkgs are built that way, and it will also generate kernels suitable for NixOS.
[`pkgs.linuxManualConfig`](https://github.com/NixOS/nixpkgs/blob/d77bda728d5041c1294a68fb25c79e2d161f62b9/pkgs/os-specific/linux/kernel/manual-config.nix) requires a complete configuration to be passed.
[`pkgs.linuxManualConfig`](https://github.com/NixOS/nixpkgs/blob/d77bda728d5041c1294a68fb25c79e2d161f62b9/pkgs/os-specific/linux/kernel/build.nix) requires a complete configuration to be passed.
It has fewer additional features than `pkgs.buildLinux`, which provides common configuration values and exposes the `features` attribute, as explained below.
Both functions have an argument `kernelPatches` which should be a list of `{name, patch, extraConfig}` attribute sets, where `name` is the name of the patch (which is included in the kernels `meta.description` attribute), `patch` is the patch itself (possibly compressed), and `extraConfig` (optional) is a string specifying extra options to be concatenated to the kernel configuration file (`.config`).
@@ -75,7 +75,7 @@ pkgs.linuxPackages_custom {
:::
Additional attributes can be used with `linuxManualConfig` for further customisation instead of `linuxPackages_custom`. You're encouraged to read [the `pkgs.linuxManualConfig` source code](https://github.com/NixOS/nixpkgs/blob/d77bda728d5041c1294a68fb25c79e2d161f62b9/pkgs/os-specific/linux/kernel/manual-config.nix) to understand how to use them.
Additional attributes can be used with `linuxManualConfig` for further customisation instead of `linuxPackages_custom`. You're encouraged to read [the `pkgs.linuxManualConfig` source code](https://github.com/NixOS/nixpkgs/blob/d77bda728d5041c1294a68fb25c79e2d161f62b9/pkgs/os-specific/linux/kernel/build.nix) to understand how to use them.
To edit the `.config` file for Linux X.Y from within Nix, proceed as follows:
+13
View File
@@ -0,0 +1,13 @@
# nrfutil {#sec-nrfutil}
nrfutil can be built with its installables as following:
```nix
(nrfutil.withExtensions [
"nrfutil-completion"
"nrfutil-device"
"nrfutil-trace"
])
```
Keep in mind that all installables might not be available for every supported platform.
+76 -11
View File
@@ -58,9 +58,8 @@
],
"cuda-updating-redistributables": [
"index.html#cuda-updating-redistributables",
"index.html#updating-cuda-redistributables"
],
"cuda-updating-cutensor": [
"index.html#updating-cuda-redistributables",
"index.html#updating-the-cuda-toolkit",
"index.html#cuda-updating-cutensor",
"index.html#updating-cutensor"
],
@@ -72,10 +71,6 @@
"index.html#cuda-updating-the-cuda-package-set",
"index.html#updating-the-cuda-package-set"
],
"cuda-updating-the-cuda-toolkit": [
"index.html#cuda-updating-the-cuda-toolkit",
"index.html#updating-the-cuda-toolkit"
],
"cuda-user-guide": [
"index.html#cuda-user-guide"
],
@@ -124,6 +119,48 @@
"inkscape-plugins": [
"index.html#inkscape-plugins"
],
"libcxxhardeningextensive": [
"index.html#libcxxhardeningextensive"
],
"libcxxhardeningfast": [
"index.html#libcxxhardeningfast"
],
"julec-hook": [
"index.html#julec-hook"
],
"julec-hook-example-code-snippet": [
"index.html#julec-hook-example-code-snippet"
],
"julec-hook-variable-dontusejulecbuild": [
"index.html#julec-hook-variable-dontusejulecbuild"
],
"julec-hook-variable-dontusejuleccheck": [
"index.html#julec-hook-variable-dontusejuleccheck"
],
"julec-hook-variable-dontusejulecinstall": [
"index.html#julec-hook-variable-dontusejulecinstall"
],
"julec-hook-variable-jule-out-dir": [
"index.html#julec-hook-variable-jule-out-dir"
],
"julec-hook-variable-jule-out-name": [
"index.html#julec-hook-variable-jule-out-name"
],
"julec-hook-variable-jule-src-dir": [
"index.html#julec-hook-variable-jule-src-dir"
],
"julec-hook-variable-jule-test-dir": [
"index.html#julec-hook-variable-jule-test-dir"
],
"julec-hook-variable-jule-test-out-dir": [
"index.html#julec-hook-variable-jule-test-out-dir"
],
"julec-hook-variable-jule-test-out-name": [
"index.html#julec-hook-variable-jule-test-out-name"
],
"julec-hook-variables": [
"index.html#julec-hook-variables"
],
"major-ghc-deprecation": [
"index.html#major-ghc-deprecation"
],
@@ -216,6 +253,9 @@
"sec-building-packages-with-llvm-using-clang-stdenv": [
"index.html#sec-building-packages-with-llvm-using-clang-stdenv"
],
"sec-darwin-availability-checks": [
"index.html#sec-darwin-availability-checks"
],
"sec-darwin-libcxx-deployment-targets": [
"index.html#sec-darwin-libcxx-deployment-targets"
],
@@ -322,6 +362,9 @@
"sec-nixpkgs-release-25.05-notable-changes": [
"release-notes.html#sec-nixpkgs-release-25.05-notable-changes"
],
"sec-nrfutil": [
"index.html#sec-nrfutil"
],
"sec-overlays-install": [
"index.html#sec-overlays-install"
],
@@ -355,6 +398,24 @@
"sec-pkg-overrideDerivation": [
"index.html#sec-pkg-overrideDerivation"
],
"sec-platform-breakdown": [
"index.html#sec-platform-breakdown"
],
"sec-platform-tier1": [
"index.html#sec-platform-tier1"
],
"sec-platform-tier2": [
"index.html#sec-platform-tier2"
],
"sec-platform-tier3": [
"index.html#sec-platform-tier3"
],
"sec-platform-tier4-7": [
"index.html#sec-platform-tier4-7"
],
"sec-platform-tiers": [
"index.html#sec-platform-tiers"
],
"sec-lib-makeOverridable": [
"index.html#sec-lib-makeOverridable"
],
@@ -1190,7 +1251,8 @@
"index.html#sec-purity-in-nixpkgs"
],
"sec-hardening-in-nixpkgs": [
"index.html#sec-hardening-in-nixpkgs"
"index.html#sec-hardening-in-nixpkgs",
"index.html#pie"
],
"sec-hardening-flags-enabled-by-default": [
"index.html#sec-hardening-flags-enabled-by-default"
@@ -1225,9 +1287,6 @@
"sec-hardening-flags-disabled-by-default": [
"index.html#sec-hardening-flags-disabled-by-default"
],
"pie": [
"index.html#pie"
],
"shadowstack": [
"index.html#shadowstack"
],
@@ -2366,6 +2425,9 @@
"cmake-build-dir": [
"index.html#cmake-build-dir"
],
"cmake-build-type": [
"index.html#cmake-build-type"
],
"dont-use-cmake-configure": [
"index.html#dont-use-cmake-configure"
],
@@ -3765,6 +3827,9 @@
"buildpythonpackage-parameters": [
"index.html#buildpythonpackage-parameters"
],
"overriding-python-build-helpers": [
"index.html#overriding-python-build-helpers"
],
"overriding-python-packages": [
"index.html#overriding-python-packages"
],
+110 -15
View File
@@ -12,6 +12,18 @@
- The default GHC version has been updated from 9.8 to 9.10.
`haskellPackages` correspondingly uses Stackage LTS 24 (instead of LTS 23) as a baseline.
- **This release of Nixpkgs requires macOS Sonoma 14.0 or newer, as announced in the 25.05 release notes.**
The default SDK is now 14.4, but the minimum version is 14.0.
cc-wrapper will enforce that availability annotations are used or an appropriate deployment target is set.
See the Darwin platform notes for details.
- **We expect to drop support for `x86_64-darwin` by Nixpkgs 26.11,** in light of Apples announcement that macOS 26 will be the final version to support Intel Macs.
When support is fully removed, we will no longer build packages for the platform or guarantee that it can build at all.
This may happen in stages, depending on our available build and maintenance resources and decisions made by projects we rely on.
By the time of 26.11s release, Homebrew will offer only limited [Tier 3](https://docs.brew.sh/Support-Tiers#tier-3) support for the platform, but MacPorts will likely continue to support it for a long time.
We also recommend users consider installing NixOS, which should continue to run on essentially all Intel Macs, especially after Apple stops security support for macOS 26 in 2028.
- Darwin has switched to using the system libc++. This was done for improved compatibility and to avoid ODR violations.
If a newer C++ library feature is not available on the default deployment target, you will need to increase the deployment target.
See the Darwin platform documentation for more details.
@@ -30,6 +42,11 @@
- Everything related to `bower` was removed, as it is deprecated and not used by anything in nixpkgs.
- `reaction` has been updated to version 2, which includes some breaking changes.
For more information, [check the release article](https://blog.ppom.me/en-reaction-v2).
- `mealie` has been updated to 3.0.2: This update introduces breaking changes in some API endpoints (see the [release changelog](https://github.com/mealie-recipes/mealie/releases/tag/v3.0.0)).
- The `offrss` package was removed due to lack of upstream maintenance since 2012. It's recommended for users to migrate to another RSS reader
- `installShellFiles`: Allow installManPage to take a piped input, add the `--name` flag for renaming the file when installed. Can also append `--` to opt-out of all subsequent parsing.
@@ -38,10 +55,16 @@
- LLVM 12, 13, 14, 15, 16, and 17 have been removed, as they have reached endoflife upstream and are no longer supported.
- The `vlock` output from kbd has been removed. Instead a new package `kbdVlock` has been introduced. Use this package now instead of the output.
- GHCJS 8.10, exposed via `haskell.compiler.ghcjs` and `haskell.compiler.ghcjs810`, has been removed. Downstream users should migrate their projects to the new JavaScript backend of GHC proper which can be used via `pkgsCross.ghcjs` from Nixpkgs. Haskell packaging code, like `haskellPackages.mkDerivation`, `ghcWithPackages` and `hoogleWithPackages`, also no longer supports GHCJS.
- GHC 8.6, 8.10, 9.0, 9.2, and their package sets have been removed.
- The `haskellPackages.mkDerivation` builder now converts packages' cabal files to Unix line endings before `patchPhase`. This behavior can be disabled using `dontConvertCabalFileToUnix`.
- `webkitgtk_4_0` has been removed because it depends on an unmaintained version of security-critical libsoup library (`libsoup_2`) and the support will be [dropped upstream soon](https://webkitgtk.org/2025/10/07/webkitgtk-soup2-deprecation.html).
- Support for bootstrapping native GHC compilers on 32bit ARM and littleendian 64bit PowerPC has been dropped.
The latter was probably broken anyway.
If there is interest in restoring support for these architectures, it should be possible to crosscompile a bootstrap GHC binary.
@@ -59,6 +82,9 @@
- `base16-builder` node package has been removed due to lack of upstream maintenance.
- The default glibc ELF ABI for the powerpc64-linux platform has been changed from ELFv2 back to ELFv1, due to the latter having less remaining issues when bootstrapping & building Nixpkgs packages on hardware.
If glibc ELFv2 is desired, use a fuller target triplet like `powerpc64-unknown-linux-gnuabielfv2` or the provided `ppc64-elfv2` platform example.
- `python3Packages.bjoern` has been removed, as the upstream is unmaintained and it depends on a 14-year-old version of http-parser with numerous vulnerabilities.
- `buildGoModule` removes the compatibility layer of `CGO_ENABLED` not specified via `env`.
@@ -90,10 +116,16 @@
- `forgejo` main program has been renamed to `bin/forgejo` from the previous `bin/gitea`.
- the "pie" hardening flag has been removed. compilers are expected to enable PIE by default, as has been common practice since 2016 outside of nixpkgs. If a package needs "pie" disabled pass `-no-pie` in `CFLAGS`. It is unlikely this will be necessary in many cases; due to the prevalance of default PIE toolchains most packages incompatible with PIE already pass no-pie.
- `wayclip` now uses the `ext-data-control-v1` Wayland protocol instead of `wlr-data-control-unstable-v1`.
- `cudaPackages.cudatoolkit-legacy-runfile` has been removed.
- `conduwuit` was removed due to upstream ceasing development and deleting their repository. For existing data, a migration to `matrix-conduit`, `matrix-continuwuity` or `matrix-tuwunel` may be possible.
- `asciidoctor-with-extension` had its `asciidoctor-mathematical` extension removed, because it fails to build, and it is not maintained properly.
- The `ghcInfo` and `controlPhases` functions have been removed from `haskell.lib.compose` and `haskell.lib`. They were unused and would return incorrect results.
- Instead of using `controlPhases`, the `doCheck` attribute of derivations can be accessed which is more accurate. `doBenchmark` can't be introspected at the moment.
@@ -121,6 +153,8 @@
- Zig 0.12 has been removed.
- `services.forgejo.dump.age` now defaults to `4w`, which deletes dumps older than 4 weeks. This new behaviour could result in older backups being deleted.
- `ansible-later` has been removed because it was discontinued by the author.
- `k3s` airgap images passthru attributes have changed:
@@ -134,8 +168,12 @@
- `meilisearch_1_11` has been removed, as it is no longer supported.
- `budgie-desktop` has been updated [10.9.3](https://github.com/BuddiesOfBudgie/budgie-desktop/releases/tag/v10.9.3), this changes `XDG_CURRENT_DESKTOP` from `Budgie:GNOME` to `Budgie`.
- Greetd and its original greeters (`tuigreet`, `gtkgreet`, `qtgreet`, `regreet`, `wlgreet`) were moved from `greetd` namespace to top level (`greetd.tuigreet` -> `tuigreet`, `greetd.greetd` -> `greetd`, etc). The original attrs are available for compatibility as passthrus of `greetd`, but will emit a warning. They will be removed in future releases.
- `carla` no longer support `gtk2` override.
- The `archipelago-minecraft` package was removed, as upstream no longer provides support for the Minecraft APWorld.
- `pcp` has been removed because the upstream repo was archived and it hasn't been updated since 2021.
@@ -168,6 +206,8 @@
- `proton-caller` has been removed due to lack of upstream maintenance.
- `android-udev-rules` has been removed, as it is effectively superseded by built-in uaccess rules in systemd.
- `lima` package now only includes the guest agent for the host's architecture by default. If your guest VM's architecture differs from your Lima host's, you'll need to enable the `lima-additional-guestagents` package by setting `withAdditionalGuestAgents = true` when overriding lima with this input.
- `mongodb-6_0` was removed as it is end of life as of 2025-07-31.
@@ -188,12 +228,15 @@
- `sail-riscv` 0.8 follows [upstream](https://github.com/riscv/sail-riscv/blob/7cc4620eb1a57bfe04832baccdcf5727e9459bd4/doc/ChangeLog.md) and provides only a single binary, `sail_riscv_sim`.
- `moar` has been updated from `1.33.0` to `2.0.0`, and renamed to `moor` following an upstream decision. See the [release notes](https://github.com/walles/moor/releases/tag/v2.0.0) for more.
- `podofo` has been updated from `0.9.8` to `1.0.0`. These releases are by nature very incompatible due to major API changes. The legacy versions can be found under `podofo_0_10` and `podofo_0_9`.
Changelog: https://github.com/podofo/podofo/blob/1.0.0/CHANGELOG.md, API-Migration-Guide: https://github.com/podofo/podofo/blob/1.0.0/API-MIGRATION.md.
- NetBox was updated to `>= 4.3.0`. Have a look at the breaking changes
of the [4.3 release](https://github.com/netbox-community/netbox/releases/tag/v4.2.0),
make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_3;` in your configuration.
- NetBox was updated to `>= 4.4.0`. Have a look at the breaking changes
of the [4.3 release](https://github.com/netbox-community/netbox/releases/tag/v4.3.0)
and the [4.4 release](https://github.com/netbox-community/netbox/releases/tag/v4.4.0),
make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_4;` in your configuration.
- `privatebin` has been updated to `2.0.0`. This release changes configuration defaults including switching the template and removing legacy features. See the [v2.0.0 changelog entry](https://github.com/PrivateBin/PrivateBin/releases/tag/2.0.0) for details on how to upgrade.
@@ -207,6 +250,8 @@
- [private-gpt](https://github.com/zylon-ai/private-gpt) service has been removed by lack of maintenance upstream.
- `rabbitmq-server` has been updated from 4.0.9 to 4.1.4. The 4.1.0 release includes breaking changes. For more information read the [changelog of 4.1.0](https://github.com/rabbitmq/rabbitmq-server/releases/tag/v4.1.0)
- `lxde` scope has been removed, and its packages have been moved the top-level.
- `pulsemeeter` has been updated to `2.0.0`. The configuration file from older versions has to be deleted. For more information and instructions see the [v2.0.0 changelog entry](https://github.com/theRealCarneiro/pulsemeeter/releases/tag/v2.0.0).
@@ -217,6 +262,8 @@
- The main binary of `tomlq` has been renamed from `tomlq` to `tq`.
- `opensoldat` binaries and user configuration directory names have been prefixed by 'open', becoming opensoldat and opensoldatserver. Configuration will be moved automatically before launch when possible.
- `emacs-macport` has been moved to a fork of Mitsuharu Yamamoto's patched source code starting with Emacs v30 as the original project seems to be currently dormant. All older versions of this package have been dropped.
This introduces some backwardsincompatible 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.
@@ -228,6 +275,14 @@
- `nodePackages.rimraf` has been removed, as it is a Javascript library, and packages that want to use it should depend directly on it instead.
- `mariadb` now defaults to `mariadb_114` instead of `mariadb_1011`, meaning the default version was upgraded from 10.11.x to 11.4.x. See the [upgrade notes](https://mariadb.com/kb/en/upgrading-from-mariadb-10-11-to-mariadb-11-4/) for potential issues.
- `qt5.full` and `qt6.full` aliases have been removed. Their use has always been discouraged, and downstream projects should use `qtN.env` with the right set of packages.
- `python3Packages.duckduckgo-search` has been updated to v9+ and therefore has been renamed to ddgs.
Use `python3Packages.ddgs` instead.
See [release note for v9.0.0](https://github.com/deedy5/ddgs/releases/tag/v9.0.0)
## Other Notable Changes {#sec-nixpkgs-release-25.11-notable-changes}
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
@@ -237,17 +292,29 @@
- Added `gitConfig` and `gitConfigFile` option to the nixpkgs `config`, to allow for setting a default `gitConfigFile` for all `fetchgit` invocations.
- Added `npmRegistryOverrides` and `npmRegistryOverridesString` option to the nixpkgs `config`, to allow for setting a default `npmRegistryOverridesString` for all `fetchNpmDeps` invocations.
- The `dockerTools.streamLayeredImage` builder now uses a better algorithm for generating layered docker images, such that much more sharing is possible when the number of store paths exceeds the layer limit. It gives each of the largest store paths its own layer and adds dependencies to those layers when they aren't used elsewhere.
- The systemd initrd will now respect `x-systemd.wants` and `x-systemd.requires` for reliably unlocking multi-disk bcachefs volumes.
- `neovim`: Added support for the `vim.o.exrc` option, the `VIMINIT` environment variable, and sourcing of `sysinit.vim`.
See the neovim help page [`:help startup`](https://neovim.io/doc/user/starting.html#startup) for more information, as well as [the nixpkgs neovim wrapper documentation](#neovim-custom-configuration).
- `cloudflare-ddns`: Added package cloudflare-ddns.
- `clickhouse`: Added `serverConfig`, `usersConfig` configuration options accepting Nix attribute sets. Also added `extraServerConfig` and `extraUsersConfig` options accepting plain text (expecting XML configuration).
- [`homebox` 0.20.0](https://github.com/sysadminsmedia/homebox/releases/tag/v0.20.0) changed how assets are stored and hashed. It is recommended to back up your database before this update. In particular, `--storage-data` was replaced with `--storage-conn-string` and `--storage-prefix-path`. If your configuration set `HBOX_STORAGE_DATA` manually, you must migrate it to `HBOX_STORAGE_CONN_STRING` and `HBOX_STORAGE_PREFIX_PATH`.
- GIMP now defaults to version 3. Use `gimp2` for the old version.
- `installShellCompletion`: now supports Nushell completion files
- New hardening flags, `strictflexarrays1` and `strictflexarrays3` were made available, corresponding to the gcc/clang options `-fstrict-flex-arrays=1` and `-fstrict-flex-arrays=3` respectively.
- `idris2` supports being instantiated with a package environment with `idris.withPackages (p: [ ])`
- New hardening flags `strictflexarrays1`, `strictflexarrays3`, `glibcxxassertions`, `libcxxhardeningfast` and `libcxxhardeningextensive` were made available.
- `gramps` has been updated to 6.0.0
Upstream recommends [backing up your Family Trees](https://gramps-project.org/wiki/index.php/Gramps_6.0_Wiki_Manual_-_Manage_Family_Trees#Backing_up_a_Family_Tree) before upgrading.
@@ -256,6 +323,8 @@
- `forgejo-runner`: The upgrade to version 11 brings a license change from MIT to GPLv3-or-later.
- `waydroid-nftables`: New variant of `waydroid` that supports nftables instead of iptables.
- `lisp-modules` were brought in sync with the [June 2025 Quicklisp release](http://blog.quicklisp.org/2025/07/june-2025-quicklisp-dist-now-available.html).
- `ffmpeg_8`, `ffmpeg_8-headless`, and `ffmpeg_8-full` have been added. The default version of FFmpeg remains ffmpeg_7 for now, though this may change before release.
@@ -263,7 +332,12 @@
- `searx` was updated to use `envsubst` instead of `sed` for parsing secrets from environment variables.
If your previous configuration included a secret reference like `server.secret_key = "@SEARX_SECRET_KEY@"`, you must migrate to the new envsubst syntax: `server.secret_key = "$SEARX_SECRET_KEY"`.
- A new hardening flag, `glibcxxassertions` was made available, corresponding to the glibc `_GLIBCXX_ASSERTIONS` option.
- `jellyfin` was updated to `10.11.x`, which includes heavy backend changes.
Make sure to backup your data and configuration directories
and read the [Jellyfin 10.11.0 release announcement](https://jellyfin.org/posts/jellyfin-release-10.11.0/).
**Important:** make sure you're running Jellyfin version `10.10.7` before upgrading,
and beware that the migration may take several hours depending on your library size and state.
The process must not be interrupted.
- `versionCheckHook`: Packages that previously relied solely on `pname` to locate the program used to version check, but have a differing `meta.mainProgram` entry, might now fail.
@@ -281,6 +355,8 @@
- `nix-prefetch-git`: Added a `--no-add-path` argument to disable adding the path to the store; this is useful when working with a [read-only store](https://nix.dev/manual/nix/2.28/command-ref/new-cli/nix3-help-stores#store-experimental-local-overlay-store-read-only).
- `fetchNpmDeps`: Add `npmRegistryOverridesString` argument to pass NPM registry overrides to the fetcher.
- `sftpman` has been updated to version 2, a rewrite in Rust which is mostly backward compatible but does include some changes to the CLI.
For more information, [check the project's README](https://github.com/spantaleev/sftpman-rs#is-sftpman-v2-compatible-with-sftpman-v1).
@@ -290,39 +366,55 @@
- `emacs` now disables the GC mark trace buffer by default. This improves GC performance by 5%, but can make GC issues harder to debug. This is configurable with `withGcMarkTrace`.
- Passing `stdenv` to `buildPythonPackage` or `buildPythonApplication` has been deprecated and will trigger an error in a future release.
Instead, you should _override_ the python build helper, e.g., `(buildPythonPackage.override { stdenv = customStdenv; })`.
See [](#overriding-python-build-helpers).
- `buildPythonPackage` and `buildPythonApplication` now default to `nix-update-script` as their default `updateScript`. This should improve automated updates, since nix-update is better maintained than the in-tree update script and has more robust fetcher support.
- `plasma6`: Fixed the `ksycoca` cache not being re-built when `$XDG_CACHE_HOME` is set to something that isn't `$HOME/.cache`.
## Nixpkgs Library {#sec-nixpkgs-release-25.11-lib}
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
- `mealie` has been updated to 3.0.2: This update introduces breaking changes in some API endpoints (see the [release changelog](https://github.com/mealie-recipes/mealie/releases/tag/v3.0.0)).
### Breaking changes {#sec-nixpkgs-release-25.11-lib-breaking}
- `reaction` has been updated to version 2, which includes some breaking changes.
For more information, [check the release article](https://blog.ppom.me/en-reaction-v2).
- `lib.literalExample` has been removed, use `lib.literalExpression` instead, or use `lib.literalMD` for a non-Nix description.
- `lib.replaceChars` has been removed, it was a deprecated alias of `lib.replaceStrings`.
- `lib.readPathsFromFile` has been removed, use a list instead
- `lib.mapAttrsFlatten` has been removed, following its deprecation in NixOS 24.11. Use `lib.attrsets.mapAttrsToList` instead.
- `lib.strings.isCoercibleToString` has been in favor of either `lib.strings.isStringLike` or `lib.strings.isConvertibleWithToString`. Only use the latter if it needs to return true for null, numbers, booleans, or a list of those.
- `lib.types.string` has been removed. See [this pull request](https://github.com/NixOS/nixpkgs/pull/66346) for better alternative types like `lib.types.str`.
- `lib.modules.defaultPriority` has been removed, please use `lib.modules.defaultOverridePriority` instead.
- `lib.attrsets.cartesianProductOfSets` has been removed, following its deprecation in NixOS 24.11. Use `lib.attrsets.cartesianProduct` instead.
- `lib.sources.pathType`, `lib.sources.pathIsDirectory` and `lib.sources.pathIsRegularFile` have been replaced by `lib.filesystem.pathType`, `lib.filesystem.pathIsDirectory` and `lib.filesystem.pathIsRegularFile` respectively.
- `lib.attrsets.zip` has been removed, following its deprecation in 2013. Use `lib.attrsets.zipAttrsWith` instead.
- `lib.attrsets.zipWithNames` has been removed, following its deprecation in 2009. Use `lib.attrsets.zipAttrsWithNames` instead.
- `lib.options.mkPackageOptionMD` has been removed, following its deprecation in NixOS 24.11. Use `lib.options.mkPackageOption` instead.
- `haskell.lib.addOptparseApplicativeCompletionScripts` has been removed, use `haskellPackages.generateOptparseApplicativeCompletions` instead.
- The `buildPythonPackage` and `buildPythonApplication` functions now require
an explicit `format` attribute. Previously the default format used setuptools
and called `setup.py` from the source tree, which is deprecated.
The modern alternative is to configure `pyproject = true` with `build-system = [ setuptools ]`.
- `boot.enableContainers` is only turned on when a declarative NixOS container is defined in `containers`.
If you use the `nixos-container` tool for imperative container management, set `boot.enableContainers = true;` explicitly.
### Deprecations {#sec-nixpkgs-release-25.11-lib-deprecations}
- `lib.options.mkAliasOptionModuleMD` is now obsolete; use the identical [`lib.options.mkAliasOptionModule`] instead.
- `types.either` silently accepted mismatching types when used in `freeformType`. Module maintainers should fix the used type
In most cases wrapping `either` with `attrsOf` should be sufficient.
@@ -331,10 +423,13 @@
- `number`
- `numbers.*`
- `lib.cli.toGNUCommandLine` and `lib.cli.toGNUCommandLineShell` have been deprecated in favor of `lib.cli.toCommandLine`, `lib.cli.toCommandLineShell`, `lib.cli.toCommandLineGNU` and `lib.cli.toCommandLineShellGNU`.
### Additions and Improvements {#sec-nixpkgs-release-25.11-lib-additions-improvements}
- `neovim`: Added support for the `vim.o.exrc` option, the `VIMINIT` environment variable, and sourcing of `sysinit.vim`.
- `lib.cli.toCommandLine`, `lib.cli.toCommandLineShell`, `lib.cli.toCommandLineGNU` and `lib.cli.toCommandLineShellGNU` have been added to address multiple issues in `lib.cli.toGNUCommandLine` and `lib.cli.toGNUCommandLineShell`.
See the neovim help page [`:help startup`](https://neovim.io/doc/user/starting.html#startup) for more information, as well as [the nixpkgs neovim wrapper documentation](#neovim-custom-configuration).
- `ugrep`: Added `wrapWithFilterUtils` package flag for optionally wrapping `ugrep+` and `ug+` with filter utilities for grepping other file types.
- `ugrep`: Added `createGrepReplacementLinks` package flag for optionally creating drop-in replacement symlinks for `gnugrep`.
- `cloudflare-ddns`: Added package cloudflare-ddns.
+15 -7
View File
@@ -47,6 +47,17 @@ See below for how to use a newer deployment target.
For example, `std::print` depends on features that are only available on macOS 13.3 or newer.
To make them available, set the deployment target to 13.3 using `darwinMinVersionHook`.
#### Package fails to build due to missing API availability checks {#sec-darwin-availability-checks}
This is normally a bug in the package or a misconfigured deployment target.
* If it is using an API from a newer release (e.g., from macOS 26.0 while targeting macOS 14.0), it needs to use an availability check.
The code should be patched to use [`__builtin_available`](https://clang.llvm.org/docs/LanguageExtensions.html#objective-c-available).
Note that while the linked documentation is for Objective-C, it is applicable to C and C++ except that you use `__builtin_available` in place of `@available`.
* If the package intends to require the newer platform (i.e., it does not support running on older versions with reduced functionality), use `darwinMinVersionHook` to set the deployment target to the required version.
See below for how to use a newer deployment target.
* If the package actually handles this through some other mechanism (e.g., MoltenVK relies on the running platforms MSL version), the error can be suppressed.
To suppress the error, add `-Wno-error=unguarded-availability` to `env.NIX_CFLAGS_COMPILE`.
#### Package requires a non-default SDK or fails to build due to missing frameworks or symbols {#sec-darwin-troubleshooting-using-sdks}
In some cases, you may have to use a non-default SDK.
@@ -106,13 +117,10 @@ The following is a list of Xcode versions, the SDK version in Nixpkgs, and the a
Check your packages documentation (platform support or installation instructions) to find which Xcode or SDK version to use.
Generally, only the last SDK release for a major version is packaged.
| Xcode version | SDK version | Nixpkgs attribute |
|--------------------|--------------------|------------------------------|
| 12.012.5.1 | 11.3 | `apple-sdk_11` / `apple-sdk` |
| 13.013.4.1 | 12.3 | `apple-sdk_12` |
| 14.014.3.1 | 13.3 | `apple-sdk_13` |
| 15.015.4 | 14.4 | `apple-sdk_14` |
| 16.0 | 15.0 | `apple-sdk_15` |
| Xcode version | SDK version | Nixpkgs attribute |
|--------------------|--------------------|-------------------------------|
| 15.015.4 | 14.4 | `apple-sdk_14` / `apple-sdk` |
| 16.0 | 15.0 | `apple-sdk_15` |
#### Darwin Default SDK versions {#sec-darwin-troubleshooting-darwin-defaults}
+12 -13
View File
@@ -1631,19 +1631,6 @@ The following flags are disabled by default and should be enabled with `hardenin
This flag adds the `-fno-strict-aliasing` compiler option, which prevents the compiler from assuming code has been written strictly following the standard in regards to pointer aliasing and therefore performing optimizations that may be unsafe for code that has not followed these rules.
#### `pie` {#pie}
This flag is disabled by default for normal `glibc` based NixOS package builds, but enabled by default for
- `musl`-based package builds, except on Aarch64 and Aarch32, where there are issues.
- Statically-linked for OpenBSD builds, where it appears to be required to get a working binary.
Adds the `-fPIE` compiler and `-pie` linker options. Position Independent Executables are needed to take advantage of Address Space Layout Randomization, supported by modern kernel versions. While ASLR can already be enforced for data areas in the stack and heap (brk and mmap), the code areas must be compiled as position-independent. Shared libraries already do this with the `pic` flag, so they gain ASLR automatically, but binary .text regions need to be build with `pie` to gain ASLR. When this happens, ROP attacks are much harder since there are no static locations to bounce off of during a memory corruption attack.
Static libraries need to be compiled with `-fPIE` so that executables can link them in with the `-pie` linker option.
If the libraries lack `-fPIE`, you will get the error `recompile with -fPIE`.
#### `strictflexarrays1` {#strictflexarrays1}
This flag adds the `-fstrict-flex-arrays=1` compiler option, which reduces the cases the compiler treats as "flexible arrays" to those declared with length `[1]`, `[0]` or (the correct) `[]`. This increases the coverage of fortify checks, because such arrays declared as the trailing element of a structure can normally not have their intended length determined by the compiler.
@@ -1688,6 +1675,18 @@ Adds the `-D_GLIBCXX_ASSERTIONS` compiler flag. This flag only has an effect on
These checks may have an impact on performance in some cases.
#### `libcxxhardeningfast` {#libcxxhardeningfast}
Adds the `-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST` compiler flag. This flag only has an effect on libc++ targets, and when defined, enables a set of assertions that prevent undefined behavior caused by violating preconditions of the standard library. libc++ provides several hardening modes, and this "fast" mode contains a set of security-critical checks that can be done with relatively little overhead in constant time.
Disabling `libcxxhardeningfast` implies disablement of checks from `libcxxhardeningextensive`.
#### `libcxxhardeningextensive` {#libcxxhardeningextensive}
Adds the `-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE` compiler flag. This flag only has an effect on libc++ targets, and when defined, enables a set of assertions that prevent undefined behavior caused by violating preconditions of the standard library. libc++ provides several hardening modes, and this "extensive" mode adds checks for undefined behavior that incur relatively little overhead but arent security-critical. The additional rigour impacts performance more than fast mode: benchmarking is recommended to determine if it is acceptable for a particular application.
Enabling this flag implies enablement of checks from `libcxxhardeningfast`. Disabling this flag does not imply disablement of checks from `libcxxhardeningfast`.
#### `pacret` {#pacret}
This flag adds the `-mbranch-protection=pac-ret` compiler option on aarch64-linux targets. This uses ARM v8.3's Pointer Authentication feature to sign function return pointers before adding them to the stack. The pointer's authenticity is then validated before returning to its destination. This dramatically increases the difficulty of ROP exploitation techniques.
+2 -2
View File
@@ -2,12 +2,12 @@
{
lib,
runCommand,
invalidateFetcherByDrvHash,
testers,
cacert,
python3,
}:
invalidateFetcherByDrvHash (
testers.invalidateFetcherByDrvHash (
{
name ? "manual_check-manpage-urls",
script ? ./manpage-urls.py,
+42 -11
View File
@@ -1,18 +1,49 @@
# Platform Support {#chap-platform-support}
Packages receive varying degrees of support, both in terms of maintainer attention and available computation resources for continuous integration (CI).
Packages receive varying degrees of support, both in terms of maintainer attention and available computation resources for continuous integration (CI). We have 7 defined tiers denoting how well supported each platform is.
Below is the list of the best supported platforms:
## Tiers {#sec-platform-tiers}
- `x86_64-linux`: Highest level of support.
- `aarch64-linux`: Well supported, with most packages building successfully in CI.
- `aarch64-darwin`: Receives better support than `x86_64-darwin`.
- `x86_64-darwin`: Receives some support.
### Tier 1 {#sec-platform-tier1}
There are many other platforms with varying levels of support.
The provisional platform list in [Appendix A] of [RFC046], while not up to date, can be used as guidance.
[Tier 1](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-1) platforms receive the highest level of support where problems can block updates, platform-specific patches are freely applied, and most packages are expected to work.
A more formal definition of the platform support tiers is provided in [RFC046], but has not been fully implemented yet.
### Tier 2 {#sec-platform-tier2}
[RFC046]: https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md
[Appendix A]: https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#appendix-a-non-normative-description-of-platforms-in-november-2019
[Tier 2](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-2) platforms are expected to remain functional with updates, receive platform-specific patches as needed, and have many packages built by Hydra with full ofBorg support.
### Tier 3 {#sec-platform-tier3}
[Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) platforms may receive non-intrusive platform-specific fixes, have native bootstrap tools available with cross-build toolchains in binary cache, but updates might break builds on these platforms.
### Tier 4-7 {#sec-platform-tier4-7}
Platform Tiers [4 through 7](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-4) indicate varying levels of minimal support going from receiving only limited fixes to platforms with no support, but a path to support.
## Breakdown {#sec-platform-breakdown}
| Triple | Support Tier | Channel Blockers | Hydra Support | Ofborg Support | Bootstrap Tarballs | Cross Compiling Support |
| ------------------------------------- | ------------ | ---------------- | ------------- | -------------- | ------------------ | ----------------------- |
| `x86_64-unknown-linux-gnu` | [Tier 1](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-1) | Many | ✔️ | ✔️ | ✔️ | ✔️ |
| `aarch64-unknown-linux-gnu` | [Tier 2](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-2) | Some | ✔️ | ✔️ | ✔️ | ✔️ |
| `x86_64-unknown-linux-musl` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | Limited | ❌ | ✔️ | ✔️ |
| `aarch64-unknown-linux-musl` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | Limited | ❌ | ✔️ | ✔️ |
| `x86_64-unknown-unknown-freebsd` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `arm64-apple-darwin` | [Tier 2](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-2) | Some | ✔️ | ✔️ | ✔️ | ❌\* |
| `x86_64-apple-darwin` | [Tier 2](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-2) | Some | ✔️ | ✔️ | ✔️ | ❌\* |
| `i686-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | Limited | ❌ | ✔️ | ✔️ |
| `riscv32-unknown-linux-gnu` | [Tier 4](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-4) | None | ❌ | ❌ | ❌ | ✔️ |
| `riscv64-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `loongarch64-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `armv6l-unknown-linux-gnueabihf` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `armv6l-unknown-linux-musleabihf` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `armv7l-unknown-linux-gnueabihf` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `armv5tel-unknown-linux-gnueabi` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `mips64el-unknown-linux-gnuabi64` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `mips64el-unknown-linux-gnuabin32` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `mipsel-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `powerpc64-unknown-linux-gnuabielfv2` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `powerpc64le-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
| `s390x-unknown-linux-gnu` | [Tier 3](https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md#tier-3) | None | ❌ | ❌ | ✔️ | ✔️ |
\* - Cross compiling is only supported on Darwin hosts.
+224 -33
View File
@@ -1,6 +1,6 @@
{ lib }:
rec {
{
/**
Automatically convert an attribute set to command-line options.
@@ -20,6 +20,7 @@ rec {
: The attributes to transform into arguments.
# Examples
:::{.example}
## `lib.cli.toGNUCommandLineShell` usage example
@@ -38,7 +39,10 @@ rec {
:::
*/
toGNUCommandLineShell = options: attrs: lib.escapeShellArgs (toGNUCommandLine options attrs);
toGNUCommandLineShell =
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2511)
"lib.cli.toGNUCommandLineShell is deprecated, please use lib.cli.toCommandLineShell or lib.cli.toCommandLineShellGNU instead."
(options: attrs: lib.escapeShellArgs (lib.cli.toGNUCommandLine options attrs));
/**
Automatically convert an attribute set to a list of command-line options.
@@ -55,7 +59,7 @@ rec {
: The attributes to transform into arguments.
# Options
## Options
`mkOptionName`
@@ -85,6 +89,7 @@ rec {
This is useful if the command requires equals, for example, `-c=5`.
# Examples
:::{.example}
## `lib.cli.toGNUCommandLine` usage example
@@ -111,38 +116,224 @@ rec {
:::
*/
toGNUCommandLine =
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2511)
"lib.cli.toGNUCommandLine is deprecated, please use lib.cli.toCommandLine or lib.cli.toCommandLineShellGNU instead."
(
{
mkOptionName ? k: if builtins.stringLength k == 1 then "-${k}" else "--${k}",
mkBool ? k: v: lib.optional v (mkOptionName k),
mkList ? k: v: lib.concatMap (mkOption k) v,
mkOption ?
k: v:
if v == null then
[ ]
else if optionValueSeparator == null then
[
(mkOptionName k)
(lib.generators.mkValueStringDefault { } v)
]
else
[ "${mkOptionName k}${optionValueSeparator}${lib.generators.mkValueStringDefault { } v}" ],
optionValueSeparator ? null,
}:
options:
let
render =
k: v:
if builtins.isBool v then
mkBool k v
else if builtins.isList v then
mkList k v
else
mkOption k v;
in
builtins.concatLists (lib.mapAttrsToList render options)
);
/**
Converts the given attributes into a single shell-escaped command-line string.
Similar to `toCommandLineGNU`, but returns a single escaped string instead of an array of arguments.
For further reference see: [`lib.cli.toCommandLineGNU`](#function-library-lib.cli.toCommandLineGNU)
*/
toCommandLineShellGNU =
options: attrs: lib.escapeShellArgs (lib.cli.toCommandLineGNU options attrs);
/**
Converts an attribute set into a list of GNU-style command line options.
`toCommandLineGNU` returns a list of string arguments.
# Inputs
`options`
: Options, see below.
`attrs`
: The attributes to transform into arguments.
## Options
`isLong`
: A function that determines whether an option is long or short.
`explicitBool`
: Whether or not boolean option arguments should be formatted explicitly.
`formatArg`
: A function that turns the option argument into a string.
# Examples
:::{.example}
## `lib.cli.toCommandLineGNU` usage example
```nix
lib.cli.toCommandLineGNU {} {
v = true;
verbose = [true true false null];
i = ".bak";
testsuite = ["unit" "integration"];
e = ["s/a/b/" "s/b/c/"];
n = false;
data = builtins.toJSON {id = 0;};
}
=> [
"--data={\"id\":0}"
"-es/a/b/"
"-es/b/c/"
"-i.bak"
"--testsuite=unit"
"--testsuite=integration"
"-v"
"--verbose"
"--verbose"
]
```
:::
*/
toCommandLineGNU =
{
mkOptionName ? k: if builtins.stringLength k == 1 then "-${k}" else "--${k}",
mkBool ? k: v: lib.optional v (mkOptionName k),
mkList ? k: v: lib.concatMap (mkOption k) v,
mkOption ?
k: v:
if v == null then
[ ]
else if optionValueSeparator == null then
[
(mkOptionName k)
(lib.generators.mkValueStringDefault { } v)
]
else
[ "${mkOptionName k}${optionValueSeparator}${lib.generators.mkValueStringDefault { } v}" ],
optionValueSeparator ? null,
isLong ? optionName: builtins.stringLength optionName > 1,
explicitBool ? false,
formatArg ? lib.generators.mkValueStringDefault { },
}:
options:
let
render =
k: v:
if builtins.isBool v then
mkBool k v
else if builtins.isList v then
mkList k v
else
mkOption k v;
optionFormat = optionName: {
option = if isLong optionName then "--${optionName}" else "-${optionName}";
sep = if isLong optionName then "=" else "";
inherit explicitBool formatArg;
};
in
builtins.concatLists (lib.mapAttrsToList render options);
lib.cli.toCommandLine optionFormat;
/**
Converts the given attributes into a single shell-escaped command-line string.
Similar to `toCommandLine`, but returns a single escaped string instead of an array of arguments.
For further reference see: [`lib.cli.toCommandLine`](#function-library-lib.cli.toCommandLine)
*/
toCommandLineShell =
optionFormat: attrs: lib.escapeShellArgs (lib.cli.toCommandLine optionFormat attrs);
/**
Converts an attribute set into a list of command line options.
`toCommandLine` returns a list of string arguments.
# Inputs
`optionFormat`
: The option format that describes how options and their arguments should be formatted.
`attrs`
: The attributes to transform into arguments.
# Examples
:::{.example}
## `lib.cli.toCommandLine` usage example
```nix
let
optionFormat = optionName: {
option = "-${optionName}";
sep = "=";
explicitBool = true;
};
in lib.cli.toCommandLine optionFormat {
v = true;
verbose = [true true false null];
i = ".bak";
testsuite = ["unit" "integration"];
e = ["s/a/b/" "s/b/c/"];
n = false;
data = builtins.toJSON {id = 0;};
}
=> [
"-data={\"id\":0}"
"-e=s/a/b/"
"-e=s/b/c/"
"-i=.bak"
"-n=false"
"-testsuite=unit"
"-testsuite=integration"
"-v=true"
"-verbose=true"
"-verbose=true"
"-verbose=false"
]
```
:::
*/
toCommandLine =
optionFormat: attrs:
let
handlePair =
k: v:
if k == "" then
lib.throw "lib.cli.toCommandLine only accepts non-empty option names."
else if builtins.isList v then
builtins.concatMap (handleOption k) v
else
handleOption k v;
handleOption = k: renderOption (optionFormat k) k;
renderOption =
{
option,
sep,
explicitBool,
formatArg ? lib.generators.mkValueStringDefault { },
}:
k: v:
if v == null || (!explicitBool && v == false) then
[ ]
else if !explicitBool && v == true then
[ option ]
else
let
arg = formatArg v;
in
if sep != null then
[ "${option}${sep}${arg}" ]
else
[
option
arg
];
in
builtins.concatLists (lib.mapAttrsToList handlePair attrs);
}
+165 -11
View File
@@ -22,7 +22,6 @@ let
filterAttrs
optionalString
flip
pathIsDirectory
head
pipe
isDerivation
@@ -305,9 +304,10 @@ rec {
arg:
let
loc = unsafeGetAttrPos arg fargs;
loc' = if loc != null then loc.file + ":" + toString loc.line else "<unknown location>";
in
"Function called without required argument \"${arg}\" at "
+ "${loc.file}:${toString loc.line}${prettySuggestions (getSuggestions arg)}";
+ "${loc'}${prettySuggestions (getSuggestions arg)}";
# Only show the error for the first missing argument
error = errorForArg (head (attrNames missingArgs));
@@ -763,11 +763,26 @@ rec {
# Inputs
`extendMkDerivation`-specific configurations
: `constructDrv`: Base build helper, the `mkDerivation`-like build helper to extend.
: `excludeDrvArgNames`: Argument names not to pass from the input fixed-point arguments to `constructDrv`. Note: It doesn't apply to the updating arguments returned by `extendDrvArgs`.
: `extendDrvArgs` : An extension (overlay) of the argument set, like the one taken by [overrideAttrs](#sec-pkg-overrideAttrs) but applied before passing to `constructDrv`.
: `inheritFunctionArgs`: Whether to inherit `__functionArgs` from the base build helper (default to `true`).
: `transformDrv`: Function to apply to the result derivation (default to `lib.id`).
: `constructDrv` (required)
: Base build helper, the `mkDerivation`-like build helper to extend.
`excludeDrvArgNames` (default to `[ ]`)
: Argument names not to pass from the input fixed-point arguments to `constructDrv`.
It doesn't apply to the updating arguments returned by `extendDrvArgs`.
`excludeFunctionArgNames` (default to `[ ]`)
: `__functionArgs` attribute names to remove from the result build helper.
`excludeFunctionArgNames` is useful for argument deprecation while avoiding ellipses.
`extendDrvArgs` (required)
: An extension (overlay) of the argument set, like the one taken by [overrideAttrs](#sec-pkg-overrideAttrs) but applied before passing to `constructDrv`.
`inheritFunctionArgs` (default to `true`)
: Whether to inherit `__functionArgs` from the base build helper.
Set `inheritFunctionArgs` to `false` when `extendDrvArgs`'s `args` set pattern does not contain an ellipsis.
`transformDrv` (default to `lib.id`)
: Function to apply to the result derivation.
# Type
@@ -776,6 +791,7 @@ rec {
{
constructDrv :: ((FixedPointArgs | AttrSet) -> a)
excludeDrvArgNames :: [ String ],
excludeFunctionArgNames :: [ String ]
extendDrvArgs :: (AttrSet -> AttrSet -> AttrSet)
inheritFunctionArgs :: Bool,
transformDrv :: a -> a,
@@ -836,6 +852,7 @@ rec {
{
constructDrv,
excludeDrvArgNames ? [ ],
excludeFunctionArgNames ? [ ],
extendDrvArgs,
inheritFunctionArgs ? true,
transformDrv ? id,
@@ -850,10 +867,12 @@ rec {
)
# Add __functionArgs
(
# Inherit the __functionArgs from the base build helper
optionalAttrs inheritFunctionArgs (removeAttrs (functionArgs constructDrv) excludeDrvArgNames)
# Recover the __functionArgs from the derived build helper
// functionArgs (extendDrvArgs { })
removeAttrs (
# Inherit the __functionArgs from the base build helper
optionalAttrs inheritFunctionArgs (removeAttrs (functionArgs constructDrv) excludeDrvArgNames)
# Recover the __functionArgs from the derived build helper
// functionArgs (extendDrvArgs { })
) excludeFunctionArgNames
)
// {
inherit
@@ -864,4 +883,139 @@ rec {
transformDrv
;
};
/**
Removes a prefix from the attribute names of a cross index.
A cross index (short for "Cross Platform Pair Index") is a 6-field structure
organizing values by cross-compilation platform relationships.
# Inputs
`prefix`
: The prefix to remove from cross index attribute names
`crossIndex`
: A cross index with prefixed names
# Type
```
renameCrossIndexFrom :: String -> AttrSet -> AttrSet
```
# Examples
:::{.example}
## `lib.customisation.renameCrossIndexFrom` usage example
```nix
renameCrossIndexFrom "pkgs" { pkgsBuildBuild = ...; pkgsBuildHost = ...; ... }
=> { buildBuild = ...; buildHost = ...; ... }
```
:::
*/
renameCrossIndexFrom = prefix: x: {
buildBuild = x."${prefix}BuildBuild";
buildHost = x."${prefix}BuildHost";
buildTarget = x."${prefix}BuildTarget";
hostHost = x."${prefix}HostHost";
hostTarget = x."${prefix}HostTarget";
targetTarget = x."${prefix}TargetTarget";
};
/**
Adds a prefix to the attribute names of a cross index.
A cross index (short for "Cross Platform Pair Index") is a 6-field structure
organizing values by cross-compilation platform relationships.
# Inputs
`prefix`
: The prefix to add to cross index attribute names
`crossIndex`
: A cross index to be prefixed
# Type
```
renameCrossIndexTo :: String -> AttrSet -> AttrSet
```
# Examples
:::{.example}
## `lib.customisation.renameCrossIndexTo` usage example
```nix
renameCrossIndexTo "self" { buildBuild = ...; buildHost = ...; ... }
=> { selfBuildBuild = ...; selfBuildHost = ...; ... }
```
:::
*/
renameCrossIndexTo = prefix: x: {
"${prefix}BuildBuild" = x.buildBuild;
"${prefix}BuildHost" = x.buildHost;
"${prefix}BuildTarget" = x.buildTarget;
"${prefix}HostHost" = x.hostHost;
"${prefix}HostTarget" = x.hostTarget;
"${prefix}TargetTarget" = x.targetTarget;
};
/**
Takes a function and applies it pointwise to each field of a cross index.
A cross index (short for "Cross Platform Pair Index") is a 6-field structure
organizing values by cross-compilation platform relationships.
# Inputs
`f`
: Function to apply to each cross index value
`crossIndex`
: A cross index to transform
# Type
```
mapCrossIndex :: (a -> b) -> AttrSet -> AttrSet
```
# Examples
:::{.example}
## `lib.customisation.mapCrossIndex` usage example
```nix
mapCrossIndex (x: x * 10) { buildBuild = 1; buildHost = 2; ... }
=> { buildBuild = 10; buildHost = 20; ... }
```
```nix
# Extract a package from package sets
mapCrossIndex (pkgs: pkgs.hello) crossIndexedPackageSets
```
:::
*/
mapCrossIndex =
f:
{
buildBuild,
buildHost,
buildTarget,
hostHost,
hostTarget,
targetTarget,
}:
{
buildBuild = f buildBuild;
buildHost = f buildHost;
buildTarget = f buildTarget;
hostHost = f hostHost;
hostTarget = f hostTarget;
targetTarget = f targetTarget;
};
}
+4 -4
View File
@@ -63,7 +63,7 @@ let
customisation = callLibs ./customisation.nix;
derivations = callLibs ./derivations.nix;
maintainers = import ../maintainers/maintainer-list.nix;
teams = callLibs ../maintainers/team-list.nix;
teams = callLibs ../maintainers/computed-team-list.nix;
meta = callLibs ./meta.nix;
versions = callLibs ./versions.nix;
@@ -344,7 +344,6 @@ let
escapeRegex
escapeURL
escapeXML
replaceChars
lowerChars
upperChars
toLower
@@ -377,7 +376,6 @@ let
fixedWidthNumber
toInt
toIntBase10
readPathsFromFile
fileContents
;
inherit (self.stringsWithDeps)
@@ -399,6 +397,9 @@ let
makeScopeWithSplicing
makeScopeWithSplicing'
extendMkDerivation
renameCrossIndexFrom
renameCrossIndexTo
mapCrossIndex
;
inherit (self.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate;
inherit (self.generators) mkLuaInline;
@@ -495,7 +496,6 @@ let
optionAttrSetToDocList'
scrubOptionValue
literalExpression
literalExample
showOption
showOptionWithDefLocs
showFiles
+46
View File
@@ -101,6 +101,7 @@ let
inherit (import ./internal.nix { inherit lib; })
_coerce
_coerceResult
_singleton
_coerceMany
_toSourceFilter
@@ -1005,4 +1006,49 @@ in
{
submodules = recurseSubmodules;
};
/**
The empty fileset. It can be useful as a default value or as starting accumulator for a folding operation.
# Type
```
empty :: FileSet
```
*/
empty = _emptyWithoutBase;
/**
Tests whether a given value is a fileset, or can be used in place of a fileset.
# Inputs
`value`
: The value to test
# Type
```
isFileset :: Any -> Bool
```
# Examples
:::{.example}
## `lib.fileset.isFileset` usage example
```nix
isFileset ./.
=> true
isFileset (unions [ ])
=> true
isFileset 1
=> false
```
:::
*/
isFileset = x: (_coerceResult "" x).success;
}
+35 -12
View File
@@ -165,14 +165,27 @@ rec {
_noEval = throw _noEvalMessage;
};
# Coerce a value to a fileset, erroring when the value cannot be coerced.
# The string gives the context for error messages.
# Type: String -> (fileset | Path) -> fileset
_coerce =
# Coerce a value to a fileset. Return a set containing the attribute `success`
# indicating whether coercing succeeded, and either `value` when `success ==
# true`, or an error `message` when `success == false`. The string gives the
# context for error messages.
#
# Type: String -> (fileset | Path) -> { success :: Bool, value :: fileset } ] -> { success :: Bool, message :: String }
_coerceResult =
let
ok = value: {
success = true;
inherit value;
};
error = message: {
success = false;
inherit message;
};
in
context: value:
if value._type or "" == "fileset" then
if value._internalVersion > _currentVersion then
throw ''
error ''
${context} is a file set created from a future version of the file set library with a different internal representation:
- Internal version of the file set: ${toString value._internalVersion}
- Internal version of the library: ${toString _currentVersion}
@@ -184,27 +197,37 @@ rec {
_currentVersion - value._internalVersion
) migrations;
in
foldl' (value: migration: migration value) value migrationsToApply
ok (foldl' (value: migration: migration value) value migrationsToApply)
else
value
ok value
else if !isPath value then
if value ? _isLibCleanSourceWith then
throw ''
error ''
${context} is a `lib.sources`-based value, but it should be a file set or a path instead.
To convert a `lib.sources`-based value to a file set you can use `lib.fileset.fromSource`.
Note that this only works for sources created from paths.''
else if isStringLike value then
throw ''
error ''
${context} ("${toString value}") is a string-like value, but it should be a file set or a path instead.
Paths represented as strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.''
else
throw ''${context} is of type ${typeOf value}, but it should be a file set or a path instead.''
error ''${context} is of type ${typeOf value}, but it should be a file set or a path instead.''
else if !pathExists value then
throw ''
error ''
${context} (${toString value}) is a path that does not exist.
To create a file set from a path that may not exist, use `lib.fileset.maybeMissing`.''
else
_singleton value;
ok (_singleton value);
# Coerce a value to a fileset, erroring when the value cannot be coerced.
# The string gives the context for error messages.
# Type: String -> (fileset | Path) -> fileset
_coerce =
context: value:
let
result = _coerceResult context value;
in
if result.success then result.value else throw result.message;
# Coerce many values to filesets, erroring when any value cannot be coerced,
# or if the filesystem root of the values doesn't match.
+21 -5
View File
@@ -172,6 +172,11 @@ lib.mapAttrs mkLicense (
fullName = "Baekmuk License";
};
bitstreamCharter = {
spdxId = "Bitstream-Charter";
fullName = "Bitstream Charter Font License";
};
bitstreamVera = {
spdxId = "Bitstream-Vera";
fullName = "Bitstream Vera Font License";
@@ -615,11 +620,6 @@ lib.mapAttrs mkLicense (
fullName = "Unspecified free software license";
};
freeimage = {
spdxId = "FreeImage";
fullName = "FreeImage Public License v1.0";
};
fsl11Mit = {
fullName = "Functional Source License, Version 1.1, MIT Future License";
spdxId = "FSL-1.1-MIT";
@@ -665,6 +665,11 @@ lib.mapAttrs mkLicense (
url = "https://www.gust.org.pl/projects/e-foundry/licenses/GUST-FONT-SOURCE-LICENSE.txt";
};
gnuplot = {
spdxId = "gnuplot";
fullName = "gnuplot License";
};
gpl1Only = {
spdxId = "GPL-1.0-only";
fullName = "GNU General Public License v1.0 only";
@@ -791,6 +796,12 @@ lib.mapAttrs mkLicense (
free = false;
};
intel-eula = {
fullName = "Intel End User License Agreement for Developer Tools";
url = "https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/WordDocuments/intelamtsdklicense.htm";
free = false;
};
interbase = {
spdxId = "Interbase-1.0";
fullName = "Interbase Public License v1.0";
@@ -982,6 +993,11 @@ lib.mapAttrs mkLicense (
fullName = "CMU License";
};
mit-enna = {
spdxId = "MIT-enna";
fullName = "enna License";
};
mit-feh = {
spdxId = "MIT-feh";
fullName = "feh License";
+33 -14
View File
@@ -1126,6 +1126,29 @@ let
__toString = _: showOption loc;
};
# Check that a type with v2 merge has a coherent check attribute.
# Throws an error if the type uses an ad-hoc `type // { check }` override.
# Returns the last argument like `seq`, allowing usage: checkV2MergeCoherence loc type expr
checkV2MergeCoherence =
loc: type: result:
if type.check.isV2MergeCoherent or false then
result
else
throw ''
The option `${showOption loc}' has a type `${type.description}' that uses
an ad-hoc `type // { check = ...; }' override, which is incompatible with
the v2 merge mechanism.
Please use `lib.types.addCheck` instead of `type // { check }' to add
custom validation. For example:
lib.types.addCheck baseType (value: /* your check */)
instead of:
baseType // { check = value: /* your check */; }
'';
# Merge definitions of a value of a given type.
mergeDefinitions = loc: type: defs: rec {
defsFinal' =
@@ -1201,10 +1224,13 @@ let
(
if type.merge ? v2 then
let
r = type.merge.v2 {
inherit loc;
defs = defsFinal;
};
# Check for v2 merge coherence
r = checkV2MergeCoherence loc type (
type.merge.v2 {
inherit loc;
defs = defsFinal;
}
);
in
r
// {
@@ -1468,11 +1494,6 @@ let
mkForce = mkOverride 50;
mkVMOverride = mkOverride 10; # used by nixos-rebuild build-vm
defaultPriority =
warnIf (oldestSupportedReleaseIsAtLeast 2305)
"lib.modules.defaultPriority is deprecated, please use lib.modules.defaultOverridePriority instead."
defaultOverridePriority;
mkFixStrictness = warn "lib.mkFixStrictness has no effect and will be removed. It returns its argument unmodified, so you can just remove any calls." id;
mkOrder = priority: content: {
@@ -1787,11 +1808,10 @@ let
};
/**
Transitional version of mkAliasOptionModule that uses MD docs.
This function is no longer necessary and merely an alias of `mkAliasOptionModule`.
Deprecated alias of mkAliasOptionModule that uses MD docs.
This function is no longer necessary will be removed in 26.05`.
*/
mkAliasOptionModuleMD = mkAliasOptionModule;
mkAliasOptionModuleMD = lib.warn "mkAliasOptionModuleMD is deprecated and will be removed in 26.05; please use mkAliasOptionModule." mkAliasOptionModule;
/**
mkDerivedConfig : Option a -> (a -> Definition b) -> Definition b
@@ -2163,7 +2183,6 @@ private
inherit
defaultOrderPriority
defaultOverridePriority
defaultPriority
doRename
evalModules
evalOptionValue # for use by lib.types
-2
View File
@@ -684,8 +684,6 @@ rec {
inherit text;
};
literalExample = lib.warn "lib.literalExample is deprecated, use lib.literalExpression instead, or use lib.literalMD for a non-Nix description." literalExpression;
/**
For use in the `defaultText` and `example` option attributes. Causes the
given MD text to be inserted verbatim in the documentation, for when
-16
View File
@@ -509,22 +509,6 @@ let
in
{
pathType =
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2305)
"lib.sources.pathType has been moved to lib.filesystem.pathType."
lib.filesystem.pathType;
pathIsDirectory =
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2305)
"lib.sources.pathIsDirectory has been moved to lib.filesystem.pathIsDirectory."
lib.filesystem.pathIsDirectory;
pathIsRegularFile =
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2305)
"lib.sources.pathIsRegularFile has been moved to lib.filesystem.pathIsRegularFile."
lib.filesystem.pathIsRegularFile;
inherit
pathIsGitRepo
commitIdFromGitRepo
-81
View File
@@ -1467,9 +1467,6 @@ rec {
[ "\"" "'" "<" ">" "&" ]
[ "&quot;" "&apos;" "&lt;" "&gt;" "&amp;" ];
# warning added 12-12-2022
replaceChars = lib.warn "lib.replaceChars is a deprecated alias of lib.replaceStrings." builtins.replaceStrings;
# Case conversion utilities.
lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
@@ -2578,30 +2575,6 @@ rec {
in
lib.warnIf (!precise) "Imprecise conversion from float to string ${result}" result;
/**
Check whether a value `val` can be coerced to a string.
:::{.warning}
Soft-deprecated function. While the original implementation is available as
`isConvertibleWithToString`, consider using `isStringLike` instead, if suitable.
:::
# Inputs
`val`
: 1\. Function argument
# Type
```
isCoercibleToString :: a -> bool
```
*/
isCoercibleToString =
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2305)
"lib.strings.isCoercibleToString is deprecated in favor of either isStringLike or isConvertibleWithToString. Only use the latter if it needs to return true for null, numbers, booleans and list of similarly coercibles."
isConvertibleWithToString;
/**
Check whether a list or other value `x` can be passed to toString.
@@ -2842,60 +2815,6 @@ rec {
else
parsedInput;
/**
Read a list of paths from `file`, relative to the `rootPath`.
Lines beginning with `#` are treated as comments and ignored.
Whitespace is significant.
:::{.warning}
This function is deprecated and should be avoided.
:::
:::{.note}
This function is not performant and should be avoided.
:::
# Inputs
`rootPath`
: 1\. Function argument
`file`
: 2\. Function argument
# Type
```
readPathsFromFile :: string -> string -> [string]
```
# Examples
:::{.example}
## `lib.strings.readPathsFromFile` usage example
```nix
readPathsFromFile /prefix
./pkgs/development/libraries/qt-5/5.4/qtbase/series
=> [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch"
"/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch"
"/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch"
"/prefix/nix-profiles-library-paths.patch"
"/prefix/compose-search-path.patch" ]
```
:::
*/
readPathsFromFile = lib.warn "lib.readPathsFromFile is deprecated, use a list instead." (
rootPath: file:
let
lines = lib.splitString "\n" (readFile file);
removeComments = lib.filter (line: line != "" && !(lib.hasPrefix "#" line));
relativePaths = removeComments lines;
absolutePaths = map (path: rootPath + "/${path}") relativePaths;
in
absolutePaths
);
/**
Read the contents of a file removing the trailing \n
+10 -3
View File
@@ -360,8 +360,8 @@ let
null;
# The canonical name for this attribute is darwinSdkVersion, but some
# platforms define the old name "sdkVer".
darwinSdkVersion = final.sdkVer or "11.3";
darwinMinVersion = final.darwinSdkVersion;
darwinSdkVersion = final.sdkVer or "14.4";
darwinMinVersion = "14.0";
darwinMinVersionVariable =
if final.isMacOS then
"MACOSX_DEPLOYMENT_TARGET"
@@ -491,6 +491,13 @@ let
}
.${cpu.name} or cpu.name;
vendor_ = final.rust.platform.vendor;
abi_ =
# We're very explicit about the POWER ELF ABI w/ glibc in our parsing, while Rust is not.
# TODO: Somehow ensure that Rust actually *uses* the correct ABI, and not just a libc-based default.
if (lib.strings.hasPrefix "powerpc" cpu.name) && (lib.strings.hasPrefix "gnuabielfv" abi.name) then
"gnu"
else
abi.name;
in
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
args.rust.rustcTarget or args.rustc.config or (
@@ -501,7 +508,7 @@ let
if final.isWasi then
"${cpu_}-wasip1"
else
"${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"
"${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi_}"}"
);
# The name of the rust target if it is standard, or the json file
-10
View File
@@ -23,7 +23,6 @@ rec {
ppc64-elfv1 = {
config = "powerpc64-unknown-linux-gnuabielfv1";
rust.rustcTarget = "powerpc64-unknown-linux-gnu";
};
ppc64-elfv2 = {
config = "powerpc64-unknown-linux-gnuabielfv2";
@@ -303,15 +302,6 @@ rec {
libc = "newlib";
};
#
# Redox
#
x86_64-unknown-redox = {
config = "x86_64-unknown-redox";
libc = "relibc";
};
#
# Darwin
#
+2 -2
View File
@@ -919,9 +919,9 @@ rec {
else if isLinux parsed || isWindows parsed then
if isAarch32 parsed then
if versionAtLeast (parsed.cpu.version or "0") "6" then abis.gnueabihf else abis.gnueabi
# Default ppc64 BE to ELFv2
# Default ppc64 BE to ELFv1
else if isPower64 parsed && isBigEndian parsed then
abis.gnuabielfv2
abis.gnuabielfv1
else
abis.gnu
else
+158
View File
@@ -3106,6 +3106,86 @@ runTests {
expected = "-X PUT --data '{\"id\":0}' --retry 3 --url https://example.com/foo --url https://example.com/bar --verbose";
};
testToCommandLine = {
expr =
let
optionFormat = optionName: {
option = "-${optionName}";
sep = "=";
explicitBool = true;
};
in
cli.toCommandLine optionFormat {
v = true;
verbose = [
true
true
false
null
];
i = ".bak";
testsuite = [
"unit"
"integration"
];
e = [
"s/a/b/"
"s/b/c/"
];
n = false;
data = builtins.toJSON { id = 0; };
};
expected = [
"-data={\"id\":0}"
"-e=s/a/b/"
"-e=s/b/c/"
"-i=.bak"
"-n=false"
"-testsuite=unit"
"-testsuite=integration"
"-v=true"
"-verbose=true"
"-verbose=true"
"-verbose=false"
];
};
testToCommandLineGNU = {
expr = cli.toCommandLineGNU { } {
v = true;
verbose = [
true
true
false
null
];
i = ".bak";
testsuite = [
"unit"
"integration"
];
e = [
"s/a/b/"
"s/b/c/"
];
n = false;
data = builtins.toJSON { id = 0; };
};
expected = [
"--data={\"id\":0}"
"-es/a/b/"
"-es/b/c/"
"-i.bak"
"--testsuite=unit"
"--testsuite=integration"
"-v"
"--verbose"
"--verbose"
];
};
testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName {
name = "..foo";
expected = "foo";
@@ -4661,4 +4741,82 @@ runTests {
expected = "/non-existent/this/does/not/exist/for/real/please-dont-mess-with-your-local-fs/default.nix";
};
# Tests for cross index utilities
testRenameCrossIndexFrom = {
expr = lib.renameCrossIndexFrom "pkgs" {
pkgsBuildBuild = "dummy-build-build";
pkgsBuildHost = "dummy-build-host";
pkgsBuildTarget = "dummy-build-target";
pkgsHostHost = "dummy-host-host";
pkgsHostTarget = "dummy-host-target";
pkgsTargetTarget = "dummy-target-target";
};
expected = {
buildBuild = "dummy-build-build";
buildHost = "dummy-build-host";
buildTarget = "dummy-build-target";
hostHost = "dummy-host-host";
hostTarget = "dummy-host-target";
targetTarget = "dummy-target-target";
};
};
testRenameCrossIndexTo = {
expr = lib.renameCrossIndexTo "self" {
buildBuild = "dummy-build-build";
buildHost = "dummy-build-host";
buildTarget = "dummy-build-target";
hostHost = "dummy-host-host";
hostTarget = "dummy-host-target";
targetTarget = "dummy-target-target";
};
expected = {
selfBuildBuild = "dummy-build-build";
selfBuildHost = "dummy-build-host";
selfBuildTarget = "dummy-build-target";
selfHostHost = "dummy-host-host";
selfHostTarget = "dummy-host-target";
selfTargetTarget = "dummy-target-target";
};
};
testMapCrossIndex = {
expr = lib.mapCrossIndex (x: x * 10) {
buildBuild = 1;
buildHost = 2;
buildTarget = 3;
hostHost = 4;
hostTarget = 5;
targetTarget = 6;
};
expected = {
buildBuild = 10;
buildHost = 20;
buildTarget = 30;
hostHost = 40;
hostTarget = 50;
targetTarget = 60;
};
};
testMapCrossIndexString = {
expr = lib.mapCrossIndex (x: "prefix-${x}") {
buildBuild = "bb";
buildHost = "bh";
buildTarget = "bt";
hostHost = "hh";
hostTarget = "ht";
targetTarget = "tt";
};
expected = {
buildBuild = "prefix-bb";
buildHost = "prefix-bh";
buildTarget = "prefix-bt";
hostHost = "prefix-hh";
hostTarget = "prefix-ht";
targetTarget = "prefix-tt";
};
};
}
+39
View File
@@ -223,6 +223,26 @@ checkConfigError 'A definition for option .* is not of type .path in the Nix sto
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: ".*/store/.links"' config.pathInStore.bad4 ./types.nix
checkConfigError 'A definition for option .* is not of type .path in the Nix store.. Definition values:\n\s*- In .*: "/foo/bar"' config.pathInStore.bad5 ./types.nix
# types.externalPath
checkConfigOutput '".*/foo/bar"' config.externalPath.ok1 ./types.nix
checkConfigOutput '".*/"' config.externalPath.ok2 ./types.nix
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad1 ./types.nix
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad2 ./types.nix
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad3 ./types.nix
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad4 ./types.nix
checkConfigError 'A definition for option .* is not of type .absolute path not in the Nix store.' config.externalPath.bad5 ./types.nix
# types.fileset
checkConfigOutput '^0$' config.filesetCardinal.ok1 ./fileset.nix
checkConfigOutput '^1$' config.filesetCardinal.ok2 ./fileset.nix
checkConfigOutput '^1$' config.filesetCardinal.ok3 ./fileset.nix
checkConfigOutput '^1$' config.filesetCardinal.ok4 ./fileset.nix
checkConfigOutput '^0$' config.filesetCardinal.ok5 ./fileset.nix
checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err1 ./fileset.nix
checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err2 ./fileset.nix
checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err3 ./fileset.nix
checkConfigError 'A definition for option .* is not of type .fileset.. Definition values:\n.*' config.filesetCardinal.err4 ./fileset.nix
# Check boolean option.
checkConfigOutput '^false$' config.enable ./declare-enable.nix
checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*: true' config.enable ./define-enable.nix
@@ -806,6 +826,25 @@ checkConfigError 'A definition for option .* is not of type .signed integer.*' c
checkConfigOutput '^true$' config.v2checkedPass ./add-check.nix
checkConfigError 'A definition for option .* is not of type .attribute set of signed integer.*' config.v2checkedFail ./add-check.nix
# v2 merge check coherence
# Tests checkV2MergeCoherence call in modules.nix (mergeDefinitions for lazyAttrsOf)
checkConfigError 'ad-hoc.*override.*incompatible' config.adhocFail.foo ./v2-check-coherence.nix
# Tests checkV2MergeCoherence call in modules.nix (mergeDefinitions for lazyAttrsOf)
checkConfigError 'ad-hoc.*override.*incompatible' config.adhocOuterFail.bar ./v2-check-coherence.nix
# Tests checkV2MergeCoherence call in types.nix (either t1)
checkConfigError 'ad-hoc.*override.*incompatible' config.eitherLeftFail ./v2-check-coherence.nix
# Tests checkV2MergeCoherence call in types.nix (either t2)
checkConfigError 'ad-hoc.*override.*incompatible' config.eitherRightFail ./v2-check-coherence.nix
# Tests checkV2MergeCoherence call in types.nix (coercedTo coercedType)
checkConfigError 'ad-hoc.*override.*incompatible' config.coercedFromFail.bar ./v2-check-coherence.nix
# Tests checkV2MergeCoherence call in types.nix (coercedTo finalType)
checkConfigError 'ad-hoc.*override.*incompatible' config.coercedToFail.foo ./v2-check-coherence.nix
# Tests checkV2MergeCoherence call in types.nix (addCheck elemType)
checkConfigError 'ad-hoc.*override.*incompatible' config.addCheckNested.foo ./v2-check-coherence.nix
checkConfigError 'Please use.*lib.types.addCheck.*instead' config.adhocFail.foo ./v2-check-coherence.nix
checkConfigError 'A definition for option .* is not of type .*' config.addCheckFail.bar.baz ./v2-check-coherence.nix
checkConfigOutput '^true$' config.result ./v2-check-coherence.nix
cat <<EOF
====== module tests ======
+50
View File
@@ -0,0 +1,50 @@
{ config, lib, ... }:
let
inherit (lib)
mkOption
mkIf
types
mapAttrs
length
;
inherit (lib.fileset)
empty
unions
toList
;
in
{
options = {
fileset = mkOption { type = with types; lazyAttrsOf fileset; };
## The following option is only here as a proxy to test `fileset` that does
## not work so well with `modules.sh` because it is not JSONable. It exposes
## the number of elements in the fileset.
filesetCardinal = mkOption { default = mapAttrs (_: fs: length (toList fs)) config.fileset; };
};
config = {
fileset.ok1 = empty;
fileset.ok2 = ./fileset;
fileset.ok3 = unions [
empty
./fileset
];
# fileset.ok4: see imports below
fileset.ok5 = mkIf false ./fileset;
fileset.err1 = 1;
fileset.err2 = "foo";
fileset.err3 = "./.";
fileset.err4 = [ empty ];
};
imports = [
{ fileset.ok4 = ./fileset; }
{ fileset.ok4 = empty; }
{ fileset.ok4 = ./fileset; }
];
}
+1
View File
@@ -0,0 +1 @@
Do not remove. This file is used by the tests in `../fileset.nix`.
+9 -1
View File
@@ -15,6 +15,7 @@ in
{
options = {
pathInStore = mkOption { type = types.lazyAttrsOf types.pathInStore; };
externalPath = mkOption { type = types.lazyAttrsOf types.externalPath; };
assertions = mkOption { };
};
config = {
@@ -26,6 +27,13 @@ in
pathInStore.bad3 = "${storeDir}/";
pathInStore.bad4 = "${storeDir}/.links"; # technically true, but not reasonable
pathInStore.bad5 = "/foo/bar";
externalPath.bad1 = "${storeDir}/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv";
externalPath.bad2 = "${storeDir}/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15";
externalPath.bad3 = "${storeDir}/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15/bin/bash";
externalPath.bad4 = "";
externalPath.bad5 = "./foo/bar";
externalPath.ok1 = "/foo/bar";
externalPath.ok2 = "/";
assertions =
with lib.types;
@@ -380,7 +388,7 @@ in
assert (pathWith { absolute = null; }).description == "path";
assert (pathWith { inStore = false; }).description == "path not in the Nix store";
assert (pathWith { inStore = null; }).description == "path";
assert (separatedString "").description == "Concatenated string";
assert (separatedString "").description == "strings concatenated with \"\"";
assert (separatedString ",").description == "strings concatenated with \",\"";
assert (separatedString "\n").description == ''strings concatenated with "\n"'';
assert (separatedString ":").description == "strings concatenated with \":\"";
+117
View File
@@ -0,0 +1,117 @@
# Tests for v2 merge check coherence
{ lib, ... }:
let
inherit (lib) types mkOption;
# The problematic pattern: overriding check with //
# This inner type should reject everything (check always returns false)
adhocOverrideType = (types.lazyAttrsOf types.raw) // {
check = _: false;
};
# Using addCheck is the correct way to add custom checks
properlyCheckedType = types.addCheck (types.lazyAttrsOf types.raw) (v: v ? foo);
# Using addCheck with a check that will fail
failingCheckedType = types.addCheck (types.lazyAttrsOf types.raw) (v: v ? foo);
# Ad-hoc override on outer type
adhocOuterType = types.lazyAttrsOf types.int // {
check = _: false;
};
# Ad-hoc override on left side of either
adhocEitherLeft = types.lazyAttrsOf types.raw // {
check = _: false;
};
# Ad-hoc override on coercedType in coercedTo
adhocCoercedFrom = types.lazyAttrsOf types.raw // {
check = _: false;
};
# Ad-hoc override on finalType in coercedTo
adhocCoercedTo = types.lazyAttrsOf types.raw // {
check = _: false;
};
# Ad-hoc override wrapped in addCheck
adhocAddCheck = types.addCheck (types.lazyAttrsOf types.raw // { check = _: false; }) (v: true);
in
{
# Test 1: Ad-hoc check override in nested type should be detected
options.adhocFail = mkOption {
type = types.lazyAttrsOf adhocOverrideType;
default = { };
};
config.adhocFail = {
foo = { };
};
# Test 1b: Ad-hoc check override in outer type should be detected
options.adhocOuterFail = mkOption {
type = adhocOuterType;
default = { };
};
config.adhocOuterFail.bar = 42;
# Test 1c: Ad-hoc check override on left side of either type
options.eitherLeftFail = mkOption {
type = types.either adhocEitherLeft types.int;
};
config.eitherLeftFail.foo = { };
# Test 1d: Ad-hoc check override on right side of either type
options.eitherRightFail = mkOption {
type = types.either types.int (types.lazyAttrsOf types.raw // { check = _: false; });
};
config.eitherRightFail.foo = { };
# Test 1e: Ad-hoc check override on coercedType in coercedTo
options.coercedFromFail = mkOption {
type = types.coercedTo adhocCoercedFrom (x: { bar = 1; }) (types.lazyAttrsOf types.int);
};
config.coercedFromFail = {
foo = { };
};
# Test 1f: Ad-hoc check override on finalType in coercedTo
options.coercedToFail = mkOption {
type = types.coercedTo types.str (x: { }) adhocCoercedTo;
};
config.coercedToFail.foo = { };
# Test 1g: Ad-hoc check override wrapped in addCheck
options.addCheckNested = mkOption {
type = adhocAddCheck;
};
config.addCheckNested.foo = { };
# Test 2: Using addCheck should work correctly
options.addCheckPass = mkOption {
type = types.lazyAttrsOf properlyCheckedType;
default = { };
};
config.addCheckPass.bar.foo = "value";
# Test 3: addCheck should validate values properly
options.addCheckFail = mkOption {
type = types.lazyAttrsOf failingCheckedType;
default = { };
};
config.addCheckFail.bar.baz = "value"; # Missing required 'foo' attribute
# Test 4: Normal v2 types should work without coherence errors
options.normalPass = mkOption {
type = types.lazyAttrsOf (types.attrsOf types.int);
default = { };
};
config.normalPass.foo.bar = 42;
# Success assertion - only checks things that should succeed
options.result = mkOption {
type = types.bool;
default = false;
};
config.result = true;
}
+9 -1
View File
@@ -24,13 +24,21 @@ let
default = false;
};
members = lib.mkOption {
type = types.listOf (types.submodule (import ./maintainer-module.nix { inherit lib; }));
type = types.listOf (types.submodule ./maintainer-module.nix);
default = [ ];
};
github = lib.mkOption {
type = types.str;
default = "";
};
githubId = lib.mkOption {
type = types.int;
default = 0;
};
githubMaintainers = lib.mkOption {
type = types.listOf (types.submodule ./maintainer-module.nix);
default = [ ];
};
};
};
+87 -36
View File
@@ -66,6 +66,11 @@ let
fixupOptionType
mergeOptionDecls
;
inherit (lib.fileset)
isFileset
unions
empty
;
inAttrPosSuffix =
v: name:
@@ -106,6 +111,29 @@ let
in
if invalidDefs != [ ] then { message = "Definition values: ${showDefs invalidDefs}"; } else null;
# Check that a type with v2 merge has a coherent check attribute.
# Throws an error if the type uses an ad-hoc `type // { check }` override.
# Returns the last argument like `seq`, allowing usage: checkV2MergeCoherence loc type expr
checkV2MergeCoherence =
loc: type: result:
if type.check.isV2MergeCoherent or false then
result
else
throw ''
The option `${showOption loc}' has a type `${type.description}' that uses
an ad-hoc `type // { check = ...; }' override, which is incompatible with
the v2 merge mechanism.
Please use `lib.types.addCheck` instead of `type // { check }' to add
custom validation. For example:
lib.types.addCheck baseType (value: /* your check */)
instead of:
baseType // { check = value: /* your check */; }
'';
outer_types = rec {
isType = type: x: (x._type or "") == type;
@@ -559,11 +587,7 @@ let
sep:
mkOptionType rec {
name = "separatedString";
description =
if sep == "" then
"Concatenated string" # for types.string.
else
"strings concatenated with ${builtins.toJSON sep}";
description = "strings concatenated with ${builtins.toJSON sep}";
descriptionClass = "noun";
check = isString;
merge = loc: defs: concatStringsSep sep (getValues defs);
@@ -578,19 +602,6 @@ let
commas = separatedString ",";
envVar = separatedString ":";
# Deprecated; should not be used because it quietly concatenates
# strings, which is usually not what you want.
# We use a lib.warn because `deprecationMessage` doesn't trigger in nested types such as `attrsOf string`
string =
lib.warn
"The type `types.string` is deprecated. See https://github.com/NixOS/nixpkgs/pull/66346 for better alternative types."
(
separatedString ""
// {
name = "string";
}
);
passwdEntry =
entryType:
addCheck entryType (str: !(hasInfix ":" str || hasInfix "\n" str))
@@ -612,6 +623,15 @@ let
};
};
fileset = mkOptionType {
name = "fileset";
description = "fileset";
descriptionClass = "noun";
check = isFileset;
merge = loc: defs: unions (map (x: x.value) defs);
emptyValue.value = empty;
};
# A package is a top-level store path (/nix/store/hash-name). This includes:
# - derivations
# - more generally, attribute sets with an `outPath` or `__toString` attribute
@@ -656,6 +676,11 @@ let
inStore = true;
};
externalPath = pathWith {
absolute = true;
inStore = false;
};
pathWith =
{
inStore ? null,
@@ -711,7 +736,10 @@ let
optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType
}";
descriptionClass = "composite";
check = isList;
check = {
__functor = _self: isList;
isV2MergeCoherent = true;
};
merge = {
__functor =
self: loc: defs:
@@ -824,7 +852,10 @@ let
(if lazy then "lazy attribute set" else "attribute set")
+ " of ${optionDescriptionPhrase (class: class == "noun" || class == "composite") elemType}";
descriptionClass = "composite";
check = isAttrs;
check = {
__functor = _self: isAttrs;
isV2MergeCoherent = true;
};
merge = {
__functor =
self: loc: defs:
@@ -1236,7 +1267,10 @@ let
name = "submodule";
check = x: isAttrs x || isFunction x || path.check x;
check = {
__functor = _self: x: isAttrs x || isFunction x || path.check x;
isV2MergeCoherent = true;
};
in
mkOptionType {
inherit name;
@@ -1420,7 +1454,10 @@ let
) t2
}";
descriptionClass = "conjunction";
check = x: t1.check x || t2.check x;
check = {
__functor = _self: x: t1.check x || t2.check x;
isV2MergeCoherent = true;
};
merge = {
__functor =
self: loc: defs:
@@ -1430,7 +1467,7 @@ let
let
t1CheckedAndMerged =
if t1.merge ? v2 then
t1.merge.v2 { inherit loc defs; }
checkV2MergeCoherence loc t1 (t1.merge.v2 { inherit loc defs; })
else
{
value = t1.merge loc defs;
@@ -1439,7 +1476,7 @@ let
};
t2CheckedAndMerged =
if t2.merge ? v2 then
t2.merge.v2 { inherit loc defs; }
checkV2MergeCoherence loc t2 (t2.merge.v2 { inherit loc defs; })
else
{
value = t2.merge loc defs;
@@ -1464,7 +1501,7 @@ let
One or more definitions did not pass the type-check of the 'either' type.
${headError.message}
If `either`, `oneOf` or similar is used in freeformType, ensure that it is preceded by an 'attrsOf' such as: `freeformType = types.attrsOf (types.either t1 t2)`.
Otherwise consider using the correct type for the option `${showOption loc}`. This will be an error in Nixpkgs 26.06.
Otherwise consider using the correct type for the option `${showOption loc}`. This will be an error in Nixpkgs 26.05.
'' (mergeOneOption loc defs);
};
in
@@ -1509,7 +1546,10 @@ let
description = "${optionDescriptionPhrase (class: class == "noun") finalType} or ${
optionDescriptionPhrase (class: class == "noun") coercedType
} convertible to it";
check = x: (coercedType.check x && finalType.check (coerceFunc x)) || finalType.check x;
check = {
__functor = _self: x: (coercedType.check x && finalType.check (coerceFunc x)) || finalType.check x;
isV2MergeCoherent = true;
};
merge = {
__functor =
self: loc: defs:
@@ -1524,10 +1564,16 @@ let
// {
value =
let
merged = coercedType.merge.v2 {
inherit loc;
defs = [ def ];
};
merged =
if coercedType.merge ? v2 then
checkV2MergeCoherence loc coercedType (
coercedType.merge.v2 {
inherit loc;
defs = [ def ];
}
)
else
null;
in
if coercedType.merge ? v2 then
if merged.headError == null then coerceFunc def.value else def.value
@@ -1540,10 +1586,12 @@ let
);
in
if finalType.merge ? v2 then
finalType.merge.v2 {
inherit loc;
defs = finalDefs;
}
checkV2MergeCoherence loc finalType (
finalType.merge.v2 {
inherit loc;
defs = finalDefs;
}
)
else
{
value = finalType.merge loc finalDefs;
@@ -1576,7 +1624,10 @@ let
if elemType.merge ? v2 then
elemType
// {
check = x: elemType.check x && check x;
check = {
__functor = _self: x: elemType.check x && check x;
isV2MergeCoherent = true;
};
merge = {
__functor =
self: loc: defs:
@@ -1584,7 +1635,7 @@ let
v2 =
{ loc, defs }:
let
orig = elemType.merge.v2 { inherit loc defs; };
orig = checkV2MergeCoherence loc elemType (elemType.merge.v2 { inherit loc defs; });
headError' = if orig.headError != null then orig.headError else checkDefsForError check loc defs;
in
orig
+6 -2
View File
@@ -32,7 +32,7 @@ Commit access to the Nixpkgs repository is not required for that.
In order to do so, add yourself to the [`maintainer-list.nix`](./maintainer-list.nix), and then to the desired package's `meta.maintainers` list, and send a PR with the changes.
If you're adding yourself as a maintainer as part of another PR (in which you become a maintainer of a package, for example), make your change to
`maintainer-list.nix` in a separate commit titled `maintainers: add <name>`.
`maintainer-list.nix` in a separate commit titled `maintainers: add <handle>`.
### Losing maintainer status
@@ -158,13 +158,17 @@ If the team lists no specific membership policy, feel free to merge changes to t
> [!IMPORTANT]
> If a team says it is a closed group, do not merge additions to the team without an approval by at least one existing member.
A corresponding GitHub team can be created by any org member.
#### Synced GitHub teams
As an alternative to tracking team members in `team-list.nix`, a corresponding GitHub team can be created by any org member and its members subsequently managed directly on GitHub.
When creating the team it should be created with the `nixpkgs-maintainers` team as parent.
Once approved, the team will have the right privileges to be pinged and requested for review in Nixpkgs.
> [!TIP]
> The team name should be as short as possible; because it is nested under the maintainers group, no -maintainers suffix is needed.
After the first [weekly team sync](../.github/workflows/team-sync.yml) with the new team, it's then also possible to link it to the entry in `team-list.nix` by setting its `github` field to the GitHub team name.
# Maintainer scripts
Various utility scripts, which are mainly useful for nixpkgs maintainers, are available under `./scripts/`.
+59
View File
@@ -0,0 +1,59 @@
# Extends ./team-list.nix with synced GitHub information from ./github-teams.json
{ lib }:
let
githubTeams = lib.importJSON ./github-teams.json;
teams = import ./team-list.nix { inherit lib; };
# A fast maintainer id lookup table
maintainersById = lib.pipe lib.maintainers [
lib.attrValues
(lib.groupBy (attrs: toString attrs.githubId))
(lib.mapAttrs (_: lib.head))
];
maintainerSetToList =
githubTeam:
lib.mapAttrsToList (
login: id:
maintainersById.${toString id}
or (throw "lib.teams: No maintainer entry for GitHub user @${login} who's part of the @NixOS/${githubTeam} team")
);
in
# TODO: Consider automatically exposing all GitHub teams under `lib.teams`,
# so that no manual PRs are necessary to add such teams anymore
lib.mapAttrs (
name: attrs:
if attrs ? github then
let
githubTeam =
githubTeams.${attrs.github}
or (throw "lib.teams.${name}: Corresponding GitHub team ${attrs.github} not known yet, make sure to create it and wait for the regular team sync");
in
# TODO: Consider specifying `githubId` in team-list.nix and inferring `github` from it (or even dropping it)
# This would make renames easier because no additional place needs to keep track of them.
# Though in a future where all Nixpkgs GitHub teams are automatically exposed under `lib.teams`,
# this would also not be an issue anymore.
assert lib.assertMsg (!attrs ? githubId)
"lib.teams.${name}: Both `githubId` and `github` is set, but the former is synced from the latter";
assert lib.assertMsg (!attrs ? shortName)
"lib.teams.${name}: Both `shortName` and `github` is set, but the former is synced from the latter";
assert lib.assertMsg (
!attrs ? scope
) "lib.teams.${name}: Both `scope` and `github` is set, but the former is synced from the latter";
assert lib.assertMsg (
!attrs ? members
) "lib.teams.${name}: Both `members` and `github` is set, but the former is synced from the latter";
attrs
// {
githubId = githubTeam.id;
shortName = githubTeam.name;
scope = githubTeam.description;
members =
maintainerSetToList attrs.github githubTeam.maintainers
++ maintainerSetToList attrs.github githubTeam.members;
githubMaintainers = maintainerSetToList attrs.github githubTeam.maintainers;
}
else
attrs
) teams
+985
View File
@@ -0,0 +1,985 @@
{
"acme": {
"description": "Maintain ACME-related packages and modules.",
"id": 3806126,
"maintainers": {},
"members": {
"arianvp": 628387,
"emilazy": 18535642,
"m1cr0man": 3044438
},
"name": "ACME"
},
"agda": {
"description": "Maintain Agda-related packages and modules",
"id": 14416551,
"maintainers": {
"alexarice": 17208985,
"ncfavier": 4323933,
"turion": 303489
},
"members": {
"phijor": 10487782
},
"name": "agda"
},
"android": {
"description": "Maintain Android-related tooling in nixpkgs",
"id": 11821955,
"maintainers": {
"RossComputerGuy": 19699320,
"numinit": 369111
},
"members": {
"JohnRTitor": 50095635,
"adrian-gierakowski": 330177,
"hadilq": 5190539
},
"name": "Android"
},
"bazel": {
"description": "Maintenance of https://bazel.build/ and related packages",
"id": 5468470,
"maintainers": {
"Profpatsch": 3153638
},
"members": {
"aherrmann": 732652,
"avdv": 3471749,
"ethercrow": 222467,
"groodt": 343415,
"kalbasit": 87115,
"mboes": 51356,
"uri-canva": 33242106
},
"name": "Bazel"
},
"beam": {
"description": "Maintain BEAM-related packages and modules.",
"id": 4502493,
"maintainers": {
"happysalada": 5317234
},
"members": {
"Br1ght0ne": 12615679,
"DianaOlympos": 15774340,
"adamcstephens": 2071575,
"ankhers": 750786,
"gleber": 33185,
"minijackson": 1200507,
"savtrip": 42227195,
"yurrriq": 1866448
},
"name": "Beam"
},
"bootstrapping": {
"description": "coordinates efforts towards bootstrappable builds (see https://bootstrappable.org/)",
"id": 9141350,
"maintainers": {
"zeuner": 2545850
},
"members": {
"philiptaron": 43863
},
"name": "bootstrapping"
},
"categorization": {
"description": "Maintain the categorization system in Nixpkgs, per RFC 0146. This team has authority over all categorization issues in Nixpkgs. Matrix: https://matrix.to/#/%23nixpkgs-categorization:matrix.org",
"id": 11723948,
"maintainers": {
"7c6f434c": 1891350,
"Aleksanaa": 42209822,
"GetPsyched": 43472218,
"Lyndeno": 13490857,
"SigmaSquadron": 174749595,
"fgaz": 8182846,
"natsukium": 25083790,
"philiptaron": 43863,
"pyrotelekinetic": 29682759,
"tomodachi94": 68489118
},
"members": {},
"name": "Categorization"
},
"channel-updaters": {
"description": "Users who can update the Nixpkgs/NixOS channels",
"id": 3475569,
"maintainers": {},
"members": {
"nixos-channel-bot": 56695723
},
"name": "channel-updaters"
},
"coq": {
"description": "Coq proof assistant and package maintainers",
"id": 7617261,
"maintainers": {
"Alizter": 8614547,
"Zimmi48": 1108325,
"vbgl": 2612464
},
"members": {},
"name": "Coq"
},
"cosmic": {
"description": "Maintain the COSMIC DE and related packages.",
"id": 12734494,
"maintainers": {
"alyssais": 2768870,
"nyabinary": 97130632
},
"members": {
"HeitorAugustoLN": 44377258,
"Pandapip1": 45835846,
"a-kenji": 65275785,
"ahoneybun": 4884946,
"drakon64": 6444703,
"michaelBelsanti": 62124625,
"thefossguy": 44400303
},
"name": "COSMIC"
},
"crystal-lang": {
"description": "The Crystal Lang eco-system",
"id": 3806663,
"maintainers": {
"peterhoeg": 722550
},
"members": {
"Br1ght0ne": 12615679,
"manveru": 3507
},
"name": "crystal-lang"
},
"cuda-maintainers": {
"description": "Maintain CUDA-enabled packages",
"id": 5704451,
"maintainers": {
"ConnorBaker": 3880346,
"SomeoneSerge": 9720532,
"samuela": 226872
},
"members": {
"GaetanLepage": 33058747,
"prusnak": 42201
},
"name": "cuda-maintainers"
},
"darwin-core": {
"description": "Maintain core platform support and packages for macOS and other Apple platforms",
"id": 11265438,
"maintainers": {
"emilazy": 18535642
},
"members": {
"reckenrode": 7413633,
"toonn": 1486805
},
"name": "darwin-core"
},
"darwin-maintainers": {
"description": "Improve Darwin-support across Nixpkgs and help maintainers without access to Darwin hardware. Apply to join through https://github.com/NixOS/nixpkgs/issues/323144 to keep the process transparent.",
"id": 2385202,
"maintainers": {
"toonn": 1486805
},
"members": {
"0xMRTT": 105598867,
"21CSM": 81891917,
"Aleksanaa": 42209822,
"CorbanR": 1918683,
"Emin017": 99674037,
"Et7f3": 29592775,
"FlameFlag": 57304299,
"Guanran928": 68757440,
"LnL7": 689294,
"Mastermindaxe": 33257997,
"Moraxyc": 69713071,
"Prince213": 25235514,
"Samasaur1": 30577766,
"Sciencentistguy": 4983935,
"Steven0351": 24440751,
"afh": 16507,
"azuwis": 9315,
"booxter": 90200,
"carlossless": 498906,
"cideM": 4246921,
"cidkidnix": 67574902,
"copumpkin": 2623,
"devusb": 4951663,
"domenkozar": 126339,
"donn": 12652988,
"dwt": 57199,
"emilazy": 18535642,
"ethancedwards8": 60861925,
"fiddlerwoaroof": 808745,
"frogamic": 10263813,
"gador": 1883533,
"griff": 4368,
"groodt": 343415,
"gshpychka": 23005347,
"hexagonal-sun": 222664,
"heywoodlh": 18178614,
"iedame": 60272,
"isabelroses": 71222764,
"jonhermansen": 660911,
"juliusrickert": 5007494,
"jwiegley": 8460,
"kashw2": 15855440,
"kevingriffin": 209729,
"khaneliman": 1778670,
"kloenk": 12898828,
"konradmalik": 13033392,
"kubukoz": 894884,
"lutzmor": 9321736,
"magistau": 43097806,
"maljub01": 350635,
"martinjlowm": 110860,
"matteo-pacini": 3139724,
"matthewbauer": 19036,
"mexisme": 329349,
"mikroskeem": 3490861,
"mirkolenz": 5160954,
"mstone": 412508,
"n8henrie": 1234956,
"niklaskorz": 590517,
"ofalvai": 1694986,
"pasqui23": 6931743,
"prusnak": 42201,
"pupbrained": 33522919,
"reckenrode": 7413633,
"ryand56": 22267679,
"samrose": 115821,
"sarahec": 11277967,
"shaunsingh": 71196912,
"sikmir": 688044,
"siraben": 8219659,
"siriobalmelli": 23038812,
"starcraft66": 1858154,
"stepbrobd": 81826728,
"stephank": 89950,
"stephenstubbs": 18033664,
"t-monaghan": 62273348,
"thefloweringash": 42933,
"tricktron": 16036882,
"uncenter": 47499684,
"uri-canva": 33242106,
"usertam": 22500027,
"veprbl": 245573,
"viraptor": 188063,
"wegank": 9713184,
"willcohen": 5185341,
"willjr": 5904828,
"wldhx": 15619766,
"yeongsheng-tan": 809030,
"youhaveme9": 58213083,
"yzx9": 41458459,
"zachcoyle": 908716
},
"name": "darwin-maintainers"
},
"documentation-team": {
"description": "Maintain nixpkgs/NixOS documentation and tools for building it. https://nixos.org/community/teams/documentation",
"id": 6590023,
"maintainers": {
"alejandrosame": 1078000
},
"members": {
"hsjobeki": 50398876,
"wamirez": 24505474
},
"name": "Documentation Team"
},
"dotnet": {
"description": "Maintain the .NET ecosystem in nixpkgs",
"id": 8173604,
"maintainers": {
"corngood": 3077118,
"mdarocha": 11572618
},
"members": {
"GGG-KILLER": 5892127,
"IvarWithoutBones": 41924494,
"JamieMagee": 1358764,
"anpin": 6060545,
"meenzen": 22305878,
"raphaelr": 121178
},
"name": "Dotnet"
},
"emacs": {
"description": "",
"id": 2516946,
"maintainers": {
"jian-lin": 75130626,
"matthewbauer": 19036
},
"members": {
"AndersonTorres": 5954806,
"adisbladis": 63286,
"panchoh": 471059,
"ttuegel": 563054
},
"name": "emacs"
},
"enlightenment": {
"description": "Maintain Enlightenment desktop environment and related packages",
"id": 5937665,
"maintainers": {
"romildo": 1217934
},
"members": {},
"name": "Enlightenment"
},
"exotic-platform-maintainers": {
"description": "",
"id": 3727378,
"maintainers": {
"Ericson2314": 1055245,
"matthewbauer": 19036
},
"members": {
"aaronjanse": 16829510,
"alyssais": 2768870,
"emilytrau": 13267947,
"minijackson": 1200507,
"r-burns": 52847440,
"siraben": 8219659,
"sternenseemann": 3154475
},
"name": "exotic-platform-maintainers"
},
"flutter": {
"description": "Maintain Flutter and Dart-related packages and build tools",
"id": 7718198,
"maintainers": {
"RossComputerGuy": 19699320
},
"members": {
"FlafyDev": 44374434,
"hacker1024": 20849728,
"mkg20001": 7735145
},
"name": "Flutter"
},
"freedesktop": {
"description": "Maintain Freedesktop.org packages for graphical desktop.",
"id": 3806136,
"maintainers": {},
"members": {
"jtojnar": 705123
},
"name": "Freedesktop"
},
"geospatial": {
"description": "Maintain geospatial, remote sensing and OpenStreetMap software",
"id": 7084621,
"maintainers": {
"imincik": 476346
},
"members": {
"autra": 1576598,
"l0b0": 168301,
"nh2": 399535,
"nialov": 47318483,
"sikmir": 688044,
"willcohen": 5185341
},
"name": "Geospatial"
},
"gitlab": {
"description": "Maintain GitLab-related packages and modules",
"id": 8157777,
"maintainers": {
"leona-ya": 11006031,
"xanderio": 6298052,
"yayayayaka": 73759599
},
"members": {
"talyz": 63433
},
"name": "GitLab"
},
"gnome": {
"description": "Maintain GNOME desktop environment and platform.",
"id": 3806133,
"maintainers": {},
"members": {
"bobby285271": 20080233,
"dasj19": 7589338,
"hedning": 71978,
"jtojnar": 705123
},
"name": "GNOME"
},
"golang": {
"description": "Maintain Golang compilers",
"id": 4020424,
"maintainers": {
"Mic92": 96200,
"kalbasit": 87115,
"zowoq": 59103226
},
"members": {
"katexochen": 49727155,
"mfrw": 4929861,
"qbit": 68368
},
"name": "golang"
},
"haskell": {
"description": "Maintain Haskell packages and infrastructure",
"id": 4769723,
"maintainers": {
"cdepillabout": 64804,
"maralorn": 1651325,
"sternenseemann": 3154475
},
"members": {
"wolfgangwalther": 9132420
},
"name": "Haskell"
},
"hyprland": {
"description": "Maintain Hyprland compositor and ecosystem.",
"id": 12048885,
"maintainers": {
"khaneliman": 1778670
},
"members": {
"JohnRTitor": 50095635,
"NotAShelf": 62766066,
"donovanglover": 2374245,
"fufexan": 36706276
},
"name": "hyprland"
},
"java": {
"description": "Maintainers of the Nixpkgs Java ecosystem (JDK, JVM, Java, Gradle, Maven, Ant, and adjacent projects); frequently lingering in #jvm:nixos.org on Matrix",
"id": 11412419,
"maintainers": {
"FliegendeWurst": 12560461,
"Infinidoge": 22727114,
"chayleaf": 9590981,
"tomodachi94": 68489118
},
"members": {
"msgilligan": 61612
},
"name": "Java"
},
"k3s": {
"description": "Maintain K3s package, NixOS module, NixOS tests, update script",
"id": 7725877,
"maintainers": {
"Mic92": 96200,
"euank": 2147649,
"marcusramberg": 5526,
"wrmilling": 6162814
},
"members": {
"frederictobiasc": 26125115,
"heywoodlh": 18178614,
"rorosen": 76747196
},
"name": "k3s"
},
"kodi": {
"description": "Maintain Kodi related packages.",
"id": 4642467,
"maintainers": {
"aanderse": 7755101
},
"members": {
"cpages": 411324,
"edwtjo": 54799,
"minijackson": 1200507,
"peterhoeg": 722550,
"sephalon": 893474
},
"name": "kodi"
},
"kubernetes": {
"description": "Maintain the Kubernetes package and module",
"id": 5536808,
"maintainers": {
"johanot": 998763,
"offlinehacker": 585547,
"saschagrunert": 695473,
"srhb": 219362
},
"members": {},
"name": "kubernetes"
},
"linux-kernel": {
"description": "Maintain the Linux kernel",
"id": 13371617,
"maintainers": {
"alyssais": 2768870
},
"members": {
"K900": 386765,
"Ma27": 6025220,
"NeQuissimus": 628342,
"TredwellGit": 61860346
},
"name": "Linux kernel"
},
"lisp": {
"description": "Maintain the Lisp ecosystem",
"id": 7678368,
"maintainers": {
"7c6f434c": 1891350,
"Uthar": 15697697,
"hraban": 137852,
"lukego": 13791,
"nagy": 692274
},
"members": {},
"name": "lisp"
},
"lix-maintainers": {
"description": "Lix maintainers in nixpkgs/NixOS",
"id": 13838744,
"maintainers": {
"lf-": 6652840
},
"members": {
"9999years": 15312184,
"Qyriad": 1542224,
"RaitoBezarius": 314564,
"alois31": 36605164
},
"name": "Lix maintainers"
},
"llvm": {
"description": "Maintain LLVM package sets and related packages",
"id": 9955829,
"maintainers": {
"RossComputerGuy": 19699320,
"alyssais": 2768870
},
"members": {
"Ericson2314": 1055245,
"dtzWill": 817330,
"emilazy": 18535642,
"lovek323": 265084,
"rrbutani": 7833358,
"sternenseemann": 3154475
},
"name": "LLVM"
},
"loongarch64": {
"description": "Maintain LoongArch64 related packages and code",
"id": 13371619,
"maintainers": {
"Aleksanaa": 42209822
},
"members": {
"Cryolitia": 23723294,
"darkyzhou": 7220778,
"dramforever": 2818072,
"wegank": 9713184
},
"name": "loongarch64"
},
"lua": {
"description": "Maintain the lua ecosystem",
"id": 5255750,
"maintainers": {
"teto": 886074
},
"members": {
"7c6f434c": 1891350,
"Shados": 338268,
"khaneliman": 1778670,
"spencerpogo": 34356756
},
"name": "lua"
},
"lumina": {
"description": "Maintain lumina desktop environment and related packages",
"id": 5937661,
"maintainers": {
"romildo": 1217934
},
"members": {},
"name": "Lumina"
},
"lxc": {
"description": "LXC, LXD, Incus",
"id": 9223525,
"maintainers": {
"adamcstephens": 2071575
},
"members": {
"aanderse": 7755101,
"jnsgruk": 668505,
"megheaiulian": 1788114,
"mkg20001": 7735145
},
"name": "LXC"
},
"lxqt": {
"description": "Maintain LXQt desktop environment and related packages",
"id": 5937679,
"maintainers": {
"romildo": 1217934
},
"members": {},
"name": "LXQt"
},
"marketing-team": {
"description": "Marketing team [leader=@djacu]",
"id": 7709512,
"maintainers": {
"djacu": 7043297,
"tomberek": 178444
},
"members": {
"flyfloh": 74379,
"thilobillerbeck": 7442383
},
"name": "Marketing Team"
},
"mate": {
"description": "Maintainers of the MATE Desktop Environment",
"id": 5937667,
"maintainers": {
"romildo": 1217934
},
"members": {
"bobby285271": 20080233
},
"name": "MATE"
},
"neovim": {
"description": "Maintain the vim and neovim text editors and related packages",
"id": 6748534,
"maintainers": {
"GaetanLepage": 33058747
},
"members": {
"PerchunPak": 68118654,
"khaneliman": 1778670,
"mrcjkb": 12857160
},
"name": "neovim"
},
"nix-formatting": {
"description": "Maintain the official Nix formatter and apply it to Nixpkgs: https://nixos.org/community/teams/formatting/",
"id": 9676823,
"maintainers": {
"infinisil": 20525370,
"jfly": 277474
},
"members": {
"0x4A6F": 9675338,
"MattSturgeon": 5046562,
"Sereja313": 112060595,
"dasJ": 4971975,
"piegamesde": 14054505
},
"name": "nix-formatting"
},
"nix-team": {
"description": "",
"id": 174820,
"maintainers": {
"tomberek": 178444
},
"members": {
"Ericson2314": 1055245,
"Mic92": 96200,
"edolstra": 1148549,
"xokdvium": 145775305
},
"name": "Nix team"
},
"nixos-release-managers": {
"description": "Manage the current nixpkgs/NixOS release",
"id": 3620128,
"maintainers": {
"jopejoe1": 34899572,
"leona-ya": 11006031
},
"members": {},
"name": "nixos-release-managers"
},
"nixpkgs-ci": {
"description": "Maintain Nixpkgs' in-tree Continuous Integration, including GitHub Actions",
"id": 13362067,
"maintainers": {
"philiptaron": 43863,
"wolfgangwalther": 9132420
},
"members": {
"MattSturgeon": 5046562,
"Mic92": 96200,
"zowoq": 59103226
},
"name": "Nixpkgs CI"
},
"nixpkgs-core": {
"description": "Provides leadership for and has authority over Nixpkgs.",
"id": 14317027,
"maintainers": {
"alyssais": 2768870,
"emilazy": 18535642,
"wolfgangwalther": 9132420
},
"members": {},
"name": "Nixpkgs core"
},
"nixpkgs-merge-bot": {
"description": "This team exists as a target for triggering the nixpkgs merge bot.",
"id": 13926892,
"maintainers": {},
"members": {},
"name": "nixpkgs-merge-bot"
},
"node": {
"description": "Maintains Node.js runtimes and build tooling.\r\n\r\n\"JS is hecked, thanks for coming to my TED Talk\" - @lilyinstarlight",
"id": 7309872,
"maintainers": {
"lilyinstarlight": 298109,
"winterqt": 78392041
},
"members": {},
"name": "Node"
},
"ocaml": {
"description": "Maintain the OCaml compiler and package set",
"id": 7553013,
"maintainers": {
"Alizter": 8614547,
"romildo": 1217934,
"ulrikstrid": 1607770
},
"members": {
"redianthus": 16472988
},
"name": "OCaml"
},
"pantheon": {
"description": "Maintain Pantheon desktop environment and platform",
"id": 4786995,
"maintainers": {
"davidak": 91113
},
"members": {
"bobby285271": 20080233
},
"name": "Pantheon"
},
"php": {
"description": "Maintain PHP related packages and extensions.",
"id": 3806182,
"maintainers": {},
"members": {
"Ma27": 6025220,
"aanderse": 7755101,
"piotrkwiecinski": 2151333,
"talyz": 63433
},
"name": "php"
},
"podman": {
"description": "Maintain Podman and CRI-O related packages and modules.",
"id": 3806184,
"maintainers": {},
"members": {
"saschagrunert": 695473,
"vdemeester": 6508
},
"name": "podman"
},
"postgres": {
"description": "Maintain the PostgreSQL package and plugins along with the NixOS module.",
"id": 11455958,
"maintainers": {
"Ma27": 6025220,
"thoughtpolice": 3416,
"wolfgangwalther": 9132420
},
"members": {},
"name": "postgres"
},
"qt-kde": {
"description": "Maintain the Qt framework, KDE application suite, Plasma desktop environment and related projects",
"id": 4341481,
"maintainers": {
"ttuegel": 563054
},
"members": {
"FRidh": 2129135,
"K900": 386765,
"LunNova": 782440,
"NickCao": 15247171,
"SCOTT-HAMILTON": 24496705,
"SuperSandro2000": 7258858,
"bkchr": 5718007,
"ilya-fedin": 17829319,
"mjm": 1181,
"nyanloutre": 7677321,
"peterhoeg": 722550
},
"name": "Qt-KDE"
},
"reproducible": {
"description": "Team that is interested in reproducible builds",
"id": 7625643,
"maintainers": {
"raboof": 131856,
"zimbatm": 3248
},
"members": {
"Artturin": 56650223,
"Atemu": 18599032,
"RaitoBezarius": 314564,
"davidak": 91113,
"mschwaig": 3856390
},
"name": "reproducible"
},
"risc-v": {
"description": "RISC-V team",
"id": 13425811,
"maintainers": {
"0x4A6F": 9675338
},
"members": {
"DarkKirb": 23011243,
"dramforever": 2818072,
"fgaz": 8182846,
"jonhermansen": 660911
},
"name": "RISC-V"
},
"rocm": {
"description": "Maintain ROCm and related packages",
"id": 6921873,
"maintainers": {
"mschwaig": 3856390
},
"members": {
"Flakebi": 6499211,
"GZGavinZhao": 74938940,
"LunNova": 782440
},
"name": "rocm"
},
"rust": {
"description": "Maintain the Rust compiler toolchain and nixpkgs integration",
"id": 7304571,
"maintainers": {
"Mic92": 96200,
"winterqt": 78392041,
"zowoq": 59103226
},
"members": {
"tjni": 3806110
},
"name": "rust"
},
"scala": {
"description": "Maintain Scala-related packages and modules.",
"id": 11193149,
"maintainers": {
"dottybot": 12519979,
"hamzaremmal": 56235032
},
"members": {
"natsukagami": 9061737
},
"name": "Scala"
},
"sdl": {
"description": "Maintain core SDL libraries",
"id": 13033942,
"maintainers": {
"pbsds": 140964
},
"members": {
"LordGrimmauld": 49513131,
"evysgarden": 92547295,
"jansol": 2588851,
"marcin-serwin": 12128106
},
"name": "SDL"
},
"static": {
"description": "People interested in static builds (creation of `.a` files, fully static executables, etc.) and keeping them working",
"id": 5906550,
"maintainers": {
"nh2": 399535
},
"members": {
"Ericson2314": 1055245,
"aherrmann": 732652,
"alyssais": 2768870,
"basvandijk": 576355,
"cdepillabout": 64804,
"dtzWill": 817330,
"lorenzleutgeb": 542154,
"matthewbauer": 19036,
"r-burns": 52847440,
"rnhmjoj": 2817565,
"sternenseemann": 3154475
},
"name": "static"
},
"stdenv": {
"description": "Maintain the standard environment and its surrounding logic.",
"id": 11265412,
"maintainers": {
"RossComputerGuy": 19699320
},
"members": {
"Artturin": 56650223,
"Ericson2314": 1055245,
"emilazy": 18535642,
"philiptaron": 43863,
"reckenrode": 7413633
},
"name": "stdenv"
},
"systemd": {
"description": "Maintain systemd for NixOS",
"id": 3894007,
"maintainers": {
"flokli": 183879
},
"members": {
"ElvishJerricco": 1365692,
"LordGrimmauld": 49513131,
"aanderse": 7755101,
"arianvp": 628387
},
"name": "systemd"
},
"xen-project": {
"description": "Maintain the Xen Project Hypervisor and the related tooling ecosystem. Members listed as \"Maintainers\" are recipients of Xen Security Advisories sent to xsa@nixos.org.",
"id": 11127725,
"maintainers": {
"CertainLach": 6235312,
"SigmaSquadron": 174749595,
"hehongbo": 665472
},
"members": {
"digitalrane": 1829286
},
"name": "Xen Project"
},
"xfce": {
"description": "Maintainers of the Xfce Desktop Environment",
"id": 5421745,
"maintainers": {
"romildo": 1217934
},
"members": {
"bobby285271": 20080233
},
"name": "Xfce"
}
}

Some files were not shown because too many files have changed in this diff Show More