Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,6 @@
"eslint.useESLintClass": true,
"yaml.schemas": {
"https://json.schemastore.org/container-structure-test.json": "/dev/docker/ci/tests/*.yml"
}
},
"js/ts.tsdk.path": "node_modules/typescript/lib"
}
187 changes: 94 additions & 93 deletions dist/legacy/setup-cpp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/legacy/setup-cpp.js.map

Large diffs are not rendered by default.

191 changes: 96 additions & 95 deletions dist/modern/setup-cpp.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/modern/setup-cpp.mjs.map

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions packages/setup-apt/__tests__/qualify-install.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { execaSync } from "execa"

import { getAptEnv } from "../src/apt-env.js"
import { hasAptGet } from "../src/get-apt.js"
import { filterAndQualifyAptPackages } from "../src/qualify-install.js"

function indexedGccVariants() {
try {
const { stdout } = execaSync("apt-cache", ["search", "--names-only", "^gcc-[0-9]+$"], {
env: getAptEnv("apt-get"),
stdio: "pipe",
})
return stdout.split("\n")
.map((line) => line.trim().split(/\s+/u)[0])
.filter((name): name is string => name !== undefined && /^gcc-\d+$/u.test(name))
.sort((first, second) => {
const firstVersion = Number.parseInt(first.slice("gcc-".length), 10)
const secondVersion = Number.parseInt(second.slice("gcc-".length), 10)
return secondVersion - firstVersion
})
} catch {
return []
}
}

describe("filterAndQualifyAptPackages", () => {
if (!hasAptGet()) {
test.skip("filters installed packages", () => {})
return
}

it("filters an installed package when upgrade is disabled", async () => {
await expect(filterAndQualifyAptPackages([{ name: "apt", upgrade: false }])).resolves.toEqual([])
})

it("retains an installed package when upgrade is requested", async () => {
await expect(filterAndQualifyAptPackages([{ name: "apt", upgrade: true }])).resolves.toEqual(["apt"])
})

const gccVariants = indexedGccVariants()
if (gccVariants.length === 0) {
test.skip("resolves the highest indexed gcc-N package", () => {})
return
}

it("resolves an unversioned package to its highest indexed numeric variant", async () => {
await expect(filterAndQualifyAptPackages([{ name: "gcc" }])).resolves.toEqual([gccVariants[0]])
})
})
5 changes: 5 additions & 0 deletions packages/setup-apt/src/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export type AptPackage = {
name: string
/** The version of the package (optional) */
version?: string
/**
* Whether to allow apt to upgrade the latest package available in the default repositories.
* This would could do a major upgrade for unversioned meta packages (e.g. gcc) or minor upgrade for versioned packages (e.g. gcc-9)
*/
upgrade?: boolean
/** The repository to add before installing the package (optional) */
repository?: string
/** The key to add before installing the package (optional) */
Expand Down
38 changes: 34 additions & 4 deletions packages/setup-apt/src/qualify-install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ export async function filterAndQualifyAptPackages(packages: AptPackage[], apt: s
/**
* Qualify the package into full package name/version.
* If the package is not installed, return the full package name/version.
* If the package is already installed, return undefined
* If the package is already installed and upgrade is not requested, return undefined
*/
export async function qualifiedNeededAptPackage(pack: AptPackage, apt: string = getApt()) {
// By default, leave the package in the install list so apt can select the candidate.
const upgrade = pack.upgrade ?? true
// Qualify the package into full package name/version
const qualified = await getAptArg(apt, pack)
// filter out the package that are already installed
return (await isAptPackInstalled(qualified)) ? undefined : qualified
// Filter out packages that are already installed unless they should be upgraded.
return (await isAptPackInstalled(qualified)) && !upgrade ? undefined : qualified
}

async function aptPackageType(
Expand Down Expand Up @@ -118,7 +120,14 @@ async function aptCacheShowHasPackage(apt: string, arg: string) {
}

async function getAptArg(apt: string, pack: AptPackage) {
const { name, version, fallBackToLatest = false } = pack
const { name, version, upgrade = true, fallBackToLatest = false } = pack

if ((version === undefined || version === "") && upgrade) {
const numericVariant = await findHighestNumericAptPackage(apt, name)
if (numericVariant !== undefined) {
return numericVariant
}
}

const package_type = await aptPackageType(apt, name, version, fallBackToLatest)
switch (package_type) {
Expand All @@ -133,3 +142,24 @@ async function getAptArg(apt: string, pack: AptPackage) {
throw new Error(`Could not find package '${name}' ${version ?? "with unspecified version"}`)
}
}

async function findHighestNumericAptPackage(apt: string, name: string) {
const packageNamePattern = new RegExp(`^${escapeRegex(name)}-([0-9]+)$`, "u")

try {
const { stdout } = await execa("apt-cache", [
"search",
"--names-only",
`^${escapeRegex(name)}-[0-9]+$`,
], { env: getAptEnv(apt), stdio: "pipe" })
const candidates = stdout.split("\n").flatMap((line) => {
const packageName: string | undefined = line.trim().split(/\s+/u)[0]
const match = packageName.match(packageNamePattern)
return match === null ? [] : [{ packageName, version: Number.parseInt(match[1], 10) }]
})

return candidates.sort((first, second) => second.version - first.version)[0]?.packageName
} catch {
return undefined
}
}
Loading