diff --git a/.github/actions/get-merge-commit/action.yml b/.github/actions/get-merge-commit/action.yml index 3766ad4f4ea0..aec17bf76858 100644 --- a/.github/actions/get-merge-commit/action.yml +++ b/.github/actions/get-merge-commit/action.yml @@ -3,9 +3,15 @@ name: Get merge commit 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.' inputs: + mergedSha: + description: "The merge commit SHA, previously collected." + type: string merged-as-untrusted: description: "Whether to checkout the merge commit in the ./untrusted folder." type: boolean + targetSha: + description: "The target commit SHA, previously collected." + type: string target-as-trusted: description: "Whether to checkout the target commit in the ./trusted folder." type: boolean @@ -22,6 +28,7 @@ runs: using: composite steps: - id: commits + if: ${{ !inputs.mergedSha && !inputs.targetSha }} uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | @@ -72,17 +79,17 @@ runs: } throw new Error("Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com.") - - if: inputs.merged-as-untrusted && steps.commits.outputs.mergedSha + - if: inputs.merged-as-untrusted && (inputs.mergedSha || steps.commits.outputs.mergedSha) # Would be great to do the checkouts in git worktrees of the existing spare checkout instead, # but Nix is broken with them: # https://github.com/NixOS/nix/issues/6073 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ steps.commits.outputs.mergedSha }} + ref: ${{ inputs.mergedSha || steps.commits.outputs.mergedSha }} path: untrusted - - if: inputs.target-as-trusted && steps.commits.outputs.targetSha + - if: inputs.target-as-trusted && (inputs.targetSha || steps.commits.outputs.targetSha) uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ steps.commits.outputs.targetSha }} + ref: ${{ inputs.targetSha || steps.commits.outputs.targetSha }} path: trusted diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 897bde43b645..594081924604 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,10 @@ name: Build on: workflow_call: + inputs: + mergedSha: + required: true + type: string secrets: CACHIX_AUTH_TOKEN: required: true @@ -39,6 +43,7 @@ jobs: - name: Check if the PR can be merged and checkout the merge commit uses: ./.github/actions/get-merge-commit with: + mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted: true - uses: cachix/install-nix-action@17fe5fb4a23ad6cbbe47d6b3f359611ad276644c # v31 diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index f7ce71e4dde1..bbc50810f5d1 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -2,6 +2,15 @@ name: Eval on: workflow_call: + inputs: + mergedSha: + required: true + type: string + targetSha: + type: string + systems: + required: true + type: string secrets: OWNER_APP_PRIVATE_KEY: required: false @@ -13,34 +22,12 @@ defaults: shell: bash jobs: - prepare: - runs-on: ubuntu-24.04-arm - outputs: - mergedSha: ${{ steps.get-merge-commit.outputs.mergedSha }} - targetSha: ${{ steps.get-merge-commit.outputs.targetSha }} - systems: ${{ steps.systems.outputs.systems }} - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - sparse-checkout: | - .github/actions - ci/supportedSystems.json - - name: Check if the PR can be merged and get the test merge commit - uses: ./.github/actions/get-merge-commit - id: get-merge-commit - - - name: Load supported systems - id: systems - run: | - echo "systems=$(jq -c > "$GITHUB_OUTPUT" - eval: runs-on: ubuntu-24.04-arm - needs: [prepare] strategy: fail-fast: false matrix: - system: ${{ fromJSON(needs.prepare.outputs.systems) }} + system: ${{ fromJSON(inputs.systems) }} name: ${{ matrix.system }} steps: - name: Enable swap @@ -53,7 +40,7 @@ jobs: - name: Check out the PR at the test merge commit uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.prepare.outputs.mergedSha }} + ref: ${{ inputs.mergedSha }} path: untrusted - name: Install Nix @@ -78,12 +65,12 @@ jobs: path: merged/* - name: Get target run id - if: needs.prepare.outputs.targetSha + if: inputs.targetSha id: targetRunId uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: MATRIX_SYSTEM: ${{ matrix.system }} - TARGET_SHA: ${{ needs.prepare.outputs.targetSha }} + TARGET_SHA: ${{ inputs.targetSha }} with: script: | const system = process.env.MATRIX_SYSTEM @@ -92,14 +79,13 @@ jobs: let run_id try { run_id = (await github.rest.actions.listWorkflowRuns({ - owner: context.repo.owner, - repo: context.repo.repo, + ...context.repo, workflow_id: 'push.yml', event: 'push', head_sha: targetSha })).data.workflow_runs[0].id } catch { - throw new Error(`Could not find an push.yml workflow run for ${targetSha}.`) + throw new Error(`Could not find a push.yml workflow run for ${targetSha}.`) } core.setOutput('targetRunId', run_id) @@ -108,8 +94,7 @@ jobs: // 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, + ...context.repo, run_id, name: `merged-${system}` }) @@ -147,11 +132,9 @@ jobs: compare: runs-on: ubuntu-24.04-arm - needs: [prepare, eval] - if: needs.prepare.outputs.targetSha + needs: [eval] + if: inputs.targetSha permissions: - issues: write # needed to create *new* labels - pull-requests: write statuses: write steps: - name: Download output paths and eval stats for all systems @@ -164,7 +147,7 @@ jobs: - name: Check out the PR at the target commit uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.prepare.outputs.targetSha }} + ref: ${{ inputs.targetSha }} path: trusted - name: Install Nix @@ -182,8 +165,8 @@ jobs: env: AUTHOR_ID: ${{ github.event.pull_request.user.id }} run: | - git -C trusted fetch --depth 1 origin ${{ needs.prepare.outputs.mergedSha }} - git -C trusted diff --name-only ${{ needs.prepare.outputs.mergedSha }} \ + git -C trusted fetch --depth 1 origin ${{ inputs.mergedSha }} + git -C trusted diff --name-only ${{ inputs.mergedSha }} \ | jq --raw-input --slurp 'split("\n")[:-1]' > touched-files.json # Use the target branch to get accurate maintainer info @@ -224,34 +207,14 @@ jobs: `${serverUrl}/${repo.owner}/${repo.repo}/actions/runs/${runId}?pr=${payload.pull_request.number}` await github.rest.repos.createCommitStatus({ - owner: repo.owner, - repo: repo.repo, + ...repo, sha: payload.pull_request.head.sha, - context: 'Eval / Summary', + context: 'Eval Summary', state: 'success', description, target_url }) - labels: - name: Labels - needs: [compare] - uses: ./.github/workflows/labels.yml - permissions: - issues: write - pull-requests: write - - reviewers: - name: Reviewers - # No dependency on "compare", so that it can start at the same time. - # We only wait for the "comparison" artifact to be available, which makes the start-to-finish time - # for the eval workflow considerably faster. - needs: [prepare, eval] - if: needs.prepare.outputs.targetSha - uses: ./.github/workflows/reviewers.yml - secrets: - OWNER_APP_PRIVATE_KEY: ${{ secrets.OWNER_APP_PRIVATE_KEY }} - misc: if: ${{ github.event_name != 'push' }} runs-on: ubuntu-24.04-arm diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 19540306148a..4bf917d800db 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,6 +2,13 @@ name: Lint on: workflow_call: + inputs: + mergedSha: + required: true + type: string + targetSha: + required: true + type: string permissions: {} @@ -19,6 +26,7 @@ jobs: - name: Check if the PR can be merged and checkout the merge commit uses: ./.github/actions/get-merge-commit with: + mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted: true - uses: cachix/install-nix-action@17fe5fb4a23ad6cbbe47d6b3f359611ad276644c # v31 @@ -50,6 +58,7 @@ jobs: - name: Check if the PR can be merged and checkout the merge commit uses: ./.github/actions/get-merge-commit with: + mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted: true - uses: cachix/install-nix-action@17fe5fb4a23ad6cbbe47d6b3f359611ad276644c # v31 @@ -72,7 +81,9 @@ jobs: - name: Check if the PR can be merged and checkout merged and target commits uses: ./.github/actions/get-merge-commit with: + mergedSha: ${{ inputs.mergedSha }} merged-as-untrusted: true + targetSha: ${{ inputs.targetSha }} target-as-trusted: true - uses: cachix/install-nix-action@17fe5fb4a23ad6cbbe47d6b3f359611ad276644c # v31 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 924a60d3752d..8e92b5aed816 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -18,6 +18,27 @@ concurrency: permissions: {} jobs: + prepare: + runs-on: ubuntu-24.04-arm + outputs: + mergedSha: ${{ steps.get-merge-commit.outputs.mergedSha }} + targetSha: ${{ steps.get-merge-commit.outputs.targetSha }} + systems: ${{ steps.systems.outputs.systems }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + sparse-checkout: | + .github/actions + ci/supportedSystems.json + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + + - name: Load supported systems + id: systems + run: | + echo "systems=$(jq -c > "$GITHUB_OUTPUT" + check: name: Check uses: ./.github/workflows/check.yml @@ -27,21 +48,67 @@ jobs: lint: name: Lint + needs: [prepare] uses: ./.github/workflows/lint.yml + with: + mergedSha: ${{ needs.prepare.outputs.mergedSha }} + targetSha: ${{ needs.prepare.outputs.targetSha }} eval: name: Eval + needs: [prepare] uses: ./.github/workflows/eval.yml permissions: # compare + statuses: write + secrets: + OWNER_APP_PRIVATE_KEY: ${{ secrets.OWNER_APP_PRIVATE_KEY }} + with: + mergedSha: ${{ needs.prepare.outputs.mergedSha }} + targetSha: ${{ needs.prepare.outputs.targetSha }} + systems: ${{ needs.prepare.outputs.systems }} + + labels: + name: Labels + needs: [eval] + uses: ./.github/workflows/labels.yml + permissions: issues: write pull-requests: write - statuses: write + + reviewers: + name: Reviewers + needs: [prepare, eval] + if: needs.prepare.outputs.targetSha + uses: ./.github/workflows/reviewers.yml secrets: OWNER_APP_PRIVATE_KEY: ${{ secrets.OWNER_APP_PRIVATE_KEY }} build: name: Build + needs: [prepare] uses: ./.github/workflows/build.yml secrets: CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} + with: + mergedSha: ${{ needs.prepare.outputs.mergedSha }} + + # This job's only purpose is to serve as a target for the "Required Status Checks" branch ruleset. + # It "needs" all the jobs that should block merging a PR. + # If they pass, it is skipped — which counts as "success" for purposes of the branch ruleset. + # However, if any of them fail, this job will also fail — thus blocking the branch ruleset. + no-pr-failures: + # Modify this list to add or remove jobs from required status checks. + needs: + - check + - lint + - eval + - build + # WARNING: + # Do NOT change the name of this job, otherwise the rule will not catch it anymore. + # This would prevent all PRs from merging. + name: no PR failures + if: ${{ failure() }} + runs-on: ubuntu-24.04-arm + steps: + - run: exit 1 diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index a6836b186e06..407b77194f35 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -18,8 +18,24 @@ on: permissions: {} jobs: + prepare: + runs-on: ubuntu-24.04-arm + outputs: + systems: ${{ steps.systems.outputs.systems }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + sparse-checkout: | + ci/supportedSystems.json + + - name: Load supported systems + id: systems + run: | + echo "systems=$(jq -c > "$GITHUB_OUTPUT" + eval: name: Eval + needs: [prepare] uses: ./.github/workflows/eval.yml # Those are not actually used on push, but will throw an error if not set. permissions: @@ -27,3 +43,6 @@ jobs: issues: write pull-requests: write statuses: write + with: + mergedSha: ${{ github.sha }} + systems: ${{ needs.prepare.outputs.systems }} diff --git a/ci/OWNERS b/ci/OWNERS index 0d1cdb3919cf..10b63db72ee2 100644 --- a/ci/OWNERS +++ b/ci/OWNERS @@ -15,9 +15,9 @@ # CI /.github/*_TEMPLATE* @SigmaSquadron -/.github/actions @NixOS/Security @Mic92 @zowoq @infinisil @azuwis @wolfgangwalther @philiptaron -/.github/workflows @NixOS/Security @Mic92 @zowoq @infinisil @azuwis @wolfgangwalther @philiptaron -/ci @NixOS/Security @Mic92 @zowoq @infinisil @azuwis @wolfgangwalther @philiptaron +/.github/actions @NixOS/nixpkgs-ci +/.github/workflows @NixOS/nixpkgs-ci +/ci @NixOS/nixpkgs-ci /ci/OWNERS @infinisil @philiptaron # Development support diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index cbbdba9a9094..55fda4abe90f 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -28015,6 +28015,12 @@ { fingerprint = "E22F 760E E074 E57A 21CB 1733 8DD2 9BB5 2C25 EA09"; } ]; }; + Zirconium419122 = { + name = "Rasmus Liaskar"; + github = "Zirconium419122"; + email = "rasmus@liaskar.net"; + githubId = 152716976; + }; zlepper = { name = "Rasmus Hansen"; github = "zlepper"; diff --git a/maintainers/team-list.nix b/maintainers/team-list.nix index 3b62839ec5c5..bdde7f2f5d41 100644 --- a/maintainers/team-list.nix +++ b/maintainers/team-list.nix @@ -186,6 +186,19 @@ with lib.maintainers; shortName = "Categorization"; }; + ci = { + members = [ + MattSturgeon + mic92 + philiptaron + wolfgangwalther + zowoq + ]; + githubTeams = [ "nixpkgs-ci" ]; + scope = "Maintain Nixpkgs' in-tree Continuous Integration, including GitHub Actions."; + shortName = "CI"; + }; + cinnamon = { members = [ bobby285271 diff --git a/nixos/doc/manual/release-notes/rl-2511.section.md b/nixos/doc/manual/release-notes/rl-2511.section.md index e6a98cc93c12..295d9ede7118 100644 --- a/nixos/doc/manual/release-notes/rl-2511.section.md +++ b/nixos/doc/manual/release-notes/rl-2511.section.md @@ -8,6 +8,8 @@ - Secure boot support can now be enabled for the Limine bootloader through {option}`boot.loader.limine.secureBoot.enable`. Bootloader install script signs the bootloader, then kernels are hashed during system rebuild and written to a config. This allows Limine to boot only the kernels installed through NixOS system. +- The default PostgreSQL version for new NixOS installations (i.e. with `system.stateVersion >= 25.11`) is v17. + ## New Modules {#sec-release-25.11-new-modules} @@ -74,6 +76,8 @@ - `vmalert` now supports multiple instances with the option `services.vmalert.instances."".enable` +- The `wstunnel` module was converted to RFC42-style settings, you will need to update your NixOS config if you make use of this module. + ## Other Notable Changes {#sec-release-25.11-notable-changes} diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index dc8b05b46458..2c0a8f5d4106 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -124,7 +124,9 @@ in type = types.package; example = literalExpression "pkgs.postgresql_15"; defaultText = literalExpression '' - if versionAtLeast config.system.stateVersion "24.11" then + if versionAtLeast config.system.stateVersion "25.11" then + pkgs.postgresql_17 + else if versionAtLeast config.system.stateVersion "24.11" then pkgs.postgresql_16 else if versionAtLeast config.system.stateVersion "23.11" then pkgs.postgresql_15 @@ -671,7 +673,9 @@ in ''; base = # XXX Don't forget to keep `defaultText` of `services.postgresql.package` up to date! - if versionAtLeast config.system.stateVersion "24.11" then + if versionAtLeast config.system.stateVersion "25.11" then + pkgs.postgresql_17 + else if versionAtLeast config.system.stateVersion "24.11" then pkgs.postgresql_16 else if versionAtLeast config.system.stateVersion "23.11" then pkgs.postgresql_15 diff --git a/nixos/modules/services/networking/wstunnel.nix b/nixos/modules/services/networking/wstunnel.nix index 45375d1cf5fb..80a67a602230 100644 --- a/nixos/modules/services/networking/wstunnel.nix +++ b/nixos/modules/services/networking/wstunnel.nix @@ -8,21 +8,29 @@ let cfg = config.services.wstunnel; - hostPortToString = { host, port }: "${host}:${toString port}"; - - hostPortSubmodule = { - options = { - host = lib.mkOption { - description = "The hostname."; - type = lib.types.str; - }; - port = lib.mkOption { - description = "The port."; - type = lib.types.port; - }; - }; + argsFormat = { + type = + let + inherit (lib.types) + attrsOf + listOf + oneOf + bool + int + str + ; + in + attrsOf (oneOf [ + bool + int + str + (listOf str) + ]); + generate = lib.cli.toGNUCommandLineShell { }; }; + hostPortToString = { host, port, ... }: "${host}:${toString port}"; + commonOptions = { enable = lib.mkEnableOption "this `wstunnel` instance" // { default = true; @@ -34,39 +42,6 @@ let default = true; }; - extraArgs = lib.mkOption { - description = '' - Extra command line arguments to pass to `wstunnel`. - Attributes of the form `argName = true;` will be translated to `--argName`, - and `argName = \"value\"` to `--argName value`. - ''; - type = with lib.types; attrsOf (either str bool); - default = { }; - example = { - "someNewOption" = true; - "someNewOptionWithValue" = "someValue"; - }; - }; - - # The original argument name `websocketPingFrequency` is a misnomer, as the frequency is the inverse of the interval. - websocketPingInterval = lib.mkOption { - description = "Frequency at which the client will send websocket ping to the server."; - type = lib.types.nullOr lib.types.ints.unsigned; - default = null; - }; - - loggingLevel = lib.mkOption { - description = '' - Passed to --log-lvl - - Control the log verbosity. i.e: TRACE, DEBUG, INFO, WARN, ERROR, OFF - For more details, checkout [EnvFilter](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax) - ''; - type = lib.types.nullOr lib.types.str; - example = "INFO"; - default = null; - }; - environmentFile = lib.mkOption { description = '' Environment file to be passed to the systemd service. @@ -83,8 +58,45 @@ let }; serverSubmodule = + let + outerConfig = config; + in { config, ... }: + let + certConfig = outerConfig.security.acme.certs.${config.useACMEHost}; + in { + imports = + [ + ../../misc/assertions.nix + + (lib.mkRenamedOptionModule + [ + "enableHTTPS" + ] + [ + "listen" + "enableHTTPS" + ] + ) + ] + ++ lib.map + ( + option: + lib.mkRemovedOptionModule [ option ] '' + The wstunnel module now uses RFC-42-style settings, please modify your config accordingly + '' + ) + [ + "extraArgs" + "websocketPingInterval" + "loggingLevel" + + "restrictTo" + "tlsCertificate" + "tlsKey" + ]; + options = commonOptions // { listen = lib.mkOption { description = '' @@ -92,57 +104,36 @@ let Setting the port to a value below 1024 will also give the process the required `CAP_NET_BIND_SERVICE` capability. ''; - type = lib.types.submodule hostPortSubmodule; - default = { - host = "0.0.0.0"; - port = if config.enableHTTPS then 443 else 80; + type = lib.types.submodule { + options = { + host = lib.mkOption { + description = "The hostname."; + type = lib.types.str; + }; + port = lib.mkOption { + description = "The port."; + type = lib.types.port; + }; + enableHTTPS = lib.mkOption { + description = "Use HTTPS for the tunnel server."; + type = lib.types.bool; + default = true; + }; + }; }; - defaultText = lib.literalExpression '' + default = + { config, ... }: { host = "0.0.0.0"; - port = if enableHTTPS then 443 else 80; - } - ''; - }; - - restrictTo = lib.mkOption { - description = '' - Accepted traffic will be forwarded only to this service. - ''; - type = lib.types.listOf (lib.types.submodule hostPortSubmodule); - default = [ ]; - example = [ + port = if config.enableHTTPS then 443 else 80; + }; + defaultText = lib.literalExpression '' + { config, ... }: { - host = "127.0.0.1"; - port = 51820; + host = "0.0.0.0"; + port = if config.enableHTTPS then 443 else 80; } - ]; - }; - - enableHTTPS = lib.mkOption { - description = "Use HTTPS for the tunnel server."; - type = lib.types.bool; - default = true; - }; - - tlsCertificate = lib.mkOption { - description = '' - TLS certificate to use instead of the hardcoded one in case of HTTPS connections. - Use together with `tlsKey`. ''; - type = lib.types.nullOr lib.types.path; - default = null; - example = "/var/lib/secrets/cert.pem"; - }; - - tlsKey = lib.mkOption { - description = '' - TLS key to use instead of the hardcoded on in case of HTTPS connections. - Use together with `tlsCertificate`. - ''; - type = lib.types.nullOr lib.types.path; - default = null; - example = "/var/lib/secrets/key.pem"; }; useACMEHost = lib.mkOption { @@ -154,12 +145,93 @@ let default = null; example = "example.com"; }; + + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = argsFormat.type; + + options = { + restrict-to = lib.mkOption { + type = lib.types.listOf ( + lib.types.submodule { + options = { + host = lib.mkOption { + description = "The hostname."; + type = lib.types.str; + }; + port = lib.mkOption { + description = "The port."; + type = lib.types.port; + }; + }; + } + ); + default = [ ]; + example = [ + { + host = "127.0.0.1"; + port = 51820; + } + ]; + description = '' + Restrictions on the connections that the server will accept. + For more flexibility, and the possibility to also allow reverse tunnels, + look into the `restrict-config` option that takes a path to a yaml file. + ''; + }; + }; + }; + default = { }; + description = '' + Command line arguments to pass to `wstunnel`. + Attributes of the form `argName = true;` will be translated to `--argName`, + and `argName = \"value\"` to `--argName value`. + ''; + example = { + "someNewOption" = true; + "someNewOptionWithValue" = "someValue"; + }; + }; + }; + + config = { + settings = lib.mkIf (config.useACMEHost != null) { + tls-certificate = "${certConfig.directory}/fullchain.pem"; + tls-private-key = "${certConfig.directory}/key.pem"; + }; }; }; clientSubmodule = { config, ... }: { + imports = + [ + ../../misc/assertions.nix + ] + ++ lib.map + ( + option: + lib.mkRemovedOptionModule [ option ] '' + The wstunnel module now uses RFC-42-style settings, please modify your config accordingly + '' + ) + [ + "extraArgs" + "websocketPingInterval" + "loggingLevel" + + "localToRemote" + "remoteToLocal" + "httpProxy" + "soMark" + "upgradePathPrefix" + "tlsSNI" + "tlsVerifyCertificate" + "upgradeCredentials" + "customHeaders" + ]; + options = commonOptions // { connectTo = lib.mkOption { description = "Server address and port to connect to."; @@ -167,102 +239,36 @@ let example = "https://wstunnel.server.com:8443"; }; - localToRemote = lib.mkOption { - description = "Listen on local and forwards traffic from remote."; - type = lib.types.listOf (lib.types.str); - default = [ ]; - example = [ - "tcp://1212:google.com:443" - "unix:///tmp/wstunnel.sock:g.com:443" - ]; - }; - - remoteToLocal = lib.mkOption { - description = "Listen on remote and forwards traffic from local. Only tcp is supported"; - type = lib.types.listOf lib.types.str; - default = [ ]; - example = [ - "tcp://1212:google.com:443" - "unix://wstunnel.sock:g.com:443" - ]; - }; - addNetBind = lib.mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024"; - httpProxy = lib.mkOption { - description = '' - Proxy to use to connect to the wstunnel server (`USER:PASS@HOST:PORT`). + settings = lib.mkOption { + type = lib.types.submodule { + freeformType = argsFormat.type; - ::: {.warning} - Passwords specified here will be world-readable in the Nix store! - To pass a password to the service, point the `environmentFile` option - to a file containing `PROXY_PASSWORD=` and set - this option to `:$PROXY_PASSWORD@:`. - Note however that this will also locally leak the passwords at - runtime via e.g. /proc//cmdline. - ::: - ''; - type = lib.types.nullOr lib.types.str; - default = null; - }; - - soMark = lib.mkOption { - description = '' - Mark network packets with the SO_MARK sockoption with the specified value. - Setting this option will also enable the required `CAP_NET_ADMIN` capability - for the systemd service. - ''; - type = lib.types.nullOr lib.types.ints.unsigned; - default = null; - }; - - upgradePathPrefix = lib.mkOption { - description = '' - Use a specific HTTP path prefix that will show up in the upgrade - request to the `wstunnel` server. - Useful when running `wstunnel` behind a reverse proxy. - ''; - type = lib.types.nullOr lib.types.str; - default = null; - example = "wstunnel"; - }; - - tlsSNI = lib.mkOption { - description = "Use this as the SNI while connecting via TLS. Useful for circumventing hostname-based firewalls."; - type = lib.types.nullOr lib.types.str; - default = null; - }; - - tlsVerifyCertificate = lib.mkOption { - description = "Whether to verify the TLS certificate of the server. It might be useful to set this to `false` when working with the `tlsSNI` option."; - type = lib.types.bool; - default = true; - }; - - upgradeCredentials = lib.mkOption { - description = '' - Use these credentials to authenticate during the HTTP upgrade request - (Basic authorization type, `USER:[PASS]`). - - ::: {.warning} - Passwords specified here will be world-readable in the Nix store! - To pass a password to the service, point the `environmentFile` option - to a file containing `HTTP_PASSWORD=` and set this - option to `:$HTTP_PASSWORD`. - Note however that this will also locally leak the passwords at runtime - via e.g. /proc//cmdline. - ::: - ''; - type = lib.types.nullOr lib.types.str; - default = null; - }; - - customHeaders = lib.mkOption { - description = "Custom HTTP headers to send during the upgrade request."; - type = lib.types.attrsOf lib.types.str; + options = { + http-headers = lib.mkOption { + type = lib.types.coercedTo (lib.types.attrsOf lib.types.str) (lib.mapAttrsToList ( + n: v: "${n}:${v}" + )) (lib.types.listOf lib.types.str); + default = { }; + example = { + "X-Some-Header" = "some-value"; + }; + description = '' + Custom headers to send in the upgrade request + ''; + }; + }; + }; default = { }; + description = '' + Command line arguments to pass to `wstunnel`. + Attributes of the form `argName = true;` will be translated to `--argName`, + and `argName = \"value\"` to `--argName value`. + ''; example = { - "X-Some-Header" = "some-value"; + "someNewOption" = true; + "someNewOptionWithValue" = "someValue"; }; }; }; @@ -286,8 +292,6 @@ let ]; wantedBy = lib.optional serverCfg.autoStart "multi-user.target"; - environment.RUST_LOG = serverCfg.loggingLevel; - serviceConfig = { Type = "exec"; EnvironmentFile = lib.optional (serverCfg.environmentFile != null) serverCfg.environmentFile; @@ -296,7 +300,13 @@ let PrivateTmp = true; AmbientCapabilities = lib.optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ]; NoNewPrivileges = true; - RestrictNamespaces = "uts ipc pid user cgroup"; + RestrictNamespaces = [ + "uts" + "ipc" + "pid" + "user" + "cgroup" + ]; ProtectSystem = "strict"; ProtectHome = true; ProtectKernelTunables = true; @@ -309,35 +319,22 @@ let RestartSec = 2; RestartSteps = 20; RestartMaxDelaySec = "5min"; - }; - script = with serverCfg; '' - ${lib.getExe package} \ - server \ - ${ - lib.cli.toGNUCommandLineShell { } ( - lib.recursiveUpdate { - restrict-to = map hostPortToString restrictTo; - websocket-ping-frequency-sec = websocketPingInterval; - tls-certificate = - if !enableHTTPS then - null - else if useACMEHost != null then - "${certConfig.directory}/fullchain.pem" - else - "${tlsCertificate}"; - tls-private-key = - if !enableHTTPS then - null - else if useACMEHost != null then - "${certConfig.directory}/key.pem" - else - "${tlsKey}"; - } extraArgs - ) - } \ - ${lib.escapeShellArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString listen}"} - ''; + ExecStart = + let + convertedSettings = serverCfg.settings // { + restrict-to = lib.map hostPortToString serverCfg.settings.restrict-to; + }; + in + '' + ${lib.getExe serverCfg.package} \ + server \ + ${argsFormat.generate convertedSettings} \ + ${lib.escapeShellArg "${ + if serverCfg.listen.enableHTTPS then "wss" else "ws" + }://${hostPortToString serverCfg.listen}"} + ''; + }; }; }; @@ -355,8 +352,6 @@ let ]; wantedBy = lib.optional clientCfg.autoStart "multi-user.target"; - environment.RUST_LOG = clientCfg.loggingLevel; - serviceConfig = { Type = "exec"; EnvironmentFile = lib.optional (clientCfg.environmentFile != null) clientCfg.environmentFile; @@ -364,9 +359,15 @@ let PrivateTmp = true; AmbientCapabilities = (lib.optionals clientCfg.addNetBind [ "CAP_NET_BIND_SERVICE" ]) - ++ (lib.optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]); + ++ (lib.optionals ((clientCfg.settings.socket-so-mark or null) != null) [ "CAP_NET_ADMIN" ]); NoNewPrivileges = true; - RestrictNamespaces = "uts ipc pid user cgroup"; + RestrictNamespaces = [ + "uts" + "ipc" + "pid" + "user" + "cgroup" + ]; ProtectSystem = "strict"; ProtectHome = true; ProtectKernelTunables = true; @@ -379,29 +380,14 @@ let RestartSec = 2; RestartSteps = 20; RestartMaxDelaySec = "5min"; - }; - script = with clientCfg; '' - ${lib.getExe package} \ - client \ - ${ - lib.cli.toGNUCommandLineShell { } ( - lib.recursiveUpdate { - local-to-remote = localToRemote; - remote-to-local = remoteToLocal; - http-headers = lib.mapAttrsToList (n: v: "${n}:${v}") customHeaders; - http-proxy = httpProxy; - socket-so-mark = soMark; - http-upgrade-path-prefix = upgradePathPrefix; - tls-sni-override = tlsSNI; - tls-verify-certificate = tlsVerifyCertificate; - websocket-ping-frequency-sec = websocketPingInterval; - http-upgrade-credentials = upgradeCredentials; - } extraArgs - ) - } \ - ${lib.escapeShellArg connectTo} - ''; + ExecStart = '' + ${lib.getExe clientCfg.package} \ + client \ + ${argsFormat.generate clientCfg.settings} \ + ${lib.escapeShellArg clientCfg.connectTo} + ''; + }; }; }; in @@ -418,16 +404,18 @@ in listen = { host = "0.0.0.0"; port = 8080; + enableHTTPS = true; + }; + settings = { + tls-certificate = "/var/lib/secrets/fullchain.pem"; + tls-private-key = "/var/lib/secrets/key.pem"; + restrict-to = [ + { + host = "127.0.0.1"; + port = 51820; + } + ]; }; - enableHTTPS = true; - tlsCertificate = "/var/lib/secrets/fullchain.pem"; - tlsKey = "/var/lib/secrets/key.pem"; - restrictTo = [ - { - host = "127.0.0.1"; - port = 51820; - } - ]; }; }; }; @@ -454,35 +442,56 @@ in config = lib.mkIf cfg.enable { systemd.services = - (lib.mapAttrs' generateServerUnit (lib.filterAttrs (n: v: v.enable) cfg.servers)) - // (lib.mapAttrs' generateClientUnit (lib.filterAttrs (n: v: v.enable) cfg.clients)); + (lib.mapAttrs' generateServerUnit (lib.filterAttrs (_: v: v.enable) cfg.servers)) + // (lib.mapAttrs' generateClientUnit (lib.filterAttrs (_: v: v.enable) cfg.clients)); assertions = (lib.mapAttrsToList (name: serverCfg: { - assertion = !(serverCfg.useACMEHost != null && serverCfg.tlsCertificate != null); + assertion = + serverCfg.listen.enableHTTPS + -> + (serverCfg.useACMEHost != null) + || ( + (serverCfg.settings.tls-certificate or null) != null + && (serverCfg.settings.tls-private-key or null) != null + ); message = '' - Options services.wstunnel.servers."${name}".useACMEHost and services.wstunnel.servers."${name}".{tlsCertificate, tlsKey} are mutually exclusive. + If services.wstunnel.servers."${name}".listen.enableHTTPS is set to true, either services.wstunnel.servers."${name}".useACMEHost or both services.wstunnel.servers."${name}".settings.tls-private-key and services.wstunnel.servers."${name}".settings.tls-certificate need to be set. ''; }) cfg.servers) - ++ + ++ (lib.foldlAttrs ( + assertions: _: server: + assertions ++ server.assertions + ) [ ] cfg.servers) - (lib.mapAttrsToList (name: serverCfg: { + ++ (lib.mapAttrsToList ( + name: clientCfg: + let + isListAttrDefined = settings: attr: (settings.${attr} or [ ]) != [ ]; + in + { assertion = - serverCfg.enableHTTPS - -> - (serverCfg.useACMEHost != null) || (serverCfg.tlsCertificate != null && serverCfg.tlsKey != null); + isListAttrDefined clientCfg.settings "local-to-remote" + || isListAttrDefined clientCfg.settings "remote-to-local"; message = '' - If services.wstunnel.servers."${name}".enableHTTPS is set to true, either services.wstunnel.servers."${name}".useACMEHost or both services.wstunnel.servers."${name}".tlsKey and services.wstunnel.servers."${name}".tlsCertificate need to be set. + Either one of services.wstunnel.clients."${name}".settings.local-to-remote or services.wstunnel.clients."${name}".settings.remote-to-local must be set. ''; - }) cfg.servers) - ++ + } + ) cfg.clients) + ++ (lib.foldlAttrs ( + assertions: _: client: + assertions ++ client.assertions + ) [ ] cfg.clients); - (lib.mapAttrsToList (name: clientCfg: { - assertion = !(clientCfg.localToRemote == [ ] && clientCfg.remoteToLocal == [ ]); - message = '' - Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".remoteToLocal must be set. - ''; - }) cfg.clients); + warnings = + (lib.foldlAttrs ( + warnings: _: server: + warnings ++ server.warnings + ) [ ] cfg.servers) + ++ (lib.foldlAttrs ( + warnings: _: client: + warnings ++ client.warnings + ) [ ] cfg.clients); }; meta.maintainers = with lib.maintainers; [ diff --git a/nixos/tests/wstunnel.nix b/nixos/tests/wstunnel.nix index 7a0a8ce3496a..97ef562e8f8f 100644 --- a/nixos/tests/wstunnel.nix +++ b/nixos/tests/wstunnel.nix @@ -30,8 +30,10 @@ in host = "10.0.0.1"; port = 443; }; - tlsCertificate = certs.${domain}.cert; - tlsKey = certs.${domain}.key; + settings = { + tls-certificate = "${certs.${domain}.cert}"; + tls-private-key = "${certs.${domain}.key}"; + }; }; }; }; @@ -45,9 +47,9 @@ in useNetworkd = true; useDHCP = false; firewall.enable = false; - extraHosts = '' - 10.0.0.1 ${domain} - ''; + hosts = { + "10.0.0.1" = [ domain ]; + }; }; systemd.network.networks."01-eth1" = { @@ -60,8 +62,10 @@ in clients.my-client = { autoStart = false; connectTo = "wss://${domain}:443"; - localToRemote = [ "tcp://8080:localhost:2080" ]; - remoteToLocal = [ "tcp://2081:localhost:8081" ]; + settings = { + local-to-remote = [ "tcp://8080:localhost:2080" ]; + remote-to-local = [ "tcp://2081:localhost:8081" ]; + }; }; }; }; diff --git a/pkgs/applications/emulators/libretro/cores/ppsspp.nix b/pkgs/applications/emulators/libretro/cores/ppsspp.nix index ebc67bfa4f89..e857f79702a2 100644 --- a/pkgs/applications/emulators/libretro/cores/ppsspp.nix +++ b/pkgs/applications/emulators/libretro/cores/ppsspp.nix @@ -13,13 +13,13 @@ }: mkLibretroCore { core = "ppsspp"; - version = "0-unstable-2025-06-09"; + version = "0-unstable-2025-06-19"; src = fetchFromGitHub { owner = "hrydgard"; repo = "ppsspp"; - rev = "435b26588857dc60d982c2981e04cd28f7a2c38e"; - hash = "sha256-i6xxj8EKFyDiu041oKXwB/Wg9tg7+9yLaRnU5gfSO2s="; + rev = "b0df912935040b572aaa08d6a8d99f8b45e3da80"; + hash = "sha256-qsCUZgaGy79wd1MRz5gVQmHyyVM1wDRlwZj8Qh3j7yU="; fetchSubmodules = true; }; diff --git a/pkgs/applications/networking/browsers/nyxt/default.nix b/pkgs/applications/networking/browsers/nyxt/default.nix index c97bf204e483..d8817dccddbf 100644 --- a/pkgs/applications/networking/browsers/nyxt/default.nix +++ b/pkgs/applications/networking/browsers/nyxt/default.nix @@ -17,6 +17,7 @@ pango, webkitgtk_4_1, openssl, + sqlite, gstreamer, gst-libav, gst-plugins-base, @@ -69,6 +70,7 @@ stdenv.mkDerivation (finalAttrs: { gtk3 webkitgtk_4_1 openssl + sqlite libfixposix ]; diff --git a/pkgs/by-name/ap/aprutil/package.nix b/pkgs/by-name/ap/aprutil/package.nix index 1f23af533b76..d431a348d42c 100644 --- a/pkgs/by-name/ap/aprutil/package.nix +++ b/pkgs/by-name/ap/aprutil/package.nix @@ -22,12 +22,12 @@ assert sslSupport -> openssl != null; assert bdbSupport -> db != null; assert ldapSupport -> openldap != null; -stdenv.mkDerivation rec { +stdenv.mkDerivation (finalAttrs: { pname = "apr-util"; version = "1.6.3"; src = fetchurl { - url = "mirror://apache/apr/${pname}-${version}.tar.bz2"; + url = "mirror://apache/apr/apr-util-${finalAttrs.version}.tar.bz2"; sha256 = "sha256-pBB243EHRjJsOUUEKZStmk/KwM4Cd92P6gdv7DyXcrU="; }; @@ -44,6 +44,7 @@ stdenv.mkDerivation rec { "dev" ]; outputBin = "dev"; + passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe nativeBuildInputs = [ makeWrapper @@ -121,4 +122,4 @@ stdenv.mkDerivation rec { platforms = platforms.unix; license = licenses.asl20; }; -} +}) diff --git a/pkgs/by-name/ca/cairo/package.nix b/pkgs/by-name/ca/cairo/package.nix index 1d96951ac59c..b4f59a0b7153 100644 --- a/pkgs/by-name/ca/cairo/package.nix +++ b/pkgs/by-name/ca/cairo/package.nix @@ -49,6 +49,7 @@ stdenv.mkDerivation ( "devdoc" ]; outputBin = "dev"; # very small + passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe separateDebugInfo = true; nativeBuildInputs = [ diff --git a/pkgs/by-name/da/daed/package.nix b/pkgs/by-name/da/daed/package.nix index df772b3334d3..5fcd7389b94d 100644 --- a/pkgs/by-name/da/daed/package.nix +++ b/pkgs/by-name/da/daed/package.nix @@ -95,6 +95,12 @@ buildGoModule rec { runHook postBuild ''; + postInstall = '' + install -Dm444 $src/install/daed.service -t $out/lib/systemd/system + substituteInPlace $out/lib/systemd/system/daed.service \ + --replace-fail /usr/bin $out/bin + ''; + passthru.updateScript = _experimental-update-script-combinators.sequence [ (nix-update-script { attrPath = "daed.web"; diff --git a/pkgs/by-name/db/dbus-glib/package.nix b/pkgs/by-name/db/dbus-glib/package.nix index e5e4854aadbd..4ab5a596f14a 100644 --- a/pkgs/by-name/db/dbus-glib/package.nix +++ b/pkgs/by-name/db/dbus-glib/package.nix @@ -11,12 +11,12 @@ glib, }: -stdenv.mkDerivation rec { +stdenv.mkDerivation (finalAttrs: { pname = "dbus-glib"; version = "0.114"; src = fetchurl { - url = "${meta.homepage}/releases/dbus-glib/dbus-glib-${version}.tar.gz"; + url = "${finalAttrs.meta.homepage}/releases/dbus-glib/dbus-glib-${finalAttrs.version}.tar.gz"; sha256 = "sha256-wJxcCFsqDjkbjufXg6HWP+RE6WcXzBgU1htej8KCenw="; }; @@ -26,6 +26,7 @@ stdenv.mkDerivation rec { "devdoc" ]; outputBin = "dev"; + passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe nativeBuildInputs = [ pkg-config @@ -64,4 +65,4 @@ stdenv.mkDerivation rec { maintainers = [ ]; platforms = lib.platforms.unix; }; -} +}) diff --git a/pkgs/by-name/de/devmode/package.nix b/pkgs/by-name/de/devmode/package.nix index 011127b26ea1..f0769853f27d 100644 --- a/pkgs/by-name/de/devmode/package.nix +++ b/pkgs/by-name/de/devmode/package.nix @@ -1,123 +1,109 @@ { - lib, - findutils, - nodejs_latest, + live-server, parallel, - rsync, watchexec, - writeShellScriptBin, + writeShellApplication, # arguments to `nix-build`, e.g. `"foo.nix -A bar"` buildArgs ? "", # what path to open a browser at open ? "/index.html", }: let - inherit (nodejs_latest.pkgs) live-server; + error-page = writeShellApplication { + name = "error-page"; + text = '' + rm -rf "''${serve:?}" + mkdir -p "$(dirname "''${error_page_absolute:?}")" - error-page = writeShellScriptBin "error-page" '' - cat << EOF - - - - - -
$1
- - EOF - ''; + cat > "''${error_page_absolute:?}" << EOF + + + + + +
building...
+      EOF
+    '';
+  };
 
