Skip to content

Ci

ci

Node CI gold-standard checks — GitHub Actions workflow with lint/test jobs.

Ports the intent of the Python checks.ci to a node project: a CI workflow that lints, type-checks, tests across a node-version matrix, and audits dependencies. Reads .github/workflows/*.yml as text (the same lenient substring approach the Python checks use — no YAML parse needed).

check_ci_dependabot(project)

Check: automated dependency updates are configured (dependabot/renovate).

Node analog of the Python ci.dependabot check.

Source code in packages/axm-init/src/axm_init/checks/node/ci.py
Python
def check_ci_dependabot(project: Path) -> CheckResult:
    """Check: automated dependency updates are configured (dependabot/renovate).

    Node analog of the Python ``ci.dependabot`` check.
    """
    candidates = (
        project / ".github" / "dependabot.yml",
        project / ".github" / "dependabot.yaml",
        project / "renovate.json",
        project / ".github" / "renovate.json",
    )
    if any(p.is_file() for p in candidates):
        return CheckResult(
            name="ci.dependabot",
            category="ci",
            passed=True,
            weight=2,
            message="Automated dependency updates configured",
            details=[],
            fix="",
        )
    return CheckResult(
        name="ci.dependabot",
        category="ci",
        passed=False,
        weight=2,
        message="No dependabot/renovate config",
        details=[],
        fix="Add .github/dependabot.yml (or renovate.json) for the npm ecosystem.",
    )

check_ci_lint_job(project)

Check: CI runs lint / type-check.

Source code in packages/axm-init/src/axm_init/checks/node/ci.py
Python
def check_ci_lint_job(project: Path) -> CheckResult:
    """Check: CI runs lint / type-check."""
    content = _read_ci(project) or ""
    lowered = content.lower()
    if "lint" in lowered or "eslint" in lowered or "tsc" in lowered:
        return CheckResult(
            name="ci.ci_lint_job",
            category="ci",
            passed=True,
            weight=3,
            message="Lint job present",
            details=[],
            fix="",
        )
    return CheckResult(
        name="ci.ci_lint_job",
        category="ci",
        passed=False,
        weight=3,
        message="No lint job in CI",
        details=["CI should run eslint / tsc"],
        fix="Add a lint job running `npm run lint` and `npm run typecheck`.",
    )

check_ci_publish(project)

Check: a publish/release workflow with npm provenance.

Node analog of the Python ci.trusted_publishing check — a workflow that publishes to npm, ideally with provenance (--provenance / id-token).

Source code in packages/axm-init/src/axm_init/checks/node/ci.py
Python
def check_ci_publish(project: Path) -> CheckResult:
    """Check: a publish/release workflow with npm provenance.

    Node analog of the Python ``ci.trusted_publishing`` check — a workflow that
    publishes to npm, ideally with provenance (``--provenance`` / ``id-token``).
    """
    content = _read_ci(project) or ""
    lowered = content.lower()
    has_publish = "npm publish" in lowered or "changeset" in lowered
    if has_publish and ("provenance" in lowered or "id-token" in lowered):
        return CheckResult(
            name="ci.trusted_publishing",
            category="ci",
            passed=True,
            weight=2,
            message="Publish workflow with provenance",
            details=[],
            fix="",
        )
    return CheckResult(
        name="ci.trusted_publishing",
        category="ci",
        passed=False,
        weight=2,
        message="No npm publish workflow with provenance",
        details=["Publish to npm with provenance (id-token: write)"],
        fix="Add a release workflow running `npm publish --provenance`.",
    )

check_ci_security_job(project)

Check: CI runs a dependency-audit / security step.

Source code in packages/axm-init/src/axm_init/checks/node/ci.py
Python
def check_ci_security_job(project: Path) -> CheckResult:
    """Check: CI runs a dependency-audit / security step."""
    content = _read_ci(project) or ""
    lowered = content.lower()
    if "audit" in lowered or "codeql" in lowered or "security" in lowered:
        return CheckResult(
            name="ci.ci_security_job",
            category="ci",
            passed=True,
            weight=2,
            message="Security job present",
            details=[],
            fix="",
        )
    return CheckResult(
        name="ci.ci_security_job",
        category="ci",
        passed=False,
        weight=2,
        message="No security/audit job in CI",
        details=["CI should run `npm audit` or CodeQL"],
        fix="Add a job running `npm audit --audit-level high`.",
    )

check_ci_test_job(project)

Check: CI runs tests across a node-version matrix.

Source code in packages/axm-init/src/axm_init/checks/node/ci.py
Python
def check_ci_test_job(project: Path) -> CheckResult:
    """Check: CI runs tests across a node-version matrix."""
    content = _read_ci(project) or ""
    lowered = content.lower()
    has_test = "test" in lowered or "vitest" in lowered
    has_matrix = "node-version" in lowered or "matrix" in lowered
    if has_test and has_matrix:
        return CheckResult(
            name="ci.ci_test_job",
            category="ci",
            passed=True,
            weight=3,
            message="Matrix test job present",
            details=[],
            fix="",
        )
    return CheckResult(
        name="ci.ci_test_job",
        category="ci",
        passed=False,
        weight=3,
        message="No matrix test job in CI",
        details=["CI must run tests with a strategy.matrix.node-version"],
        fix="Add a test job with strategy.matrix.node-version running `npm test`.",
    )

check_ci_workflow_exists(project)

Check: a GitHub Actions workflow exists.

Source code in packages/axm-init/src/axm_init/checks/node/ci.py
Python
def check_ci_workflow_exists(project: Path) -> CheckResult:
    """Check: a GitHub Actions workflow exists."""
    if _read_ci(project) is not None:
        return CheckResult(
            name="ci.ci_workflow_exists",
            category="ci",
            passed=True,
            weight=4,
            message="CI workflow found",
            details=[],
            fix="",
        )
    return CheckResult(
        name="ci.ci_workflow_exists",
        category="ci",
        passed=False,
        weight=4,
        message="CI workflow not found",
        details=["Expected: .github/workflows/*.yml"],
        fix="Create .github/workflows/ci.yml with lint, test, and audit jobs.",
    )