test(lint-once): match RUN case-insensitively, follow buildx builds
All checks were successful
check / check (push) Successful in 1m8s
All checks were successful
check / check (push) Successful in 1m8s
WIP round 9: items 1-3. Verification matrix still to re-run.
This commit is contained in:
@@ -317,6 +317,21 @@ const scripts = packageScripts();
|
|||||||
// the rest of the file, for the same reason every other dead end here is.
|
// the rest of the file, for the same reason every other dead end here is.
|
||||||
const HEREDOC_OPEN = /<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1/;
|
const HEREDOC_OPEN = /<<-?\s*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1/;
|
||||||
|
|
||||||
|
// Dockerfile instruction keywords are case-insensitive and are separated from
|
||||||
|
// their arguments by any run of whitespace, so `run yarn run prettier ...` and
|
||||||
|
// a tab-separated `RUN\tyarn run prettier ...` are both instructions the image
|
||||||
|
// really executes. A literal `line.startsWith("RUN ")` saw neither, and either
|
||||||
|
// one appended to the Dockerfile put a second prettier pass into the image
|
||||||
|
// that `script/cibuild` builds while this file still reported green. The
|
||||||
|
// separator is consumed by the match rather than by a fixed-width `slice`, so
|
||||||
|
// the captured command is the same string for a space and for a tab, and the
|
||||||
|
// heredoc terminator is read off that command exactly as before.
|
||||||
|
//
|
||||||
|
// Leading whitespace before the keyword — which a Dockerfile also permits — is
|
||||||
|
// already gone by this point: `joinContinuations` trims every line, so an
|
||||||
|
// indented instruction arrives here flush and needs nothing further.
|
||||||
|
const DOCKER_RUN = /^RUN\s+(.*)$/i;
|
||||||
|
|
||||||
const dockerRunCommands = (text: string): string[] => {
|
const dockerRunCommands = (text: string): string[] => {
|
||||||
const commands: string[] = [];
|
const commands: string[] = [];
|
||||||
let terminator: string | null = null;
|
let terminator: string | null = null;
|
||||||
@@ -326,8 +341,9 @@ const dockerRunCommands = (text: string): string[] => {
|
|||||||
else commands.push(line);
|
else commands.push(line);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!line.startsWith("RUN ")) continue;
|
const run = DOCKER_RUN.exec(line);
|
||||||
const command = line.slice("RUN ".length);
|
if (run === null) continue;
|
||||||
|
const command = run[1] as string;
|
||||||
terminator = HEREDOC_OPEN.exec(command)?.[2] ?? null;
|
terminator = HEREDOC_OPEN.exec(command)?.[2] ?? null;
|
||||||
commands.push(command);
|
commands.push(command);
|
||||||
}
|
}
|
||||||
@@ -346,8 +362,10 @@ const resolve = (node: string): string[] => {
|
|||||||
}
|
}
|
||||||
// The `run:` steps of a workflow, in file order. `uses:` steps are actions,
|
// The `run:` steps of a workflow, in file order. `uses:` steps are actions,
|
||||||
// not commands, and have no edges into this repo's graph. A `run: |` block
|
// not commands, and have no edges into this repo's graph. A `run: |` block
|
||||||
// would resolve to the bare `|`, which reaches nothing and therefore fails
|
// resolves to the bare `|`, which reaches nothing — so the count is exactly
|
||||||
// the count rather than passing quietly.
|
// what such a step does NOT move, and it is the pinned resolved list, not
|
||||||
|
// the count, that turns it red. See the exact-equality test at the bottom
|
||||||
|
// of this file.
|
||||||
if (node.startsWith("workflow:")) {
|
if (node.startsWith("workflow:")) {
|
||||||
return executable(read(node.slice("workflow:".length)))
|
return executable(read(node.slice("workflow:".length)))
|
||||||
.filter((line) => /^-?\s*run:\s*\S/.test(line))
|
.filter((line) => /^-?\s*run:\s*\S/.test(line))
|
||||||
@@ -390,6 +408,28 @@ const commandsOf = (node: string): string[] => {
|
|||||||
return commands;
|
return commands;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Which invocation shapes read as a build, and which do not.
|
||||||
|
//
|
||||||
|
// RECOGNISED: `docker build`, `docker buildx build`, and either of those
|
||||||
|
// reached through global flags between the command and the subcommand —
|
||||||
|
// `docker --context ci build`, `docker -H tcp://host:2375 build`,
|
||||||
|
// `docker --debug buildx build`. A global flag's separate argument is stepped
|
||||||
|
// over (`--context ci`), and an `=`-joined one is a single token
|
||||||
|
// (`--context=ci`). `docker buildx build` was the concrete miss: `\bdocker\s+
|
||||||
|
// build\b` cannot reach across `buildx`, so `docker buildx build -f
|
||||||
|
// Dockerfile.extra .` in script/cibuild emitted no edge at all — not even the
|
||||||
|
// default `Dockerfile` one — and a tree running prettier twice reported green.
|
||||||
|
//
|
||||||
|
// NOT RECOGNISED, deliberately: `docker compose build`, which builds compose
|
||||||
|
// services out of a compose file this parser does not read, so resolving it
|
||||||
|
// against a Dockerfile path would be a wrong answer rather than a missing one;
|
||||||
|
// `docker builder build`; and any spelling that puts a non-flag word other
|
||||||
|
// than `buildx` between `docker` and `build`. Only `-`-prefixed tokens (plus
|
||||||
|
// their arguments) and the single literal `buildx` are stepped over, so
|
||||||
|
// `docker run -f build` and `docker image build`-style shapes do not match.
|
||||||
|
const DOCKER_BUILD =
|
||||||
|
/\bdocker(?:\s+-{1,2}[A-Za-z][\w-]*(?:=\S+)?(?:\s+[^-\s]\S*)?)*(?:\s+buildx)?\s+build\b/g;
|
||||||
|
|
||||||
const edgesOf = (line: string): string[] => {
|
const edgesOf = (line: string): string[] => {
|
||||||
const edges: string[] = [];
|
const edges: string[] = [];
|
||||||
|
|
||||||
@@ -469,7 +509,7 @@ const edgesOf = (line: string): string[] => {
|
|||||||
// separators and nothing more: a separator inside quotes or a `$(...)`
|
// separators and nothing more: a separator inside quotes or a `$(...)`
|
||||||
// substitution ends the slice anyway, and a newline-separated command list
|
// substitution ends the slice anyway, and a newline-separated command list
|
||||||
// is already one line per invocation by the time it gets here.
|
// is already one line per invocation by the time it gets here.
|
||||||
for (const match of line.matchAll(/\bdocker\s+build\b/g)) {
|
for (const match of line.matchAll(DOCKER_BUILD)) {
|
||||||
const invocation = line
|
const invocation = line
|
||||||
.slice(match.index)
|
.slice(match.index)
|
||||||
.split(/&&|\|\||;|\|/)[0] as string;
|
.split(/&&|\|\||;|\|/)[0] as string;
|
||||||
|
|||||||
Reference in New Issue
Block a user