···66name: "Label PR"
7788on:
99+ schedule:
1010+ - cron: '37 * * * *'
911 workflow_call:
1010- workflow_run:
1111- workflows:
1212- - Review dismissed
1313- - Review submitted
1414- types: [completed]
1212+ workflow_dispatch:
1313+ inputs:
1414+ updatedWithin:
1515+ description: 'Updated within [hours]'
1616+ type: number
1717+ required: false
1818+ default: 0 # everything since last run
15191620concurrency:
1717- group: labels-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
1818- cancel-in-progress: true
2121+ # This explicitly avoids using `run_id` for the concurrency key to make sure that only
2222+ # *one* non-PR run can run at a time.
2323+ group: labels-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number }}
2424+ # PR- and manually-triggered runs will be cancelled, but scheduled runs will be queued.
2525+ cancel-in-progress: ${{ github.event_name != 'schedule' }}
19262027permissions:
2128 issues: write # needed to create *new* labels
···3138 runs-on: ubuntu-24.04-arm
3239 if: "!contains(github.event.pull_request.title, '[skip treewide]')"
3340 steps:
3434- - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
3535- id: eval
3636- with:
3737- script: |
3838- const run_id = (await github.rest.actions.listWorkflowRuns({
3939- owner: context.repo.owner,
4040- repo: context.repo.repo,
4141- workflow_id: 'eval.yml',
4242- event: 'pull_request_target',
4343- head_sha: context.payload.pull_request?.head.sha ?? context.payload.workflow_run.head_sha
4444- })).data.workflow_runs[0]?.id
4545- core.setOutput('run-id', run_id)
4646-4747- - name: Download the comparison results
4848- if: steps.eval.outputs.run-id
4949- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
5050- with:
5151- run-id: ${{ steps.eval.outputs.run-id }}
5252- github-token: ${{ github.token }}
5353- pattern: comparison
5454- path: comparison
5555- merge-multiple: true
4141+ - name: Install dependencies
4242+ run: npm install @actions/artifact
56435757- - name: Labels from eval
5858- if: steps.eval.outputs.run-id && github.event_name != 'pull_request'
4444+ - name: Labels from API data and Eval results
5945 uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
4646+ env:
4747+ UPDATED_WITHIN: ${{ inputs.updatedWithin }}
6048 with:
6149 script: |
5050+ const path = require('node:path')
5151+ const { DefaultArtifactClient } = require('@actions/artifact')
6252 const { readFile } = require('node:fs/promises')
63536464- let pull_requests
6565- if (context.payload.workflow_run) {
6666- // PRs from forks don't have any PRs associated by default.
6767- // Thus, we request the PR number with an API call *to* the fork's repo.
6868- // Multiple pull requests can be open from the same head commit, either via
6969- // different base branches or head branches.
7070- const { head_repository, head_sha, repository } = context.payload.workflow_run
7171- pull_requests = (await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit, {
7272- owner: head_repository.owner.login,
7373- repo: head_repository.name,
7474- commit_sha: head_sha
7575- })).filter(pull_request => pull_request.base.repo.id == repository.id)
7676- } else {
7777- pull_requests = [ context.payload.pull_request ]
5454+ const artifactClient = new DefaultArtifactClient()
5555+5656+ if (process.env.UPDATED_WITHIN && !/^\d+$/.test(process.env.UPDATED_WITHIN))
5757+ throw new Error('Please enter "updated within" as integer in hours.')
5858+5959+ const cutoff = new Date(await (async () => {
6060+ // Always run for Pull Request triggers, no cutoff since there will be a single
6161+ // response only anyway. 0 is the Unix epoch, so always smaller.
6262+ if (context.payload.pull_request?.number) return 0
6363+6464+ // Manually triggered via UI when updatedWithin is set. Will fallthrough to the last
6565+ // option if the updatedWithin parameter is set to 0, which is the default.
6666+ const updatedWithin = Number.parseInt(process.env.UPDATED_WITHIN, 10)
6767+ if (updatedWithin) return new Date().getTime() - updatedWithin * 60 * 60 * 1000
6868+6969+ // Normally a scheduled run, but could be workflow_dispatch, see above. Go back as far
7070+ // as the last successful run of this workflow to make sure we are not leaving anyone
7171+ // behind on GHA failures.
7272+ // Defaults to go back 1 hour on the first run.
7373+ return (await github.rest.actions.listWorkflowRuns({
7474+ ...context.repo,
7575+ workflow_id: 'labels.yml',
7676+ event: 'schedule',
7777+ status: 'success',
7878+ exclude_pull_requests: true
7979+ })).data.workflow_runs[0]?.created_at ?? new Date().getTime() - 1 * 60 * 60 * 1000
8080+ })())
8181+ core.info('cutoff timestamp: ' + cutoff.toISOString())
8282+8383+ // To simplify this action's logic we fetch the pull_request data again below, even if
8484+ // we are already in a pull_request event's context and would have the data readily
8585+ // available. We do this by filtering the list of pull requests with head and base
8686+ // branch - there can only be a single open Pull Request for any such combination.
8787+ const prEventCondition = !context.payload.pull_request ? undefined : {
8888+ // "label" is in the format of `user:branch` or `org:branch`
8989+ head: context.payload.pull_request.head.label,
9090+ base: context.payload.pull_request.base.ref
7891 }
79928080- await Promise.all(
8181- pull_requests.map(async (pull_request) => {
8282- const pr = {
8383- owner: context.repo.owner,
8484- repo: context.repo.repo,
8585- issue_number: pull_request.number
8686- }
9393+ await github.paginate(
9494+ github.rest.pulls.list,
9595+ {
9696+ ...context.repo,
9797+ state: 'open',
9898+ sort: 'updated',
9999+ direction: 'desc',
100100+ ...prEventCondition
101101+ },
102102+ async (response, done) => (await Promise.allSettled(response.data.map(async (pull_request) => {
103103+ try {
104104+ const log = (k,v) => core.info(`PR #${pull_request.number} - ${k}: ${v}`)
871058888- // Get all currently set labels that we manage
8989- const before =
9090- (await github.paginate(github.rest.issues.listLabelsOnIssue, pr))
9191- .map(({ name }) => name)
9292- .filter(name =>
9393- name.startsWith('10.rebuild') ||
9494- name == '11.by: package-maintainer' ||
9595- name.startsWith('12.approvals:') ||
9696- name == '12.approved-by: package-maintainer'
9797- )
106106+ log('Last updated at', pull_request.updated_at)
107107+ if (new Date(pull_request.updated_at) < cutoff) return done()
981089999- const approvals = new Set(
100100- (await github.paginate(github.rest.pulls.listReviews, {
101101- owner: context.repo.owner,
102102- repo: context.repo.repo,
103103- pull_number: pull_request.number
104104- }))
105105- .filter(review => review.state == 'APPROVED')
106106- .map(review => review.user.id)
107107- )
109109+ const run_id = (await github.rest.actions.listWorkflowRuns({
110110+ ...context.repo,
111111+ workflow_id: 'eval.yml',
112112+ event: 'pull_request_target',
113113+ // For PR events, the workflow run is still in progress with this job itself.
114114+ status: prEventCondition ? 'in_progress' : 'success',
115115+ exclude_pull_requests: true,
116116+ head_sha: pull_request.head.sha
117117+ })).data.workflow_runs[0]?.id
108118109109- const maintainers = new Set(Object.keys(
110110- JSON.parse(await readFile('comparison/maintainers.json', 'utf-8'))
111111- ))
119119+ // Newer PRs might not have run Eval to completion, yet. We can skip them, because this
120120+ // job will be run as part of that Eval run anyway.
121121+ log('Last eval run', run_id)
122122+ if (!run_id) return;
112123113113- // And the labels that should be there
114114- const after = JSON.parse(await readFile('comparison/changed-paths.json', 'utf-8')).labels
115115- if (approvals.size > 0) after.push(`12.approvals: ${approvals.size > 2 ? '3+' : approvals.size}`)
116116- if (Array.from(maintainers).some(m => approvals.has(m))) after.push('12.approved-by: package-maintainer')
124124+ const artifact = (await github.rest.actions.listWorkflowRunArtifacts({
125125+ ...context.repo,
126126+ run_id,
127127+ name: 'comparison'
128128+ })).data.artifacts[0]
117129118118- // Remove the ones not needed anymore
119119- await Promise.all(
120120- before.filter(name => !after.includes(name))
121121- .map(name => github.rest.issues.removeLabel({
122122- ...pr,
123123- name
124124- }))
125125- )
130130+ // Instead of checking the boolean artifact.expired, we will give us a minute to
131131+ // actually download the artifact in the next step and avoid that race condition.
132132+ log('Artifact expires at', artifact.expires_at)
133133+ if (new Date(artifact.expires_at) < new Date(new Date().getTime() + 60 * 1000)) return;
126134127127- // And add the ones that aren't set already
128128- const added = after.filter(name => !before.includes(name))
129129- if (added.length > 0) {
130130- await github.rest.issues.addLabels({
131131- ...pr,
132132- labels: added
135135+ await artifactClient.downloadArtifact(artifact.id, {
136136+ findBy: {
137137+ repositoryName: context.repo.repo,
138138+ repositoryOwner: context.repo.owner,
139139+ token: core.getInput('github-token')
140140+ },
141141+ path: path.resolve(pull_request.number.toString()),
142142+ expectedHash: artifact.digest
133143 })
144144+145145+ // Get all currently set labels that we manage
146146+ const before =
147147+ pull_request.labels.map(({ name }) => name)
148148+ .filter(name =>
149149+ name.startsWith('10.rebuild') ||
150150+ name == '11.by: package-maintainer' ||
151151+ name.startsWith('12.approvals:') ||
152152+ name == '12.approved-by: package-maintainer'
153153+ )
154154+155155+ const approvals = new Set(
156156+ (await github.paginate(github.rest.pulls.listReviews, {
157157+ ...context.repo,
158158+ pull_number: pull_request.number
159159+ }))
160160+ .filter(review => review.state == 'APPROVED')
161161+ .map(review => review.user.id)
162162+ )
163163+164164+ const maintainers = new Set(Object.keys(
165165+ JSON.parse(await readFile(`${pull_request.number}/maintainers.json`, 'utf-8'))
166166+ ))
167167+168168+ // And the labels that should be there
169169+ const after = JSON.parse(await readFile(`${pull_request.number}/changed-paths.json`, 'utf-8')).labels
170170+ if (approvals.size > 0) after.push(`12.approvals: ${approvals.size > 2 ? '3+' : approvals.size}`)
171171+ if (Array.from(maintainers).some(m => approvals.has(m))) after.push('12.approved-by: package-maintainer')
172172+173173+ // Remove the ones not needed anymore
174174+ await Promise.all(
175175+ before.filter(name => !after.includes(name))
176176+ .map(name => github.rest.issues.removeLabel({
177177+ ...context.repo,
178178+ issue_number: pull_request.number,
179179+ name
180180+ }))
181181+ )
182182+183183+ // And add the ones that aren't set already
184184+ const added = after.filter(name => !before.includes(name))
185185+ if (added.length > 0) {
186186+ await github.rest.issues.addLabels({
187187+ ...context.repo,
188188+ issue_number: pull_request.number,
189189+ labels: added
190190+ })
191191+ }
192192+ } catch (cause) {
193193+ throw new Error(`Labeling PR #${pull_request.number} failed.`, { cause })
134194 }
135135- })
195195+ })))
196196+ .filter(({ status }) => status == 'rejected')
197197+ .map(({ reason }) => core.setFailed(`${reason.message}\n${reason.cause.stack}`))
136198 )
137199138200 - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
139201 name: Labels from touched files
140202 if: |
141141- github.event_name != 'workflow_run' &&
203203+ github.event_name == 'pull_request_target' &&
142204 github.event.pull_request.head.repo.owner.login != 'NixOS' || !(
143205 github.head_ref == 'haskell-updates' ||
144206 github.head_ref == 'python-updates' ||
···153215 - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0
154216 name: Labels from touched files (no sync)
155217 if: |
156156- github.event_name != 'workflow_run' &&
218218+ github.event_name == 'pull_request_target' &&
157219 github.event.pull_request.head.repo.owner.login != 'NixOS' || !(
158220 github.head_ref == 'haskell-updates' ||
159221 github.head_ref == 'python-updates' ||
···171233 # This is to avoid the mass of labels there, which is mostly useless - and really annoying for
172234 # the backport labels.
173235 if: |
174174- github.event_name != 'workflow_run' &&
236236+ github.event_name == 'pull_request_target' &&
175237 github.event.pull_request.head.repo.owner.login == 'NixOS' && (
176238 github.head_ref == 'haskell-updates' ||
177239 github.head_ref == 'python-updates' ||
-17
.github/workflows/review-submitted.yml
···11-name: Review submitted
22-33-on:
44- pull_request_review:
55- types: [submitted]
66-77-permissions: {}
88-99-defaults:
1010- run:
1111- shell: bash
1212-1313-jobs:
1414- trigger:
1515- runs-on: ubuntu-24.04-arm
1616- steps:
1717- - run: echo This is a no-op only used as a trigger for workflow_run.
+393
doc/languages-frameworks/javascript.section.md
···879879})
880880```
881881882882+### buildDenoPackage {#javascript-buildDenoPackage}
883883+884884+`buildDenoPackage` allows you to package [Deno](https://deno.com/) projects in Nixpkgs without the use of an auto-generated dependencies file (as used in [node2nix](#javascript-node2nix)).
885885+It works by utilizing Deno's cache functionality -- creating a reproducible cache that contains the dependencies of a project, and pointing Deno to it.
886886+887887+#### buildDenoDeps {#javascript-buildDenoPackage-buildDenoDeps}
888888+889889+For every `buildDenoPackage`, first, a [fixed output derivation](https://nix.dev/manual/nix/2.18/language/advanced-attributes.html#adv-attr-outputHash) is
890890+created with all the dependencies mentioned in the `deno.lock`.
891891+This works as follows:
892892+1. They are installed using `deno install`.
893893+1. All non-reproducible data is pruned.
894894+1. The directories `.deno`, `node_modules` and `vendor` are copied to `$out`.
895895+1. The output of the FOD is checked against the `denoDepsHash`.
896896+1. The output is copied into the build of `buildDenoPackage`, which is not an FOD.
897897+1. The dependencies are installed again using `deno install`, this time from the local cache only.
898898+899899+The `buildDenoDeps` derivation is in `passthru`, so it can be accessed from a `buildDenoPackage` derivation with `.denoDeps`
900900+901901+Related options:
902902+903903+*`denoDepsHash`* (String)
904904+905905+: The output hash of the `buildDenoDeps` fixed output derivation.
906906+907907+*`denoInstallFlags`* (Array of strings; optional)
908908+909909+: The Flags passed to `deno install`.
910910+911911+: _Default:_ `[ "--allow-scripts" "--frozen" "--cached-only" ]` for `buildDenoPackage`
912912+: _Default:_ `[ "--allow-scripts" "--frozen" ]` for `buildDenoDeps` (`"--cached-only"` is filtered out)
913913+914914+::: {.tip}
915915+If you receive errors like these:
916916+917917+```
918918+error: The lockfile is out of date. Run `deno install --frozen=false`, or rerun with `--frozen=false` to update it.
919919+```
920920+921921+or
922922+923923+```
924924+error: Import '<url>' failed.
925925+ 0: error sending request for url (<url>): client error (Connect): dns error: failed to lookup address information: Temporary failure in name resolution: failed to lookup address information:Temporary failure in name resolution
926926+ 1: client error (Connect)
927927+ 2: dns error: failed to lookup address information: Temporary failure in name resolution
928928+ 3: failed to lookup address information: Temporary failure in name resolution
929929+ at file:///build/source/src/lib/helpers/verifyRequest.ts:2:21
930930+build for <your-package> failed in buildPhase with exit code 1
931931+```
932932+933933+or
934934+935935+```
936936+error: Specifier not found in cache: "<url>", --cached-only is specified.
937937+938938+ERROR: deno failed to install dependencies
939939+```
940940+941941+This can happen due to the `deno install` command deducing different packages than what the actual package needs.
942942+943943+To fix this, add the entrypoint to the install flags:
944944+945945+```nix
946946+{ buildDenoPackage, nix-gitignore }:
947947+buildDenoPackage {
948948+ pname = "myPackage";
949949+ version = "0.1.0";
950950+ denoDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
951951+ src = nix-gitignore.gitignoreSource [ ] ./.;
952952+ binaryEntrypointPath = "main.ts";
953953+ denoInstallFlags = [
954954+ "--allow-scripts"
955955+ "--frozen"
956956+ "--cached-only"
957957+ "--entrypoint"
958958+ "<path/to/entrypoint/script>"
959959+ ];
960960+}
961961+```
962962+963963+:::
964964+965965+#### Private registries {#javascript-buildDenoPackage-private-registries}
966966+There are currently 2 options, which enable the use of private registries in a `buildDenoPackage` derivation.
967967+968968+*`denoDepsImpureEnvVars`* (Array of strings; optional)
969969+970970+: Names of impure environment variables passed to the `buildDenoDeps` derivation. They are forwarded to `deno install`.
971971+972972+: _Example:_ `[ "NPM_TOKEN" ]`
973973+974974+: It can be used to set tokens for private NPM registries (in a `.npmrc` file).
975975+976976+: In a single-user installation of Nix, you can put the variables into the environment, when running the nix build.
977977+978978+: In multi-user installations of Nix, it's necessary to set the environment variables in the nix-daemon, probably with systemd.
979979+980980+:::{.example}
981981+982982+##### configure nix-daemon {#javascript-buildDenoPackage-private-registries-daemon-example}
983983+In NixOS:
984984+985985+```nix
986986+# configuration.nix
987987+{
988988+ config,
989989+ lib,
990990+ pkgs,
991991+ ...
992992+}:
993993+{
994994+ systemd.services.nix-daemon.environment.NPM_TOKEN = "<token>";
995995+}
996996+```
997997+998998+In other Linux distributions use
999999+10001000+```
10011001+$ sudo systemctl edit nix-daemon
10021002+$ sudo systemctl cat nix-daemon
10031003+$ sudo systemctl restart nix-daemon
10041004+```
10051005+10061006+:::
10071007+10081008+*`denoDepsInjectedEnvVars`* (Attrset; optional)
10091009+10101010+: Environment variables as key value pairs. They are forwarded to `deno install`.
10111011+10121012+: _Example:_ `{ "NPM_TOKEN" = "<token>"; }`
10131013+10141014+: It can be used to set tokens for private NPM registries (in a `.npmrc` file).
10151015+You could pass these tokens from the Nix CLI with `--arg`,
10161016+however this can hurt the reproducibility of your builds and such an injected
10171017+token will also need to be injected in every build that depends on this build.
10181018+10191019+:::{.example}
10201020+10211021+##### example `.npmrc` {#javascript-buildDenoPackage-private-registries-npmrc-example}
10221022+10231023+```ini
10241024+@<scope>:registry=https://<domain>/<path to private registry>
10251025+//<domain>/<path to private registry>:_authToken=${NPM_TOKEN}
10261026+```
10271027+10281028+:::
10291029+10301030+::: {.caution}
10311031+10321032+Hardcoding a token into your NixOS configuration or some other nix build, will as a consequence write that token into `/nix/store`, which is considered world readable.
10331033+10341034+:::
10351035+10361036+::: {.note}
10371037+Neither approach is ideal. For `buildNpmPackage`, there exists a third
10381038+option called `sourceOverrides`, which allows the user to inject Nix packages into
10391039+the output `node_modules` folder.
10401040+Since a Nix build implicitly uses the SSH keys of the machine,
10411041+this offers a third option to access private packages.
10421042+But this creates the requirement, that the imported package is packaged with nix first,
10431043+and that the source code can be retrieved with SSH.
10441044+This is possible for Deno, too, albeit it not
10451045+completely analogous to `buildNpmPackage`'s solution.
10461046+However, it has not been implemented yet.
10471047+:::
10481048+10491049+#### Compile to binary {#javascript-buildDenoPackage-compile-to-binary}
10501050+10511051+It's possible to compile a Deno project to a single binary using `deno compile`.
10521052+The binary will be named like the `.name` property in `deno.json`, if available,
10531053+or the `name` attribute of the derivation.
10541054+10551055+:::{.caution}
10561056+When using packages with a `npm:` specifier, the resulting binary will not be reproducible.
10571057+See [this issue](https://github.com/denoland/deno/issues/29619) for more information.
10581058+:::
10591059+10601060+Related options:
10611061+10621062+*`hostPlatform`* (String; optional)
10631063+10641064+: The [host platform](#ssec-cross-platform-parameters) the binary is built for.
10651065+10661066+: _Default:_ `builtins.currentSystem`.
10671067+10681068+: _Supported values:_
10691069+ - `"x86_64-darwin"`
10701070+ - `"aarch64-darwin"`
10711071+ - `"x86_64-linux"`
10721072+ - `"aarch64-linux"`
10731073+10741074+*`denoCompileFlags`* (Array of string; optional)
10751075+10761076+: Flags passed to `deno compile [denoTaskFlags] ${binaryEntrypointPath} [extraCompileFlags]`.
10771077+10781078+*`extraCompileFlags`* (Array of string; optional)
10791079+10801080+: Flags passed to `deno compile [denoTaskFlags] ${binaryEntrypointPath} [extraCompileFlags]`.
10811081+10821082+*`binaryEntrypointPath`* (String or null; optional)
10831083+10841084+: If not `null`, a binary is created using the specified path as the entry point.
10851085+The binary is copied to `$out/bin` in the `installPhase`.
10861086+10871087+: _Default:_ `null`
10881088+10891089+: It's prefixed by `denoWorkspacePath`.
10901090+10911091+*`denortPackage`* (Derivation; optional)
10921092+10931093+: The package used as the Deno runtime, which is bundled with the JavaScript code to create the binary.
10941094+10951095+: _Default:_ `pkgs.denort`
10961096+10971097+: Don't use `pkgs.deno` for this, since that is the full Deno CLI, with all the development tooling.
10981098+10991099+: If you're cross compiling, this needs to be the `denort` of the `hostPlatform`.
11001100+11011101+::: {.note}
11021102+The binary will be dynamically linked and not executable on NixOS without [nix-ld](https://github.com/nix-community/nix-ld)
11031103+or [other methods](https://unix.stackexchange.com/questions/522822/different-methods-to-run-a-non-nixos-executable-on-nixos).
11041104+11051105+```nix
11061106+# configuration.nix
11071107+{
11081108+ config,
11091109+ lib,
11101110+ pkgs,
11111111+ ...
11121112+}:
11131113+{
11141114+ programs.nix-ld.enable = true;
11151115+ programs.nix-ld.libraries = with pkgs; [
11161116+ glibc
11171117+ gcc-unwrapped
11181118+ ];
11191119+}
11201120+```
11211121+11221122+:::
11231123+11241124+:::{.example}
11251125+11261126+##### example binary build {#javascript-buildDenoPackage-compile-to-binary-example}
11271127+11281128+```nix
11291129+{ buildDenoPackage, nix-gitignore }:
11301130+buildDenoPackage {
11311131+ pname = "myPackage";
11321132+ version = "0.1.0";
11331133+ denoDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
11341134+ src = nix-gitignore.gitignoreSource [ ] ./.;
11351135+ binaryEntrypointPath = "main.ts";
11361136+}
11371137+```
11381138+11391139+:::
11401140+11411141+#### Create artifacts in the build {#javascript-buildDenoPackage-artifacts-in-build}
11421142+11431143+Instead of compiling to a binary, `deno task` can be executed inside the build
11441144+to produce some artifact, which can then be copied out in the `installPhase`.
11451145+11461146+Related options:
11471147+11481148+*`denoTaskScript`* (String; optional)
11491149+11501150+: The task in `deno.json` that's executed with `deno task`.
11511151+11521152+: _Default:_ `"build"`
11531153+11541154+*`denoTaskFlags`* (Array of strings; optional)
11551155+11561156+: The flags passed to `deno task [denoTaskFlags] ${denoTaskScript} [extraTaskFlags]`.
11571157+11581158+*`extraTaskFlags`* (Array of strings; optional)
11591159+11601160+: The flags passed to `deno task [denoTaskFlags] ${denoTaskScript} [extraTaskFlags]`.
11611161+11621162+*`denoTaskPrefix`* (String; optional)
11631163+11641164+: An unquoted string injected before `deno task`.
11651165+11661166+*`denoTaskSuffix`* (String; optional)
11671167+11681168+: An unquoted string injected after `deno task` and all its flags. For example to pipe stdout to a file.
11691169+11701170+:::{.example}
11711171+11721172+##### example artifact build {#javascript-buildDenoPackage-artifacts-in-build-example}
11731173+11741174+`deno.json`
11751175+11761176+```json
11771177+{
11781178+ "tasks": {
11791179+ "build": "deno run --allow-all main.ts"
11801180+ }
11811181+}
11821182+```
11831183+11841184+```nix
11851185+{ buildDenoPackage, nix-gitignore }:
11861186+buildDenoPackage {
11871187+ pname = "myPackage";
11881188+ version = "0.1.0";
11891189+ denoDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
11901190+ src = nix-gitignore.gitignoreSource [ ] ./.;
11911191+ denoTaskSuffix = ">out.txt";
11921192+ installPhase = ''
11931193+ cp ./out.txt $out
11941194+ '';
11951195+}
11961196+```
11971197+11981198+:::
11991199+12001200+#### Workspaces {#javascript-buildDenoPackage-workspaces}
12011201+12021202+Deno's workspaces are supported.
12031203+12041204+To make them work, the whole project needs to be added as source, since the `deno.lock`
12051205+is always in the root of the project and contains all dependencies.
12061206+12071207+This means a build with only the required dependencies of a workspace is not possible.
12081208+Also, the `denoDepsHash` for all workspaces is the same, since they
12091209+all share the same dependencies.
12101210+12111211+When [running a task inside the build](#javascript-buildDenoPackage-artifacts-in-build),
12121212+`denoWorkspacePath` can be used to let the task run inside a workspace.
12131213+12141214+When [compiling to a binary](#javascript-buildDenoPackage-compile-to-binary),
12151215+`binaryEntrypointPath` is prefixed by `denoWorkspacePath`.
12161216+12171217+Related options:
12181218+12191219+*`denoWorkspacePath`* (String; optional)
12201220+12211221+: The path to a workspace.
12221222+12231223+:::{.example}
12241224+12251225+##### example workspaces {#javascript-buildDenoPackage-workspaces-example}
12261226+12271227+```nix
12281228+{ buildDenoPackage, nix-gitignore }:
12291229+rec {
12301230+ sub1 = buildDenoPackage {
12311231+ pname = "sub1";
12321232+ version = "0.1.0";
12331233+ denoDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
12341234+ src = nix-gitignore.gitignoreSource [ ] ./.;
12351235+ denoWorkspacePath = "./sub1";
12361236+ denoTaskFlags = [
12371237+ "--text"
12381238+ "sub1"
12391239+ ];
12401240+ denoTaskSuffix = ">out.txt";
12411241+ installPhase = ''
12421242+ cp out.txt $out
12431243+ '';
12441244+ };
12451245+ sub2 = buildDenoPackage {
12461246+ # Note that we are reusing denoDeps and src,
12471247+ # since they must be the same for both workspaces.
12481248+ inherit (sub1) denoDeps src;
12491249+ pname = "sub2";
12501250+ version = "0.1.0";
12511251+ denoWorkspacePath = "./sub2";
12521252+ binaryEntrypointPath = "./main.ts";
12531253+ };
12541254+}
12551255+```
12561256+12571257+:::
12581258+12591259+#### Other Options {#javascript-buildDenoPackage-other-options}
12601260+12611261+*`denoDir`* (String; optional)
12621262+12631263+: `DENO_DIR` will be set to this value for all `deno` commands.
12641264+12651265+*`denoFlags`* (Array of string; optional)
12661266+12671267+: The flags passed to all `deno` commands.
12681268+12691269+*`denoPackage`* (Derivation; optional)
12701270+12711271+: The Deno CLI used for all `deno` commands inside the build.
12721272+12731273+: _Default:_ `pkgs.deno`
12741274+8821275## Outside Nixpkgs {#javascript-outside-nixpkgs}
88312768841277There are some other tools available, which are written in the Nix language.
···551551552552- `ddclient` was updated from 3.11.2 to 4.0.0 [Release notes](https://github.com/ddclient/ddclient/releases/tag/v4.0.0)
553553554554+- `buildDenoPackage` was added [see docs](https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/javascript.section.md#avascript-buildDenoPackage) for more details
555555+554556## Nixpkgs Library {#sec-nixpkgs-release-25.05-lib}
555557556558### Breaking changes {#sec-nixpkgs-release-25.05-lib-breaking}
···11+# NOTE: much of this structure is inspired from https://github.com/NixOS/nixpkgs/tree/fff29a3e5f7991512e790617d1a693df5f3550f6/pkgs/build-support/node
22+{
33+ stdenvNoCC,
44+ deno,
55+ denort,
66+ diffutils,
77+ zip,
88+ jq,
99+ fetchDenoDeps,
1010+ buildPackages,
1111+ lib,
1212+}:
1313+{
1414+ name ? "${args.pname}-${args.version}",
1515+ src ? null,
1616+ # The output hash of the dependencies for this project.
1717+ denoDepsHash ? lib.fakeHash,
1818+ # The host platform, the output binary is compiled for.
1919+ hostPlatform ? stdenvNoCC.hostPlatform.system,
2020+ # A list of strings, which are names of impure env vars passed to the deps build.
2121+ # Example:
2222+ # `[ "NPM_TOKEN" ]`
2323+ # They will be forwarded to `deno install`.
2424+ # It can be used to set tokens for private NPM registries (in an `.npmrc` file).
2525+ # In multi user installations of Nix, you need to set the env vars in the daemon (probably with systemd).
2626+ # In nixos: `systemd.services.nix-daemon.environment.NPM_TOKEN = "<token>";`
2727+ denoDepsImpureEnvVars ? [ ],
2828+ # An attr set with env vars as key value pairs.
2929+ # Example:
3030+ # `{ "NPM_TOKEN" = "<token>"; }`
3131+ # They will be forwarded to `deno install`.
3232+ # It can be used to set tokens for private NPM registries (in an `.npmrc` file).
3333+ # You could pass these tokens from the cli with `--arg` (this can make your builds painful).
3434+ denoDepsInjectedEnvVars ? { },
3535+ # TODO: source overrides like in buildNpmPackage, i.e. injecting nix packages into the denoDeps
3636+ # this is more involved, since they can't directly be injected into the fixed output derivation
3737+ # of fetchDenoDeps. Instead we need to patch the lock file and remove the packages we intend to
3838+ # inject, then we need to build the rest of the packages like before and in a
3939+ # second step create normal derivation with the injected packages.
4040+ # then the two need to be merged into a single denoDeps derivation and finally the lock file needs
4141+ # to be reverted back to it's original form.
4242+ # It is possible to manipulate the registry.json files of the injected packages so that deno accepts them as is.
4343+ denoDeps ? fetchDenoDeps {
4444+ inherit
4545+ src
4646+ denoDepsInjectedEnvVars
4747+ denoDepsImpureEnvVars
4848+ denoFlags
4949+ denoDir
5050+ ;
5151+ denoInstallFlags = builtins.filter (e: e != "--cached-only") denoInstallFlags;
5252+ name = "${name}-deno-deps";
5353+ hash = denoDepsHash;
5454+ },
5555+ # The package used for every deno command in the build
5656+ denoPackage ? deno,
5757+ # The package used as the runtime that is bundled with the the src to create the binary.
5858+ denortPackage ? denort,
5959+ # The script to run to build the project.
6060+ # You still need to specify in the installPhase, what artifacts to copy to `$out`.
6161+ denoTaskScript ? "build",
6262+ # If not null, create a binary using the specified path as the entrypoint,
6363+ # copy it to `$out/bin` in installPhase and fix it in fixupPhase.
6464+ binaryEntrypointPath ? null,
6565+ # Flags to pass to all deno commands.
6666+ denoFlags ? [ ],
6767+ # Flags to pass to `deno task [denoTaskFlags] ${denoTaskScript}`.
6868+ denoTaskFlags ? [ ],
6969+ # Flags to pass to `deno compile [denoTaskFlags] ${binaryEntrypointPath}`.
7070+ denoCompileFlags ? [ ],
7171+ # Flags to pass to `deno install [denoInstallFlags]`.
7272+ denoInstallFlags ? [
7373+ "--allow-scripts"
7474+ "--frozen"
7575+ "--cached-only"
7676+ ],
7777+ # Flags to pass to `deno task [denoTaskFlags] ${denoTaskScript} [extraTaskFlags]`.
7878+ extraTaskFlags ? [ ],
7979+ # Flags to pass to `deno compile [denoTaskFlags] ${binaryEntrypointPath} [extraCompileFlags]`.
8080+ extraCompileFlags ? [ ],
8181+ nativeBuildInputs ? [ ],
8282+ dontFixup ? true,
8383+ # Custom denoConfigHook
8484+ denoConfigHook ? null,
8585+ # Custom denoBuildHook
8686+ denoBuildHook ? null,
8787+ # Custom denoInstallHook
8888+ denoInstallHook ? null,
8989+ # Path to deno workspace, where the denoTaskScript should be run
9090+ denoWorkspacePath ? null,
9191+ # Unquoted string injected before `deno task`
9292+ denoTaskPrefix ? "",
9393+ # Unquoted string injected after `deno task` and all its flags
9494+ denoTaskSuffix ? "",
9595+ # Used as the name of the local DENO_DIR
9696+ denoDir ? "./.deno",
9797+ ...
9898+}@args:
9999+let
100100+ denoFlags_ = builtins.concatStringsSep " " denoFlags;
101101+ denoTaskFlags_ = builtins.concatStringsSep " " denoTaskFlags;
102102+ denoCompileFlags_ = builtins.concatStringsSep " " denoCompileFlags;
103103+ denoInstallFlags_ = builtins.concatStringsSep " " denoInstallFlags;
104104+ extraTaskFlags_ = builtins.concatStringsSep " " extraTaskFlags;
105105+ extraCompileFlags_ = builtins.concatStringsSep " " extraCompileFlags;
106106+107107+ args' = builtins.removeAttrs args [ "denoDepsInjectedEnvVars" ];
108108+109109+ denoHooks =
110110+ (buildPackages.denoHooks.override {
111111+ denort = denortPackage;
112112+ })
113113+ {
114114+ inherit denoTaskSuffix denoTaskPrefix binaryEntrypointPath;
115115+ };
116116+ systemLookupTable = {
117117+ "x86_64-darwin" = "x86_64-apple-darwin";
118118+ "arm64-darwin" = "aarch64-apple-darwin";
119119+ "aarch64-darwin" = "aarch64-apple-darwin";
120120+ "x86_64-linux" = "x86_64-unknown-linux-gnu";
121121+ "arm64-linux" = "aarch64-unknown-linux-gnu";
122122+ "aarch64-linux" = "aarch64-unknown-linux-gnu";
123123+ };
124124+ hostPlatform_ =
125125+ if builtins.hasAttr hostPlatform systemLookupTable then
126126+ systemLookupTable."${hostPlatform}"
127127+ else
128128+ (lib.systems.elaborate hostPlatform).config;
129129+in
130130+stdenvNoCC.mkDerivation (
131131+ args'
132132+ // {
133133+ inherit
134134+ name
135135+ denoDeps
136136+ src
137137+ denoFlags_
138138+ denoTaskFlags_
139139+ denoCompileFlags_
140140+ denoInstallFlags_
141141+ extraTaskFlags_
142142+ extraCompileFlags_
143143+ binaryEntrypointPath
144144+ hostPlatform_
145145+ denoWorkspacePath
146146+ denoTaskScript
147147+ ;
148148+149149+ nativeBuildInputs = nativeBuildInputs ++ [
150150+ # Prefer passed hooks
151151+ (if denoConfigHook != null then denoConfigHook else denoHooks.denoConfigHook)
152152+ (if denoBuildHook != null then denoBuildHook else denoHooks.denoBuildHook)
153153+ (if denoInstallHook != null then denoInstallHook else denoHooks.denoInstallHook)
154154+ denoPackage
155155+ diffutils
156156+ zip
157157+ jq
158158+ ];
159159+160160+ DENO_DIR = denoDir;
161161+162162+ dontFixup = if binaryEntrypointPath != null then false else dontFixup;
163163+164164+ passthru = {
165165+ inherit denoDeps;
166166+ };
167167+168168+ meta = (args.meta or { }) // {
169169+ platforms = args.meta.platforms or denoPackage.meta.platforms;
170170+ };
171171+ }
172172+)
···11+# shellcheck shell=bash
22+33+denoConfigHook() {
44+ echo "Executing denoConfigHook"
55+66+ if [ -z "${denoDeps-}" ]; then
77+ echo
88+ echo "ERROR: no dependencies were specified"
99+ echo 'Hint: set `denoDeps` if using these hooks individually. If this is happening with `buildDenoPackage`, please open an issue.'
1010+ echo
1111+1212+ exit 1
1313+ fi
1414+1515+ local -r cacheLockfile="$denoDeps/deno.lock"
1616+ local -r srcLockfile="$PWD/deno.lock"
1717+1818+ echo "Validating consistency between $srcLockfile and $cacheLockfile"
1919+2020+ if ! diff "$srcLockfile" "$cacheLockfile"; then
2121+ # If the diff failed, first double-check that the file exists, so we can
2222+ # give a friendlier error msg.
2323+ if ! [ -e "$srcLockfile" ]; then
2424+ echo
2525+ echo "ERROR: Missing deno.lock from src. Expected to find it at: $srcLockfile"
2626+ echo
2727+2828+ exit 1
2929+ fi
3030+3131+ if ! [ -e "$cacheLockfile" ]; then
3232+ echo
3333+ echo "ERROR: Missing lockfile from cache. Expected to find it at: $cacheLockfile"
3434+ echo
3535+3636+ exit 1
3737+ fi
3838+3939+ echo
4040+ echo "ERROR: denoDepsHash is out of date"
4141+ echo
4242+ echo "The deno.lock in src is not the same as the in $denoDeps."
4343+ echo
4444+ echo "To fix the issue:"
4545+ echo '1. Use `lib.fakeHash` as the denoDepsHash value'
4646+ echo "2. Build the derivation and wait for it to fail with a hash mismatch"
4747+ echo "3. Copy the 'got: sha256-' value back into the denoDepsHash field"
4848+ echo
4949+5050+ exit 1
5151+ fi
5252+5353+ # NOTE: we need to use vendor in the build too, since we used it for the deps
5454+ useVendor() {
5555+ jq '.vendor = true' deno.json >temp.json &&
5656+ rm -f deno.json &&
5757+ mv temp.json deno.json
5858+ }
5959+ echo "Adding vendor to deno.json"
6060+ useVendor
6161+6262+ echo "Installing dependencies"
6363+6464+ export DENO_DIR="$(pwd)"/"$DENO_DIR"
6565+6666+ installDeps() {
6767+ if [[ -d "$denoDeps/.deno" ]]; then
6868+ cp -r --no-preserve=mode "$denoDeps/.deno" "$DENO_DIR"
6969+ fi
7070+ if [[ -d "$denoDeps/vendor" ]]; then
7171+ cp -r --no-preserve=mode "$denoDeps/vendor" ./vendor
7272+ fi
7373+ if [[ -d "$denoDeps/node_modules" ]]; then
7474+ cp -r --no-preserve=mode "$denoDeps/node_modules" ./node_modules
7575+ fi
7676+ }
7777+ installDeps
7878+7979+ if ! deno install $denoInstallFlags_ $denoFlags_; then
8080+ echo
8181+ echo "ERROR: deno failed to install dependencies"
8282+ echo
8383+8484+ exit 1
8585+ fi
8686+8787+ installDenort() {
8888+ version="$(deno --version | head -1 | awk '{print $2}')"
8989+ zipfile=denort-"$hostPlatform_".zip
9090+ dir="$DENO_DIR"/dl/release/v"$version"
9191+ mkdir -p "$dir"
9292+ cp "@denortBinary@" ./denort
9393+ zip "$dir"/"$zipfile" ./denort
9494+ rm ./denort
9595+ }
9696+ if [ -n "${binaryEntrypointPath-}" ]; then
9797+ echo "Installing denort for binary build"
9898+ installDenort
9999+ fi
100100+101101+ patchShebangs .deno
102102+ patchShebangs node_modules
103103+ patchShebangs vendor
104104+105105+ echo "Finished denoConfigHook"
106106+}
107107+108108+postPatchHooks+=(denoConfigHook)
···11-{ buildNpmPackage, anubis }:
22-33-buildNpmPackage {
44- pname = "${anubis.pname}-xess";
55- inherit (anubis) version src;
66-77- npmDepsHash = "sha256-wI8XCUGq3aI20B++RAT3lc/nBrDMEmE9+810lewzXa0=";
88-99- buildPhase = ''
1010- runHook preBuild
1111-1212- npx postcss ./xess/xess.css -o xess.min.css
1313-1414- runHook postBuild
1515- '';
1616-1717- installPhase = ''
1818- runHook preInstall
1919-2020- install -Dm644 xess.min.css $out/xess.min.css
2121-2222- runHook postInstall
2323- '';
2424-2525- meta = anubis.meta // {
2626- description = "Xess files for Anubis";
2727- longDescription = ''
2828- This package is consumed by the main `anubis` package to render the final
2929- styling for the bot check page.
3030-3131- **It is not supposed to be used as a standalone package**, and it exists to
3232- ensure Anubis' styling is override-able by downstreams.
3333- '';
3434- };
3535-}
···11-diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
22-index e4e474ab6e..5548599802 100644
33---- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
44-+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
55-@@ -1693,7 +1693,7 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
66-77- // Populate the cache. We call this before pub get below so that the
88- // sky_engine package is available in the flutter cache for pub to find.
99-- if (shouldUpdateCache) {
1010-+ if (false) {
1111- // First always update universal artifacts, as some of these (e.g.
1212- // ios-deploy on macOS) are required to determine `requiredArtifacts`.
1313- final bool offline;
1414-diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
1515-index 50783f8435..db94062840 100644
1616---- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
1717-+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
1818-@@ -377,11 +377,7 @@ class FlutterCommandRunner extends CommandRunner<void> {
1919- globals.analytics.suppressTelemetry();
2020- }
2121-2222-- globals.flutterVersion.ensureVersionFile();
2323- final bool machineFlag = topLevelResults[FlutterGlobalOptions.kMachineFlag] as bool? ?? false;
2424-- if (await _shouldCheckForUpdates(topLevelResults, topLevelMachineFlag: machineFlag)) {
2525-- await globals.flutterVersion.checkFlutterVersionFreshness();
2626-- }
2727-2828- // See if the user specified a specific device.
2929- final String? specifiedDeviceId = topLevelResults[FlutterGlobalOptions.kDeviceIdOption] as String?;
3030-
···11-From 6df275df3b8694daf16302b407520e3b1dee6724 Mon Sep 17 00:00:00 2001
22-From: Philip Hayes <philiphayes9@gmail.com>
33-Date: Thu, 12 Sep 2024 13:23:00 -0700
44-Subject: [PATCH] fix: cleanup xcode_backend.sh to fix iOS build w/
55- `NixOS/nixpkgs` flutter
66-77-This patch cleans up `xcode_backend.sh`. It now effectively just runs
88-`exec $FLUTTER_ROOT/bin/dart ./xcode_backend.dart`.
99-1010-The previous `xcode_backend.sh` tries to discover `$FLUTTER_ROOT` from
1111-argv[0], even though its presence is already guaranteed (the wrapped
1212-`xcode_backend.dart` also relies on this env).
1313-1414-When using nixpkgs flutter, the flutter SDK directory is composed of several
1515-layers, joined together using symlinks (called a `symlinkJoin`). Without this
1616-patch, the auto-discover traverses the symlinks into the wrong layer, and so it
1717-uses an "unwrapped" `dart` command instead of a "wrapped" dart that sets some
1818-important envs/flags (like `$FLUTTER_ROOT`).
1919-2020-Using the "unwrapped" dart then manifests in this error when compiling, since
2121-it doesn't see the ios build-support artifacts:
2222-2323-```
2424-$ flutter run -d iphone
2525-Running Xcode build...
2626-Xcode build done. 6.4s
2727-Failed to build iOS app
2828-Error (Xcode): Target debug_unpack_ios failed: Error: Flutter failed to create a directory at "/<nix-store>/XXXX-flutter-3.24.1-unwrapped/bin/cache/artifacts".
2929-```
3030----
3131- packages/flutter_tools/bin/xcode_backend.sh | 25 ++++-----------------
3232- 1 file changed, 4 insertions(+), 21 deletions(-)
3333-3434-diff --git a/packages/flutter_tools/bin/xcode_backend.sh b/packages/flutter_tools/bin/xcode_backend.sh
3535-index 2889d7c8e4..48b9d06c6e 100755
3636---- a/packages/flutter_tools/bin/xcode_backend.sh
3737-+++ b/packages/flutter_tools/bin/xcode_backend.sh
3838-@@ -6,24 +6,7 @@
3939- # exit on error, or usage of unset var
4040- set -euo pipefail
4141-4242--# Needed because if it is set, cd may print the path it changed to.
4343--unset CDPATH
4444--
4545--function follow_links() (
4646-- cd -P "$(dirname -- "$1")"
4747-- file="$PWD/$(basename -- "$1")"
4848-- while [[ -h "$file" ]]; do
4949-- cd -P "$(dirname -- "$file")"
5050-- file="$(readlink -- "$file")"
5151-- cd -P "$(dirname -- "$file")"
5252-- file="$PWD/$(basename -- "$file")"
5353-- done
5454-- echo "$file"
5555--)
5656--
5757--PROG_NAME="$(follow_links "${BASH_SOURCE[0]}")"
5858--BIN_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
5959--FLUTTER_ROOT="$BIN_DIR/../../.."
6060--DART="$FLUTTER_ROOT/bin/dart"
6161--
6262--"$DART" "$BIN_DIR/xcode_backend.dart" "$@"
6363-+# Run `dart ./xcode_backend.dart` with the dart from $FLUTTER_ROOT.
6464-+dart="${FLUTTER_ROOT}/bin/dart"
6565-+xcode_backend_dart="${BASH_SOURCE[0]%.sh}.dart"
6666-+exec "${dart}" "${xcode_backend_dart}" "$@"
6767---
6868-2.46.0
6969-
···11-This patch introduces an intermediate Gradle build step to alter the behavior
22-of flutter_tools' Gradle project, specifically moving the creation of `build`
33-and `.gradle` directories from within the Nix Store to somewhere in `$HOME/.cache/flutter/nix-flutter-tools-gradle/$engineShortRev`.
44-55-Without this patch, flutter_tools' Gradle project tries to generate `build` and `.gradle`
66-directories within the Nix Store. Resulting in read-only errors when trying to build a
77-Flutter Android app at runtime.
88-99-This patch takes advantage of the fact settings.gradle takes priority over settings.gradle.kts to build the intermediate Gradle project
1010-when a Flutter app runs `includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")`
1111-1212-`rootProject.buildFileName = "/dev/null"` so that the intermediate project doesn't use `build.gradle.kts` that's in the same directory.
1313-1414-The intermediate project makes a `settings.gradle` file in `$HOME/.cache/flutter/nix-flutter-tools-gradle/<short engine rev>/` and `includeBuild`s it.
1515-This Gradle project will build the actual `packages/flutter_tools/gradle` project by setting
1616-`rootProject.projectDir = new File("$settingsDir")` and `apply from: new File("$settingsDir/settings.gradle.kts")`.
1717-1818-Now the `.gradle` will be built in `$HOME/.cache/flutter/nix-flutter-tools-gradle/<short engine rev>/`, but `build` doesn't.
1919-To move `build` to `$HOME/.cache/flutter/nix-flutter-tools-gradle/<short engine rev>/` as well, we need to set `buildDirectory`.
2020-diff --git a/packages/flutter_tools/gradle/settings.gradle b/packages/flutter_tools/gradle/settings.gradle
2121-new file mode 100644
2222-index 0000000000..b2485c94b4
2323---- /dev/null
2424-+++ b/packages/flutter_tools/gradle/settings.gradle
2525-@@ -0,0 +1,19 @@
2626-+rootProject.buildFileName = "/dev/null"
2727-+
2828-+def engineShortRev = (new File("$settingsDir/../../../bin/internal/engine.version")).text.take(10)
2929-+def dir = new File("$System.env.HOME/.cache/flutter/nix-flutter-tools-gradle/$engineShortRev")
3030-+dir.mkdirs()
3131-+def file = new File(dir, "settings.gradle")
3232-+
3333-+file.text = """
3434-+rootProject.projectDir = new File("$settingsDir")
3535-+apply from: new File("$settingsDir/settings.gradle.kts")
3636-+
3737-+gradle.allprojects { project ->
3838-+ project.beforeEvaluate {
3939-+ project.layout.buildDirectory = new File("$dir/build")
4040-+ }
4141-+}
4242-+"""
4343-+
4444-+includeBuild(dir)
···7777 disabledTests = [
7878 # Assertion issues
7979 "test_textual_env_var"
8080+8181+ # Fail since tree-sitter-markdown was updated to 0.5.0
8282+ # ValueError: Incompatible Language version 15. Must be between 13 and 14
8383+ # https://github.com/Textualize/textual/issues/5868
8484+ "test_setting_builtin_language_via_attribute"
8585+ "test_setting_builtin_language_via_constructor"
8086 ];
81878288 pytestFlagsArray = [
···690690 flutter319 = throw "flutter319 has been removed because it isn't updated anymore, and no packages in nixpkgs use it. If you still need it, use flutter.mkFlutter to get a custom version"; # Added 2024-12-03
691691 flutter322 = throw "flutter322 has been removed because it isn't updated anymore, and no packages in nixpkgs use it. If you still need it, use flutter.mkFlutter to get a custom version"; # Added 2024-10-05
692692 flutter323 = throw "flutter323 has been removed because it isn't updated anymore, and no packages in nixpkgs use it. If you still need it, use flutter.mkFlutter to get a custom version"; # Added 2024-10-05
693693+ flutter326 = throw "flutter326 has been removed because it isn't updated anymore, and no packages in nixpkgs use it. If you still need it, use flutter.mkFlutter to get a custom version"; # Added 2025-06-08
693694 fluxctl = throw "fluxctl is unmaintained and has been removed. Migration to flux2 is recommended"; # Added 2025-05-11
694695 fluxus = throw "fluxus has been removed because it hasn't been updated in 9 years and depended on insecure Racket 7.9"; # Added 2024-12-06
695696 fmt_8 = throw "fmt_8 has been removed as it is obsolete and was no longer used in the tree"; # Added 2024-11-12