@catladder/cli 5.1.0 → 5.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithubImageCasing.d.ts +15 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithubImageCasing.js +60 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithubImageCasing.js.map +1 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js +2 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js.map +1 -1
- package/dist/apps/cli/src/utils/github.js +2 -2
- package/dist/apps/cli/src/utils/github.js.map +1 -1
- package/dist/bundles/catenv/index.js +169 -19
- package/dist/bundles/cli/index.js +240 -21
- package/dist/bundles/skills/catladder-config/SKILL.md +3 -1
- package/dist/bundles/skills/catladder-migrate-ci-backend/SKILL.md +11 -0
- package/dist/bundles/skills/catladder-pipelines/SKILL.md +27 -2
- package/dist/packages/pipeline/src/backends/github/GithubBackend.js +11 -5
- package/dist/packages/pipeline/src/backends/github/GithubBackend.js.map +1 -1
- package/dist/packages/pipeline/src/backends/github/ciVariables.d.ts +13 -2
- package/dist/packages/pipeline/src/backends/github/ciVariables.js +18 -5
- package/dist/packages/pipeline/src/backends/github/ciVariables.js.map +1 -1
- package/dist/packages/pipeline/src/backends/github/createGithubJobs.js +1 -1
- package/dist/packages/pipeline/src/backends/github/createGithubJobs.js.map +1 -1
- package/dist/packages/pipeline/src/backends/github/ghcr.d.ts +29 -0
- package/dist/packages/pipeline/src/backends/github/ghcr.js +40 -0
- package/dist/packages/pipeline/src/backends/github/ghcr.js.map +1 -0
- package/dist/packages/pipeline/src/backends/github/index.d.ts +2 -0
- package/dist/packages/pipeline/src/backends/github/index.js +2 -0
- package/dist/packages/pipeline/src/backends/github/index.js.map +1 -1
- package/dist/packages/pipeline/src/backends/github/registryImage.d.ts +20 -0
- package/dist/packages/pipeline/src/backends/github/registryImage.js +49 -0
- package/dist/packages/pipeline/src/backends/github/registryImage.js.map +1 -0
- package/dist/packages/pipeline/src/catci/shippedCatci.d.ts +8 -0
- package/dist/packages/pipeline/src/catci/shippedCatci.js +19 -4
- package/dist/packages/pipeline/src/catci/shippedCatci.js.map +1 -1
- package/dist/packages/pipeline/src/customImages/jobImagesPlan.d.ts +15 -3
- package/dist/packages/pipeline/src/customImages/jobImagesPlan.js +14 -3
- package/dist/packages/pipeline/src/customImages/jobImagesPlan.js.map +1 -1
- package/dist/packages/pipeline/src/types/config.d.ts +12 -0
- package/dist/skills/catladder-config/SKILL.md +3 -1
- package/dist/skills/catladder-migrate-ci-backend/SKILL.md +11 -0
- package/dist/skills/catladder-pipelines/SKILL.md +27 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/apps/cli/commands/project/doctor/checkGithubImageCasing.ts +87 -0
- package/src/apps/cli/commands/project/doctor/index.ts +2 -0
- package/src/utils/github.ts +2 -4
package/package.json
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readdir, readFile } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import type { Config } from "@catladder/pipeline";
|
|
4
|
+
import {
|
|
5
|
+
GHCR_REPOSITORY_EXPRESSION,
|
|
6
|
+
getEnabledPipelineTypes,
|
|
7
|
+
getPipelineGitRemote,
|
|
8
|
+
} from "@catladder/pipeline";
|
|
9
|
+
import { getConfiguredGithubRepo } from "../../../../../secrets";
|
|
10
|
+
import type { DoctorReport } from "./DoctorReport";
|
|
11
|
+
|
|
12
|
+
const WORKFLOWS_FOLDER = ".github/workflows";
|
|
13
|
+
const GENERATED_FILE_PREFIX = "catladder-";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* the generated workflow files, or an empty list when none exist yet
|
|
17
|
+
*/
|
|
18
|
+
const readGeneratedWorkflows = async (): Promise<string[]> => {
|
|
19
|
+
const entries = await readdir(WORKFLOWS_FOLDER).catch(() => []);
|
|
20
|
+
return Promise.all(
|
|
21
|
+
entries
|
|
22
|
+
.filter((file) => file.startsWith(GENERATED_FILE_PREFIX))
|
|
23
|
+
.map((file) => readFile(join(WORKFLOWS_FOLDER, file), "utf8")),
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* ghcr image paths must be lowercase, but `${{ github.repository }}`
|
|
29
|
+
* interpolates github's display casing — so a repository whose owner or
|
|
30
|
+
* name contains an uppercase character needs a lowercased literal in the
|
|
31
|
+
* generated workflows. Generation bakes that in, which means committed
|
|
32
|
+
* workflows generated by an older catladder (or hand-edited ones) still
|
|
33
|
+
* carry the expression and fail every docker job with
|
|
34
|
+
* `repository name must be lowercase`.
|
|
35
|
+
*
|
|
36
|
+
* Unlike the rest of the github checks this needs no `gh` login — only
|
|
37
|
+
* the git remote and the committed files.
|
|
38
|
+
*/
|
|
39
|
+
export const checkGithubImageCasing = async (
|
|
40
|
+
report: DoctorReport,
|
|
41
|
+
config: Config,
|
|
42
|
+
) => {
|
|
43
|
+
if (!getEnabledPipelineTypes(config).includes("github")) return;
|
|
44
|
+
|
|
45
|
+
const repo = await getConfiguredGithubRepo();
|
|
46
|
+
if (!repo || repo === repo.toLowerCase()) {
|
|
47
|
+
// an all-lowercase repository keeps the expression on purpose (it
|
|
48
|
+
// stays correct in forks), and an unresolvable remote is reported
|
|
49
|
+
// by checkGithub already
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
report.section("github image names");
|
|
54
|
+
|
|
55
|
+
const workflows = await readGeneratedWorkflows();
|
|
56
|
+
if (workflows.length === 0) {
|
|
57
|
+
report.warn(
|
|
58
|
+
`no generated workflows in ${WORKFLOWS_FOLDER} to check`,
|
|
59
|
+
"run: catenv",
|
|
60
|
+
);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const stale = workflows.filter((content) =>
|
|
65
|
+
content.includes(GHCR_REPOSITORY_EXPRESSION),
|
|
66
|
+
);
|
|
67
|
+
if (stale.length > 0) {
|
|
68
|
+
report.fail(
|
|
69
|
+
`'${repo}' is mixed-case, but ${stale.length}/${workflows.length} generated workflows still use ${GHCR_REPOSITORY_EXPRESSION} — ghcr rejects uppercase image paths, so every docker job fails with "repository name must be lowercase"`,
|
|
70
|
+
"run: catenv (and commit the regenerated workflows)",
|
|
71
|
+
);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const uppercase = workflows.flatMap((content) =>
|
|
76
|
+
[...content.matchAll(/ghcr\.io\/\S*[A-Z]\S*/g)].map(([match]) => match),
|
|
77
|
+
);
|
|
78
|
+
if (uppercase.length > 0) {
|
|
79
|
+
report.fail(
|
|
80
|
+
`uppercase ghcr image paths in the generated workflows: ${[...new Set(uppercase)].join(", ")}`,
|
|
81
|
+
`run: catenv — if it persists, set pipelines.github.repository (the '${getPipelineGitRemote(config, "github")}' remote resolves to '${repo}')`,
|
|
82
|
+
);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
report.ok(`ghcr image paths lowercased for '${repo}'`);
|
|
87
|
+
};
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import type { IO } from "../../../../../core/types";
|
|
8
8
|
import { checkCloudRun } from "./checkCloudRun";
|
|
9
9
|
import { checkGithub } from "./checkGithub";
|
|
10
|
+
import { checkGithubImageCasing } from "./checkGithubImageCasing";
|
|
10
11
|
import { checkGitlab } from "./checkGitlab";
|
|
11
12
|
import { checkStore } from "./checkStore";
|
|
12
13
|
import { DoctorReport } from "./DoctorReport";
|
|
@@ -53,6 +54,7 @@ export const doctorProject = async (
|
|
|
53
54
|
await checkGitlab(instance, report, contexts);
|
|
54
55
|
}
|
|
55
56
|
await checkCloudRun(report, contexts);
|
|
57
|
+
await checkGithubImageCasing(report, config);
|
|
56
58
|
await checkGithub(report, config);
|
|
57
59
|
|
|
58
60
|
report.summarize();
|
package/src/utils/github.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFile as execFileCb, spawn } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
|
+
import { parseGithubRepoFromRemoteUrl } from "@catladder/pipeline";
|
|
3
4
|
|
|
4
5
|
const execFile = promisify(execFileCb);
|
|
5
6
|
|
|
@@ -25,10 +26,7 @@ export const getGithubRepoFromRemote = async (
|
|
|
25
26
|
): Promise<string | undefined> => {
|
|
26
27
|
try {
|
|
27
28
|
const { stdout } = await execFile("git", ["remote", "get-url", remoteName]);
|
|
28
|
-
|
|
29
|
-
/github\.com[:/]([\w.-]+\/[\w.-]+?)(\.git)?\s*$/,
|
|
30
|
-
);
|
|
31
|
-
return match?.[1];
|
|
29
|
+
return parseGithubRepoFromRemoteUrl(stdout);
|
|
32
30
|
} catch {
|
|
33
31
|
return undefined;
|
|
34
32
|
}
|