Skip to content

Commit a10a05b

Browse files
committed
Resolved multiple issues and made some improvements related to low memory situations
- Added a section to the README on how to detect failed package builds because of low memory - Adjusted the builder OOM score to make it more likely the builder will be killed in low RAM situations instead of (potentially more crucial) applications on the host machine - Fixed issue that could cause the builder docker image build to fail due to an outdated archlinux-keyring when you have really unfortunate timing - Unpromisified a couple of functions that were synchronous inside, as this has no added benefit - AUR packages that have a different package base are now cloned correctly - Example: The rog-control-center AUR package has asusctl set as it's base, causing the GIT clone URL to be different - Fixed a dependency resolve loop in very specific cases - Example: The rog-control-center AUR package has asusctl as a dependency, but the rog-control-center package is provided by the asusctl package, causing an infinite loop
1 parent 22e9c39 commit a10a05b

File tree

7 files changed

+92
-64
lines changed

7 files changed

+92
-64
lines changed

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@ Simply download the package from there and then install it manually with `pacman
116116
> Older versions of packages are automatically cleaned up after 30 days to preserve storage space on your server.
117117
118118

119+
### Package build fails with a SIGKILL
120+
Chances are that the build process consumed too much memory and was killed by the Docker engine or the host operating system.
121+
The builder process has been given a high OOM score to make sure it is killed first, instead of other _(potentially more crucial)_ applications.
122+
123+
To resolve this, you will have to raise the builder memory limit, free up more RAM on the host machine or upgrade the host machine to have more RAM.
124+
125+
> [!TIP]
126+
> Sometimes the AUR also provides `-bin` _(precompiled binary)_ variants of a package, this could also be a solution to work around the issue.
127+
128+
119129
### Access the build reports
120130
The application generates a report every time it runs the build process, allowing you to more easily troubleshoot troublesome packages.
121131

build-manager/docker/Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ FROM --platform=linux/amd64 archlinux:multilib-devel
22

33
# Update the image and install dependencies
44
RUN pacman-key --init
5+
RUN pacman -Sy --noconfirm archlinux-keyring
56
RUN pacman -Syu --noconfirm fcron nodejs-lts-iron npm docker
67

78
# Create the builder user

build-manager/src/build-manager.ts

+1
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ const handlePackageList = async (aurPackageListPath: string) => {
214214
User: 'builder',
215215
Cmd: ['/bin/bash', '-c', command],
216216
HostConfig: {
217+
OomScoreAdj: 1000, // Make it more likely the builder will be killed in low RAM situations instead of (potentially more crucial) applications
217218
Mounts: BuilderHelper.getBuilderMounts(),
218219
CpusetCpus: packageListConfiguration.builderLimit.cpusetCpus,
219220
Memory: FilesystemHelper.stringifiedSizeToBytes(packageListConfiguration.builderLimit.memory)

builder/Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ FROM --platform=linux/amd64 archlinux:multilib-devel
33
# Update the image and install dependencies
44
ENV CARCH=x86_64
55
RUN pacman-key --init
6+
RUN pacman -Sy --noconfirm archlinux-keyring
67
RUN pacman -Syu --noconfirm git base-devel fcron nodejs-lts-iron npm
78

89
# Create the builder user

builder/src/Helpers/MakepkgHelper.ts

+22-24
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,35 @@ import { execSync } from "child_process";
33
export default class MakepkgHelper {
44
private static makepkgPrintSrcRegex = new RegExp(/^\s*([a-z0-9]+)\ \=\ (.+)$/gm);
55

6-
public static parsePkgbuildFile(pkgbuildFilePath: string): Promise<object> {
7-
return new Promise(async (resolve) => {
8-
const consoleOutput = execSync(`cd $(dirname $(readlink -f "${pkgbuildFilePath}")); makepkg --printsrcinfo`);
9-
const matches = consoleOutput.toString().matchAll(MakepkgHelper.makepkgPrintSrcRegex);
6+
public static parsePkgbuildFile(pkgbuildFilePath: string): object {
7+
const consoleOutput = execSync(`cd $(dirname $(readlink -f "${pkgbuildFilePath}")); makepkg --printsrcinfo`);
8+
const matches = consoleOutput.toString().matchAll(MakepkgHelper.makepkgPrintSrcRegex);
109

11-
const parsedData: Record<string, any> = {};
10+
const parsedData: Record<string, any> = {};
1211

13-
for (const match of matches) {
14-
const key = match[1];
15-
const value = match[2];
12+
for (const match of matches) {
13+
const key = match[1];
14+
const value = match[2];
1615

17-
// Does the key already exist and is it not an array yet?
18-
// If so, wrap it into an array
19-
if (key in parsedData && ! (parsedData[key] instanceof Array)) {
20-
parsedData[key] = [parsedData[key]];
21-
}
22-
23-
// Does the key already exist and is it already an array?
24-
// If so, push the item
25-
if (key in parsedData && parsedData[key] instanceof Array) {
26-
parsedData[key].push(value);
16+
// Does the key already exist and is it not an array yet?
17+
// If so, wrap it into an array
18+
if (key in parsedData && ! (parsedData[key] instanceof Array)) {
19+
parsedData[key] = [parsedData[key]];
20+
}
2721

28-
continue;
29-
}
22+
// Does the key already exist and is it already an array?
23+
// If so, push the item
24+
if (key in parsedData && parsedData[key] instanceof Array) {
25+
parsedData[key].push(value);
3026

31-
// It's a "regular" item, simply push it
32-
parsedData[key] = value;
27+
continue;
3328
}
3429

35-
resolve(parsedData);
36-
});
30+
// It's a "regular" item, simply push it
31+
parsedData[key] = value;
32+
}
33+
34+
return parsedData;
3735
}
3836

3937
public static getDependsFromPkgbuildData(pkgbuildData: object): Array<string> {

builder/src/Helpers/PackageHelper.ts

+48-37
Original file line numberDiff line numberDiff line change
@@ -14,50 +14,53 @@ export default class PackageHelper {
1414
return PackageHelper.validPackageNameRegex.test(packageName);
1515
}
1616

17-
public static isSystemPackage(packageName: string): Promise<boolean> {
18-
return new Promise(async (resolve, reject) => {
19-
if (! PackageHelper.isValidPackageName(packageName)) {
20-
console.warn(`[builder] System package "${packageName}" has an invalid name`);
17+
public static isSystemPackage(packageName: string): boolean {
18+
if (! PackageHelper.isValidPackageName(packageName)) {
19+
console.warn(`[builder] System package "${packageName}" has an invalid name`);
2120

22-
return resolve(false);
23-
}
21+
return false;
22+
}
2423

25-
try {
26-
execSync(`pacman -Si ${packageName}`);
24+
try {
25+
execSync(`pacman -Si ${packageName}`);
2726

28-
return resolve(true);
29-
} catch (error: any) {
30-
const errorStatus = error.status;
31-
const errorOutput = error?.stderr?.toString();
27+
return true;
28+
} catch (error: any) {
29+
const errorStatus = error.status;
30+
const errorOutput = error?.stderr?.toString();
3231

33-
// Check if the error is it telling us the package doesn't exist
34-
if (errorStatus === 1 && PackageHelper.packageNotFoundRegex.test(errorOutput)) {
35-
console.warn(`[builder] System package ${packageName} doesn't exist`);
36-
return resolve(false);
37-
}
32+
// Check if the error is it telling us the package doesn't exist
33+
if (errorStatus === 1 && PackageHelper.packageNotFoundRegex.test(errorOutput)) {
34+
console.warn(`[builder] System package ${packageName} doesn't exist`);
3835

39-
reject(error);
36+
return false;
4037
}
41-
});
38+
39+
throw error;
40+
}
4241
}
4342