-  # The following would have been simpler:
-  # 1. serve from `$serve`
-  # 2. pass each build a `--out-link $serve/result`
-  # But that way live-server does not seem to detect changes and therefore no
-  # auto-reloads occur.
-  # Instead, we copy the contents of each build to the `$serve` directory.
-  # Using rsync here, instead of `cp`, to get as close to an atomic
-  # directory copy operation as possible. `--delay-updates` should
-  # also go towards that.
-  build-and-copy = writeShellScriptBin "build-and-copy" ''
-    set -euxo pipefail
+  build-and-link = writeShellApplication {
+    name = "build-and-link";
+    runtimeInputs = [ error-page ];
+    text = ''
+      error-page
 
-    set +e
-    stderr=$(2>&1 nix-build --out-link $out_link ${buildArgs})
-    exit_status=$?
-    set -e
+      set +e
+      2>&1 nix-build --out-link "''${staging:?}" ${buildArgs} \
+        | tee -a "''${error_page_absolute:?}"
+      exit_status=$?
+      set -e
 
-    if [ $exit_status -eq 0 ];
-    then
-      # setting permissions to be able to clean up
-      ${lib.getExe rsync} \
-        --recursive \
-        --chmod=u=rwX \
-        --delete-before \
-        --delay-updates \
-        --links \
-        $out_link/ \
-        $serve/
-    else
-      set +x
-      ${lib.getExe error-page} "$stderr" > $error_page_absolute
-      set -x
-
-      ${lib.getExe findutils} $serve \
-        -type f \
-        ! -name $error_page_relative \
-        -delete
-    fi
-  '';
+      if [ $exit_status -eq 0 ]; then
+        rm -rf "''${serve:?}"
+        mv "''${staging:?}" "''${serve:?}"
+      fi
+    '';
+  };
 
   # https://watchexec.github.io/
