Validate expression block-scalar chomping
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import {registerLogger} from "./log";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {validate} from "./validate";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
});
|
||||
|
||||
describe("block scalar chomping - allowed cases", () => {
|
||||
it("does NOT warn for step.run with clip chomping (exception)", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
echo \${{ github.event_name }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not warn for inline expression", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
if: \${{ github.event_name == 'push' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not warn for quoted string", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
if: "\${{ github.event_name == 'push' }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
import {DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {registerLogger} from "./log";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {validate} from "./validate";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
});
|
||||
|
||||
describe("block scalar chomping - boolean fields", () => {
|
||||
describe("job continue-on-error", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: |
|
||||
\${{ matrix.experimental }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: |+
|
||||
\${{ matrix.experimental }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: |-
|
||||
\${{ matrix.experimental }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("step continue-on-error", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
continue-on-error: |
|
||||
\${{ matrix.experimental }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
continue-on-error: |+
|
||||
\${{ matrix.experimental }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
continue-on-error: |-
|
||||
\${{ matrix.experimental }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,188 @@
|
||||
import {DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {registerLogger} from "./log";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {validate} from "./validate";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
});
|
||||
|
||||
describe("block scalar chomping - if fields", () => {
|
||||
describe("job-if", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
if: |
|
||||
\${{ github.event_name == 'push' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
if: |+
|
||||
\${{ github.event_name == 'push' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
if: |-
|
||||
\${{ github.event_name == 'push' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
|
||||
it("errors without ${{ }} (isExpression)", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
if: |
|
||||
github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("uses > indicator in error message for folded scalars", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
if: >
|
||||
\${{ github.event_name == 'push' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '>-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("step-if", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- if: |
|
||||
\${{ github.event_name == 'push' }}
|
||||
run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- if: |+
|
||||
\${{ github.event_name == 'push' }}
|
||||
run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks boolean evaluation. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- if: |-
|
||||
\${{ github.event_name == 'push' }}
|
||||
run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,428 @@
|
||||
import {DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {registerLogger} from "./log";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {validate} from "./validate";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
});
|
||||
|
||||
describe("block scalar chomping - number fields", () => {
|
||||
describe("job timeout-minutes", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: |
|
||||
\${{ matrix.timeout }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: |+
|
||||
\${{ matrix.timeout }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: |-
|
||||
\${{ matrix.timeout }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("container.ports", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:16
|
||||
ports: |
|
||||
\${{ fromJSON('[80, 443]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:16
|
||||
ports: |+
|
||||
\${{ fromJSON('[8080, 9090]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:16
|
||||
ports: |-
|
||||
\${{ fromJSON('[80, 443]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("container.volumes", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:16
|
||||
volumes: |
|
||||
\${{ fromJSON('["/data:/data"]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:16
|
||||
volumes: |+
|
||||
\${{ fromJSON('["/data:/data"]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:16
|
||||
volumes: |-
|
||||
\${{ fromJSON('["/data:/data"]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("services.ports", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
ports: |
|
||||
\${{ fromJSON('[5432]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
ports: |+
|
||||
\${{ fromJSON('[5432]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
ports: |-
|
||||
\${{ fromJSON('[5432]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("services.volumes", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
volumes: |
|
||||
\${{ fromJSON('["/var/lib/postgresql/data"]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
volumes: |+
|
||||
\${{ fromJSON('["/var/lib/postgresql/data"]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
volumes: |-
|
||||
\${{ fromJSON('["/var/lib/postgresql/data"]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("step timeout-minutes", () => {
|
||||
it("errors with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
timeout-minutes: |
|
||||
\${{ matrix.timeout }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("errors with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
timeout-minutes: |+
|
||||
\${{ matrix.timeout }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline which breaks number parsing. Use '|-' to strip trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Error
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not error with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
timeout-minutes: |-
|
||||
\${{ matrix.timeout }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
||||
import {DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {registerLogger} from "./log";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {validate} from "./validate";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
});
|
||||
|
||||
describe("expression validation", () => {
|
||||
describe("block scalar chomping - fields that warn only for clip", () => {
|
||||
describe("env", () => {
|
||||
it("warns with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
echo $VAR
|
||||
env:
|
||||
VAR: |
|
||||
\${{ matrix.value }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline to expression result. Use '|-' to strip or '|+' to keep trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Warning
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not warn with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
echo $VAR
|
||||
env:
|
||||
VAR: |+
|
||||
\${{ matrix.value }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not warn with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
echo $VAR
|
||||
env:
|
||||
VAR: |-
|
||||
\${{ matrix.value }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
|
||||
it("uses > indicator in warning message for folded scalars", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
echo $VAR
|
||||
env:
|
||||
VAR: >
|
||||
\${{ matrix.value }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline to expression result. Use '>-' to strip or '>+' to keep trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Warning
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("action input", () => {
|
||||
it("warns with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: |
|
||||
\${{ github.ref }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline to expression result. Use '|-' to strip or '|+' to keep trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Warning
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not warn with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: |+
|
||||
\${{ github.ref }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not warn with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: |-
|
||||
\${{ github.ref }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("matrix value", () => {
|
||||
it("warns with clip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: |
|
||||
\${{ fromJSON('[1, 2, 3]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toContainEqual(
|
||||
expect.objectContaining({
|
||||
message:
|
||||
"Block scalar adds trailing newline to expression result. Use '|-' to strip or '|+' to keep trailing newlines.",
|
||||
code: "expression-block-scalar-chomping",
|
||||
severity: DiagnosticSeverity.Warning
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("does not warn with keep chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: |+
|
||||
\${{ fromJSON('[1, 2, 3]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not warn with strip chomping", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
version: |-
|
||||
\${{ fromJSON('[1, 2, 3]') }}
|
||||
steps:
|
||||
- run: echo hi
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result.filter(d => d.code === "expression-block-scalar-chomping")).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,12 +3,14 @@ import {Expr} from "@actions/expressions/ast";
|
||||
import {ParseWorkflowResult, WorkflowTemplate, isBasicExpression, isString} from "@actions/workflow-parser";
|
||||
import {ErrorPolicy} from "@actions/workflow-parser/model/convert";
|
||||
import {splitAllowedContext} from "@actions/workflow-parser/templates/allowed-context";
|
||||
import {Definition} from "@actions/workflow-parser/templates/schema/definition";
|
||||
import {BasicExpressionToken} from "@actions/workflow-parser/templates/tokens/basic-expression-token";
|
||||
import {StringToken} from "@actions/workflow-parser/templates/tokens/string-token";
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
||||
import {TokenRange} from "@actions/workflow-parser/templates/tokens/token-range";
|
||||
import {File} from "@actions/workflow-parser/workflows/file";
|
||||
import {FileProvider} from "@actions/workflow-parser/workflows/file-provider";
|
||||
import {Scalar} from "yaml";
|
||||
import {TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {Diagnostic, DiagnosticSeverity, URI} from "vscode-languageserver-types";
|
||||
import {ActionMetadata, ActionReference} from "./action";
|
||||
@@ -106,6 +108,8 @@ async function additionalValidations(
|
||||
config?.contextProviderConfig,
|
||||
getProviderContext(documentUri, template, root, token.range)
|
||||
);
|
||||
|
||||
validateChomp(diagnostics, token, parent, key, validationDefinition);
|
||||
}
|
||||
|
||||
if (token.definition?.key === "regular-step" && token.range) {
|
||||
@@ -217,3 +221,98 @@ async function validateExpression(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateChomp(
|
||||
diagnostics: Diagnostic[],
|
||||
token: BasicExpressionToken,
|
||||
parent: TemplateToken | undefined,
|
||||
key: TemplateToken | undefined,
|
||||
validationDefinition: Definition | undefined
|
||||
): void {
|
||||
// Not "clip" or "keep" chomp style?
|
||||
if (token.chompStyle !== "clip" && token.chompStyle !== "keep") {
|
||||
return;
|
||||
}
|
||||
|
||||
// No definition? This can happen when the token is in an invalid position or the workflow has parse errors.
|
||||
if (!validationDefinition) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step "run"?
|
||||
if (parent?.definition?.key === "run-step" && key?.isScalar && key.toString() === "run") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Block scalar indicator, i.e. | or >
|
||||
const scalarIndicator = token.scalarType === Scalar.BLOCK_LITERAL ? "|" : ">";
|
||||
|
||||
const defKey = validationDefinition.key;
|
||||
const parentDefKey = parent?.definition?.key;
|
||||
|
||||
// Error for boolean fields
|
||||
if (
|
||||
defKey === "job-if" ||
|
||||
defKey === "step-if" ||
|
||||
defKey === "step-continue-on-error" ||
|
||||
(parentDefKey === "job-factory" && key?.isScalar && key.toString() === "continue-on-error")
|
||||
) {
|
||||
diagnostics.push({
|
||||
message: `Block scalar adds trailing newline which breaks boolean evaluation. Use '${scalarIndicator}-' to strip trailing newlines.`,
|
||||
range: mapRange(token.range),
|
||||
severity: DiagnosticSeverity.Error,
|
||||
code: "expression-block-scalar-chomping",
|
||||
source: "github-actions"
|
||||
});
|
||||
}
|
||||
// Error for number fields
|
||||
else if (
|
||||
defKey === "step-timeout-minutes" ||
|
||||
(parentDefKey === "container-mapping" && key?.isScalar && ["ports", "volumes"].includes(key.toString())) ||
|
||||
(parentDefKey === "job-factory" && key?.isScalar && key.toString() === "timeout-minutes")
|
||||
) {
|
||||
diagnostics.push({
|
||||
message: `Block scalar adds trailing newline which breaks number parsing. Use '${scalarIndicator}-' to strip trailing newlines.`,
|
||||
range: mapRange(token.range),
|
||||
severity: DiagnosticSeverity.Error,
|
||||
code: "expression-block-scalar-chomping",
|
||||
source: "github-actions"
|
||||
});
|
||||
}
|
||||
// Error for specific string fields
|
||||
else if (
|
||||
defKey === "run-name" ||
|
||||
defKey === "step-name" ||
|
||||
defKey === "container" ||
|
||||
defKey === "services-container" ||
|
||||
defKey === "job-environment" ||
|
||||
defKey === "job-environment-name" ||
|
||||
defKey === "runs-on" ||
|
||||
defKey === "runs-on-labels" ||
|
||||
(parentDefKey === "container-mapping" && key?.isScalar && ["image", "credentials"].includes(key.toString())) ||
|
||||
(parentDefKey === "job-defaults-run" && key?.isScalar && ["shell", "working-directory"].includes(key.toString())) ||
|
||||
(parentDefKey === "job-environment-mapping" && key?.isScalar && key.toString() === "url") ||
|
||||
(parentDefKey === "job-factory" && key?.isScalar && key.toString() === "name") ||
|
||||
(parentDefKey === "workflow-job" && key?.isScalar && key.toString() === "name") ||
|
||||
(parentDefKey === "run-step" && key?.isScalar && key.toString() === "working-directory") ||
|
||||
(parentDefKey === "runs-on-mapping" && key?.isScalar && key.toString() === "group")
|
||||
) {
|
||||
diagnostics.push({
|
||||
message: `Block scalar adds trailing newline. Use '${scalarIndicator}-' to strip trailing newlines.`,
|
||||
range: mapRange(token.range),
|
||||
severity: DiagnosticSeverity.Error,
|
||||
code: "expression-block-scalar-chomping",
|
||||
source: "github-actions"
|
||||
});
|
||||
}
|
||||
// Warning for everything else, but only on clip (default)
|
||||
else if (token.chompStyle === "clip") {
|
||||
diagnostics.push({
|
||||
message: `Block scalar adds trailing newline to expression result. Use '${scalarIndicator}-' to strip or '${scalarIndicator}+' to keep trailing newlines.`,
|
||||
range: mapRange(token.range),
|
||||
severity: DiagnosticSeverity.Warning,
|
||||
code: "expression-block-scalar-chomping",
|
||||
source: "github-actions"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user