diff --git a/data/ubuntu-distro-info.json b/data/ubuntu-distro-info.json new file mode 100644 index 0000000000000000000000000000000000000000..75d602ec51f7809b01d2409b2c004979636b6599 --- /dev/null +++ b/data/ubuntu-distro-info.json @@ -0,0 +1,38 @@ +{ + "4.10": "warty", + "5.04": "hoary", + "5.10": "breezy", + "6.06": "dapper", + "6.10": "edgy", + "7.04": "feisty", + "7.10": "gutsy", + "8.04": "hardy", + "8.10": "intrepid", + "9.04": "jaunty", + "9.10": "karmic", + "10.04": "lucid", + "10.10": "maverick", + "11.04": "natty", + "11.10": "oneiric", + "12.04": "precise", + "12.10": "quantal", + "13.04": "raring", + "13.10": "saucy", + "14.04": "trusty", + "14.10": "utopic", + "15.04": "vivid", + "15.10": "wily", + "16.04": "xenial", + "16.10": "yakkety", + "17.04": "zesty", + "17.10": "artful", + "18.04": "bionic", + "18.10": "cosmic", + "19.04": "disco", + "19.10": "eoan", + "20.04": "focal", + "20.10": "groovy", + "21.04": "hirsute", + "21.10": "impish", + "22.04": "jammy" +} diff --git a/docs/usage/docker.md b/docs/usage/docker.md index d7632eb12488319d9684501b303d8224383f1a22..da6fd8dc3fcb25f7efebb3d4f4076dff3f283bf1 100644 --- a/docs/usage/docker.md +++ b/docs/usage/docker.md @@ -117,6 +117,27 @@ If you wish to enable major versions then add the preset `docker:enableMajor` to Renovate has some Docker-specific intelligence when it comes to versions. For example: +### Ubuntu codenames + +Renovate understands [Ubuntu release code names](https://wiki.ubuntu.com/Releases) and will offer upgrades to the latest LTS release (e.g. from `ubuntu:xenial` to `ubuntu:focal`). + +For this to work you must follow this naming scheme: + +- The first term of the full codename is used (e.g. `bionic` for `Bionic Beaver` release) +- The codename is in lowercase + +For example, Renovate will offer to upgrade the following `Dockerfile` layer: + +```dockerfile +FROM ubuntu:yakkety +``` + +To + +```dockerfile +FROM ubuntu:focal +``` + ## Configuring/Disabling If you wish to make changes that apply to all Docker managers, then add them to the `docker` config object. diff --git a/lib/modules/versioning/ubuntu/distribution.ts b/lib/modules/versioning/ubuntu/distribution.ts new file mode 100644 index 0000000000000000000000000000000000000000..999e3c382c4934ea98215a5737df9d03e35bab93 --- /dev/null +++ b/lib/modules/versioning/ubuntu/distribution.ts @@ -0,0 +1,40 @@ +import dataFiles from '../../../data-files.generated'; + +export type UbuntuDistroInfo = Record<string, string>; + +// Data file generated with: +// distro-json-generate.mjs +const ubuntuJsonKey = 'data/ubuntu-distro-info.json'; + +const ubuntuDistroInfo: UbuntuDistroInfo = JSON.parse( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + dataFiles.get(ubuntuJsonKey)! +); + +const codenameToVersion = new Map<string, string>(); + +for (const version of Object.keys(ubuntuDistroInfo)) { + const codename = ubuntuDistroInfo[version]; + codenameToVersion.set(codename, version); +} + +export function isCodename(input: string): boolean { + return codenameToVersion.has(input); +} + +export function getVersionByCodename(input: string): string { + const ver = codenameToVersion.get(input); + if (ver) { + return ver; + } + return input; +} + +export function getCodenameByVersion(input: string): string { + const codename = ubuntuDistroInfo[input]; + if (codename) { + return codename; + } + // istanbul ignore next + return input; +} diff --git a/lib/modules/versioning/ubuntu/index.spec.ts b/lib/modules/versioning/ubuntu/index.spec.ts index c3e65b5abb93e6325f02698403b156e6e95fd4cf..1d286adfabc33bffeba6d73aa31adebe707adcd3 100644 --- a/lib/modules/versioning/ubuntu/index.spec.ts +++ b/lib/modules/versioning/ubuntu/index.spec.ts @@ -2,45 +2,82 @@ import { api as ubuntu } from '.'; describe('modules/versioning/ubuntu/index', () => { test.each` - version | expected - ${undefined} | ${false} - ${null} | ${false} - ${''} | ${false} - ${'xenial'} | ${false} - ${'04.10'} | ${true} - ${'05.04'} | ${true} - ${'05.10'} | ${true} - ${'6.06'} | ${true} - ${'6.10'} | ${true} - ${'7.04'} | ${true} - ${'7.10'} | ${true} - ${'8.04'} | ${true} - ${'8.10'} | ${true} - ${'9.04'} | ${true} - ${'9.10'} | ${true} - ${'10.04.4'} | ${true} - ${'10.10'} | ${true} - ${'11.04'} | ${true} - ${'11.10'} | ${true} - ${'12.04.5'} | ${true} - ${'12.10'} | ${true} - ${'13.04'} | ${true} - ${'13.10'} | ${true} - ${'14.04.6'} | ${true} - ${'14.10'} | ${true} - ${'15.04'} | ${true} - ${'15.10'} | ${true} - ${'16.04.7'} | ${true} - ${'16.10'} | ${true} - ${'17.04'} | ${true} - ${'17.10'} | ${true} - ${'18.04.5'} | ${true} - ${'18.10'} | ${true} - ${'19.04'} | ${true} - ${'19.10'} | ${true} - ${'20.04'} | ${true} - ${'20.10'} | ${true} - ${'2020.04'} | ${false} + version | expected + ${undefined} | ${false} + ${null} | ${false} + ${''} | ${false} + ${'xenial'} | ${true} + ${'04.10'} | ${true} + ${'05.04'} | ${true} + ${'05.10'} | ${true} + ${'6.06'} | ${true} + ${'6.10'} | ${true} + ${'7.04'} | ${true} + ${'7.10'} | ${true} + ${'8.04'} | ${true} + ${'8.10'} | ${true} + ${'9.04'} | ${true} + ${'9.10'} | ${true} + ${'10.04.4'} | ${true} + ${'10.10'} | ${true} + ${'11.04'} | ${true} + ${'11.10'} | ${true} + ${'12.04.5'} | ${true} + ${'12.10'} | ${true} + ${'13.04'} | ${true} + ${'13.10'} | ${true} + ${'14.04.6'} | ${true} + ${'14.10'} | ${true} + ${'15.04'} | ${true} + ${'15.10'} | ${true} + ${'16.04.7'} | ${true} + ${'16.10'} | ${true} + ${'17.04'} | ${true} + ${'17.10'} | ${true} + ${'18.04.5'} | ${true} + ${'18.10'} | ${true} + ${'19.04'} | ${true} + ${'19.10'} | ${true} + ${'20.04'} | ${true} + ${'20.10'} | ${true} + ${'2020.04'} | ${false} + ${'xenial'} | ${true} + ${'warty'} | ${true} + ${'hoary'} | ${true} + ${'breezy'} | ${true} + ${'dapper'} | ${true} + ${'edgy'} | ${true} + ${'feisty'} | ${true} + ${'gutsy'} | ${true} + ${'hardy'} | ${true} + ${'intrepid'} | ${true} + ${'jaunty'} | ${true} + ${'karmic'} | ${true} + ${'lucid.4'} | ${false} + ${'maverick'} | ${true} + ${'natty'} | ${true} + ${'oneiric'} | ${true} + ${'precise.5'} | ${false} + ${'quantal'} | ${true} + ${'raring'} | ${true} + ${'saucy'} | ${true} + ${'trusty.6'} | ${false} + ${'utopic'} | ${true} + ${'vivid'} | ${true} + ${'wily'} | ${true} + ${'xenial.7'} | ${false} + ${'yakkety'} | ${true} + ${'zesty'} | ${true} + ${'artful'} | ${true} + ${'bionic.5'} | ${false} + ${'cosmic'} | ${true} + ${'disco'} | ${true} + ${'eoan'} | ${true} + ${'focal'} | ${true} + ${'groovy'} | ${true} + ${'hirsute'} | ${true} + ${'impish'} | ${true} + ${'jammy'} | ${true} `('isValid("$version") === $expected', ({ version, expected }) => { expect(!!ubuntu.isValid(version)).toBe(expected); }); @@ -52,6 +89,8 @@ describe('modules/versioning/ubuntu/index', () => { ${''} | ${undefined} | ${false} ${'04.10'} | ${undefined} | ${true} ${'20.10'} | ${undefined} | ${true} + ${'warty'} | ${undefined} | ${true} + ${'groovy'} | ${undefined} | ${true} `( 'isCompatible("$version") === $expected', ({ version, range, expected }) => { @@ -72,56 +111,92 @@ describe('modules/versioning/ubuntu/index', () => { }); test.each` - version | expected - ${undefined} | ${false} - ${null} | ${false} - ${''} | ${false} - ${'04.10'} | ${false} - ${'05.04'} | ${false} - ${'05.10'} | ${false} - ${'6.06'} | ${false} - ${'6.10'} | ${false} - ${'7.04'} | ${false} - ${'7.10'} | ${false} - ${'8.04'} | ${true} - ${'8.10'} | ${false} - ${'9.04'} | ${false} - ${'9.10'} | ${false} - ${'10.04.4'} | ${true} - ${'10.10'} | ${false} - ${'11.04'} | ${false} - ${'11.10'} | ${false} - ${'12.04.5'} | ${true} - ${'12.10'} | ${false} - ${'13.04'} | ${false} - ${'13.10'} | ${false} - ${'14.04.6'} | ${true} - ${'14.10'} | ${false} - ${'15.04'} | ${false} - ${'15.10'} | ${false} - ${'16.04.7'} | ${true} - ${'16.10'} | ${false} - ${'17.04'} | ${false} - ${'17.10'} | ${false} - ${'18.04.5'} | ${true} - ${'18.10'} | ${false} - ${'19.04'} | ${false} - ${'19.10'} | ${false} - ${'20.04'} | ${true} - ${'20.10'} | ${false} - ${'42.01'} | ${false} - ${'42.02'} | ${false} - ${'42.03'} | ${false} - ${'42.04'} | ${true} - ${'42.05'} | ${false} - ${'42.06'} | ${false} - ${'42.07'} | ${false} - ${'42.08'} | ${false} - ${'42.09'} | ${false} - ${'42.10'} | ${false} - ${'42.11'} | ${false} - ${'2020.04'} | ${false} - ${'22.04'} | ${false} + version | expected + ${undefined} | ${false} + ${null} | ${false} + ${''} | ${false} + ${'04.10'} | ${false} + ${'05.04'} | ${false} + ${'05.10'} | ${false} + ${'6.06'} | ${false} + ${'6.10'} | ${false} + ${'7.04'} | ${false} + ${'7.10'} | ${false} + ${'8.04'} | ${true} + ${'8.10'} | ${false} + ${'9.04'} | ${false} + ${'9.10'} | ${false} + ${'10.04.4'} | ${true} + ${'10.10'} | ${false} + ${'11.04'} | ${false} + ${'11.10'} | ${false} + ${'12.04.5'} | ${true} + ${'12.10'} | ${false} + ${'13.04'} | ${false} + ${'13.10'} | ${false} + ${'14.04.6'} | ${true} + ${'14.10'} | ${false} + ${'15.04'} | ${false} + ${'15.10'} | ${false} + ${'16.04.7'} | ${true} + ${'16.10'} | ${false} + ${'17.04'} | ${false} + ${'17.10'} | ${false} + ${'18.04.5'} | ${true} + ${'18.10'} | ${false} + ${'19.04'} | ${false} + ${'19.10'} | ${false} + ${'20.04'} | ${true} + ${'20.10'} | ${false} + ${'42.01'} | ${false} + ${'42.02'} | ${false} + ${'42.03'} | ${false} + ${'42.04'} | ${true} + ${'42.05'} | ${false} + ${'42.06'} | ${false} + ${'42.07'} | ${false} + ${'42.08'} | ${false} + ${'42.09'} | ${false} + ${'42.10'} | ${false} + ${'42.11'} | ${false} + ${'2020.04'} | ${false} + ${'22.04'} | ${false} + ${'warty'} | ${false} + ${'hoary'} | ${false} + ${'breezy'} | ${false} + ${'dapper'} | ${false} + ${'edgy'} | ${false} + ${'feisty'} | ${false} + ${'gutsy'} | ${false} + ${'hardy'} | ${true} + ${'intrepid'} | ${false} + ${'jaunty'} | ${false} + ${'karmic'} | ${false} + ${'lucid'} | ${true} + ${'maverick'} | ${false} + ${'natty'} | ${false} + ${'oneiric'} | ${false} + ${'precise'} | ${true} + ${'quantal'} | ${false} + ${'raring'} | ${false} + ${'saucy'} | ${false} + ${'trusty'} | ${true} + ${'utopic'} | ${false} + ${'vivid'} | ${false} + ${'wily'} | ${false} + ${'xenial'} | ${true} + ${'yakkety'} | ${false} + ${'zesty'} | ${false} + ${'artful'} | ${false} + ${'bionic'} | ${true} + ${'cosmic'} | ${false} + ${'disco'} | ${false} + ${'eoan'} | ${false} + ${'focal'} | ${true} + ${'groovy'} | ${false} + ${'hirsute'} | ${false} + ${'impish'} | ${false} + ${'jammy'} | ${false} `('isStable("$version") === $expected', ({ version, expected }) => { const res = !!ubuntu.isStable(version); expect(res).toBe(expected); @@ -152,20 +227,47 @@ describe('modules/versioning/ubuntu/index', () => { ${'20.10'} | ${true} ${'30.11'} | ${true} ${'2020.04'} | ${false} + ${'warty'} | ${true} + ${'hoary'} | ${true} + ${'dapper'} | ${true} + ${'hardy'} | ${true} + ${'jaunty'} | ${true} + ${'lucid'} | ${true} + ${'precise'} | ${true} + ${'raring'} | ${true} + ${'trusty'} | ${true} + ${'vivid'} | ${true} + ${'xenial'} | ${true} + ${'yakkety'} | ${true} + ${'zesty'} | ${true} + ${'bionic'} | ${true} + ${'cosmic'} | ${true} + ${'focal'} | ${true} + ${'groovy'} | ${true} + ${'hirsute'} | ${true} + ${'impish'} | ${true} + ${'jammy'} | ${true} + ${'Groovy'} | ${false} + ${'Hirsute'} | ${false} + ${'impish-'} | ${false} + ${'JAMMY'} | ${false} `('isVersion("$version") === $expected', ({ version, expected }) => { expect(!!ubuntu.isVersion(version)).toBe(expected); }); test.each` - version | major | minor | patch - ${undefined} | ${null} | ${null} | ${null} - ${null} | ${null} | ${null} | ${null} - ${''} | ${null} | ${null} | ${null} - ${'42'} | ${null} | ${null} | ${null} - ${'2020.04'} | ${null} | ${null} | ${null} - ${'04.10'} | ${4} | ${10} | ${null} - ${'18.04.5'} | ${18} | ${4} | ${5} - ${'20.04'} | ${20} | ${4} | ${null} + version | major | minor | patch + ${undefined} | ${null} | ${null} | ${null} + ${null} | ${null} | ${null} | ${null} + ${''} | ${null} | ${null} | ${null} + ${'42'} | ${null} | ${null} | ${null} + ${'2020.04'} | ${null} | ${null} | ${null} + ${'04.10'} | ${4} | ${10} | ${null} + ${'18.04.5'} | ${18} | ${4} | ${5} + ${'20.04'} | ${20} | ${4} | ${null} + ${'intrepid'} | ${8} | ${10} | ${null} + ${'bionic'} | ${18} | ${4} | ${null} + ${'focal'} | ${20} | ${4} | ${null} `( 'getMajor, getMinor, getPatch for "$version"', ({ version, major, minor, patch }) => { @@ -176,11 +278,16 @@ describe('modules/versioning/ubuntu/index', () => { ); test.each` - a | b | expected - ${'20.04'} | ${'2020.04'} | ${false} - ${'focal'} | ${'20.04'} | ${false} - ${'20.04'} | ${'focal'} | ${false} - ${'19.10'} | ${'19.10'} | ${true} + a | b | expected + ${'20.04'} | ${'2020.04'} | ${false} + ${'17.10'} | ${'artful'} | ${true} + ${'xenial'} | ${'artful'} | ${false} + ${'17.04'} | ${'artful'} | ${false} + ${'artful'} | ${'17.10'} | ${true} + ${'16.04'} | ${'xenial'} | ${true} + ${'focal'} | ${'20.04'} | ${true} + ${'20.04'} | ${'focal'} | ${true} + ${'19.10'} | ${'19.10'} | ${true} `('equals($a, $b) === $expected', ({ a, b, expected }) => { expect(ubuntu.equals(a, b)).toBe(expected); }); @@ -198,6 +305,16 @@ describe('modules/versioning/ubuntu/index', () => { ${'19.10.1'} | ${'20.04.1'} | ${false} ${'20.04.1'} | ${'19.10.1'} | ${true} ${'xxx'} | ${'yyy'} | ${false} + ${'focal'} | ${'groovy'} | ${false} + ${'groovy'} | ${'focal'} | ${true} + ${'eoan'} | ${'focal'} | ${false} + ${'focal'} | ${'eoan'} | ${true} + ${'vivid'} | ${'saucy'} | ${true} + ${'impish'} | ${'focal'} | ${true} + ${'eoan'} | ${'quantal'} | ${true} + ${'focal'} | ${'lucid'} | ${true} + ${'eoan'} | ${'focal'} | ${false} + ${'focal'} | ${'eoan'} | ${true} `('isGreaterThan("$a", "$b") === $expected', ({ a, b, expected }) => { expect(ubuntu.isGreaterThan(a, b)).toBe(expected); }); @@ -209,6 +326,11 @@ describe('modules/versioning/ubuntu/index', () => { ${['18.10', '19.04', '19.10', '20.04']} | ${'20.04'} | ${'20.04'} ${['18.10', '19.04', '19.10', '20.04']} | ${'19.10'} | ${'19.10'} ${['18.10', '19.04', '19.10', '20.04']} | ${'04.10'} | ${null} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'2020.04'} | ${null} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'foobar'} | ${null} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'focal'} | ${'focal'} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'eoan'} | ${'eoan'} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'warty'} | ${null} `( 'getSatisfyingVersion($versions, "$range") === "$expected"', ({ versions, range, expected }) => { @@ -223,6 +345,11 @@ describe('modules/versioning/ubuntu/index', () => { ${['18.10', '19.04', '19.10', '20.04']} | ${'20.04'} | ${'20.04'} ${['18.10', '19.04', '19.10', '20.04']} | ${'19.10'} | ${'19.10'} ${['18.10', '19.04', '19.10', '20.04']} | ${'04.10'} | ${null} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'2020.04'} | ${null} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'foobar'} | ${null} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'focal'} | ${'focal'} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'eoan'} | ${'eoan'} + ${['cosmic', 'disco', 'eoan', 'focal']} | ${'warty'} | ${null} `( 'minSatisfyingVersion($versions, "$range") === "$expected"', ({ versions, range, expected }) => { @@ -233,6 +360,10 @@ describe('modules/versioning/ubuntu/index', () => { test.each` currentValue | rangeStrategy | currentVersion | newVersion | expected ${undefined} | ${undefined} | ${undefined} | ${'foobar'} | ${'foobar'} + ${'xenial'} | ${undefined} | ${undefined} | ${'20.04'} | ${'focal'} + ${'xenial'} | ${undefined} | ${undefined} | ${'focal'} | ${'focal'} + ${'16.04'} | ${undefined} | ${undefined} | ${'20.04'} | ${'20.04'} + ${'16.04'} | ${undefined} | ${undefined} | ${'focal'} | ${'20.04'} `( 'getNewValue("$currentValue", "$rangeStrategy", "$currentVersion", "$newVersion") === "$expected"', ({ currentValue, rangeStrategy, currentVersion, newVersion, expected }) => { @@ -248,8 +379,9 @@ describe('modules/versioning/ubuntu/index', () => { ); test.each` - versions | expected - ${['17.03', '18.04', '18.04', '6.10', '19.10']} | ${['6.10', '17.03', '18.04', '18.04', '19.10']} + versions | expected + ${['17.03', '18.04', '18.04', '6.10', '19.10']} | ${['6.10', '17.03', '18.04', '18.04', '19.10']} + ${['17.03', 'zesty', 'bionic', 'bionic', 'edgy', 'eoan']} | ${['edgy', '17.03', 'zesty', 'bionic', 'bionic', 'eoan']} `('$versions -> sortVersions -> $expected ', ({ versions, expected }) => { expect(versions.sort(ubuntu.sortVersions)).toEqual(expected); }); diff --git a/lib/modules/versioning/ubuntu/index.ts b/lib/modules/versioning/ubuntu/index.ts index f1b4833f68a1a5fb79ba4d91998636f0f14b276d..7b57ba9e1c71be7c8c0ea30f61ee4d52d16ca772 100644 --- a/lib/modules/versioning/ubuntu/index.ts +++ b/lib/modules/versioning/ubuntu/index.ts @@ -1,5 +1,10 @@ import { regEx } from '../../../util/regex'; import type { NewValueConfig, VersioningApi } from '../types'; +import { + getCodenameByVersion, + getVersionByCodename, + isCodename, +} from './distribution'; export const id = 'ubuntu'; export const displayName = 'Ubuntu'; @@ -13,8 +18,11 @@ const temporarilyUnstable = ['22.04']; function isValid(input: string): boolean { return ( - typeof input === 'string' && - regEx(/^(0[4-5]|[6-9]|[1-9][0-9])\.[0-9][0-9](\.[0-9]{1,2})?$/).test(input) + (typeof input === 'string' && + regEx(/^(0[4-5]|[6-9]|[1-9][0-9])\.[0-9][0-9](\.[0-9]{1,2})?$/).test( + input + )) || + isCodename(input) ); } @@ -31,36 +39,40 @@ function isSingleVersion(version: string): boolean { } function isStable(version: string): boolean { - if (!isValid(version)) { + const ver = getVersionByCodename(version); + if (!isValid(ver)) { return false; } - if (temporarilyUnstable.includes(version)) { + if (temporarilyUnstable.includes(ver)) { return false; } - return regEx(/^\d?[02468]\.04/).test(version); + return regEx(/^\d?[02468]\.04/).test(ver); } // digestion of version function getMajor(version: string): null | number { - if (isValid(version)) { - const [major] = version.split('.'); + const ver = getVersionByCodename(version); + if (isValid(ver)) { + const [major] = ver.split('.'); return parseInt(major, 10); } return null; } function getMinor(version: string): null | number { - if (isValid(version)) { - const [, minor] = version.split('.'); + const ver = getVersionByCodename(version); + if (isValid(ver)) { + const [, minor] = ver.split('.'); return parseInt(minor, 10); } return null; } function getPatch(version: string): null | number { - if (isValid(version)) { - const [, , patch] = version.split('.'); + const ver = getVersionByCodename(version); + if (isValid(ver)) { + const [, , patch] = ver.split('.'); return patch ? parseInt(patch, 10) : null; } return null; @@ -69,7 +81,9 @@ function getPatch(version: string): null | number { // comparison function equals(version: string, other: string): boolean { - return isVersion(version) && isVersion(other) && version === other; + const ver = getVersionByCodename(version); + const otherVer = getVersionByCodename(other); + return isVersion(ver) && isVersion(otherVer) && ver === otherVer; } function isGreaterThan(version: string, other: string): boolean { @@ -110,8 +124,16 @@ function minSatisfyingVersion( return getSatisfyingVersion(versions, range); } -function getNewValue(newValueConfig: NewValueConfig): string { - return newValueConfig.newVersion; +function getNewValue({ + currentValue, + rangeStrategy, + currentVersion, + newVersion, +}: NewValueConfig): string { + if (isCodename(currentValue)) { + return getCodenameByVersion(newVersion); + } + return getVersionByCodename(newVersion); } function sortVersions(version: string, other: string): number { diff --git a/package.json b/package.json index ac7e84490c21ba43e5c49b067e0ebacf4fb6c863..dbb106fb30b744a2eff53b9ce60a93638c898703 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "test-schema": "run-s create-json-schema", "tsc": "tsc", "type-check": "run-s generate:* \"tsc --noEmit {@}\" --", + "update:ubuntu-distro-info": "node tools/distro-json-generate.mjs", "verify": "node tools/verify.mjs" }, "repository": { diff --git a/tools/distro-json-generate.mjs b/tools/distro-json-generate.mjs new file mode 100644 index 0000000000000000000000000000000000000000..5ee96ccbce38f8d9c0464a463c57ae01c3f817a2 --- /dev/null +++ b/tools/distro-json-generate.mjs @@ -0,0 +1,73 @@ +import fs from 'fs-extra'; +import shell from 'shelljs'; + +shell.echo(`Verifying required packages...`); + +if (!shell.which(`distro-info`)) { + shell.echo('This script requires distro-info, exiting...'); + shell.exit(2); +} + +if (!shell.which(`sed`)) { + shell.echo('This script requires sed, exiting...'); + shell.exit(2); +} + +shell.echo(`OK`); + +const ubuntuDistroInfo = shell.exec( + `ubuntu-distro-info --all -f | sed -r 's/Ubuntu|"|LTS |Debian //g; s/([0-9]+.[0-9]+) /\\1 /; s/.*/\\L&/; s/( [a-z]*) [a-z]*/\\1/g; s/^[ \\t]*//'`, + { silent: true } +); + +/** + * @param {string} str + * @returns {{}} + */ +function objectify(str) { + let obj = {}; + + for (const line of str.split(/\r?\n/)) { + let [ver, codename] = line.split(' '); + // eslint-disable-next-line + // @ts-ignore + obj[ver] = codename; + } + + return obj; +} + +/** + * @param {string} file + * @param {string} newData + */ +async function updateJsonFile(file, newData) { + let oldData; + + try { + oldData = fs.existsSync(file) ? await fs.readFile(file, 'utf8') : null; + // Eliminate formatting + oldData = oldData?.replace(/\s/g, '') ?? null; + } catch (e) { + shell.echo(e.toString()); + shell.exit(1); + } + + const parsedData = JSON.stringify(objectify(newData), undefined, 2); + + if (oldData === parsedData) { + shell.echo(`${file} is up to date.`); + return; + } + + try { + shell.echo(`Updating ${file}`); + await fs.writeFile(file, parsedData); + } catch (e) { + shell.echo(e.toString()); + shell.exit(1); + } +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +updateJsonFile(`../data/ubuntu-distro-info.json`, ubuntuDistroInfo.toString());