fix(docker): Fix Dockerfile dependency sorting and enhance environment variable handling for GitHub repos

This commit is contained in:
Philipp Kunz 2024-11-17 00:32:56 +01:00
parent e9e8acafe4
commit e38cc40f11
7 changed files with 6650 additions and 3178 deletions

View File

@ -1,5 +1,13 @@
# Changelog # Changelog
## 2024-11-17 - 4.1.35 - fix(docker)
Fix Dockerfile dependency sorting and enhance environment variable handling for GitHub repos
- Refined the algorithm for sorting Dockerfiles based on dependencies to ensure proper build order.
- Enhanced environment variable handling in the NpmciEnv class to support conditional assignments.
- Updated various dependencies in package.json for improved performance and compatibility.
- Added error handling to circular dependency detection in Dockerfile sorting.
## 2024-11-05 - 4.1.34 - fix(connector) ## 2024-11-05 - 4.1.34 - fix(connector)
Remove unused typedrequest implementation in cloudlyconnector Remove unused typedrequest implementation in cloudlyconnector

View File

@ -29,13 +29,13 @@
"@git.zone/tsbuild": "^2.2.0", "@git.zone/tsbuild": "^2.2.0",
"@git.zone/tsrun": "^1.3.3", "@git.zone/tsrun": "^1.3.3",
"@git.zone/tstest": "^1.0.77", "@git.zone/tstest": "^1.0.77",
"@push.rocks/tapbundle": "^5.3.0", "@push.rocks/tapbundle": "^5.5.0",
"@types/node": "^22.8.7" "@types/node": "^22.9.0"
}, },
"dependencies": { "dependencies": {
"@api.global/typedrequest": "^3.1.10", "@api.global/typedrequest": "^3.1.10",
"@push.rocks/lik": "^6.1.0", "@push.rocks/lik": "^6.1.0",
"@push.rocks/npmextra": "^5.0.23", "@push.rocks/npmextra": "^5.1.2",
"@push.rocks/projectinfo": "^5.0.2", "@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/qenv": "^6.0.2", "@push.rocks/qenv": "^6.0.2",
"@push.rocks/smartanalytics": "^2.0.15", "@push.rocks/smartanalytics": "^2.0.15",
@ -49,12 +49,12 @@
"@push.rocks/smartobject": "^1.0.12", "@push.rocks/smartobject": "^1.0.12",
"@push.rocks/smartpath": "^5.0.11", "@push.rocks/smartpath": "^5.0.11",
"@push.rocks/smartpromise": "^4.0.4", "@push.rocks/smartpromise": "^4.0.4",
"@push.rocks/smartrequest": "^2.0.18", "@push.rocks/smartrequest": "^2.0.23",
"@push.rocks/smartshell": "^3.0.6", "@push.rocks/smartshell": "^3.0.6",
"@push.rocks/smartsocket": "^2.0.22", "@push.rocks/smartsocket": "^2.0.22",
"@push.rocks/smartssh": "^2.0.1", "@push.rocks/smartssh": "^2.0.1",
"@push.rocks/smartstring": "^4.0.8", "@push.rocks/smartstring": "^4.0.8",
"@serve.zone/api": "^4.3.1", "@serve.zone/api": "^4.3.11",
"@tsclass/tsclass": "^4.1.2", "@tsclass/tsclass": "^4.1.2",
"@types/through2": "^2.0.38", "@types/through2": "^2.0.38",
"through2": "^4.0.2" "through2": "^4.0.2"

9638
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1 +1,9 @@
{} {
"gitzone": {
"module": {
"githost": "code.foss.global",
"gitscope": "mygroup",
"gitrepo": "myrepo"
}
}
}

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@ship.zone/npmci', name: '@ship.zone/npmci',
version: '4.1.34', version: '4.1.35',
description: 'A tool to streamline Node.js and Docker workflows within CI environments, particularly GitLab CI, providing various CI/CD utilities.' description: 'A tool to streamline Node.js and Docker workflows within CI environments, particularly GitLab CI, providing various CI/CD utilities.'
} }

