Reusable GitHub Actions workflows that scan and audit Linux Foundation projects for security and supply-chain risk. Each workflow is a standalone lane: a caller invokes it directly, alongside whatever build pipeline the project already runs.
These lanes are ecosystem-agnostic. Nexus IQ CLM, for example, serves
Maven, Gradle and Node.js projects alike, which is why they live here
rather than in a language-specific repository such as
java-workflows.
Under construction. This repository starts from
workflows-template, drops its generic build/test/release skeletons,
and picks up the security lanes from
lfit/releng-reusable-workflows, which retires once they land.
| Lane | Purpose | Status |
|---|---|---|
sonatype-lifecycle.yaml |
Nexus IQ CLM dependency scan, with optional pre-scan build | Available |
sonarqube-cloud.yaml |
SonarQube Cloud static analysis, with optional pre-scan build | Available |
zizmor.yaml |
GitHub Actions workflow audit, SARIF publishing | Available |
openssf-scorecard.yaml |
OpenSSF Scorecard supply-chain health | Available |
package-hardening-audit.yaml |
Package manager hardening audit | Available |
codeql.yaml |
CodeQL static analysis, matrix over languages | Available |
action-pin-audit.yaml |
GitHub Actions uses: pin and reference audit |
Available |
All seven lanes are now available, and they supersede
lfit/releng-reusable-workflows. A migration map ships with the first
release.
Read docs/BRIEF.md for the design decisions: why the
lanes live in a dedicated repository, how they compose with build
pipelines, the input vocabulary they share, and the per-lane Gerrit
posture.
Lanes are standalone. They never call one another, and they never call a build workflow. Composition happens in the caller:
.github/workflows/gerrit-clm.yaml -> sonatype-lifecycle.yaml
.github/workflows/gerrit-verify.yaml -> java-workflows/maven-build-test.yaml
This is deliberate. Reusable workflows that reference each other across
repositories must be SHA-pinned, so every release of one forces a bump
in the other; composing at the caller avoids that entirely. Where a lane
genuinely needs a build first (the CLM lane, which scans resolved
dependencies), it runs the build itself via a build_type input rather
than calling out to another workflow.
Lanes are Gerrit-aware: a caller that sets gerrit_refspec gets a
change checked out with checkout-gerrit-change-action in place of
actions/checkout. Vote and comment casting belong in the caller, never
inside the lanes themselves; each lane's gerrit.yaml example shows the
shape.
One exception, enforced upstream rather than by choice:
openssf-scorecard.yaml cannot support Gerrit. ossf/scorecard-action
restricts which steps may run in its job, and its publish API rejects
anything else with HTTP 400. A Gerrit checkout action cannot run there.
Scorecard also scores a GitHub repository as a whole, so a per-change
scan would tell you nothing useful in any case.
sonarqube-cloud.yaml runs build_type: maven builds through
maven-build-action, which aggregates JaCoCo coverage across a
reactor's subprojects. Callers need no extra input and no pom.xml glue.
Sonar reads coverage per subproject, so by default a subproject's report counts the tests living in that same subproject and no others. Where tests in one subproject exercise another's code — normal in a layered reactor — Sonar drops that coverage without saying so. Sonar's guidance fixes this with a dedicated aggregator subproject, which in turn has to list every subproject by hand; forget to add a new one and its coverage disappears with no error. The action instead points every agent at one execution-data file and reruns the report goal once the reactor has finished, so nothing enumerates subprojects. Its README covers the cases where it leaves a project's own arrangements alone.
One of those cases needs the caller's help. A parent POM that sets the
jacoco.destFile or jacoco.dataFile property reads as the project
placing its own execution data, and the action leaves the layout alone.
Where the project sets those properties but aggregates nothing itself,
coverage stays per subproject and Sonar reports a figure that looks
plausible rather than zero. Pass jacoco_mode: 'shared' to say the
reactor may write one file. OpenDaylight projects need this: odlparent
sets both properties.
Migrating from Jenkins: drop any JaCoCo flags the job passed in.
Carried over, they read as the project arranging coverage itself, and the
aggregation steps aside. Projects that merged execution data through a
property of their own (OpenDaylight's odl.jacoco.aggregateFile, and the
pom.xml profile behind it) need neither any more.
Each lane ships two callers under examples/<lane>/, added alongside
the lane itself. Copy one into your project's .github/workflows/
directory and replace the placeholder uses: SHA with a pinned release:
github.yaml— a plain GitHub-native caller.gerrit.yaml— a Gerrit-wrapped caller for projects where Gerrit is the source of truth, integrating withgerrit_to_platformvoting/commenting.
Inputs are optional and default to the canonical behaviour; read the
inputs: block at the top of each workflow for the documented list.
Shared with python-, go-, node- and java-workflows:
- Workflow
name:prefixed[R]; noreuse-filename prefix. - Top-level
permissions: {}, explicit least-privilege per job, andtimeout-minuteson every job. - A
gerrit-validatejob as the DAG root, failing fast when a caller suppliesgerrit_refspecwithout the project, branch and URL the Gerrit checkout also needs. - Harden-runner on every network-touching job: block mode by default,
with
harden_runner_egress: 'audit'as an explicit opt-out. Anything other thanauditmeans block, so a typo cannot weaken the posture. - Every
uses:pinned to a full commit SHA (never an annotated tag object) with a# vX.Y.Zcomment. persist-credentials: falseon every checkout.- Never interpolate
${{ }}intorun:blocks; env-mediate instead. zizmor --persona auditormust report zero findings.