···23232424# see https://nixos.org/nixpkgs/manual/#chap-conventions
25252626-# Match json/lockfiles/markdown/nix/perl/python/ruby/shell/docbook files, set indent to spaces
2727-[*.{bash,js,json,lock,md,nix,pl,pm,py,rb,sh,xml}]
2626+[*.{bash,css,js,json,lock,md,nix,pl,pm,py,rb,sh,xml}]
2827indent_style = space
29283029# Match docbook files, set indent width of one
+51
.github/actions/checkout/action.yml
···11+name: Checkout
22+33+description: 'Checkout into trusted / untrusted / pinned folders consistently.'
44+55+inputs:
66+ merged-as-untrusted-at:
77+ description: "Whether and which SHA to checkout for the merge commit in the ./untrusted folder."
88+ pinned-from:
99+ description: "Whether to checkout the pinned nixpkgs for CI and from where (trusted, untrusted)."
1010+ target-as-trusted-at:
1111+ description: "Whether and which SHA to checkout for the target commit in the ./trusted folder."
1212+1313+runs:
1414+ using: composite
1515+ steps:
1616+ - if: inputs.merged-as-untrusted-at
1717+ # Would be great to do the checkouts in git worktrees of the existing spare checkout instead,
1818+ # but Nix is broken with them:
1919+ # https://github.com/NixOS/nix/issues/6073
2020+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
2121+ with:
2222+ ref: ${{ inputs.merged-as-untrusted-at }}
2323+ path: untrusted
2424+2525+ - if: inputs.target-as-trusted-at
2626+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
2727+ with:
2828+ ref: ${{ inputs.target-as-trusted-at }}
2929+ path: trusted
3030+3131+ - if: inputs.pinned-from
3232+ id: pinned
3333+ uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
3434+ env:
3535+ PINNED_FROM: ${{ inputs.pinned-from }}
3636+ with:
3737+ script: |
3838+ const path = require('node:path')
3939+ const pinned = require(path.resolve(path.join(process.env.PINNED_FROM, 'ci', 'pinned.json')))
4040+ core.setOutput('pinned-at', pinned.pins.nixpkgs.revision)
4141+4242+ - if: steps.pinned.outputs.pinned-at
4343+ uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
4444+ with:
4545+ ref: ${{ steps.pinned.outputs.pinned-at }}
4646+ path: pinned
4747+ sparse-checkout: |
4848+ lib
4949+ maintainers
5050+ nixos/lib
5151+ pkgs
-121
.github/actions/get-merge-commit/action.yml
···11-name: Get merge commit
22-33-description: 'Checks whether the Pull Request is mergeable and checks out the repo at up to two commits: The result of a temporary merge of the head branch into the target branch ("merged"), and the parent of that commit on the target branch ("target"). Handles push events and merge conflicts gracefully.'
44-55-inputs:
66- mergedSha:
77- description: "The merge commit SHA, previously collected."
88- type: string
99- merged-as-untrusted:
1010- description: "Whether to checkout the merge commit in the ./untrusted folder."
1111- type: boolean
1212- pinnedFrom:
1313- description: "Whether to checkout the pinned nixpkgs for CI and from where (trusted, untrusted)."
1414- type: string
1515- targetSha:
1616- description: "The target commit SHA, previously collected."
1717- type: string
1818- target-as-trusted:
1919- description: "Whether to checkout the target commit in the ./trusted folder."
2020- type: boolean
2121-2222-outputs:
2323- mergedSha:
2424- description: "The merge commit SHA"
2525- value: ${{ steps.commits.outputs.mergedSha }}
2626- targetSha:
2727- description: "The target commit SHA"
2828- value: ${{ steps.commits.outputs.targetSha }}
2929-3030-runs:
3131- using: composite
3232- steps:
3333- - id: commits
3434- if: ${{ !inputs.mergedSha && !inputs.targetSha }}
3535- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
3636- with:
3737- script: |
3838- if (context.eventName == 'push') return core.setOutput('mergedSha', context.sha)
3939-4040- for (const retryInterval of [5, 10, 20, 40, 80]) {
4141- console.log("Checking whether the pull request can be merged...")
4242- const prInfo = (await github.rest.pulls.get({
4343- owner: context.repo.owner,
4444- repo: context.repo.repo,
4545- pull_number: context.payload.pull_request.number
4646- })).data
4747-4848- if (prInfo.state != 'open') throw new Error ("PR is not open anymore.")
4949-5050- if (prInfo.mergeable == null) {
5151- console.log(`GitHub is still computing whether this PR can be merged, waiting ${retryInterval} seconds before trying again...`)
5252- await new Promise(resolve => setTimeout(resolve, retryInterval * 1000))
5353- continue
5454- }
5555-5656- let mergedSha, targetSha
5757-5858- if (prInfo.mergeable) {
5959- console.log("The PR can be merged.")
6060-6161- mergedSha = prInfo.merge_commit_sha
6262- targetSha = (await github.rest.repos.getCommit({
6363- owner: context.repo.owner,
6464- repo: context.repo.repo,
6565- ref: prInfo.merge_commit_sha
6666- })).data.parents[0].sha
6767- } else {
6868- console.log("The PR has a merge conflict.")
6969-7070- mergedSha = prInfo.head.sha
7171- targetSha = (await github.rest.repos.compareCommitsWithBasehead({
7272- owner: context.repo.owner,
7373- repo: context.repo.repo,
7474- basehead: `${prInfo.base.sha}...${prInfo.head.sha}`
7575- })).data.merge_base_commit.sha
7676- }
7777-7878- console.log(`Checking the commits:\nmerged:${mergedSha}\ntarget:${targetSha}`)
7979- core.setOutput('mergedSha', mergedSha)
8080- core.setOutput('targetSha', targetSha)
8181- return
8282- }
8383- throw new Error("Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com.")
8484-8585- - if: inputs.merged-as-untrusted && (inputs.mergedSha || steps.commits.outputs.mergedSha)
8686- # Would be great to do the checkouts in git worktrees of the existing spare checkout instead,
8787- # but Nix is broken with them:
8888- # https://github.com/NixOS/nix/issues/6073
8989- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
9090- with:
9191- ref: ${{ inputs.mergedSha || steps.commits.outputs.mergedSha }}
9292- path: untrusted
9393-9494- - if: inputs.target-as-trusted && (inputs.targetSha || steps.commits.outputs.targetSha)
9595- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
9696- with:
9797- ref: ${{ inputs.targetSha || steps.commits.outputs.targetSha }}
9898- path: trusted
9999-100100- - if: inputs.pinnedFrom
101101- id: pinned
102102- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
103103- env:
104104- PINNED_FROM: ${{ inputs.pinnedFrom }}
105105- with:
106106- script: |
107107- const path = require('node:path')
108108- const pinned = require(path.resolve(path.join(process.env.PINNED_FROM, 'ci', 'pinned.json')))
109109- core.setOutput('pinnedSha', pinned.pins.nixpkgs.revision)
110110-111111- - if: steps.pinned.outputs.pinnedSha
112112- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
113113- with:
114114- ref: ${{ steps.pinned.outputs.pinnedSha }}
115115- path: pinned
116116- sparse-checkout: |
117117- lib
118118- maintainers
119119- nixos/lib
120120- pkgs
121121-
+1-1
.github/workflows/README.md
···1717 This is a temporary commit that GitHub creates automatically as "what would happen, if this PR was merged into the base branch now?".
1818 The checkout could be done via the virtual branch `refs/pull/<pr-number>/merge`, but doing so would cause failures when this virtual branch doesn't exist (anymore).
1919 This can happen when the PR has conflicts, in which case the virtual branch is not created, or when the PR is getting merged while workflows are still running, in which case the branch won't exist anymore at the time of checkout.
2020- Thus, we use the `get-merge-commit.yml` workflow to check whether the PR is mergeable and the test merge commit exists and only then run the relevant jobs.
2020+ Thus, we use the `prepare` job to check whether the PR is mergeable and the test merge commit exists and only then run the relevant jobs.
21212222- Various workflows need to make comparisons against the base branch.
2323 In this case, we checkout the parent of the "test merge commit" for best results.
+9-8
.github/workflows/build.yml
···4747 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
4848 with:
4949 sparse-checkout: .github/actions
5050- - name: Check if the PR can be merged and checkout the merge commit
5151- uses: ./.github/actions/get-merge-commit
5050+ - name: Checkout the merge commit
5151+ uses: ./.github/actions/checkout
5252 with:
5353- mergedSha: ${{ inputs.mergedSha }}
5454- merged-as-untrusted: true
5555- pinnedFrom: untrusted
5353+ merged-as-untrusted-at: ${{ inputs.mergedSha }}
5454+ pinned-from: untrusted
56555756 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
5857 with:
···61606261 - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
6362 with:
6464- # This cache is for the nixpkgs repo checks and should not be trusted or used elsewhere.
6565- name: nixpkgs-ci
6666- authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
6363+ # The nixpkgs-ci cache should not be trusted or used outside of Nixpkgs and its forks' CI.
6464+ name: ${{ vars.CACHIX_NAME || 'nixpkgs-ci' }}
6565+ extraPullNames: nixpkgs-ci
6666+ authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
6767+ pushFilter: '(-source$|-nixpkgs-tarball-)'
67686869 - run: nix-env --install -f pinned -A nix-build-uncached
6970
+80
.github/workflows/check.yml
···99 headBranch:
1010 required: true
1111 type: string
1212+ mergedSha:
1313+ required: true
1414+ type: string
1515+ targetSha:
1616+ required: true
1717+ type: string
1818+ secrets:
1919+ CACHIX_AUTH_TOKEN:
2020+ required: true
2121+ OWNER_RO_APP_PRIVATE_KEY:
2222+ required: true
12231324permissions: {}
1425···7081 env:
7182 GH_TOKEN: ${{ github.token }}
7283 run: gh api /rate_limit | jq
8484+8585+ # For checking code owners, this job depends on a GitHub App with the following permissions:
8686+ # - Permissions:
8787+ # - Repository > Administration: read-only
8888+ # - Organization > Members: read-only
8989+ # - Install App on this repository, setting these variables:
9090+ # - OWNER_RO_APP_ID (variable)
9191+ # - OWNER_RO_APP_PRIVATE_KEY (secret)
9292+ #
9393+ # This should not use the same app as the job to request reviewers, because this job requires
9494+ # handling untrusted PR input.
9595+ owners:
9696+ runs-on: ubuntu-24.04-arm
9797+ timeout-minutes: 5
9898+ steps:
9999+ - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
100100+ with:
101101+ sparse-checkout: .github/actions
102102+ - name: Checkout merge and target commits
103103+ uses: ./.github/actions/checkout
104104+ with:
105105+ merged-as-untrusted-at: ${{ inputs.mergedSha }}
106106+ pinned-from: trusted
107107+ target-as-trusted-at: ${{ inputs.targetSha }}
108108+109109+ - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
110110+111111+ - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
112112+ with:
113113+ # The nixpkgs-ci cache should not be trusted or used outside of Nixpkgs and its forks' CI.
114114+ name: ${{ vars.CACHIX_NAME || 'nixpkgs-ci' }}
115115+ extraPullNames: nixpkgs-ci
116116+ authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
117117+ pushFilter: -source$
118118+119119+ - name: Build codeowners validator
120120+ run: nix-build trusted/ci --arg nixpkgs ./pinned -A codeownersValidator
121121+122122+ - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0
123123+ if: github.event_name == 'pull_request_target' && vars.OWNER_RO_APP_ID
124124+ id: app-token
125125+ with:
126126+ app-id: ${{ vars.OWNER_RO_APP_ID }}
127127+ private-key: ${{ secrets.OWNER_RO_APP_PRIVATE_KEY }}
128128+ permission-administration: read
129129+ permission-members: read
130130+131131+ - name: Log current API rate limits
132132+ if: steps.app-token.outputs.token
133133+ env:
134134+ GH_TOKEN: ${{ steps.app-token.outputs.token }}
135135+ run: gh api /rate_limit | jq
136136+137137+ - name: Validate codeowners
138138+ if: steps.app-token.outputs.token
139139+ env:
140140+ OWNERS_FILE: untrusted/ci/OWNERS
141141+ GITHUB_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }}
142142+ REPOSITORY_PATH: untrusted
143143+ OWNER_CHECKER_REPOSITORY: ${{ github.repository }}
144144+ # Set this to "notowned,avoid-shadowing" to check that all files are owned by somebody
145145+ EXPERIMENTAL_CHECKS: "avoid-shadowing"
146146+ run: result/bin/codeowners-validator
147147+148148+ - name: Log current API rate limits
149149+ if: steps.app-token.outputs.token
150150+ env:
151151+ GH_TOKEN: ${{ steps.app-token.outputs.token }}
152152+ run: gh api /rate_limit | jq
-149
.github/workflows/codeowners-v2.yml
···11-# This workflow depends on two GitHub Apps with the following permissions:
22-# - For checking code owners:
33-# - Permissions:
44-# - Repository > Administration: read-only
55-# - Organization > Members: read-only
66-# - Install App on this repository, setting these variables:
77-# - OWNER_RO_APP_ID (variable)
88-# - OWNER_RO_APP_PRIVATE_KEY (secret)
99-# - For requesting code owners:
1010-# - Permissions:
1111-# - Repository > Administration: read-only
1212-# - Organization > Members: read-only
1313-# - Repository > Pull Requests: read-write
1414-# - Install App on this repository, setting these variables:
1515-# - OWNER_APP_ID (variable)
1616-# - OWNER_APP_PRIVATE_KEY (secret)
1717-#
1818-# This split is done because checking code owners requires handling untrusted PR input,
1919-# while requesting code owners requires PR write access, and those shouldn't be mixed.
2020-#
2121-# Note that the latter is also used for ./eval.yml requesting reviewers.
2222-2323-name: Codeowners v2
2424-2525-on:
2626- pull_request:
2727- paths:
2828- - .github/workflows/codeowners-v2.yml
2929- pull_request_target:
3030- types: [opened, ready_for_review, synchronize, reopened]
3131-3232-concurrency:
3333- group: codeowners-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
3434- cancel-in-progress: true
3535-3636-permissions: {}
3737-3838-defaults:
3939- run:
4040- shell: bash
4141-4242-env:
4343- OWNERS_FILE: ci/OWNERS
4444- # Don't do anything on draft PRs
4545- DRY_MODE: ${{ github.event.pull_request.draft && '1' || '' }}
4646-4747-jobs:
4848- # Check that code owners is valid
4949- check:
5050- name: Check
5151- runs-on: ubuntu-24.04-arm
5252- timeout-minutes: 5
5353- steps:
5454- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
5555- with:
5656- sparse-checkout: .github/actions
5757- - name: Check if the PR can be merged and checkout the merge and target commits
5858- uses: ./.github/actions/get-merge-commit
5959- with:
6060- merged-as-untrusted: true
6161- target-as-trusted: true
6262-6363- - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
6464-6565- - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
6666- with:
6767- # This cache is for the nixpkgs repo checks and should not be trusted or used elsewhere.
6868- name: nixpkgs-ci
6969- authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
7070-7171- - name: Build codeowners validator
7272- run: nix-build trusted/ci -A codeownersValidator
7373-7474- - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0
7575- if: github.event_name == 'pull_request_target' && vars.OWNER_RO_APP_ID
7676- id: app-token
7777- with:
7878- app-id: ${{ vars.OWNER_RO_APP_ID }}
7979- private-key: ${{ secrets.OWNER_RO_APP_PRIVATE_KEY }}
8080- permission-administration: read
8181- permission-members: read
8282-8383- - name: Log current API rate limits
8484- if: steps.app-token.outputs.token
8585- env:
8686- GH_TOKEN: ${{ steps.app-token.outputs.token }}
8787- run: gh api /rate_limit | jq
8888-8989- - name: Validate codeowners
9090- if: steps.app-token.outputs.token
9191- env:
9292- OWNERS_FILE: untrusted/${{ env.OWNERS_FILE }}
9393- GITHUB_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }}
9494- REPOSITORY_PATH: untrusted
9595- OWNER_CHECKER_REPOSITORY: ${{ github.repository }}
9696- # Set this to "notowned,avoid-shadowing" to check that all files are owned by somebody
9797- EXPERIMENTAL_CHECKS: "avoid-shadowing"
9898- run: result/bin/codeowners-validator
9999-100100- - name: Log current API rate limits
101101- if: steps.app-token.outputs.token
102102- env:
103103- GH_TOKEN: ${{ steps.app-token.outputs.token }}
104104- run: gh api /rate_limit | jq
105105-106106- # Request reviews from code owners
107107- request:
108108- name: Request
109109- runs-on: ubuntu-24.04-arm
110110- timeout-minutes: 5
111111- steps:
112112- - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
113113-114114- # Important: Because we use pull_request_target, this checks out the base branch of the PR, not the PR head.
115115- # This is intentional, because we need to request the review of owners as declared in the base branch.
116116- - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
117117- with:
118118- path: trusted
119119-120120- - name: Build review request package
121121- run: nix-build trusted/ci -A requestReviews
122122-123123- - uses: actions/create-github-app-token@0f859bf9e69e887678d5bbfbee594437cb440ffe # v2.1.0
124124- if: github.event_name == 'pull_request_target' && vars.OWNER_APP_ID
125125- id: app-token
126126- with:
127127- app-id: ${{ vars.OWNER_APP_ID }}
128128- private-key: ${{ secrets.OWNER_APP_PRIVATE_KEY }}
129129- permission-administration: read
130130- permission-members: read
131131- permission-pull-requests: write
132132-133133- - name: Log current API rate limits
134134- if: steps.app-token.outputs.token
135135- env:
136136- GH_TOKEN: ${{ steps.app-token.outputs.token }}
137137- run: gh api /rate_limit | jq
138138-139139- - name: Request reviews
140140- if: steps.app-token.outputs.token
141141- env:
142142- GH_TOKEN: ${{ steps.app-token.outputs.token }}
143143- run: result/bin/request-code-owner-reviews.sh ${{ github.repository }} ${{ github.event.number }} "$OWNERS_FILE"
144144-145145- - name: Log current API rate limits
146146- if: steps.app-token.outputs.token
147147- env:
148148- GH_TOKEN: ${{ steps.app-token.outputs.token }}
149149- run: gh api /rate_limit | jq
+19-11
.github/workflows/eval.yml
···1616 default: false
1717 type: boolean
1818 secrets:
1919+ CACHIX_AUTH_TOKEN:
2020+ required: true
1921 OWNER_APP_PRIVATE_KEY:
2022 required: false
2123···8890 with:
8991 sparse-checkout: .github/actions
9092 - name: Check out the PR at the test merge commit
9191- uses: ./.github/actions/get-merge-commit
9393+ uses: ./.github/actions/checkout
9294 with:
9393- mergedSha: ${{ inputs.mergedSha }}
9494- merged-as-untrusted: true
9595- pinnedFrom: untrusted
9595+ merged-as-untrusted-at: ${{ inputs.mergedSha }}
9696+ pinned-from: untrusted
96979798 - name: Install Nix
9899 uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
99100101101+ - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
102102+ with:
103103+ # The nixpkgs-ci cache should not be trusted or used outside of Nixpkgs and its forks' CI.
104104+ name: ${{ vars.CACHIX_NAME || 'nixpkgs-ci' }}
105105+ extraPullNames: nixpkgs-ci
106106+ authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
107107+ pushFilter: '(-source|-single-chunk)$'
108108+100109 - name: Evaluate the ${{ matrix.system }} output paths for all derivation attributes
101110 env:
102111 MATRIX_SYSTEM: ${{ matrix.system }}
···206215 with:
207216 sparse-checkout: .github/actions
208217 - name: Check out the PR at the target commit
209209- uses: ./.github/actions/get-merge-commit
218218+ uses: ./.github/actions/checkout
210219 with:
211211- targetSha: ${{ inputs.targetSha }}
212212- target-as-trusted: true
213213- pinnedFrom: trusted
220220+ target-as-trusted-at: ${{ inputs.targetSha }}
221221+ pinned-from: trusted
214222215223 - name: Download output paths and eval stats for all systems
216224 uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
···375383 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
376384 with:
377385 sparse-checkout: .github/actions
378378- - name: Check if the PR can be merged and checkout the merge commit
379379- uses: ./.github/actions/get-merge-commit
386386+ - name: Checkout the merge commit
387387+ uses: ./.github/actions/checkout
380388 with:
381381- merged-as-untrusted: true
389389+ merged-as-untrusted-at: ${{ inputs.mergedSha }}
382390383391 - name: Install Nix
384392 uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
+36-17
.github/workflows/lint.yml
···99 targetSha:
1010 required: true
1111 type: string
1212+ secrets:
1313+ CACHIX_AUTH_TOKEN:
1414+ required: true
12151316permissions: {}
1417···2427 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
2528 with:
2629 sparse-checkout: .github/actions
2727- - name: Check if the PR can be merged and checkout the merge commit
2828- uses: ./.github/actions/get-merge-commit
3030+ - name: Checkout the merge commit
3131+ uses: ./.github/actions/checkout
2932 with:
3030- mergedSha: ${{ inputs.mergedSha }}
3131- merged-as-untrusted: true
3232- pinnedFrom: untrusted
3333+ merged-as-untrusted-at: ${{ inputs.mergedSha }}
3434+ pinned-from: untrusted
33353436 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
35373838+ # TODO: Figure out how to best enable caching for the treefmt job. Cachix won't work well,
3939+ # because the cache would be invalidated on every commit - treefmt checks every file.
4040+ # Maybe we can cache treefmt's eval-cache somehow.
4141+3642 - name: Check that files are formatted
3743 run: |
3844 # Note that it's fine to run this on untrusted code because:
···5662 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
5763 with:
5864 sparse-checkout: .github/actions
5959- - name: Check if the PR can be merged and checkout the merge commit
6060- uses: ./.github/actions/get-merge-commit
6565+ - name: Checkout the merge commit
6666+ uses: ./.github/actions/checkout
6167 with:
6262- mergedSha: ${{ inputs.mergedSha }}
6363- merged-as-untrusted: true
6464- pinnedFrom: untrusted
6868+ merged-as-untrusted-at: ${{ inputs.mergedSha }}
6969+ pinned-from: untrusted
65706671 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
67727373+ - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
7474+ with:
7575+ # The nixpkgs-ci cache should not be trusted or used outside of Nixpkgs and its forks' CI.
7676+ name: ${{ vars.CACHIX_NAME || 'nixpkgs-ci' }}
7777+ extraPullNames: nixpkgs-ci
7878+ authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
7979+ pushFilter: -source$
8080+6881 - name: Parse all nix files
6982 run: |
7083 # Tests multiple versions at once, let's make sure all of them run, so keep-going.
···7790 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
7891 with:
7992 sparse-checkout: .github/actions
8080- - name: Check if the PR can be merged and checkout merged and target commits
8181- uses: ./.github/actions/get-merge-commit
9393+ - name: Checkout merge and target commits
9494+ uses: ./.github/actions/checkout
8295 with:
8383- mergedSha: ${{ inputs.mergedSha }}
8484- merged-as-untrusted: true
8585- pinnedFrom: untrusted
8686- targetSha: ${{ inputs.targetSha }}
8787- target-as-trusted: true
9696+ merged-as-untrusted-at: ${{ inputs.mergedSha }}
9797+ pinned-from: untrusted
9898+ target-as-trusted-at: ${{ inputs.targetSha }}
889989100 - uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31
101101+102102+ - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
103103+ with:
104104+ # The nixpkgs-ci cache should not be trusted or used outside of Nixpkgs and its forks' CI.
105105+ name: ${{ vars.CACHIX_NAME || 'nixpkgs-ci' }}
106106+ extraPullNames: nixpkgs-ci
107107+ authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
108108+ pushFilter: -source$
9010991110 - name: Running nixpkgs-vet
92111 env:
···11-module.exports = async function ({ github, context, core, dry }) {
11+module.exports = async ({ github, context, core, dry }) => {
22 const { execFileSync } = require('node:child_process')
33- const { readFile } = require('node:fs/promises')
44- const { join } = require('node:path')
53 const { classify } = require('../supportedBranches.js')
64 const withRateLimit = require('./withRateLimit.js')
75···1816 run_id: context.runId,
1917 per_page: 100,
2018 })
2121- ).find(({ name }) => name == 'Check / cherry-pick').html_url +
1919+ ).find(({ name }) => name === 'Check / cherry-pick').html_url +
2220 '?pr=' +
2321 pull_number
24222523 async function extract({ sha, commit }) {
2624 const noCherryPick = Array.from(
2727- commit.message.matchAll(/^Not-cherry-picked-because: (.*)$/g)
2525+ commit.message.matchAll(/^Not-cherry-picked-because: (.*)$/g),
2826 ).at(0)
29273028 if (noCherryPick)
···148146149147 const fetch = extracted
150148 .filter(({ severity }) => !severity)
151151- .map(({ sha, original_sha }) => [ sha, original_sha ])
152152- .flat()
149149+ .flatMap(({ sha, original_sha }) => [sha, original_sha])
153150154151 if (fetch.length > 0) {
155152 // Fetching all commits we need for diff at once is much faster than any other method.
···163160 ])
164161 }
165162166166- const results = extracted.map(result => result.severity ? result : diff(result))
163163+ const results = extracted.map((result) =>
164164+ result.severity ? result : diff(result),
165165+ )
167166168167 // Log all results without truncation, with better highlighting and all whitespace changes to the job log.
169168 results.forEach(({ sha, commit, severity, message, colored_diff }) => {
···177176178177 // Only create step summary below in case of warnings or errors.
179178 // Also clean up older reviews, when all checks are good now.
180180- if (results.every(({ severity }) => severity == 'info')) {
179179+ if (results.every(({ severity }) => severity === 'info')) {
181180 if (!dry) {
182181 await Promise.all(
183182 (
···186185 pull_number,
187186 })
188187 )
189189- .filter((review) => review.user.login == 'github-actions[bot]')
188188+ .filter((review) => review.user.login === 'github-actions[bot]')
190189 .map(async (review) => {
191191- if (review.state == 'CHANGES_REQUESTED') {
190190+ if (review.state === 'CHANGES_REQUESTED') {
192191 await github.rest.pulls.dismissReview({
193192 ...context.repo,
194193 pull_number,
···214213215214 // In the case of "error" severity, we also fail the job.
216215 // Those should be considered blocking and not be dismissable via review.
217217- if (results.some(({ severity }) => severity == 'error'))
216216+ if (results.some(({ severity }) => severity === 'error'))
218217 process.exitCode = 1
219218220220- core.summary.addRaw('This report is automatically generated by the `PR / Check / cherry-pick` CI workflow.', true)
219219+ core.summary.addRaw(
220220+ 'This report is automatically generated by the `PR / Check / cherry-pick` CI workflow.',
221221+ true,
222222+ )
221223 core.summary.addEOL()
222222- core.summary.addRaw("Some of the commits in this PR require the author's and reviewer's attention.", true)
224224+ core.summary.addRaw(
225225+ "Some of the commits in this PR require the author's and reviewer's attention.",
226226+ true,
227227+ )
223228 core.summary.addEOL()
224229225230 if (results.some(({ type }) => type === 'no-commit-hash')) {
226226- core.summary.addRaw('Please follow the [backporting guidelines](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#how-to-backport-pull-requests) and cherry-pick with the `-x` flag.', true)
227227- core.summary.addRaw('This requires changes to the unstable `master` and `staging` branches first, before backporting them.', true)
231231+ core.summary.addRaw(
232232+ 'Please follow the [backporting guidelines](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#how-to-backport-pull-requests) and cherry-pick with the `-x` flag.',
233233+ true,
234234+ )
235235+ core.summary.addRaw(
236236+ 'This requires changes to the unstable `master` and `staging` branches first, before backporting them.',
237237+ true,
238238+ )
228239 core.summary.addEOL()
229229- core.summary.addRaw('Occasionally, commits are not cherry-picked at all, for example when updating minor versions of packages which have already advanced to the next major on unstable.', true)
230230- core.summary.addRaw('These commits can optionally be marked with a `Not-cherry-picked-because: <reason>` footer.', true)
240240+ core.summary.addRaw(
241241+ 'Occasionally, commits are not cherry-picked at all, for example when updating minor versions of packages which have already advanced to the next major on unstable.',
242242+ true,
243243+ )
244244+ core.summary.addRaw(
245245+ 'These commits can optionally be marked with a `Not-cherry-picked-because: <reason>` footer.',
246246+ true,
247247+ )
231248 core.summary.addEOL()
232249 }
233250234251 if (results.some(({ type }) => type === 'diff')) {
235235- core.summary.addRaw('Sometimes it is not possible to cherry-pick exactly the same patch.', true)
236236- core.summary.addRaw('This most frequently happens when resolving merge conflicts.', true)
237237- core.summary.addRaw('The range-diff will help to review the resolution of conflicts.', true)
252252+ core.summary.addRaw(
253253+ 'Sometimes it is not possible to cherry-pick exactly the same patch.',
254254+ true,
255255+ )
256256+ core.summary.addRaw(
257257+ 'This most frequently happens when resolving merge conflicts.',
258258+ true,
259259+ )
260260+ core.summary.addRaw(
261261+ 'The range-diff will help to review the resolution of conflicts.',
262262+ true,
263263+ )
238264 core.summary.addEOL()
239265 }
240266241241- core.summary.addRaw('If you need to merge this PR despite the warnings, please [dismiss](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/dismissing-a-pull-request-review) this review shortly before merging.', true)
267267+ core.summary.addRaw(
268268+ 'If you need to merge this PR despite the warnings, please [dismiss](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/dismissing-a-pull-request-review) this review shortly before merging.',
269269+ true,
270270+ )
242271243272 results.forEach(({ severity, message, diff }) => {
244244- if (severity == 'info') return
273273+ if (severity === 'info') return
245274246275 // The docs for markdown alerts only show examples with markdown blockquote syntax, like this:
247276 // > [!WARNING]
···256285 // Whether this is intended or just an implementation detail is unclear.
257286 core.summary.addRaw('<blockquote>')
258287 core.summary.addRaw(
259259- `\n\n[!${({ important: 'IMPORTANT', warning: 'WARNING', error: 'CAUTION' })[severity]}]`,
288288+ `\n\n[!${{ important: 'IMPORTANT', warning: 'WARNING', error: 'CAUTION' }[severity]}]`,
260289 true,
261290 )
262291 core.summary.addRaw(`${message}`, true)
···305334 })
306335 ).find(
307336 (review) =>
308308- review.user.login == 'github-actions[bot]' &&
337337+ review.user.login === 'github-actions[bot]' &&
309338 // If a review is still pending, we can just update this instead
310339 // of posting a new one.
311311- (review.state == 'CHANGES_REQUESTED' ||
340340+ (review.state === 'CHANGES_REQUESTED' ||
312341 // No need to post a new review, if an older one with the exact
313342 // same content had already been dismissed.
314314- review.body == body),
343343+ review.body === body),
315344 )
316345317346 if (dry) {
318347 if (pendingReview)
319319- core.info('pending review found: ' + pendingReview.html_url)
348348+ core.info(`pending review found: ${pendingReview.html_url}`)
320349 else core.info('no pending review found')
321350 } else {
322351 // Either of those two requests could fail for very long comments. This can only happen
+18-17
ci/github-script/labels.js
···11-module.exports = async function ({ github, context, core, dry }) {
11+module.exports = async ({ github, context, core, dry }) => {
22 const path = require('node:path')
33 const { DefaultArtifactClient } = require('@actions/artifact')
44 const { readFile, writeFile } = require('node:fs/promises')
···27272828 const approvals = new Set(
2929 reviews
3030- .filter((review) => review.state == 'APPROVED')
3030+ .filter((review) => review.state === 'APPROVED')
3131 .map((review) => review.user?.id),
3232 )
3333···3737 // This is intentionally less than the time that Eval takes, so that the label job
3838 // running after Eval can indeed label the PR as conflicted if that is the case.
3939 const merge_commit_sha_valid =
4040- new Date() - new Date(pull_request.created_at) > 3 * 60 * 1000
4040+ Date.now() - new Date(pull_request.created_at) > 3 * 60 * 1000
41414242 const prLabels = {
4343 // We intentionally don't use the mergeable or mergeable_state attributes.
···5353 // The second pass will then read the result from the first pass and set the label.
5454 '2.status: merge conflict':
5555 merge_commit_sha_valid && !pull_request.merge_commit_sha,
5656- '12.approvals: 1': approvals.size == 1,
5757- '12.approvals: 2': approvals.size == 2,
5656+ '12.approvals: 1': approvals.size === 1,
5757+ '12.approvals: 2': approvals.size === 2,
5858 '12.approvals: 3+': approvals.size >= 3,
5959 '12.first-time contribution': [
6060 'NONE',
···104104 // existing reviews, too.
105105 '9.needs: reviewer':
106106 !pull_request.draft &&
107107- pull_request.requested_reviewers.length == 0 &&
108108- reviews.length == 0,
107107+ pull_request.requested_reviewers.length === 0 &&
108108+ reviews.length === 0,
109109 })
110110 }
111111···125125 // called "comparison", yet, will skip the download.
126126 const expired =
127127 !artifact ||
128128- new Date(artifact?.expires_at ?? 0) <
129129- new Date(new Date().getTime() + 60 * 1000)
128128+ new Date(artifact?.expires_at ?? 0) < new Date(Date.now() + 60 * 1000)
130129 log('Artifact expires at', artifact?.expires_at ?? '<n/a>')
131130 if (!expired) {
132131 stats.artifacts++
···175174 async function handle({ item, stats }) {
176175 try {
177176 const log = (k, v, skip) => {
178178- core.info(`#${item.number} - ${k}: ${v}` + (skip ? ' (skipped)' : ''))
177177+ core.info(`#${item.number} - ${k}: ${v}${skip ? ' (skipped)' : ''}`)
179178 return skip
180179 }
181180···257256258257 // No need for an API request, if all labels are the same.
259258 const hasChanges = Object.keys(after).some(
260260- (name) => (before[name] ?? false) != after[name],
259259+ (name) => (before[name] ?? false) !== after[name],
261260 )
262261 if (log('Has changes', hasChanges, !hasChanges)) return
263262···297296 // Go back as far as the last successful run of this workflow to make sure
298297 // we are not leaving anyone behind on GHA failures.
299298 // Defaults to go back 1 hour on the first run.
300300- new Date(lastRun?.created_at ?? new Date().getTime() - 1 * 60 * 60 * 1000).getTime(),
299299+ new Date(
300300+ lastRun?.created_at ?? Date.now() - 1 * 60 * 60 * 1000,
301301+ ).getTime(),
301302 // Go back max. 1 day to prevent hitting all API rate limits immediately,
302303 // when GH API returns a wrong workflow by accident.
303303- new Date().getTime() - 24 * 60 * 60 * 1000,
304304+ Date.now() - 24 * 60 * 60 * 1000,
304305 ),
305306 )
306306- core.info('cutoff timestamp: ' + cutoff.toISOString())
307307+ core.info(`cutoff timestamp: ${cutoff.toISOString()}`)
307308308309 const updatedItems = await github.paginate(
309310 github.rest.search.issuesAndPullRequests,
···400401 .concat(updatedItems, allItems.data)
401402 .filter(
402403 (thisItem, idx, arr) =>
403403- idx ==
404404- arr.findIndex((firstItem) => firstItem.number == thisItem.number),
404404+ idx ===
405405+ arr.findIndex((firstItem) => firstItem.number === thisItem.number),
405406 )
406407407408 ;(await Promise.allSettled(items.map((item) => handle({ item, stats }))))
408408- .filter(({ status }) => status == 'rejected')
409409+ .filter(({ status }) => status === 'rejected')
409410 .map(({ reason }) =>
410411 core.setFailed(`${reason.message}\n${reason.cause.stack}`),
411412 )
+87
ci/github-script/prepare.js
···11+const { classify } = require('../supportedBranches.js')
22+33+module.exports = async ({ github, context, core }) => {
44+ const pull_number = context.payload.pull_request.number
55+66+ for (const retryInterval of [5, 10, 20, 40, 80]) {
77+ core.info('Checking whether the pull request can be merged...')
88+ const prInfo = (
99+ await github.rest.pulls.get({
1010+ ...context.repo,
1111+ pull_number,
1212+ })
1313+ ).data
1414+1515+ if (prInfo.state !== 'open') throw new Error('PR is not open anymore.')
1616+1717+ if (prInfo.mergeable == null) {
1818+ core.info(
1919+ `GitHub is still computing whether this PR can be merged, waiting ${retryInterval} seconds before trying again...`,
2020+ )
2121+ await new Promise((resolve) => setTimeout(resolve, retryInterval * 1000))
2222+ continue
2323+ }
2424+2525+ const { base, head } = prInfo
2626+2727+ let mergedSha, targetSha
2828+2929+ if (prInfo.mergeable) {
3030+ core.info('The PR can be merged.')
3131+3232+ mergedSha = prInfo.merge_commit_sha
3333+ targetSha = (
3434+ await github.rest.repos.getCommit({
3535+ ...context.repo,
3636+ ref: prInfo.merge_commit_sha,
3737+ })
3838+ ).data.parents[0].sha
3939+ } else {
4040+ core.warning('The PR has a merge conflict.')
4141+4242+ mergedSha = prInfo.head.sha
4343+ targetSha = (
4444+ await github.rest.repos.compareCommitsWithBasehead({
4545+ ...context.repo,
4646+ basehead: `${base.sha}...${head.sha}`,
4747+ })
4848+ ).data.merge_base_commit.sha
4949+ }
5050+5151+ core.info(
5252+ `Checking the commits:\nmerged: ${mergedSha}\ntarget: ${targetSha}`,
5353+ )
5454+ core.setOutput('mergedSha', mergedSha)
5555+ core.setOutput('targetSha', targetSha)
5656+5757+ core.setOutput('systems', require('../supportedSystems.json'))
5858+5959+ const baseClassification = classify(base.ref)
6060+ core.setOutput('base', baseClassification)
6161+ console.log('base classification:', baseClassification)
6262+6363+ const headClassification =
6464+ base.repo.full_name === head.repo.full_name
6565+ ? classify(head.ref)
6666+ : // PRs from forks are always considered WIP.
6767+ { type: ['wip'] }
6868+ core.setOutput('head', headClassification)
6969+ console.log('head classification:', headClassification)
7070+7171+ const files = (
7272+ await github.paginate(github.rest.pulls.listFiles, {
7373+ ...context.repo,
7474+ pull_number: context.payload.pull_request.number,
7575+ per_page: 100,
7676+ })
7777+ ).map((file) => file.filename)
7878+7979+ if (files.includes('ci/pinned.json')) core.setOutput('touched', ['pinned'])
8080+ else core.setOutput('touched', [])
8181+8282+ return
8383+ }
8484+ throw new Error(
8585+ "Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com.",
8686+ )
8787+}
+11
ci/github-script/run
···4040}
41414242program
4343+ .command('prepare')
4444+ .description('Prepare relevant information of a pull request.')
4545+ .argument('<owner>', 'Owner of the GitHub repository to check (Example: NixOS)')
4646+ .argument('<repo>', 'Name of the GitHub repository to check (Example: nixpkgs)')
4747+ .argument('<pr>', 'Number of the Pull Request to check')
4848+ .action(async (owner, repo, pr) => {
4949+ const prepare = (await import('./prepare.js')).default
5050+ run(prepare, owner, repo, pr)
5151+ })
5252+5353+program
4354 .command('commits')
4455 .description('Check commit structure of a pull request.')
4556 .argument('<owner>', 'Owner of the GitHub repository to check (Example: NixOS)')
+2-2
ci/github-script/withRateLimit.js
···11-module.exports = async function ({ github, core }, callback) {
11+module.exports = async ({ github, core }, callback) => {
22 const Bottleneck = require('bottleneck')
3344 const stats = {
···2323 // Requests to a different host do not count against the rate limit.
2424 if (options.url.startsWith('https://github.com')) return request(options)
2525 // Requests to the /rate_limit endpoint do not count against the rate limit.
2626- if (options.url == '/rate_limit') return request(options)
2626+ if (options.url === '/rate_limit') return request(options)
2727 // Search requests are in a different resource group, which allows 30 requests / minute.
2828 // We do less than a handful each run, so not implementing throttling for now.
2929 if (options.url.startsWith('/search/')) return request(options)
···11# shellcheck shell=bash
2233+_dotnetIsSolution() {
44+ dotnet sln ${1:+"$1"} list 2>/dev/null
55+}
66+37dotnetConfigurePhase() {
48 echo "Executing dotnetConfigureHook"
59···108112 dotnetBuild() {
109113 local -r projectFile="${1-}"
110114115115+ local useRuntime=
116116+ _dotnetIsSolution "$projectFile" || useRuntime=1
117117+111118 for runtimeId in "${runtimeIds[@]}"; do
112119 local runtimeIdFlags=()
113113- if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then
120120+ if [[ -n $useRuntime ]]; then
114121 runtimeIdFlags+=("--runtime" "$runtimeId")
115122 fi
116123···188195189196 local projectFile runtimeId
190197 for projectFile in "${testProjectFiles[@]-${projectFiles[@]}}"; do
198198+ local useRuntime=
199199+ _dotnetIsSolution "$projectFile" || useRuntime=1
200200+191201 for runtimeId in "${runtimeIds[@]}"; do
192202 local runtimeIdFlags=()
193193- if [[ $projectFile == *.csproj ]]; then
203203+ if [[ -n $useRuntime ]]; then
194204 runtimeIdFlags=("--runtime" "$runtimeId")
195205 fi
196206···355365356366 dotnetPublish() {
357367 local -r projectFile="${1-}"
368368+369369+ local useRuntime=
370370+ _dotnetIsSolution "$projectFile" || useRuntime=1
358371359372 for runtimeId in "${runtimeIds[@]}"; do
360360- runtimeIdFlags=()
361361- if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then
373373+ local runtimeIdFlags=()
374374+ if [[ -n $useRuntime ]]; then
362375 runtimeIdFlags+=("--runtime" "$runtimeId")
363376 fi
364377···380393 dotnetPack() {
381394 local -r projectFile="${1-}"
382395396396+ local useRuntime=
397397+ _dotnetIsSolution "$projectFile" || useRuntime=1
398398+383399 for runtimeId in "${runtimeIds[@]}"; do
400400+ local runtimeIdFlags=()
401401+ if [[ -n $useRuntime ]]; then
402402+ runtimeIdFlags+=("--runtime" "$runtimeId")
403403+ # set RuntimeIdentifier because --runtime is broken:
404404+ # https://github.com/dotnet/sdk/issues/13983
405405+ runtimeIdFlags+=(-p:RuntimeIdentifier="$runtimeId")
406406+ fi
407407+384408 dotnet pack ${1+"$projectFile"} \
385409 -maxcpucount:"$maxCpuFlag" \
386410 -p:ContinuousIntegrationBuild=true \
···390414 --configuration "$dotnetBuildType" \
391415 --no-restore \
392416 --no-build \
393393- --runtime "$runtimeId" \
417417+ "${runtimeIdFlags[@]}" \
394418 "${flags[@]}" \
395419 "${packFlags[@]}"
396420 done
+4-1
pkgs/build-support/fetchmavenartifact/default.nix
···3131 # and `urls` can be specified, not both.
3232 url ? "",
3333 urls ? [ ],
3434+ # Metadata
3535+ meta ? { },
3436 # The rest of the arguments are just forwarded to `fetchurl`.
3537 ...
3638}:
···7173 "classifier"
7274 "repos"
7375 "url"
7676+ "meta"
7477 ]
7578 // {
7679 urls = urls_;
···7982 );
8083in
8184stdenv.mkDerivation {
8282- inherit pname version;
8585+ inherit pname version meta;
8386 dontUnpack = true;
8487 # By moving the jar to $out/share/java we make it discoverable by java
8588 # packages packages that mention this derivation in their buildInputs.
···4444 buildHashes = builtins.fromJSON (builtins.readFile ./hashes.json);
45454646 # our version of buck2; this should be a git tag
4747- version = "2025-05-06";
4747+ version = "2025-08-15";
48484949 # map our platform name to the rust toolchain suffix
5050 # NOTE (aseipp): must be synchronized with update.sh!
···8282 # tooling
8383 prelude-src =
8484 let
8585- prelude-hash = "48c249f8c7b99ff501d6e857754760315072b306";
8585+ prelude-hash = "892cb85f5fc3258c7e4f89a836821ec4b8c7ee44";
8686 name = "buck2-prelude-${version}.tar.gz";
8787 hash = buildHashes."_prelude";
8888 url = "https://github.com/facebook/buck2-prelude/archive/${prelude-hash}.tar.gz";
···4848 description = "Set of nine separate and highly configurable menu items that let you know exactly what's going on inside your Mac";
4949 homepage = "https://bjango.com/mac/istatmenus/";
5050 license = lib.licenses.unfree;
5151- maintainers = with lib.maintainers; [ donteatoreo ];
5151+ maintainers = with lib.maintainers; [ FlameFlag ];
5252 platforms = lib.platforms.darwin;
5353 sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
5454 };
+1-1
pkgs/by-name/it/itsycal/package.nix
···2727 description = "Tiny menu bar calendar";
2828 homepage = "https://www.mowglii.com/itsycal/";
2929 license = lib.licenses.mit;
3030- maintainers = with lib.maintainers; [ donteatoreo ];
3030+ maintainers = with lib.maintainers; [ FlameFlag ];
3131 platforms = lib.platforms.darwin;
3232 sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
3333 };
···11+This directory contains a vendored copy of `games.json`, along with tooling to generate it.
22+33+## Purpose
44+55+The games data is fetched at runtime by NexusMods.App, however it is also included at build time for two reasons:
66+77+1. It allows tests to run against real data.
88+2. It is used as cached data, speeding up the app's initial run.
99+1010+It is not vital for the file to contain all games, however ideally it should contain all games _supported_ by this version of NexusMods.App.
1111+That way the initial run's cached data is more useful.
1212+1313+If this file grows too large, because we are including too many games, we can patch the `csproj` build spec so that `games.json` is not used at build time.
1414+We would also need to patch or disable any tests that rely on it.
1515+1616+## Generating
1717+1818+`games.json` is generated automatically by `update.sh`, using data from [nexusmods' API][url] and the games listed in `game-ids.nix`.
1919+2020+To add a new game to `games.json`:
2121+- Inspect the [nexusmods endpoint][url] to find the game's name and ID
2222+- Add the name and ID to `game-ids.nix`
2323+- Run `update.sh`
2424+- Commit the result
2525+2626+> [!Note]
2727+> Running `update.sh` may also update the existing games, so you may wish to create two separate commits using `git add --patch`.
2828+> One for updating the existing data and another for adding the new game.
2929+3030+[url]: https://data.nexusmods.com/file/nexus-data/games.json
3131+
···11+# This file lists games to be included in the vendored games.json file.
22+# It is not critical to include all games, other than those referenced by the test suite.
33+# Ideally, all games supported by the app will be included, as this can improve first-run performance.
44+{
55+ # keep-sorted start case=no numeric=yes
66+ "Baldur's Gate 3" = 3474;
77+ "Cyberpunk 2077" = 3333;
88+ "Mount & Blade II: Bannerlord" = 3174;
99+ "Stardew Valley" = 1303;
1010+ # keep-sorted end
1111+}
···5555 a consistent and modern look across all desktop platforms
5656 (Windows, macOS, Linux).
5757 '';
5858- maintainers = with lib.maintainers; [ donteatoreo ];
5858+ maintainers = with lib.maintainers; [ FlameFlag ];
5959 };
6060}
···4242 include required(file("/nix/store/ccnzr53dpipdacxgci3ii3bqacvb5hxm-hocon-test-include.conf"))
4343 include "/nix/store/ccnzr53dpipdacxgci3ii3bqacvb5hxm-hocon-test-include.conf"
4444 include url("https://example.com")
4545+4546 }
4647}
4748