Skip to content

Commit 18f6af3

Browse files
committed
feat: support installing specific versions for alpine
1 parent f737a07 commit 18f6af3

File tree

3 files changed

+62
-27
lines changed

3 files changed

+62
-27
lines changed

packages/setup-alpine/src/apk-repository.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ export async function addApkRepository(repoUrl: string): Promise<boolean> {
2525

2626
// Add repository to the file
2727
info(`Adding repository: ${repoUrl}`)
28-
await execRoot("sh", ["-c", `echo "${repoUrl}" >> ${reposFile}`], { stdio: "inherit" })
28+
await execRoot("sh", ["-c", `echo "${repoUrl}" >> ${reposFile}`])
2929

3030
// Update package index after adding repository
31-
await execRoot("apk", ["update"], { stdio: "inherit" })
31+
await execRoot("apk", ["update"])
3232

3333
info(`Successfully added repository: ${repoUrl}`)
3434
return true

packages/setup-alpine/src/index.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
11
import { execRoot } from "admina"
22
import { info, warning } from "ci-log"
33
import { hasApk } from "./has-apk.js"
4-
import { type ApkPackage, filterAndQualifyApkPackages } from "./qualify-install.js"
4+
import { type ApkPackage, filterAndQualifyApkPackages, formatPackageWithVersion } from "./qualify-install.js"
55
import { updateApkMemoized } from "./update.js"
66

77
export type { ApkPackage }
88

9-
/**
10-
* Options for installing APK packages
11-
*/
12-
export interface InstallApkPackageOptions {
13-
/** The package name to install */
14-
package: ApkPackage
15-
/** Whether to update the package index before installing */
16-
update?: boolean
17-
}
18-
199
/**
2010
* Install a package using Alpine's apk package manager
2111
* @param packages The packages to install
@@ -47,13 +37,13 @@ export async function installApkPackage(packages: ApkPackage[], update = false):
4737
}
4838

4939
// Install the packages
50-
info(`Installing ${packagesToInstall.map((pack) => pack.name).join(" ")}`)
51-
await execRoot("apk", ["add", ...packagesToInstall.map((pack) => pack.name)], { stdio: "inherit" })
40+
info(`Installing ${packagesToInstall.join(" ")}`)
41+
await execRoot("apk", ["add", ...packagesToInstall])
5242

53-
info(`Successfully installed ${packagesToInstall.map((pack) => pack.name).join(" ")}`)
43+
info(`Successfully installed ${packagesToInstall.join(" ")}`)
5444
return true
5545
} catch (error) {
56-
warning(`Failed to install ${packages.map((pack) => pack.name).join(" ")}: ${error}`)
46+
warning(`Failed to install ${packages.map((pkg) => formatPackageWithVersion(pkg)).join(" ")}: ${error}`)
5747
return false
5848
}
5949
}

packages/setup-alpine/src/qualify-install.ts

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,67 @@ export type ApkPackage = {
1717
fallBackToLatest?: boolean
1818
}
1919

20-
export async function filterAndQualifyApkPackages(packages: ApkPackage[]) {
21-
// Filter out packages that are already installed
22-
const installedPackages = await Promise.all(packages.map(checkPackageInstalled))
23-
return packages.filter((_pack, index) => !installedPackages[index])
24-
}
2520
/**
2621
* Check if a package is already installed
27-
* @param pack The package to check
22+
* @param pkg The package to check
2823
* @returns Whether the package is installed
2924
*/
30-
31-
async function checkPackageInstalled(pack: ApkPackage): Promise<boolean> {
25+
export async function checkPackageInstalled(pkg: ApkPackage): Promise<boolean> {
3226
try {
33-
const { exitCode } = await execRoot("apk", ["info", "-e", pack.name], { reject: false })
34-
return exitCode === 0
27+
// First check if the package is installed at all
28+
const { exitCode } = await execRoot("apk", ["info", "-e", pkg.name], {
29+
reject: false,
30+
stdio: ["ignore", "pipe", "ignore"],
31+
})
32+
33+
if (exitCode !== 0) {
34+
return false
35+
}
36+
37+
// If no specific version is required, we're done
38+
if (pkg.version === undefined) {
39+
return true
40+
}
41+
42+
// Check the installed version
43+
const { stdout: versionOutput } = await execRoot("apk", ["info", "-v", pkg.name], {
44+
stdio: ["ignore", "pipe", "ignore"],
45+
})
46+
47+
// Parse the version from output (format is typically "package-name-version")
48+
const installedVersion = versionOutput.trim().split("-").slice(-1)[0]
49+
50+
return installedVersion === pkg.version
3551
} catch (error) {
3652
return false
3753
}
3854
}
55+
56+
/**
57+
* Filter out packages that are already installed and qualify those that need installation
58+
* @param packages List of packages to check
59+
* @returns List of packages that need to be installed
60+
*/
61+
export async function filterAndQualifyApkPackages(packages: ApkPackage[]): Promise<string[]> {
62+
const results = await Promise.all(
63+
packages.map(async (pack) => {
64+
const isInstalled = await checkPackageInstalled(pack)
65+
return isInstalled ? undefined : pack
66+
}),
67+
)
68+
69+
return results.filter((pack): pack is ApkPackage => pack !== undefined)
70+
.map(formatPackageWithVersion)
71+
}
72+
73+
/**
74+
* Format a package with its version if specified
75+
* @param pkg The package object
76+
* @returns Formatted package string (name=version or just name)
77+
*/
78+
export function formatPackageWithVersion(pkg: ApkPackage): string {
79+
if (pkg.version !== undefined) {
80+
return `${pkg.name}=${pkg.version}`
81+
}
82+
return pkg.name
83+
}

0 commit comments

Comments
 (0)