Warning
This action is under active development and its interface may change.
A GitHub Action that lints a Julia repository with
julialint.
The action installs Julia (via juliaup) and LintApp itself, so it has no
prerequisites beyond a checkout. The exact versions of LintApp and all of
its dependencies are pinned by the committed Manifest.toml, so every run
uses the same, known-good versions. It runs the equivalent of
julialint --format sarif -o lint-results.sarif . in the workspace and then:
- emits inline
::error/::warning/::noticeannotations (visible in the PR Files view) from the SARIF output, errors first; - leaves
lint-results.sarifin the workspace (also exposed as thesarif-pathoutput) so a later job can upload it as an artifact — for example for julia-report-ci-results; - optionally uploads the SARIF to GitHub code scanning.
By default the job fails when lint errors are found (exit code 1) or when
julialint itself fails (exit code 2); the SARIF file and annotations are
still produced in the error case. Set fail-on-errors: false to make lint
findings non-fatal (a tool failure still fails the step).
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: julia-actions/julia-lint@v1
- uses: actions/upload-artifact@v7
if: ${{ !cancelled() }}
with:
name: lintresults
path: lint-results.sarif
if-no-files-found: ignore| Input | Default | Description |
|---|---|---|
path |
. |
Path to lint, relative to the workspace. |
sarif-path |
lint-results.sarif |
Path to write the SARIF results file to, relative to the workspace. |
max-warnings |
(unlimited) | Fail when the warning count exceeds this number. |
quiet |
false |
Report only errors (suppress warnings, info, and hints). |
fail-on-errors |
true |
Fail the step when lint errors are found. When false, lint findings never fail the step (a julialint tool failure still does). |
code-scanning-upload |
false |
Also upload the SARIF to GitHub code scanning. Requires the security-events: write permission; on private repositories this needs GitHub Code Security. |
| Output | Description |
|---|---|
sarif-path |
Path of the produced SARIF file, relative to the workspace. |
error-count |
Number of error-severity lint results. |
warning-count |
Number of warning-severity lint results. |
julia --project=. -e 'using Pkg; Pkg.update()'
and commit the changed Manifest.toml.
The annotation step is a small TypeScript program bundled into dist/index.js
(committed). After changing src/, run:
npm install
npm test
npm run build
and commit the updated dist/index.js together with the source change.
Linting behavior is configured with a JuliaLint.toml file in the linted
repository — see the
LintApp documentation.
The action uses two Julia depots.
julialint analyses the environments of the repository being linted in child
processes, and those resolve against the runner's default depot, ~/.julia.
The action caches it with
julia-actions/cache, so the
registries and the packages your repository's environments resolve to are
carried from one run to the next.
That depot starts out empty on a fresh runner — in particular without a
registry — so the action installs the default registries into it before
linting. Without that, resolving an environment whose Manifest.toml is not
committed fails with "no registries have been installed" and the affected
scope gets degraded missing-reference checks.
On a run that restored a cached depot the registries are refreshed instead,
so every run resolves against the current registry rather than against whatever
was current when the cache entry happened to be written. The step runs after
julia-actions/cache — that action only caches the registries directory when
it does not already exist when it runs, so installing earlier would quietly
drop it from the cache.
Neither is fatal: if the registries cannot be installed or refreshed the run
continues with a ::warning:: and whatever copy is present, since a degraded
analysis beats a failed job for a transient outage.
The action caches its own toolkit — LintApp and the tree its Manifest.toml
pins — in a depot of its own under RUNNER_TEMP, keyed on the runner OS and
architecture, the Julia version and a hash of that manifest. Nothing
run-specific enters the key, so the entry is written once and then only read,
rather than re-saved on every run and duplicated for every pull request the way
a depot cached with julia-actions/cache is.
The toolkit is also precompiled against a portable CPU target. Julia's default,
native, compiles package images for whichever machine precompiled them while
recording only the literal string native in the cache path — so a depot moved
between two runners with different CPUs looks valid, is rejected on load, and
recompiles. GitHub's runner fleet is mixed enough for that to happen regularly;
see julia-actions/cache#114.
A platform whose Julia does not accept the target gets a warning and Julia's
default instead, keeping the behaviour it had before.
None of this needs configuration, and nothing in your workflow should point at the toolkit depot.