44-
public static isAurPackage(params: Parameters, packageName: string): Promise<boolean> {
45-
return new Promise(async (resolve, reject) => {
46-
if (! PackageHelper.isValidPackageName(packageName)) {
47-
console.warn(`[builder] AUR package "${packageName}" has an invalid name`);
43+
public static isAurPackage(params: Parameters, packageName: string): boolean {
44+
return !! PackageHelper.getAurPackageInformationByPackageName(params, packageName);
45+
}
4846

49-
return resolve(false);
50-
}
47+
public static getAurPackageInformationByPackageName(params: Parameters, packageName: string): AurRpcApiPackage | null {
48+
if (! PackageHelper.isValidPackageName(packageName)) {
49+
console.warn(`[builder] AUR package "${packageName}" has an invalid name`);
5150

52-
const jsonRaw = fs.readFileSync(params.aur_package_list_path, 'utf8');
53-
const jsonParsed: Array<AurRpcApiPackage> = JSON.parse(jsonRaw);
51+
return null;
52+
}
5453

55-
const foundPackage = !! jsonParsed.find((packageItem) => packageItem.Name === packageName);
54+
const jsonRaw = fs.readFileSync(params.aur_package_list_path, 'utf8');
55+
const jsonParsed: Array<AurRpcApiPackage> = JSON.parse(jsonRaw);
5656

57-
resolve(foundPackage);
57+
const foundPackage = jsonParsed.find((packageItem) => packageItem.Name === packageName);
5858

59-
return;
60-
});
59+
if (! foundPackage) {
60+
return null;
61+
}
62+
63+
return foundPackage;
6164
}
6265

6366
public static getPackagesInDirectory(directoryPath: string): Array<string> {
@@ -84,7 +87,7 @@ export default class PackageHelper {
8487
console.log(`[builder] AUR package ${packageName} directory doesn't seem to exist yet`);
8588

8689
// Make sure it's a valid AUR package
87-
if (! await PackageHelper.isAurPackage(params, packageName)) {
90+
if (! PackageHelper.isAurPackage(params, packageName)) {
8891
console.error(`[builder] isAurPackage reports ${packageName} to not be an existing AUR package!`);
8992

9093
return reject("Invalid AUR package");
@@ -97,7 +100,7 @@ export default class PackageHelper {
97100

98101

99102
const pkgbuildPath = `${fullPackagePath}/PKGBUILD`;
100-
const pkgbuildData = await MakepkgHelper.parsePkgbuildFile(pkgbuildPath);
103+
const pkgbuildData = MakepkgHelper.parsePkgbuildFile(pkgbuildPath);
101104

102105
const dependsPackages = MakepkgHelper.getDependsFromPkgbuildData(pkgbuildData);
103106
const makeDependsPackages = MakepkgHelper.getMakeDependsFromPkgbuildData(pkgbuildData);
@@ -106,23 +109,23 @@ export default class PackageHelper {
106109
console.log(`[builder] Installing make dependencies for ${packageName}`);
107110
await Promise.all(
108111
makeDependsPackages.map((dependencyPackageName: string) =>
109-
PackageHelper.installPackage(params, dependencyPackageName)
112+
PackageHelper.installPackage(params, dependencyPackageName, packageName)
110113
)
111114
);
112115
console.log(`[builder] Done installing make dependencies for ${packageName}`);
113116

114117
console.log(`[builder] Installing check dependencies for ${packageName}`);
115118
await Promise.all(
116119
checkDependsPackages.map((dependencyPackageName: string) =>
117-
PackageHelper.installPackage(params, dependencyPackageName)
120+
PackageHelper.installPackage(params, dependencyPackageName, packageName)
118121
)
119122
);
120123
console.log(`[builder] Done installing check dependencies for ${packageName}`);
121124

122125
console.log(`[builder] Installing dependencies for ${packageName}`);
123126
await Promise.all(
124127
dependsPackages.map((dependencyPackageName: string) =>
125-
PackageHelper.installPackage(params, dependencyPackageName)
128+
PackageHelper.installPackage(params, dependencyPackageName, packageName)
126129
)
127130
);
128131
console.log(`[builder] Done installing dependencies for ${packageName}`);
@@ -168,7 +171,7 @@ export default class PackageHelper {
168171
});
169172
}
170173

171-
public static installPackage(params: Parameters, packageName: string): Promise<void> {
174+
public static installPackage(params: Parameters, packageName: string, dependencyOf: string | null = null): Promise<void> {
172175
return new Promise(async (resolve, reject) => {
173176
const packageConfiguration = params.package_configuration;
174177
let realPackageName = packageName;
@@ -189,6 +192,14 @@ export default class PackageHelper {
189192
return;
190193
}
191194

195+
if (packageType.packageToInstall === dependencyOf) {
196+
console.warn(`[builder] The package "${dependencyOf}" tells us it requires "${packageType.packageToInstall}" as a dependency, which would cause an endless loop. We will just ignore this request.`);
197+
198+
resolve();
199+
200+
return;
201+
}
202+
192203
if (packageType.type === 'system') {
193204
console.log(`[builder] Installing system package "${packageType.packageToInstall}"`);
194205

builder/src/Helpers/PackageTypeHelper.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ export default class PackageTypeHelper {
2020

2121
const aurResult = await PackageTypeHelper.checkAURPackage(params, packageName);
2222
if (aurResult) {
23+
if (aurResult.packageToInstall !== packageName) {
24+
console.warn(`[builder] Package "${packageName}" has a different package base according to the AUR, building "${aurResult.packageToInstall}" instead`);
25+
}
26+
2327
resolve(aurResult);
2428

2529
return;
@@ -54,7 +58,7 @@ export default class PackageTypeHelper {
5458
}
5559

5660
private static async checkSystemPackageViaPacman(packageName: string): Promise<PackageType | null> {
57-
const result = await PackageHelper.isSystemPackage(packageName);
61+
const result = PackageHelper.isSystemPackage(packageName);
5862

5963
if (result) {
6064
return {
@@ -67,12 +71,14 @@ export default class PackageTypeHelper {
6771
}
6872

6973
private static async checkAURPackage(params: Parameters, packageName: string): Promise<PackageType | null> {
70-
const result = await PackageHelper.isAurPackage(params, packageName);
74+
const result = PackageHelper.getAurPackageInformationByPackageName(params, packageName);
7175

7276
if (result) {
77+
const packageBaseIsDifferent = packageName !== result.PackageBase;
78+
7379
return {
7480
type: 'aur',
75-
packageToInstall: packageName
81+
packageToInstall: packageBaseIsDifferent ? result.PackageBase : packageName
7682
};
7783
}
7884

0 commit comments

Comments
 (0)