View File

@ -40,43 +40,72 @@ export class Dockerfile {
} }
/** /**
* sorts Dockerfiles into a dependency chain * Sorts Dockerfiles into a build order based on dependencies.
* @param sortableArrayArg an array of instances of class Dockerfile * @param dockerfiles An array of Dockerfile instances.
* @returns Promise<Dockerfile[]> * @returns A Promise that resolves to a sorted array of Dockerfiles.
*/ */
public static async sortDockerfiles(sortableArrayArg: Dockerfile[]): Promise<Dockerfile[]> { public static async sortDockerfiles(dockerfiles: Dockerfile[]): Promise<Dockerfile[]> {
const done = plugins.smartpromise.defer<Dockerfile[]>(); logger.log('info', 'Sorting Dockerfiles based on dependencies...');
logger.log('info', 'sorting Dockerfiles:');
const sortedArray: Dockerfile[] = []; // Map from cleanTag to Dockerfile instance for quick lookup
const cleanTagsOriginal = Dockerfile.cleanTagsArrayFunction(sortableArrayArg, sortedArray); const tagToDockerfile = new Map<string, Dockerfile>();
let sorterFunctionCounter: number = 0; dockerfiles.forEach((dockerfile) => {
const sorterFunction = () => { tagToDockerfile.set(dockerfile.cleanTag, dockerfile);
sortableArrayArg.forEach((dockerfileArg) => { });
const cleanTags = Dockerfile.cleanTagsArrayFunction(sortableArrayArg, sortedArray);
if ( // Build the dependency graph
cleanTags.indexOf(dockerfileArg.baseImage) === -1 && const graph = new Map<Dockerfile, Dockerfile[]>();
sortedArray.indexOf(dockerfileArg) === -1 dockerfiles.forEach((dockerfile) => {
) { const dependencies: Dockerfile[] = [];
sortedArray.push(dockerfileArg); const baseImage = dockerfile.baseImage;
}
if (cleanTagsOriginal.indexOf(dockerfileArg.baseImage) !== -1) { // Check if the baseImage is among the local Dockerfiles
dockerfileArg.localBaseImageDependent = true; if (tagToDockerfile.has(baseImage)) {
} const baseDockerfile = tagToDockerfile.get(baseImage);
}); dependencies.push(baseDockerfile);
if (sortableArrayArg.length === sortedArray.length) { dockerfile.localBaseImageDependent = true;
let counter = 1; dockerfile.localBaseDockerfile = baseDockerfile;
for (const dockerfile of sortedArray) { }
logger.log('info', `tag ${counter}: -> ${dockerfile.cleanTag}`);
counter++; graph.set(dockerfile, dependencies);
} });
done.resolve(sortedArray);
} else if (sorterFunctionCounter < 10) { // Perform topological sort
sorterFunctionCounter++; const sortedDockerfiles: Dockerfile[] = [];
sorterFunction(); const visited = new Set<Dockerfile>();
const tempMarked = new Set<Dockerfile>();
const visit = (dockerfile: Dockerfile) => {
if (tempMarked.has(dockerfile)) {
throw new Error(`Circular dependency detected involving ${dockerfile.cleanTag}`);
}
if (!visited.has(dockerfile)) {
tempMarked.add(dockerfile);
const dependencies = graph.get(dockerfile) || [];
dependencies.forEach((dep) => visit(dep));
tempMarked.delete(dockerfile);
visited.add(dockerfile);
sortedDockerfiles.push(dockerfile);
} }
}; };
sorterFunction();
return done.promise; try {
dockerfiles.forEach((dockerfile) => {
if (!visited.has(dockerfile)) {
visit(dockerfile);
}
});
} catch (error) {
logger.log('error', error.message);
throw error;
}
// Log the sorted order
sortedDockerfiles.forEach((dockerfile, index) => {
logger.log('info', `Build order ${index + 1}: ${dockerfile.cleanTag}`);
});
return sortedDockerfiles;
} }
/** /**
@ -117,24 +146,28 @@ export class Dockerfile {
} }
/** /**
* returns a version for a docker file * returns a version for a docker file
* @execution SYNC * @execution SYNC
*/ */
public static dockerFileVersion(dockerfileInstanceArg: Dockerfile, dockerfileNameArg: string): string { public static dockerFileVersion(
let versionString: string; dockerfileInstanceArg: Dockerfile,
const versionRegex = /Dockerfile_(.+)$/; dockerfileNameArg: string
const regexResultArray = versionRegex.exec(dockerfileNameArg); ): string {
if (regexResultArray && regexResultArray.length === 2) { let versionString: string;
versionString = regexResultArray[1]; const versionRegex = /Dockerfile_(.+)$/;
} else { const regexResultArray = versionRegex.exec(dockerfileNameArg);
versionString = 'latest'; if (regexResultArray && regexResultArray.length === 2) {
versionString = regexResultArray[1];
} else {
versionString = 'latest';
}
versionString = versionString.replace(
'##version##',
dockerfileInstanceArg.npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().projectInfo.npm
.version
);
return versionString;
} }
versionString = versionString.replace(
'##version##',
dockerfileInstanceArg.npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().projectInfo.npm.version
);
return versionString;
}
/** /**
* returns the docker base image for a Dockerfile * returns the docker base image for a Dockerfile
@ -196,22 +229,6 @@ public static dockerFileVersion(dockerfileInstanceArg: Dockerfile, dockerfileNam
return buildArgsString; return buildArgsString;
} }
/**
*
*/
public static cleanTagsArrayFunction(
dockerfileArrayArg: Dockerfile[],
trackingArrayArg: Dockerfile[]
): string[] {
const cleanTagsArray: string[] = [];
dockerfileArrayArg.forEach((dockerfileArg) => {
if (trackingArrayArg.indexOf(dockerfileArg) === -1) {
cleanTagsArray.push(dockerfileArg.cleanTag);
}
});
return cleanTagsArray;
}
// INSTANCE // INSTANCE
public npmciDockerManagerRef: NpmciDockerManager; public npmciDockerManagerRef: NpmciDockerManager;
@ -285,7 +302,10 @@ public static dockerFileVersion(dockerfileInstanceArg: Dockerfile, dockerfileNam
labels: [], labels: [],
version: this.npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().projectInfo.npm.version, version: this.npmciDockerManagerRef.npmciRef.npmciConfig.getConfig().projectInfo.npm.version,
}); });
await this.npmciDockerManagerRef.npmciRef.npmciConfig.kvStorage.writeKey('latestPushedDockerTag', this.pushTag) await this.npmciDockerManagerRef.npmciRef.npmciConfig.kvStorage.writeKey(
'latestPushedDockerTag',
this.pushTag
);
} }
/** /**

View File

@ -9,10 +9,10 @@ export class NpmciEnv {
constructor(npmciRefArg: Npmci) { constructor(npmciRefArg: Npmci) {
this.npmciRef = npmciRefArg; this.npmciRef = npmciRefArg;
if (process.env.GITLAB_CI) { if (!this.repoString && process.env.GITLAB_CI) {
this.repoString = process.env.CI_REPOSITORY_URL; this.repoString = process.env.CI_REPOSITORY_URL;
} }
if (process.env.NPMCI_COMPUTED_REPOURL) { if (!this.repoString && process.env.NPMCI_COMPUTED_REPOURL) {
this.repoString = process.env.NPMCI_COMPUTED_REPOURL; this.repoString = process.env.NPMCI_COMPUTED_REPOURL;
} }
if (!this.repoString) { if (!this.repoString) {