-  watcher = writeShellScriptBin "watcher" ''
-    set -euxo pipefail
+  watcher = writeShellApplication {
+    name = "watcher";
+    runtimeInputs = [
+      watchexec
+      build-and-link
+    ];
+    text = ''
+      watchexec \
+        --shell=none \
+        --restart \
+        build-and-link
+    '';
+  };
 
-    ${lib.getExe watchexec} \
-      --shell=none \
-      --restart \
-      --print-events \
-      ${lib.getExe build-and-copy}
-  '';
-
-  # A Rust alternative to live-server exists, but it fails to open the temporary directory.
-  # `--no-css-inject`: without this it seems that only CSS is auto-reloaded.
-  # https://www.npmjs.com/package/live-server
-  server = writeShellScriptBin "server" ''
-    set -euxo pipefail
-
-    ${lib.getExe' live-server "live-server"} \
-      --host=127.0.0.1 \
-      --verbose \
-      --no-css-inject \
-      --entry-file=$error_page_relative \
-      --open=${open} \
-      $serve
-  '';
+  # https://crates.io/crates/live-server
+  server = writeShellApplication {
+    name = "server";
+    runtimeInputs = [ live-server ];
+    text = ''
+      live-server \
+        --host=127.0.0.1 \
+        --open=${open} \
+        "''${serve:?}"
+    '';
+  };
 in
-writeShellScriptBin "devmode" ''
-  set -euxo pipefail
+writeShellApplication {
+  name = "devmode";
+  runtimeInputs = [
+    parallel
+    watcher
+    server
+    error-page
+  ];
+  text = ''
+    function handle_exit {
+      rm -rf "$tmpdir"
+    }
 
-  function handle_exit {
-    rm -rf "$tmpdir"
-  }
+    tmpdir="$(mktemp -d)"
+    trap handle_exit EXIT
 
-  tmpdir=$(mktemp -d)
-  trap handle_exit EXIT
+    export serve="$tmpdir/serve"
+    export staging="$tmpdir/staging"
+    export error_page_absolute="$serve/${open}"
 
-  export out_link="$tmpdir/result"
-  export serve="$tmpdir/serve"
-  mkdir $serve
-  export error_page_relative=error.html
-  export error_page_absolute=$serve/$error_page_relative
-  ${lib.getExe error-page} "building …" > $error_page_absolute
+    error-page
 
-  ${lib.getExe parallel} \
-    --will-cite \
-    --line-buffer \
-    --tagstr '{/}' \
-    ::: \
-    "${lib.getExe watcher}" \
-    "${lib.getExe server}"
-''
+    parallel \
+      --will-cite \
+      --line-buffer \
+      --tagstr '{/}' \
+      ::: \
+      watcher \
+      server
+  '';
+}
diff --git a/pkgs/by-name/ex/expat/package.nix b/pkgs/by-name/ex/expat/package.nix
index e8ead2467c64..3a8ea413caf8 100644
--- a/pkgs/by-name/ex/expat/package.nix
+++ b/pkgs/by-name/ex/expat/package.nix
@@ -40,6 +40,7 @@ stdenv.mkDerivation (finalAttrs: {
     "dev"
   ]; # TODO: fix referrers
   outputBin = "dev";
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   enableParallelBuilding = true;
 
diff --git a/pkgs/by-name/fa/fastchess/package.nix b/pkgs/by-name/fa/fastchess/package.nix
new file mode 100644
index 000000000000..2c4d69075f33
--- /dev/null
+++ b/pkgs/by-name/fa/fastchess/package.nix
@@ -0,0 +1,46 @@
+{
+  lib,
+  stdenv,
+  fetchFromGitHub,
+  lowdown-unsandboxed,
+  nix-update-script,
+}:
+
+stdenv.mkDerivation (finalAttrs: {
+  pname = "fastchess";
+  version = "1.4.0-alpha";
+
+  src = fetchFromGitHub {
+    owner = "Disservin";
+    repo = "fastchess";
+    tag = "v${finalAttrs.version}";
+    hash = "sha256-fzNpanfeXk7eKftzcs5MIaDBvzumaMQIhhQ8IDFjwPQ=";
+  };
+
+  nativeBuildInputs = [
+    lowdown-unsandboxed
+  ];
+
+  postPatch = ''
+    substituteInPlace app/Makefile \
+      --replace "-march=native" ""
+  '';
+
+  makeFlags = [
+    "PREFIX=${placeholder "out"}"
+    "CXX=${stdenv.cc.targetPrefix}c++"
+  ];
+
+  passthru = {
+    updateScript = nix-update-script { };
+  };
+
+  meta = {
+    description = "Versatile command-line tool designed for running chess engine tournaments";
+    homepage = "https://github.com/Disservin/fastchess";
+    license = lib.licenses.mit;
+    maintainers = with lib.maintainers; [ Zirconium419122 ];
+    platforms = with lib.platforms; unix ++ windows;
+    mainProgram = "fastchess";
+  };
+})
diff --git a/pkgs/by-name/go/gopls/package.nix b/pkgs/by-name/go/gopls/package.nix
index 53b17f3a9157..bf31ed9b5e94 100644
--- a/pkgs/by-name/go/gopls/package.nix
+++ b/pkgs/by-name/go/gopls/package.nix
@@ -11,17 +11,17 @@
 
 buildGoLatestModule (finalAttrs: {
   pname = "gopls";
-  version = "0.19.0";
+  version = "0.19.1";
 
   src = fetchFromGitHub {
     owner = "golang";
     repo = "tools";
     tag = "gopls/v${finalAttrs.version}";
-    hash = "sha256-2K93S7ApzHmsbeReKoSmIhgXuZR3oFODiTWDTO5wDOU=";
+    hash = "sha256-QJnLJNgFtc/MmJ5WWooKcavnPPTYuM4XhUHcbwlvMLY=";
   };
 
   modRoot = "gopls";
-  vendorHash = "sha256-uWbcf/PadGXw2ryg6GjJrHzrZ88kKzfhr6gtYsLTvkg=";
+  vendorHash = "sha256-P5wUGXmVvaRUpzmv/SPX8OpCXOCOg6nBI544puNOWCE=";
 
   # https://github.com/golang/tools/blob/9ed98faa/gopls/main.go#L27-L30
   ldflags = [ "-X main.version=v${finalAttrs.version}" ];
diff --git a/pkgs/by-name/gs/gspell/package.nix b/pkgs/by-name/gs/gspell/package.nix
index eb9d6f29c99c..df9f95157d20 100644
--- a/pkgs/by-name/gs/gspell/package.nix
+++ b/pkgs/by-name/gs/gspell/package.nix
@@ -17,7 +17,7 @@
   gnome,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "gspell";
   version = "1.14.0";
 
@@ -28,9 +28,10 @@ stdenv.mkDerivation rec {
   ];
 
   outputBin = "dev";
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   src = fetchurl {
-    url = "mirror://gnome/sources/${pname}/${lib.versions.majorMinor version}/${pname}-${version}.tar.xz";
+    url = "mirror://gnome/sources/gspell/${lib.versions.majorMinor finalAttrs.version}/gspell-${finalAttrs.version}.tar.xz";
     sha256 = "ZOodjp7cHCW0WpIOgNr2dVnRhm/81/hDL+z+ptD+iJc=";
   };
 
@@ -61,7 +62,7 @@ stdenv.mkDerivation rec {
 
   passthru = {
     updateScript = gnome.updateScript {
-      packageName = pname;
+      packageName = "gspell";
       versionPolicy = "none";
     };
   };
@@ -74,4 +75,4 @@ stdenv.mkDerivation rec {
     teams = [ teams.gnome ];
     platforms = platforms.unix;
   };
-}
+})
diff --git a/pkgs/by-name/gt/gtk-layer-shell/package.nix b/pkgs/by-name/gt/gtk-layer-shell/package.nix
index 195c4a56f236..2ac01848d1e7 100644
--- a/pkgs/by-name/gt/gtk-layer-shell/package.nix
+++ b/pkgs/by-name/gt/gtk-layer-shell/package.nix
@@ -25,6 +25,7 @@ stdenv.mkDerivation (finalAttrs: {
     "devdoc"
   ];
   outputBin = "devdoc"; # for demo
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   src = fetchFromGitHub {
     owner = "wmww";
diff --git a/pkgs/by-name/gt/gtk4-layer-shell/package.nix b/pkgs/by-name/gt/gtk4-layer-shell/package.nix
index c47de743da1b..f1ec6a831e8f 100644
--- a/pkgs/by-name/gt/gtk4-layer-shell/package.nix
+++ b/pkgs/by-name/gt/gtk4-layer-shell/package.nix
@@ -27,6 +27,7 @@ stdenv.mkDerivation (finalAttrs: {
     "devdoc"
   ];
   outputBin = "devdoc";
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   src = fetchFromGitHub {
     owner = "wmww";
diff --git a/pkgs/by-name/li/libadwaita/package.nix b/pkgs/by-name/li/libadwaita/package.nix
index 3dfd38cd4cad..af5c7d98a25e 100644
--- a/pkgs/by-name/li/libadwaita/package.nix
+++ b/pkgs/by-name/li/libadwaita/package.nix
@@ -31,6 +31,7 @@ stdenv.mkDerivation (finalAttrs: {
     "devdoc"
   ];
   outputBin = "devdoc"; # demo app
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   src = fetchFromGitLab {
     domain = "gitlab.gnome.org";
diff --git a/pkgs/by-name/li/libassuan/package.nix b/pkgs/by-name/li/libassuan/package.nix
index 4c6fbea82951..1ef37ce72a56 100644
--- a/pkgs/by-name/li/libassuan/package.nix
+++ b/pkgs/by-name/li/libassuan/package.nix
@@ -9,12 +9,12 @@
   gitUpdater,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "libassuan";
   version = "3.0.2";
 
   src = fetchurl {
-    url = "mirror://gnupg/libassuan/libassuan-${version}.tar.bz2";
+    url = "mirror://gnupg/libassuan/libassuan-${finalAttrs.version}.tar.bz2";
     hash = "sha256-0pMc2tJm5jNRD5lw4aLzRgVeNRuxn5t4kSR1uAdMNvY=";
   };
 
@@ -24,6 +24,7 @@ stdenv.mkDerivation rec {
     "info"
   ];
   outputBin = "dev"; # libassuan-config
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   depsBuildBuild = [ buildPackages.stdenv.cc ];
   buildInputs = [
@@ -59,9 +60,9 @@ stdenv.mkDerivation rec {
       provided.
     '';
     homepage = "https://gnupg.org/software/libassuan/";
-    changelog = "https://dev.gnupg.org/source/libassuan/browse/master/NEWS;libassuan-${version}";
+    changelog = "https://dev.gnupg.org/source/libassuan/browse/master/NEWS;libassuan-${finalAttrs.version}";
     license = lib.licenses.lgpl2Plus;
     platforms = lib.platforms.all;
     maintainers = [ ];
   };
-}
+})
diff --git a/pkgs/by-name/li/libdazzle/package.nix b/pkgs/by-name/li/libdazzle/package.nix
index 484d2968752f..9dd59a2c1bb2 100644
--- a/pkgs/by-name/li/libdazzle/package.nix
+++ b/pkgs/by-name/li/libdazzle/package.nix
@@ -19,7 +19,7 @@
   gnome,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "libdazzle";
   version = "3.44.0";
 
@@ -29,9 +29,10 @@ stdenv.mkDerivation rec {
     "devdoc"
   ];
   outputBin = "dev";
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   src = fetchurl {
-    url = "mirror://gnome/sources/libdazzle/${lib.versions.majorMinor version}/${pname}-${version}.tar.xz";
+    url = "mirror://gnome/sources/libdazzle/${lib.versions.majorMinor finalAttrs.version}/libdazzle-${finalAttrs.version}.tar.xz";
     sha256 = "PNPkXrbiaAywXVLh6A3Y+dWdR2UhLw4o945sF4PRjq4=";
   };
 
@@ -75,7 +76,7 @@ stdenv.mkDerivation rec {
 
   passthru = {
     updateScript = gnome.updateScript {
-      packageName = pname;
+      packageName = "libdazzle";
     };
   };
 
@@ -94,4 +95,4 @@ stdenv.mkDerivation rec {
     teams = [ teams.gnome ];
     platforms = platforms.unix;
   };
-}
+})
diff --git a/pkgs/by-name/li/libevent/package.nix b/pkgs/by-name/li/libevent/package.nix
index 42c2d2fe120e..ff873db34dfe 100644
--- a/pkgs/by-name/li/libevent/package.nix
+++ b/pkgs/by-name/li/libevent/package.nix
@@ -12,12 +12,12 @@
   static ? stdenv.hostPlatform.isStatic,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "libevent";
   version = "2.1.12";
 
   src = fetchurl {
-    url = "https://github.com/libevent/libevent/releases/download/release-${version}-stable/libevent-${version}-stable.tar.gz";
+    url = "https://github.com/libevent/libevent/releases/download/release-${finalAttrs.version}-stable/libevent-${finalAttrs.version}-stable.tar.gz";
     sha256 = "1fq30imk8zd26x8066di3kpc5zyfc5z6frr3zll685zcx4dxxrlj";
   };
 
@@ -48,6 +48,7 @@ stdenv.mkDerivation rec {
     "dev"
   ] ++ lib.optional sslSupport "openssl";
   outputBin = "dev";
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
   propagatedBuildOutputs = [ "out" ] ++ lib.optional sslSupport "openssl";
 
   nativeBuildInputs = [
@@ -87,4 +88,4 @@ stdenv.mkDerivation rec {
     license = licenses.bsd3;
     platforms = platforms.all;
   };
-}
+})
diff --git a/pkgs/by-name/li/libgpg-error/package.nix b/pkgs/by-name/li/libgpg-error/package.nix
index 2270bea43e21..7fe5f777e659 100644
--- a/pkgs/by-name/li/libgpg-error/package.nix
+++ b/pkgs/by-name/li/libgpg-error/package.nix
@@ -23,12 +23,13 @@ let
   };
 in
 stdenv.mkDerivation (
-  rec {
+  finalAttrs:
+  {
     pname = "libgpg-error";
     version = "1.51";
 
     src = fetchurl {
-      url = "mirror://gnupg/${pname}/${pname}-${version}.tar.bz2";
+      url = "mirror://gnupg/libgpg-error/libgpg-error-${finalAttrs.version}.tar.bz2";
       hash = "sha256-vg8bLba5Pu1VNpzfefGfcnUMjHw5/CC1d+ckVFQn5rI=";
     };
 
@@ -49,6 +50,7 @@ stdenv.mkDerivation (
       "info"
     ];
     outputBin = "dev"; # deps want just the lib, most likely
+    passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
     # If architecture-dependent MO files aren't available, they're generated
     # during build, so we need gettext for cross-builds.
@@ -78,7 +80,7 @@ stdenv.mkDerivation (
       homepage = "https://www.gnupg.org/software/libgpg-error/index.html";
       changelog = "https://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgpg-error.git;a=blob;f=NEWS;hb=refs/tags/libgpg-error-${version}";
       description = "Small library that defines common error values for all GnuPG components";
-      mainProgram = "gen-posix-lock-obj";
+      mainProgram = if genPosixLockObjOnly then "gen-posix-lock-obj" else "gpg-error";
 
       longDescription = ''
         Libgpg-error is a small library that defines common error values
diff --git a/pkgs/by-name/li/liboil/package.nix b/pkgs/by-name/li/liboil/package.nix
index f9f9cdc5ea93..cbce87f4ed28 100644
--- a/pkgs/by-name/li/liboil/package.nix
+++ b/pkgs/by-name/li/liboil/package.nix
@@ -5,12 +5,12 @@
   pkg-config,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "liboil";
   version = "0.3.17";
 
   src = fetchurl {
-    url = "${meta.homepage}/download/liboil-${version}.tar.gz";
+    url = "${finalAttrs.meta.homepage}/download/liboil-${finalAttrs.version}.tar.gz";
     sha256 = "0sgwic99hxlb1av8cm0albzh8myb7r3lpcwxfm606l0bkc3h4pqh";
   };
 
@@ -22,6 +22,7 @@ stdenv.mkDerivation rec {
     "devdoc"
   ];
   outputBin = "dev"; # oil-bugreport
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   nativeBuildInputs = [ pkg-config ];
 
@@ -42,4 +43,4 @@ stdenv.mkDerivation rec {
     maintainers = with maintainers; [ lovek323 ];
     platforms = platforms.all;
   };
-}
+})
diff --git a/pkgs/by-name/li/libpanel/package.nix b/pkgs/by-name/li/libpanel/package.nix
index b577910323a3..14aa09a355f6 100644
--- a/pkgs/by-name/li/libpanel/package.nix
+++ b/pkgs/by-name/li/libpanel/package.nix
@@ -25,6 +25,7 @@ stdenv.mkDerivation (finalAttrs: {
   ];
 
   outputBin = "dev";
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   src = fetchurl {
     url = "mirror://gnome/sources/libpanel/${lib.versions.majorMinor finalAttrs.version}/libpanel-${finalAttrs.version}.tar.xz";
diff --git a/pkgs/by-name/li/libshumate/package.nix b/pkgs/by-name/li/libshumate/package.nix
index 6e9cbb678d06..94c9a3b35e35 100644
--- a/pkgs/by-name/li/libshumate/package.nix
+++ b/pkgs/by-name/li/libshumate/package.nix
@@ -31,6 +31,7 @@ stdenv.mkDerivation (finalAttrs: {
     "devdoc"
   ];
   outputBin = "devdoc"; # demo app
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   src = fetchurl {
     url = "mirror://gnome/sources/libshumate/${lib.versions.majorMinor finalAttrs.version}/libshumate-${finalAttrs.version}.tar.xz";
diff --git a/pkgs/by-name/on/oniguruma/package.nix b/pkgs/by-name/on/oniguruma/package.nix
index 4f6e11d6a0ad..9c210efd7d2a 100644
--- a/pkgs/by-name/on/oniguruma/package.nix
+++ b/pkgs/by-name/on/oniguruma/package.nix
@@ -5,13 +5,13 @@
   autoreconfHook,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "oniguruma";
   version = "6.9.10";
 
   # Note: do not use fetchpatch or fetchFromGitHub to keep this package available in __bootPackages
   src = fetchurl {
-    url = "https://github.com/kkos/oniguruma/releases/download/v${version}/onig-${version}.tar.gz";
+    url = "https://github.com/kkos/oniguruma/releases/download/v${finalAttrs.version}/onig-${finalAttrs.version}.tar.gz";
     sha256 = "sha256-Klz8WuJZ5Ol/hraN//wVLNr/6U4gYLdwy4JyONdp/AU=";
   };
 
@@ -21,6 +21,7 @@ stdenv.mkDerivation rec {
     "out"
   ];
   outputBin = "dev"; # onig-config
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   nativeBuildInputs = [ autoreconfHook ];
   configureFlags = [ "--enable-posix-api=yes" ];
@@ -33,4 +34,4 @@ stdenv.mkDerivation rec {
     maintainers = with maintainers; [ artturin ];
     platforms = platforms.unix;
   };
-}
+})
diff --git a/pkgs/by-name/on/onlyoffice-documentserver/package.nix b/pkgs/by-name/on/onlyoffice-documentserver/package.nix
index 0d828b87580c..38a5dfa8978f 100644
--- a/pkgs/by-name/on/onlyoffice-documentserver/package.nix
+++ b/pkgs/by-name/on/onlyoffice-documentserver/package.nix
@@ -16,17 +16,17 @@ let
   # var/www/onlyoffice/documentserver/server/DocService/docservice
   onlyoffice-documentserver = stdenv.mkDerivation rec {
     pname = "onlyoffice-documentserver";
-    version = "8.3.2";
+    version = "8.3.3";
 
     src = fetchurl (
       {
         "aarch64-linux" = {
           url = "https://github.com/ONLYOFFICE/DocumentServer/releases/download/v${version}/onlyoffice-documentserver_arm64.deb";
-          sha256 = "sha256-fyxk7FiBhTRTy8f5Wx6Rp0MPX45O5Q05ZS17Krp05P0=";
+          sha256 = "sha256-wF5TdBEpNXeE8SMTmvgjuOp713Vf9gIifsI1yeujuA0=";
         };
         "x86_64-linux" = {
           url = "https://github.com/ONLYOFFICE/DocumentServer/releases/download/v${version}/onlyoffice-documentserver_amd64.deb";
-          sha256 = "sha256-dBA/TlTwG+9eRY5QdqVw0cghnXPRNCUfs9QoaNFFLB0=";
+          sha256 = "sha256-zEI9R5AOkE1gMZHL209l6HOh/yfZgmEvMw8+hb9kC+s=";
         };
       }
       .${stdenv.hostPlatform.system} or (throw "unsupported system ${stdenv.hostPlatform.system}")
diff --git a/pkgs/by-name/os/osu-lazer-bin/package.nix b/pkgs/by-name/os/osu-lazer-bin/package.nix
index de1fa81330ee..b28f8cf6a87c 100644
--- a/pkgs/by-name/os/osu-lazer-bin/package.nix
+++ b/pkgs/by-name/os/osu-lazer-bin/package.nix
@@ -65,11 +65,16 @@ if stdenvNoCC.hostPlatform.isDarwin then
       passthru
       ;
 
+    nativeBuildInputs = [ makeWrapper ];
+
     installPhase = ''
       runHook preInstall
-      APP_DIR="$out/Applications"
-      mkdir -p "$APP_DIR"
-      cp -r . "$APP_DIR"
+      OSU_WRAPPER="$out/Applications/osu!.app/Contents"
+      OSU_CONTENTS="osu!.app/Contents"
+      mkdir -p "$OSU_WRAPPER/MacOS"
+      cp -r "$OSU_CONTENTS/Info.plist" "$OSU_CONTENTS/Resources" "$OSU_WRAPPER"
+      cp -r "osu!.app" "$OSU_WRAPPER/Resources/osu-wrapped.app"
+      makeWrapper "$OSU_WRAPPER/Resources/osu-wrapped.app/Contents/MacOS/osu!" "$OSU_WRAPPER/MacOS/osu!" --set OSU_EXTERNAL_UPDATE_PROVIDER 1
       runHook postInstall
     '';
   }
diff --git a/pkgs/by-name/os/osu-lazer-bin/update.sh b/pkgs/by-name/os/osu-lazer-bin/update.sh
index 0b06950762b5..30a1fb9bfe35 100755
--- a/pkgs/by-name/os/osu-lazer-bin/update.sh
+++ b/pkgs/by-name/os/osu-lazer-bin/update.sh
@@ -5,7 +5,7 @@ cd "$(dirname "${BASH_SOURCE[0]}")"
 
 bin_file="$(realpath ./package.nix)"
 
-new_version="$(curl -s "https://api.github.com/repos/ppy/osu/releases?per_page=1" | jq -r '.[0].name')"
+new_version="$(curl -s "https://api.github.com/repos/ppy/osu/releases/latest" | jq -r '.name')"
 old_version="$(sed -nE 's/\s*version = "(.*)".*/\1/p' ./package.nix)"
 if [[ "$new_version" == "$old_version" ]]; then
     echo "Already up to date."
diff --git a/pkgs/by-name/pi/pinact/package.nix b/pkgs/by-name/pi/pinact/package.nix
index 60d5fbc98252..0134285556d3 100644
--- a/pkgs/by-name/pi/pinact/package.nix
+++ b/pkgs/by-name/pi/pinact/package.nix
@@ -13,16 +13,16 @@ let
 in
 buildGoModule (finalAttrs: {
   pname = "pinact";
-  version = "3.1.2";
+  version = "3.3.0";
 
   src = fetchFromGitHub {
     owner = "suzuki-shunsuke";
     repo = "pinact";
     tag = "v${finalAttrs.version}";
-    hash = "sha256-5jJzlMMpfk9fFDoqR0NJNacquZ4Zma0YF/pi80Miv0Y=";
+    hash = "sha256-aNRDz0mAc5fsS01W0PZxCGw0NgEcExtciTcv/Omdv3g=";
   };
 
-  vendorHash = "sha256-kK4r0mCktlbhJr6iHD0Q/k1DralieN2AUg+zREZ06DA=";
+  vendorHash = "sha256-eqT92vK8Ah7glS/O5rWp+wK/apGwC61/GIZRUtpmNFo=";
 
   env.CGO_ENABLED = 0;
 
diff --git a/pkgs/by-name/pk/pkgsite/package.nix b/pkgs/by-name/pk/pkgsite/package.nix
index a90dd4133572..f1cd15b9e76a 100644
--- a/pkgs/by-name/pk/pkgsite/package.nix
+++ b/pkgs/by-name/pk/pkgsite/package.nix
@@ -7,13 +7,13 @@
 
 buildGoModule {
   pname = "pkgsite";
-  version = "0-unstable-2025-06-08";
+  version = "0-unstable-2025-06-11";
 
   src = fetchFromGitHub {
     owner = "golang";
     repo = "pkgsite";
-    rev = "82c52f1754cd0ea741a56981d4830176071531d3";
-    hash = "sha256-bI5jVmCM5pSdiT+OJGrg1pBQ6ozPbXdZzrdLxr9cMUU=";
+    rev = "041c7c0b878cb88962867185208d4d2ec79de7d0";
+    hash = "sha256-dyBOdUwod03c8eU1qfJecSDyKzol//yFpANCOihiseo=";
   };
 
   vendorHash = "sha256-dZKm3dMI969HKPBrC95vVmY1cZmjy+NWq7xOzXsTE14=";
diff --git a/pkgs/by-name/sd/sdl2-compat/package.nix b/pkgs/by-name/sd/sdl2-compat/package.nix
index 20783cab0638..96366f7d187f 100644
--- a/pkgs/by-name/sd/sdl2-compat/package.nix
+++ b/pkgs/by-name/sd/sdl2-compat/package.nix
@@ -79,6 +79,8 @@ stdenv.mkDerivation (finalAttrs: {
   '';
 
   passthru = {
+    bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
+
     tests =
       {
         pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage;
@@ -111,6 +113,7 @@ stdenv.mkDerivation (finalAttrs: {
     homepage = "https://libsdl.org";
     changelog = "https://github.com/libsdl-org/sdl2-compat/releases/tag/${finalAttrs.src.tag}";
     license = lib.licenses.zlib;
+    mainProgram = "sdl2-config";
     maintainers = with lib.maintainers; [
       nadiaholmquist
     ];
diff --git a/pkgs/by-name/sl/slackdump/package.nix b/pkgs/by-name/sl/slackdump/package.nix
index 4edf9e05cda1..503f1d431965 100644
--- a/pkgs/by-name/sl/slackdump/package.nix
+++ b/pkgs/by-name/sl/slackdump/package.nix
@@ -9,13 +9,13 @@
 
 buildGoModule rec {
   pname = "slackdump";
-  version = "3.1.3";
+  version = "3.1.4";
 
   src = fetchFromGitHub {
     owner = "rusq";
     repo = "slackdump";
     tag = "v${version}";
-    hash = "sha256-Ophs/HLdjwVPn8Q6Jng2F/GKp+Dmy8ULxGJm9L7IQXI=";
+    hash = "sha256-unJe3hTIYmQMAvyY0N1S2JiyTtOSaKaJSqE2C9LqDh0=";
   };
 
   nativeCheckInputs = lib.optional stdenv.hostPlatform.isDarwin darwin.IOKitTools;
@@ -32,7 +32,7 @@ buildGoModule rec {
       "-skip=^${builtins.concatStringsSep "$|^" skippedTests}$"
     ];
 
-  vendorHash = "sha256-iT5hCLOSWuquNsaSe3Wra6LsJeRF4NvI3+NXYkPoLEI=";
+  vendorHash = "sha256-Uy/l3eZSWqVeFKFr6Sc+0h8UVzLGmRXVF6sbX6tnXWA=";
 
   __darwinAllowLocalNetworking = true;
 
diff --git a/pkgs/by-name/sn/snipe-it/package.nix b/pkgs/by-name/sn/snipe-it/package.nix
index caa9947b57c7..7b2053b09310 100644
--- a/pkgs/by-name/sn/snipe-it/package.nix
+++ b/pkgs/by-name/sn/snipe-it/package.nix
@@ -9,16 +9,16 @@
 
 php84.buildComposerProject2 (finalAttrs: {
   pname = "snipe-it";
-  version = "8.1.15";
+  version = "8.1.16";
 
   src = fetchFromGitHub {
     owner = "grokability";
     repo = "snipe-it";
     tag = "v${finalAttrs.version}";
-    hash = "sha256-VcFUpG8ZPi/Dt80v0RR3bpdJ5IJci47dpw4sPRjVyh0=";
+    hash = "sha256-Eo0Z6aWbNniOcPIjsgWwy9d9TXfyYZPK3AtVxbAcjac=";
   };
 
-  vendorHash = "sha256-iYKi3dit/nNFYB8Kk2xXWi+p90YEwhwEV9EVVdVV47o=";
+  vendorHash = "sha256-bQFNpms8l85d74HoTEPx2fHZxbcjtbf1MoKj4lX3AMk=";
 
   postInstall = ''
     snipe_it_out="$out/share/php/snipe-it"
diff --git a/pkgs/by-name/ti/tic-80/package.nix b/pkgs/by-name/ti/tic-80/package.nix
index c214e5f9fb75..c42152b9f97b 100644
--- a/pkgs/by-name/ti/tic-80/package.nix
+++ b/pkgs/by-name/ti/tic-80/package.nix
@@ -9,49 +9,36 @@
   fetchFromGitHub,
   libGL,
   libGLU,
-  alsa-lib,
   libX11,
-  libICE,
-  libXi,
-  libXScrnSaver,
-  libXcursor,
-  libXinerama,
-  libXext,
-  libXxf86vm,
-  libXrandr,
-  libxkbcommon,
-  wayland,
-  wayland-protocols,
-  wayland-scanner,
-  dbus,
-  udev,
-  libdecor,
-  pipewire,
-  libpulseaudio,
+  janet,
+  lua5_3,
+  quickjs,
+  SDL2,
   # Whether to build TIC-80's "Pro" version, which is an incentive to support the project financially,
   # that enables some additional features. It is, however, fully open source.
   withPro ? false,
 }:
 let
-  major = "1";
-  minor = "1";
-  revision = "2837";
-  year = "2023";
+  # git rev-list HEAD --count
+  revision = "3016";
+  year = "2025";
 in
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation {
   pname = "tic-80";
-  version = "${major}.${minor}.${revision}";
+  # use an untagged version until upstream tags a new version. We want
+  # 'PREFER_SYSTEM_LIBRARIES', and without it tic-80 won't build
+  version = "1.1-unstable-2025-05-26";
 
   src = fetchFromGitHub {
     owner = "nesbox";
     repo = "TIC-80";
-    rev = "v" + version;
-    hash = "sha256-p7OyuD/4KxAzylQDlXW681TvEZwKYDD4zq2KDRkcv48=";
-    # TIC-80 vendors its dependencies as submodules, so to use its current build system,
-    # we need to fetch them. Managing the dependencies ourselves would require a lot of
-    # changes in the build system, which doesn't seem worth it right now. In future versions,
-    # TIC-80 is switching to more modular CMake files, at which point we can reconsider.
+    rev = "663d43924abf6fd7620de6bf25c009ce5b30ab83";
+    hash = "sha256-UjBnXxYZ5gfk58sI1qek5fkKpJ7LzOVmrxdjVgONcXc=";
+    # TIC-80 vendors its dependencies as submodules. For the following dependencies,
+    # there are no (or no compatible) packages in nixpkgs yet, so we use the vendored
+    # ones as a fill-in: kubazip, wasm, squirrel, pocketpy, argparse, naett,
+    # sdlgpu, mruby.
     fetchSubmodules = true;
   };
 
@@ -61,7 +48,7 @@ stdenv.mkDerivation rec {
   # To avoid the awkward copyright range of "2017-1980", which would be caused by the
   # sandbox environment, hardcode the year of the release.
   postPatch = ''
-    substituteInPlace CMakeLists.txt \
+    substituteInPlace cmake/version.cmake \
       --replace-fail 'set(VERSION_REVISION 0)' 'set(VERSION_REVISION ${revision})' \
       --replace-fail 'string(TIMESTAMP VERSION_YEAR "%Y")' 'set(VERSION_YEAR "${year}")'
   '';
@@ -72,7 +59,20 @@ stdenv.mkDerivation rec {
     unset LD
   '';
 
-  cmakeFlags = lib.optionals withPro [ "-DBUILD_PRO=On" ] ++ [ "-DBUILD_SDLGPU=On" ];
+  cmakeFlags =
+    let
+      enableCmakeBool = (lib.flip lib.cmakeBool) true;
+    in
+    [
+      (lib.cmakeBool "BUILD_PRO" withPro)
+    ]
+    ++ (map enableCmakeBool [
+      "BUILD_STATIC"
+      "PREFER_SYSTEM_LIBRARIES"
+      "BUILD_SDLGPU"
+      "BUILD_WITH_ALL"
+    ]);
+
   nativeBuildInputs = [
     cmake
     curl
@@ -81,54 +81,15 @@ stdenv.mkDerivation rec {
     rake
   ];
   buildInputs = [
-    alsa-lib
-    dbus
-    libdecor
     libGL
     libGLU
-    libICE
-    libpulseaudio
     libX11
-    libXcursor
-    libXext
-    libXi
-    libXinerama
-    libxkbcommon
-    libXrandr
-    libXScrnSaver
-    libXxf86vm
-    pipewire
-    udev
-    wayland
-    wayland-protocols
-    wayland-scanner
+    janet
+    (lua5_3.withPackages (ps: [ ps.fennel ]))
+    quickjs
+    SDL2
   ];
 
-  # This package borrows heavily from pkgs/development/libraries/SDL2/default.nix
-  # because TIC-80 vendors SDL2, which means we need to take care and implement
-  # a similar environment in TIC-80's vendored copy of SDL2.
-  #
-  # SDL is weird in that instead of just dynamically linking with
-  # libraries when you `--enable-*` (or when `configure` finds) them
-  # it `dlopen`s them at runtime. In principle, this means it can
-  # ignore any missing optional dependencies like alsa, pulseaudio,
-  # some x11 libs, wayland, etc if they are missing on the system
-  # and/or work with wide array of versions of said libraries. In
-  # nixpkgs, however, we don't need any of that. Moreover, since we
-  # don't have a global ld-cache we have to stuff all the propagated
-  # libraries into rpath by hand or else some applications that use
-  # SDL API that requires said libraries will fail to start.
-  #
-  # You can grep SDL sources with `grep -rE 'SDL_(NAME|.*_SYM)'` to
-  # list the symbols used in this way.
-  postFixup =
-    let
-      rpath = lib.makeLibraryPath buildInputs;
-    in
-    ''
-      patchelf --set-rpath "$(patchelf --print-rpath $out/bin/tic80):${rpath}" "$out/bin/tic80"
-    '';
-
   meta = with lib; {
     description = "Free and open source fantasy computer for making, playing and sharing tiny games";
     longDescription = ''
@@ -151,7 +112,5 @@ stdenv.mkDerivation rec {
     platforms = platforms.linux;
     mainProgram = "tic80";
     maintainers = with maintainers; [ blinry ];
-    # /build/source/vendor/sdl2/src/audio/pipewire/SDL_pipewire.c:623:37: error: passing argument 1 of 'pw_node_enum_params' from incompatible pointer type [-Wincompatible-pointer-types]
-    broken = true;
   };
 }
diff --git a/pkgs/by-name/wo/worker-build/package.nix b/pkgs/by-name/wo/worker-build/package.nix
index 72be72e33f8d..587da6e4de9f 100644
--- a/pkgs/by-name/wo/worker-build/package.nix
+++ b/pkgs/by-name/wo/worker-build/package.nix
@@ -6,18 +6,18 @@
 
 rustPlatform.buildRustPackage rec {
   pname = "worker-build";
-  version = "0.5.0";
+  version = "0.6.0";
 
   src = fetchFromGitHub {
     owner = "cloudflare";
     repo = "workers-rs";
     tag = "v${version}";
-    hash = "sha256-eMuuEqHBiwgz7DKimYuK9MUPT4vnOU8rLOIIq8zsTao=";
+    hash = "sha256-wsH16hkiaTthE2FwQ8Ma2qQhkunq2rxkZXPEYR7P0Io=";
   };
 
   useFetchCargoVendor = true;
 
-  cargoHash = "sha256-yzuyenWAdj5tEdUFGxSfBU4m3w1YCCrnbajPMYMGSkI=";
+  cargoHash = "sha256-ZuO020orJBJWm3Q+39MnkQ96rsv9juicUeMtBBVWxcg=";
 
   buildAndTestSubdir = "worker-build";
 
diff --git a/pkgs/by-name/zo/zoom-us/package.nix b/pkgs/by-name/zo/zoom-us/package.nix
index d96f9e5556d8..1ba939715d19 100644
--- a/pkgs/by-name/zo/zoom-us/package.nix
+++ b/pkgs/by-name/zo/zoom-us/package.nix
@@ -61,23 +61,23 @@ let
   # and often with different versions. We write them on three lines
   # like this (rather than using {}) so that the updater script can
   # find where to edit them.
-  versions.aarch64-darwin = "6.4.10.56141";
-  versions.x86_64-darwin = "6.4.10.56141";
-  versions.x86_64-linux = "6.4.10.2027";
+  versions.aarch64-darwin = "6.4.12.56699";
+  versions.x86_64-darwin = "6.4.12.56699";
+  versions.x86_64-linux = "6.4.13.2309";
 
   srcs = {
     aarch64-darwin = fetchurl {
       url = "https://zoom.us/client/${versions.aarch64-darwin}/zoomusInstallerFull.pkg?archType=arm64";
       name = "zoomusInstallerFull.pkg";
-      hash = "sha256-LIQl+s/2WfYFIEG/ZsvpWlsWRhToB+5+ymAXCMhDqWE=";
+      hash = "sha256-rsO4HAvA6hCiGDBuLQj/qYWHR6Dlo+G9rkfhxvKBp4g=";
     };
     x86_64-darwin = fetchurl {
       url = "https://zoom.us/client/${versions.x86_64-darwin}/zoomusInstallerFull.pkg";
-      hash = "sha256-jP9ajDCo8iImS8YGFLjNMOLLh9g8uSqYIRl3aqhJAaM=";
+      hash = "sha256-MZ5dPHKH1uQuFA8Vej8Hh4CFZAjJFZe04le+e4LPDJc=";
     };
     x86_64-linux = fetchurl {
       url = "https://zoom.us/client/${versions.x86_64-linux}/zoom_x86_64.pkg.tar.xz";
-      hash = "sha256-BwYO8IlQJjZwwn/qokZ+gAgcgmAjG34uExHCajchVqs=";
+      hash = "sha256-gBUpsIUcsn+5u/1CchuS9mggnAFD8VW5J4RBv0Ziu+Y=";
     };
   };
 
diff --git a/pkgs/development/libraries/apr/default.nix b/pkgs/development/libraries/apr/default.nix
index 8ded7258aca7..3d70cc9b5055 100644
--- a/pkgs/development/libraries/apr/default.nix
+++ b/pkgs/development/libraries/apr/default.nix
@@ -6,12 +6,12 @@
   autoreconfHook,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "apr";
   version = "1.7.6";
 
   src = fetchurl {
-    url = "mirror://apache/apr/${pname}-${version}.tar.bz2";
+    url = "mirror://apache/apr/apr-${finalAttrs.version}.tar.bz2";
     hash = "sha256-SQMNktJXXac1eRtJbcMi885c/5SUd5uozCjH9Gxd6zI=";
   };
 
@@ -29,6 +29,7 @@ stdenv.mkDerivation rec {
     "dev"
   ];
   outputBin = "dev";
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   preConfigure = ''
     configureFlagsArray+=("--with-installbuilddir=$dev/share/build")
@@ -82,4 +83,4 @@ stdenv.mkDerivation rec {
     license = licenses.asl20;
     maintainers = [ ];
   };
-}
+})
diff --git a/pkgs/development/libraries/libhandy/0.x.nix b/pkgs/development/libraries/libhandy/0.x.nix
index 9df815ce2e6c..f49773d044bb 100644
--- a/pkgs/development/libraries/libhandy/0.x.nix
+++ b/pkgs/development/libraries/libhandy/0.x.nix
@@ -18,7 +18,7 @@
   hicolor-icon-theme,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "libhandy";
   version = "0.0.13";
 
@@ -28,12 +28,13 @@ stdenv.mkDerivation rec {
     "devdoc"
   ];
   outputBin = "dev";
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   src = fetchFromGitLab {
     domain = "source.puri.sm";
     owner = "Librem5";
-    repo = pname;
-    rev = "v${version}";
+    repo = "libhandy";
+    tag = "v${finalAttrs.version}";
     sha256 = "1y23k623sjkldfrdiwfarpchg5mg58smcy1pkgnwfwca15wm1ra5";
   };
 
@@ -83,4 +84,4 @@ stdenv.mkDerivation rec {
     maintainers = [ ];
     platforms = platforms.unix;
   };
-}
+})
diff --git a/pkgs/development/libraries/libhandy/default.nix b/pkgs/development/libraries/libhandy/default.nix
index f6fdf43b56b0..88abf888f1da 100644
--- a/pkgs/development/libraries/libhandy/default.nix
+++ b/pkgs/development/libraries/libhandy/default.nix
@@ -25,7 +25,7 @@
   runCommand,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "libhandy";
   version = "1.8.3";
 
@@ -41,7 +41,7 @@ stdenv.mkDerivation rec {
   outputBin = "dev";
 
   src = fetchurl {
-    url = "mirror://gnome/sources/${pname}/${lib.versions.majorMinor version}/${pname}-${version}.tar.xz";
+    url = "mirror://gnome/sources/libhandy/${lib.versions.majorMinor finalAttrs.version}/libhandy-${finalAttrs.version}.tar.xz";
     hash = "sha256-BbSXIpBz/1V/ELMm4HTFBm+HQ6MC1IIKuXvLXNLasIc=";
   };
 
@@ -122,8 +122,9 @@ stdenv.mkDerivation rec {
 
   passthru =
     {
+      bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
       updateScript = gnome.updateScript {
-        packageName = pname;
+        packageName = "libhandy";
         versionPolicy = "odd-unstable";
       };
     }
@@ -150,4 +151,4 @@ stdenv.mkDerivation rec {
     teams = [ teams.gnome ];
     platforms = platforms.unix;
   };
-}
+})
diff --git a/pkgs/development/libraries/libusb-compat/0.1.nix b/pkgs/development/libraries/libusb-compat/0.1.nix
index 904848cf78fb..0c840af64b68 100644
--- a/pkgs/development/libraries/libusb-compat/0.1.nix
+++ b/pkgs/development/libraries/libusb-compat/0.1.nix
@@ -7,7 +7,7 @@
   libusb1,
 }:
 
-stdenv.mkDerivation rec {
+stdenv.mkDerivation (finalAttrs: {
   pname = "libusb-compat";
   version = "0.1.8";
 
@@ -16,11 +16,12 @@ stdenv.mkDerivation rec {
     "dev"
   ]; # get rid of propagating systemd closure
   outputBin = "dev";
+  passthru.bin = finalAttrs.finalPackage.${finalAttrs.outputBin}; # fixes lib.getExe
 
   src = fetchFromGitHub {
     owner = "libusb";
     repo = "libusb-compat-0.1";
-    rev = "v${version}";
+    tag = "v${finalAttrs.version}";
     sha256 = "sha256-pAPERYSxoc47gwpPUoMkrbK8TOXyx03939vlFN0hHRg=";
   };
 
@@ -36,7 +37,7 @@ stdenv.mkDerivation rec {
   # without this, libusb-compat is unable to find libusb1
   postFixup = ''
     find $out/lib -name \*.so\* -type f -exec \
-      patchelf --set-rpath ${lib.makeLibraryPath buildInputs} {} \;
+      patchelf --set-rpath ${lib.makeLibraryPath finalAttrs.buildInputs} {} \;
   '';
 
   meta = with lib; {
@@ -50,4 +51,4 @@ stdenv.mkDerivation rec {
     license = licenses.lgpl2Plus;
     platforms = platforms.unix;
   };
-}
+})
diff --git a/pkgs/development/python-modules/fpylll/default.nix b/pkgs/development/python-modules/fpylll/default.nix
index f8539942e455..d0ddbf91ceda 100644
--- a/pkgs/development/python-modules/fpylll/default.nix
+++ b/pkgs/development/python-modules/fpylll/default.nix
@@ -24,14 +24,14 @@
 
 buildPythonPackage rec {
   pname = "fpylll";
-  version = "0.6.3";
+  version = "0.6.4";
   pyproject = true;
 
   src = fetchFromGitHub {
     owner = "fplll";
     repo = "fpylll";
     tag = version;
-    hash = "sha256-3+DXfCUuHQG+VSzJGEPa8qP6oxC+nngMa44XyFCJAVY=";
+    hash = "sha256-vks4rTXk6fh8183PCxJzfTXQyo3scBH4afjbQAkT6Gw=";
   };
 
   nativeBuildInputs = [
diff --git a/pkgs/development/python-modules/posthog/default.nix b/pkgs/development/python-modules/posthog/default.nix
index 944af3c4f07c..99e95ad0011d 100644
--- a/pkgs/development/python-modules/posthog/default.nix
+++ b/pkgs/development/python-modules/posthog/default.nix
@@ -19,14 +19,14 @@
 
 buildPythonPackage rec {
   pname = "posthog";
-  version = "4.2.0";
+  version = "5.0.0";
   pyproject = true;
 
   src = fetchFromGitHub {
     owner = "PostHog";
     repo = "posthog-python";
     tag = "v${version}";
-    hash = "sha256-RpD4+NuClYmmXCn9eBa2oxMW3TwvVZcTkgaV+mNOkYU=";
+    hash = "sha256-pNnttrp6s9T+tmDFJ9S3DZ/HcMTifYkr6Rs8E/8+G5c=";
   };
 
   build-system = [ setuptools ];
diff --git a/pkgs/development/tools/build-managers/gradle/default.nix b/pkgs/development/tools/build-managers/gradle/default.nix
index fa48f16d9972..69547ad9d803 100644
--- a/pkgs/development/tools/build-managers/gradle/default.nix
+++ b/pkgs/development/tools/build-managers/gradle/default.nix
@@ -225,14 +225,14 @@ rec {
   # https://docs.gradle.org/current/userguide/compatibility.html
 
   gradle_8 = gen {
-    version = "8.14.1";
-    hash = "sha256-hFlSqdavp4PbcLs7Dv+q5FrlVCyiu3kpYZ6K9Jy2NM8=";
+    version = "8.14.2";
+    hash = "sha256-cZehL0UHlJMVMkadT/IaWeosHNWaPsP4nANcPEIKaZk=";
     defaultJava = jdk21;
   };
 
   gradle_7 = gen {
-    version = "7.6.4";
-    hash = "sha256-vtHaM8yg9VerE2kcd/OLtnOIEZ5HlNET4FEDm4Cvm7E=";
+    version = "7.6.5";
+    hash = "sha256-uBL+wO230n4K41lViHuylUU2+j5E7a9IEVDaBY4VTZo=";
     defaultJava = jdk17;
   };
 
diff --git a/pkgs/kde/gear/kasts/default.nix b/pkgs/kde/gear/kasts/default.nix
index f95ea1c80892..3f7dc2de80cc 100644
--- a/pkgs/kde/gear/kasts/default.nix
+++ b/pkgs/kde/gear/kasts/default.nix
@@ -3,7 +3,7 @@
   qtsvg,
   qtmultimedia,
   pkg-config,
-  taglib,
+  taglib_1,
   libvlc,
 }:
 mkKdeDerivation {
@@ -13,7 +13,7 @@ mkKdeDerivation {
   extraBuildInputs = [
     qtsvg
     qtmultimedia
-    taglib
+    taglib_1
     libvlc
   ];
   meta.mainProgram = "kasts";
diff --git a/pkgs/servers/monitoring/grafana/plugins/victoriametrics-metrics-datasource/default.nix b/pkgs/servers/monitoring/grafana/plugins/victoriametrics-metrics-datasource/default.nix
index 05d0717d8000..37ce8cd0df44 100644
--- a/pkgs/servers/monitoring/grafana/plugins/victoriametrics-metrics-datasource/default.nix
+++ b/pkgs/servers/monitoring/grafana/plugins/victoriametrics-metrics-datasource/default.nix
@@ -2,8 +2,8 @@
 
 grafanaPlugin {
   pname = "victoriametrics-metrics-datasource";
-  version = "0.14.0";
-  zipHash = "sha256-V3sXibYtIZGQh/nBkhwdpIsPF0buoJ16l2ML2s7ijj0=";
+  version = "0.16.0";
+  zipHash = "sha256-Oy++CDFAdG2wlAkxzDKWUX6PVX+t47tZBImUEw+XUho=";
   meta = {
     description = "VictoriaMetrics metrics datasource for Grafana";
     license = lib.licenses.agpl3Only;
diff --git a/pkgs/servers/nextcloud/packages/30.json b/pkgs/servers/nextcloud/packages/30.json
index 797455c8014b..c8cd08989241 100644
--- a/pkgs/servers/nextcloud/packages/30.json
+++ b/pkgs/servers/nextcloud/packages/30.json
@@ -239,6 +239,16 @@
       "agpl"
     ]
   },
+  "nextpod": {
+    "hash": "sha256-yQD4e5R6ZfBQkEsPVpddGMLDVOlV6HSVZjttgUjEdro=",
+    "url": "https://github.com/pbek/nextcloud-nextpod/releases/download/v0.7.7/nextpod-nc.tar.gz",
+    "version": "0.7.7",
+    "description": "This Nextcloud app lets you visualize your podcast subscriptions and episode downloads from\n[GPodderSync](https://apps.nextcloud.com/apps/gpoddersync), which acts as a basic gpodder.net\napi to sync podcast consumer apps (podcatchers) like AntennaPod.\n\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!",
+    "homepage": "https://github.com/pbek/nextcloud-nextpod",
+    "licenses": [
+      "agpl"
+    ]
+  },
   "notes": {
     "hash": "sha256-/Zym7bNotcdradtR3cG+rIaAH9jZs+/3PCP7zXS6WJo=",
     "url": "https://github.com/nextcloud-releases/notes/releases/download/v4.12.1/notes-v4.12.1.tar.gz",
diff --git a/pkgs/servers/nextcloud/packages/31.json b/pkgs/servers/nextcloud/packages/31.json
index dfa8dbdd3f90..e9f37dbdf021 100644
--- a/pkgs/servers/nextcloud/packages/31.json
+++ b/pkgs/servers/nextcloud/packages/31.json
@@ -239,6 +239,16 @@
       "agpl"
     ]
   },
+  "nextpod": {
+    "hash": "sha256-yQD4e5R6ZfBQkEsPVpddGMLDVOlV6HSVZjttgUjEdro=",
+    "url": "https://github.com/pbek/nextcloud-nextpod/releases/download/v0.7.7/nextpod-nc.tar.gz",
+    "version": "0.7.7",
+    "description": "This Nextcloud app lets you visualize your podcast subscriptions and episode downloads from\n[GPodderSync](https://apps.nextcloud.com/apps/gpoddersync), which acts as a basic gpodder.net\napi to sync podcast consumer apps (podcatchers) like AntennaPod.\n\nYou need to have [GPodderSync](https://apps.nextcloud.com/apps/gpoddersync) installed to use this app!",
+    "homepage": "https://github.com/pbek/nextcloud-nextpod",
+    "licenses": [
+      "agpl"
+    ]
+  },
   "notes": {
     "hash": "sha256-/Zym7bNotcdradtR3cG+rIaAH9jZs+/3PCP7zXS6WJo=",
     "url": "https://github.com/nextcloud-releases/notes/releases/download/v4.12.1/notes-v4.12.1.tar.gz",
diff --git a/pkgs/servers/nextcloud/packages/nextcloud-apps.json b/pkgs/servers/nextcloud/packages/nextcloud-apps.json
index 0b406f9a7ed6..980f151a7a09 100644
--- a/pkgs/servers/nextcloud/packages/nextcloud-apps.json
+++ b/pkgs/servers/nextcloud/packages/nextcloud-apps.json
@@ -25,6 +25,7 @@
 , "memories": "agpl3Plus"
 , "music": "agpl3Plus"
 , "news": "agpl3Plus"
+, "nextpod": "agpl3Only"
 , "notes": "agpl3Plus"
 , "oidc_login": "agpl3Only"
 , "onlyoffice": "asl20"