Refactor if-condition to use schema-driven validation and AST-based status function detection (#218)

- Read allowed context from schema definition instead of hardcoded constants
- Parse expressions into AST to accurately detect status functions (avoids false positives from string literals)
- Export ensureStatusFunction helper that combines checking and wrapping logic
- Remove step-if.yml from skipped tests (now passes with accurate detection)
- Add tests for if-condition wrapping in hover/completion position mapping
This commit is contained in:
eric sciple
2025-11-25 08:56:34 -06:00
committed by GitHub
parent bad1fb96af
commit 03d68e89c6
9 changed files with 521 additions and 90 deletions
@@ -1505,4 +1505,174 @@ jobs:
expect(result).toEqual([]);
});
});
describe("if condition context restrictions", () => {
describe("job-level if", () => {
it("allows github context", async () => {
const input = `
on: push
jobs:
build:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- run: echo hello`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
it("allows needs context", async () => {
const input = `
on: push
jobs:
a:
runs-on: ubuntu-latest
steps:
- run: echo hello
b:
needs: a
if: needs.a.result == 'success'
runs-on: ubuntu-latest
steps:
- run: echo hello`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
it("allows inputs context", async () => {
const input = `
on:
workflow_dispatch:
inputs:
environment:
type: string
jobs:
build:
if: inputs.environment == 'prod'
runs-on: ubuntu-latest
steps:
- run: echo hello`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
// Note: vars and matrix contexts are validated at runtime based on their existence
// vars context only exists if organization/repository variables are defined
// matrix context only exists if a strategy.matrix is defined
});
describe("step-level if", () => {
it("allows steps context", async () => {
const input = `
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- id: setup
run: echo hello
- if: steps.setup.outcome == 'success'
run: echo world`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
it("allows job context", async () => {
const input = `
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- if: job.status == 'success'
run: echo hello`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
it("allows runner context", async () => {
const input = `
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- if: runner.os == 'Linux'
run: echo hello`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
it("allows env context", async () => {
const input = `
on: push
jobs:
build:
runs-on: ubuntu-latest
env:
MY_VAR: value
steps:
- if: env.MY_VAR == 'value'
run: echo hello`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
it("allows matrix context in matrix job", async () => {
const input = `
on: push
jobs:
build:
strategy:
matrix:
os: [ubuntu, windows]
runs-on: ubuntu-latest
steps:
- if: matrix.os == 'ubuntu'
run: echo hello`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
it("allows hashFiles function", async () => {
const input = `
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- if: hashFiles('**/*.txt') != ''
run: echo hello`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
it("allows all contexts together", async () => {
const input = `
on: push
jobs:
build:
runs-on: ubuntu-latest
env:
JOB_VAR: job-value
steps:
- id: first
run: echo hello
- if: github.event_name == 'push' && steps.first.outcome == 'success' && job.status == 'success' && runner.os == 'Linux' && env.JOB_VAR == 'job-value'
run: echo world`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
});
});
});