diff --git a/lib/modules/manager/nix/extract.spec.ts b/lib/modules/manager/nix/extract.spec.ts index 9645744c2a9208..035a495eb0670f 100644 --- a/lib/modules/manager/nix/extract.spec.ts +++ b/lib/modules/manager/nix/extract.spec.ts @@ -1,74 +1,603 @@ +import { fs } from '../../../../test/util'; import { GitRefsDatasource } from '../../datasource/git-refs'; import { id as nixpkgsVersioning } from '../../versioning/nixpkgs'; import { extractPackageFile } from '.'; +jest.mock('../../../util/fs'); + describe('modules/manager/nix/extract', () => { - it('returns null when no nixpkgs', () => { - const content = `{ - inputs = {}; -}`; - const res = extractPackageFile(content); + const flake1Lock = `{ + "nodes": { + "root": {} + }, + "root": "root", + "version": 7 + }`; + const flake1Nix = `{ + inputs = {}; + }`; - expect(res).toBeNull(); + it('returns null when no nixpkgs input exists', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake1Lock); + expect(await extractPackageFile(flake1Nix, 'flake.nix')).toBeNull(); }); - it('returns nixpkgs', () => { - const content = `{ - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-21.11"; - }; -}`; + const flake2Nix = `{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-21.11"; + }; + }`; + + it('match nixpkgs input', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake1Lock); + expect(await extractPackageFile(flake2Nix, 'flake.nix')).toEqual({ + deps: [ + { + depName: 'nixpkgs', + currentValue: 'nixos-21.11', + datasource: GitRefsDatasource.id, + packageName: 'https://github.com/NixOS/nixpkgs', + versioning: nixpkgsVersioning, + }, + ], + }); + }); - const res = extractPackageFile(content); + const flake3Nix = `{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11"; + }; + }`; - expect(res?.deps).toEqual([ - { - depName: 'nixpkgs', - currentValue: 'nixos-21.11', - datasource: GitRefsDatasource.id, - packageName: 'https://github.com/NixOS/nixpkgs', - versioning: nixpkgsVersioning, + it('match nixpkgs input case insensitive', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake1Lock); + expect(await extractPackageFile(flake3Nix, 'flake.nix')).toEqual({ + deps: [ + { + depName: 'nixpkgs', + currentValue: 'nixos-21.11', + datasource: GitRefsDatasource.id, + packageName: 'https://github.com/NixOS/nixpkgs', + versioning: nixpkgsVersioning, + }, + ], + }); + }); + + const flake4Nix = `{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs"; + }; + }`; + + it('includes nixpkgs input with no explicit ref', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake1Lock); + expect(await extractPackageFile(flake4Nix, 'flake.nix')).toEqual({ + deps: [ + { + currentValue: undefined, + datasource: 'git-refs', + depName: 'nixpkgs', + packageName: 'https://github.com/NixOS/nixpkgs', + versioning: 'nixpkgs', + }, + ], + }); + }); + + it('returns null when no inputs', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake1Lock); + expect(await extractPackageFile('', 'flake.nix')).toBeNull(); + }); + + const flake2Lock = `{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1720031269, + "narHash": "sha256-rwz8NJZV+387rnWpTYcXaRNvzUSnnF9aHONoJIYmiUQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } }, - ]); + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 + }`; + + it('returns nixpkgs input', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake2Lock); + expect(await extractPackageFile('', 'flake.nix')).toEqual({ + deps: [ + { + depName: 'nixpkgs', + currentDigest: '9f4128e00b0ae8ec65918efeba59db998750ead6', + currentValue: 'nixos-unstable', + datasource: GitRefsDatasource.id, + packageName: 'https://github.com/NixOS/nixpkgs', + }, + ], + }); }); - it('is case insensitive', () => { - const content = `{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.11"; - }; -}`; + const flake3Lock = `{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1728650607, + "narHash": "sha256-0lOnVTzRXzpk5uxbHLm3Ti3tyPAvirAIQDfwEUd8arg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "612ee628421ba2c1abca4c99684862f76cb3b089", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 + }`; - const res = extractPackageFile(content); + it('includes nixpkgs with no explicit ref', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake3Lock); + expect(await extractPackageFile('', 'flake.nix')).toMatchObject({ + deps: [ + { + currentDigest: '612ee628421ba2c1abca4c99684862f76cb3b089', + datasource: 'git-refs', + depName: 'nixpkgs', + packageName: 'https://github.com/NixOS/nixpkgs', + }, + ], + }); + }); - expect(res?.deps).toEqual([ - { - depName: 'nixpkgs', - currentValue: 'nixos-21.11', - datasource: GitRefsDatasource.id, - packageName: 'https://github.com/NixOS/nixpkgs', - versioning: nixpkgsVersioning, + const flake4Lock = `{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1672057183, + "narHash": "sha256-GN7/10DNNvs1FPj9tlZA2qgNdFuYKKuS3qlHTqAxasQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b139e44d78c36c69bcbb825b20dbfa51e7738347", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixpkgs-unstable", + "type": "indirect" + } }, - ]); + "patchelf": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1718457448, + "narHash": "sha256-FSoxTcRZMGHNJh8dNtKOkcUtjhmhU6yQXcZZfUPLhQM=", + "ref": "refs/heads/master", + "rev": "a0f54334df36770b335c051e540ba40afcbf8378", + "revCount": 844, + "type": "git", + "url": "https://github.com/NixOS/patchelf.git" + }, + "original": { + "type": "git", + "url": "https://github.com/NixOS/patchelf.git" + } + }, + "root": { + "inputs": { + "patchelf": "patchelf" + } + } + }, + "root": "root", + "version": 7 + }`; + + it('includes patchelf from HEAD', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake4Lock); + expect(await extractPackageFile('', 'flake.nix')).toMatchObject({ + deps: [ + { + currentDigest: 'a0f54334df36770b335c051e540ba40afcbf8378', + datasource: 'git-refs', + depName: 'patchelf', + packageName: 'https://github.com/NixOS/patchelf.git', + }, + ], + }); }); - it('includes nixpkgs with no explicit ref', () => { - const content = `{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs"; - }; -}`; + const flake5Lock = `{ + "nodes": { + "ijq": { + "flake": false, + "locked": { + "lastModified": 1723569650, + "narHash": "sha256-Ho/sAhEUeSug52JALgjrKVUPCBe8+PovbJj/lniKxp8=", + "owner": "~gpanders", + "repo": "ijq", + "rev": "88f0d9ae98942bf49cba302c42b2a0f6e05f9b58", + "type": "sourcehut" + }, + "original": { + "owner": "~gpanders", + "repo": "ijq", + "type": "sourcehut" + } + }, + "root": { + "inputs": { + "ijq": "ijq" + } + } + }, + "root": "root", + "version": 7 + }`; - const res = extractPackageFile(content); + it('includes ijq from sourcehut without a flake', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake5Lock); + expect(await extractPackageFile('', 'flake.nix')).toMatchObject({ + deps: [ + { + currentDigest: '88f0d9ae98942bf49cba302c42b2a0f6e05f9b58', + datasource: 'git-refs', + depName: 'ijq', + packageName: 'https://git.sr.ht/~gpanders/ijq', + }, + ], + }); + }); - expect(res).toMatchObject({ + const flake6Lock = `{ + "nodes": { + "home-manager": { + "flake": false, + "locked": { + "lastModified": 1728650932, + "narHash": "sha256-mGKzqdsRyLnGNl6WjEr7+sghGgBtYHhJQ4mjpgRTCsU=", + "owner": "rycee", + "repo": "home-manager", + "rev": "65ae9c147349829d3df0222151f53f79821c5134", + "type": "gitlab" + }, + "original": { + "owner": "rycee", + "repo": "home-manager", + "type": "gitlab" + } + }, + "root": { + "inputs": { + "home-manager": "home-manager" + } + } + }, + "root": "root", + "version": 7 + }`; + + it('includes home-manager from gitlab', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake6Lock); + expect(await extractPackageFile('', 'flake.nix')).toMatchObject({ deps: [ { - currentValue: undefined, + currentDigest: '65ae9c147349829d3df0222151f53f79821c5134', + datasource: 'git-refs', + depName: 'home-manager', + packageName: 'https://gitlab.com/rycee/home-manager', + }, + ], + }); + }); + + const flake7Lock = `{ + "nodes": { + "root": {} + }, + "root": "root", + "version": 6 + }`; + + it('test other version', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake7Lock); + expect(await extractPackageFile('', 'flake.nix')).toBeNull(); + }); + + const flake8Lock = `{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1728492678, + "narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=", + "ref": "nixos-unstable", + "rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7", + "shallow": true, + "type": "git", + "url": "https://github.com/NixOS/nixpkgs" + }, + "original": { + "ref": "nixos-unstable", + "shallow": true, + "type": "git", + "url": "https://github.com/NixOS/nixpkgs" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 + }`; + + it('includes nixpkgs with ref and shallow arguments', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake8Lock); + expect(await extractPackageFile('', 'flake.nix')).toMatchObject({ + deps: [ + { + currentDigest: '5633bcff0c6162b9e4b5f1264264611e950c8ec7', datasource: 'git-refs', depName: 'nixpkgs', packageName: 'https://github.com/NixOS/nixpkgs', - versioning: 'nixpkgs', + }, + ], + }); + }); + + const flake9Lock = `{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1728538411, + "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 + }`; + + it('includes nixpkgs but using indirect type that cannot be updated', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake9Lock); + expect(await extractPackageFile('', 'flake.nix')).toBeNull(); + }); + + const flake10Lock = `{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1728492678, + "narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-extra-pkgs": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + }, + "locked": { + "host": "github.corp.example.com", + "lastModified": 1728666512, + "narHash": "sha256-p+l16Zzyl2DXG695yks6KQP7NkjsnEksu5GBvtL1QYg=", + "owner": "my-org", + "repo": "nixpkgs-extra-pkgs", + "rev": "6bf2706348447df6f8b86b1c3e54f87b0afda84f", + "type": "github" + }, + "original": { + "host": "github.corp.example.com", + "owner": "my-org", + "repo": "nixpkgs-extra-pkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs-extra-pkgs": "nixpkgs-extra-pkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 + }`; + + it('includes flake from GitHub Enterprise', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake10Lock); + expect(await extractPackageFile('', 'flake.nix')).toMatchObject({ + deps: [ + { + currentDigest: '6bf2706348447df6f8b86b1c3e54f87b0afda84f', + datasource: 'git-refs', + depName: 'nixpkgs-extra-pkgs', + packageName: + 'https://github.corp.example.com/my-org/nixpkgs-extra-pkgs', + }, + ], + }); + }); + + const flake11Lock = `{ + "nodes": { + "data-mesher": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1727355895, + "narHash": "sha256-grZIaLgk5GgoDuTt49RTCLBh458H4YJdIAU4B3onXRw=", + "rev": "c7e39452affcc0f89e023091524e38b3aaf109e9", + "type": "tarball", + "url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/c7e39452affcc0f89e023091524e38b3aaf109e9.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://git.clan.lol/clan/data-mesher/archive/main.tar.gz" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "data-mesher", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1726153070, + "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1726871744, + "narHash": "sha256-V5LpfdHyQkUF7RfOaDPrZDP+oqz88lTJrMT1+stXNwo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a1d92660c6b3b7c26fb883500a80ea9d33321be2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "data-mesher": "data-mesher" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "data-mesher", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1726734507, + "narHash": "sha256-VUH5O5AcOSxb0uL/m34dDkxFKP6WLQ6y4I1B4+N3L2w=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "ee41a466c2255a3abe6bc50fc6be927cdee57a9f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 + }`; + + it('includes flake with tarball type', async () => { + fs.readLocalFile.mockResolvedValueOnce(flake11Lock); + expect(await extractPackageFile('', 'flake.nix')).toMatchObject({ + deps: [ + { + currentDigest: 'c7e39452affcc0f89e023091524e38b3aaf109e9', + datasource: 'git-refs', + depName: 'data-mesher', + packageName: 'https://git.clan.lol/clan/data-mesher', }, ], }); diff --git a/lib/modules/manager/nix/extract.ts b/lib/modules/manager/nix/extract.ts index 5170e23a07dfe0..cfe99ead002906 100644 --- a/lib/modules/manager/nix/extract.ts +++ b/lib/modules/manager/nix/extract.ts @@ -1,11 +1,28 @@ +import { logger } from '../../../logger'; +import { getSiblingFileName, readLocalFile } from '../../../util/fs'; import { regEx } from '../../../util/regex'; import { GitRefsDatasource } from '../../datasource/git-refs'; import { id as nixpkgsVersioning } from '../../versioning/nixpkgs'; import type { PackageDependency, PackageFileContent } from '../types'; +import { NixFlakeLock } from './schema'; const nixpkgsRegex = regEx(/"github:nixos\/nixpkgs(\/(?<ref>[a-z0-9-.]+))?"/i); -export function extractPackageFile(content: string): PackageFileContent | null { +// as documented upstream +// https://github.com/NixOS/nix/blob/master/doc/manual/source/protocols/tarball-fetcher.md#gitea-and-forgejo-support +const lockableHTTPTarballProtocol = regEx( + '^https://(?<domain>[^/]+)/(?<owner>[^/]+)/(?<repo>[^/]+)/archive/(?<rev>.+).tar.gz$', +); + +export async function extractPackageFile( + content: string, + packageFile: string, +): Promise<PackageFileContent | null> { + const packageLockFile = getSiblingFileName(packageFile, 'flake.lock'); + const lockContents = await readLocalFile(packageLockFile, 'utf8'); + + logger.trace(`nix.extractPackageFile(${packageLockFile})`); + const deps: PackageDependency[] = []; const match = nixpkgsRegex.exec(content); @@ -20,6 +37,118 @@ export function extractPackageFile(content: string): PackageFileContent | null { }); } + const flakeLockParsed = NixFlakeLock.safeParse(lockContents); + if (!flakeLockParsed.success) { + logger.debug( + { packageLockFile, error: flakeLockParsed.error }, + `invalid flake.lock file`, + ); + return null; + } + + const flakeLock = flakeLockParsed.data; + const rootInputs = flakeLock.nodes['root'].inputs; + + if (!rootInputs) { + logger.debug( + { packageLockFile, error: flakeLockParsed.error }, + `flake.lock is missing "root" node`, + ); + + if (deps.length) { + return { deps }; + } + return null; + } + + for (const [depName, flakeInput] of Object.entries(flakeLock.nodes)) { + // the root input is a magic string for the entrypoint and only references other flake inputs + if (depName === 'root') { + continue; + } + + // skip all locked and transitivie nodes as they cannot be updated by regular means + if (!(depName in rootInputs)) { + continue; + } + + const flakeLocked = flakeInput.locked; + const flakeOriginal = flakeInput.original; + + // istanbul ignore if: if we are not in a root node then original and locked always exist which cannot be easily expressed in the type + if (flakeLocked === undefined || flakeOriginal === undefined) { + logger.debug( + { packageLockFile, flakeInput }, + `Found empty flake input, skipping`, + ); + continue; + } + + // indirect inputs cannot be reliable updated because they depend on the flake registry + if (flakeOriginal.type === 'indirect') { + continue; + } + + switch (flakeLocked.type) { + case 'github': + deps.push({ + depName, + currentValue: flakeOriginal.ref, + currentDigest: flakeLocked.rev, + datasource: GitRefsDatasource.id, + packageName: `https://${flakeOriginal.host ?? 'github.com'}/${flakeOriginal.owner}/${flakeOriginal.repo}`, + }); + break; + case 'gitlab': + deps.push({ + depName, + currentValue: flakeOriginal.ref, + currentDigest: flakeLocked.rev, + datasource: GitRefsDatasource.id, + packageName: `https://${flakeOriginal.host ?? 'gitlab.com'}/${flakeOriginal.owner}/${flakeOriginal.repo}`, + }); + break; + case 'git': + deps.push({ + depName, + currentValue: flakeOriginal.ref, + currentDigest: flakeLocked.rev, + datasource: GitRefsDatasource.id, + packageName: flakeOriginal.url, + }); + break; + case 'sourcehut': + deps.push({ + depName, + currentValue: flakeOriginal.ref, + currentDigest: flakeLocked.rev, + datasource: GitRefsDatasource.id, + packageName: `https://${flakeOriginal.host ?? 'git.sr.ht'}/${flakeOriginal.owner}/${flakeOriginal.repo}`, + }); + break; + case 'tarball': + deps.push({ + depName, + currentValue: flakeLocked.ref, + currentDigest: flakeLocked.rev, + datasource: GitRefsDatasource.id, + // type tarball always contains this link + packageName: flakeOriginal.url!.replace( + lockableHTTPTarballProtocol, + 'https://$<domain>/$<owner>/$<repo>', + ), + }); + break; + // istanbul ignore next: just a safeguard + default: + logger.debug( + { packageLockFile }, + `Unknown flake.lock type "${flakeLocked.type}", skipping`, + ); + break; + } + } + if (deps.length) { return { deps }; } diff --git a/lib/modules/manager/nix/index.ts b/lib/modules/manager/nix/index.ts index 35fa06494f17eb..abbe3e6ec3e84f 100644 --- a/lib/modules/manager/nix/index.ts +++ b/lib/modules/manager/nix/index.ts @@ -9,7 +9,7 @@ export const url = 'https://nix.dev'; export const defaultConfig = { fileMatch: ['(^|/)flake\\.nix$'], - commitMessageTopic: 'nixpkgs', + commitMessageTopic: 'nix', commitMessageExtra: 'to {{newValue}}', enabled: false, }; diff --git a/lib/modules/manager/nix/readme.md b/lib/modules/manager/nix/readme.md index 3a196a34445c0e..af3d80c5b0b05e 100644 --- a/lib/modules/manager/nix/readme.md +++ b/lib/modules/manager/nix/readme.md @@ -1,4 +1,9 @@ The [`nix`](https://github.com/NixOS/nix) manager supports: - [`lockFileMaintenance`](../../../configuration-options.md#lockfilemaintenance) updates for `flake.lock` -- [nixpkgs](https://github.com/NixOS/nixpkgs) updates +- input updates for `flake.lock` + +For specifying `packageRules` it is important to know how `depName` and `packageName` are defined for nix updates: + +- The `depName` field is equal to the nix flake input name, eg. `nix.inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";` would have the `depName` of `nixpkgs` +- The `packageName` field is equal to the fully-qualified root URL of the package source, eg. `https://github.com/NixOS/nixpkgs` for the above example. diff --git a/lib/modules/manager/nix/schema.ts b/lib/modules/manager/nix/schema.ts new file mode 100644 index 00000000000000..af8e0e33e95dbf --- /dev/null +++ b/lib/modules/manager/nix/schema.ts @@ -0,0 +1,41 @@ +import { z } from 'zod'; +import { Json } from '../../../util/schema-utils'; + +const InputType = z.enum([ + 'git', + 'github', + 'gitlab', + 'indirect', + 'sourcehut', + 'tarball', +]); + +const LockedInput = z.object({ + ref: z.string().optional(), + rev: z.string(), + type: InputType, +}); + +const OriginalInput = z.object({ + host: z.string().optional(), + owner: z.string().optional(), + repo: z.string().optional(), + ref: z.string().optional(), + type: InputType, + url: z.string().optional(), +}); + +const NixInput = z.object({ + inputs: z.record(z.string(), z.string().or(z.array(z.string()))).optional(), + locked: LockedInput.optional(), + original: OriginalInput.optional(), +}); + +export const NixFlakeLock = Json.pipe( + z.object({ + nodes: z.record(z.string(), NixInput), + version: z.literal(7), + }), +); + +export type NixFlakeLock = z.infer<typeof NixFlakeLock>;