Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 75 additions & 5 deletions .github/workflows/gradle-build-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ on:
required: false
type: string
# yamllint disable-line rule:line-length
default: 'lfreleng-actions//.github/harden-runner/lfreleng-actions/allow_list.txt@cc89ca0f02a1c8fd875c9758896f0929097d25b7' # v0.12.0
default: 'lfreleng-actions//.github/harden-runner/lfreleng-actions/allow_list.txt@bf6642f68d58c1b81bbe993e676d6cc339ac3654' # v0.12.2

# --- Job timeouts (minutes) ---
# Sized per job rather than uniformly. The build, test-report and
Expand Down Expand Up @@ -141,6 +141,29 @@ on:
type: number
default: 30

# --- Build artefact publication ---
# Off by default. The build's packaged output is large and most
# verify lanes never look at it again, so uploading unconditionally
# would impose storage on every consumer to serve a minority. Turn
# it on where a later job, or a human, needs the built output:
# publish lanes, container builds, or downloading a jar from a
# failed run to reproduce a problem locally.
upload_build_artifacts:
description: >-
Upload the packaged build output (jar/war/ear from each
module's build/libs directory) as a workflow artefact.
required: false
type: boolean
default: false
build_artifact_retention_days:
description: >-
Retention for the build artefact. Shorter than the SBOM's 45
days: build output is reproducible from the commit, so it is
a convenience rather than a record.
required: false
type: number
default: 7

# --- Gerrit-aware checkout (empty unless dispatched via Gerrit) ---
gerrit_refspec:
description: 'Gerrit refspec of the change under test'
Expand Down Expand Up @@ -326,16 +349,16 @@ jobs:

# Detect Java/Gradle project metadata: Java version (including
# sourceCompatibility/targetCompatibility and toolchain settings),
# module layout, version.properties and any releases/*.yaml.
# Surfaces a metadata summary and lets the build pick the Java
# version when the caller does not pin one.
# module layout, version.properties and any releases/*.yaml. Lets
# the build pick the Java version when the caller does not pin
# one. The action takes no github_summary input; one was passed
# here until it was noticed in the run annotations.
- name: 'Detect build metadata'
id: metadata
# yamllint disable-line rule:line-length
uses: lfreleng-actions/build-metadata-action@6c432669e6dec2949442da27d0aa270f2bca0ecb # v0.7.1
with:
path_prefix: ${{ inputs.path_prefix }}
github_summary: 'true'

- name: 'Build with Gradle'
id: build
Expand Down Expand Up @@ -384,6 +407,53 @@ jobs:
path: junit-reports
if-no-files-found: warn

# Gradle has no m2repo equivalent in a plain build, so there is
# one shape here: whatever each module packaged into build/libs.
- name: 'Collect build artefacts'
id: build-artifacts
if: ${{ always() && inputs.upload_build_artifacts }}
shell: bash
env:
PATH_PREFIX: ${{ inputs.path_prefix }}
run: |
# Stage the build output under a single directory for upload.
set -euo pipefail
dest="build-artifacts"
count=0

while IFS= read -r -d '' dir; do
for f in "${dir}"/*.jar "${dir}"/*.war "${dir}"/*.ear; do
[ -f "${f}" ] || continue
rel="${f#"${PATH_PREFIX}/"}"
mkdir -p "${dest}/$(dirname "${rel}")"
cp "${f}" "${dest}/${rel}"
count=$((count + 1))
done
done < <(find "${PATH_PREFIX}" -type d -path '*/build/libs' -print0)

echo "count=${count}" >> "$GITHUB_OUTPUT"

{
echo "### Build artefacts"
echo ""
echo "| Property | Value |"
echo "| -------- | ----- |"
echo "| Files | ${count} |"
} >> "$GITHUB_STEP_SUMMARY"

echo "Collected ${count} file(s) from build/libs"

- name: 'Upload build artefacts'
# yamllint disable-line rule:line-length
if: ${{ always() && inputs.upload_build_artifacts && steps.build-artifacts.outputs.count != '0' }}
# yamllint disable-line rule:line-length
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: gradle-build-artifacts
path: build-artifacts
retention-days: ${{ inputs.build_artifact_retention_days }}
if-no-files-found: error

tests:
name: 'Tests'
runs-on: ubuntu-latest
Expand Down
120 changes: 115 additions & 5 deletions .github/workflows/maven-build-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ on:
required: false
type: string
# yamllint disable-line rule:line-length
default: 'lfreleng-actions//.github/harden-runner/lfreleng-actions/allow_list.txt@cc89ca0f02a1c8fd875c9758896f0929097d25b7' # v0.12.0
default: 'lfreleng-actions//.github/harden-runner/lfreleng-actions/allow_list.txt@bf6642f68d58c1b81bbe993e676d6cc339ac3654' # v0.12.2

# --- Job timeouts (minutes) ---
# Sized per job rather than uniformly. The build, test-report and
Expand Down Expand Up @@ -153,6 +153,31 @@ on:
type: number
default: 30

# --- Build artefact publication ---
# Off by default. The build's packaged output is large and most
# verify lanes never look at it again, so uploading unconditionally
# would impose storage on every consumer to serve a minority. Turn
# it on where a later job, or a human, needs the built output:
# publish/stage lanes, container builds, CSIT, or downloading a jar
# from a failed run to reproduce a problem locally.
upload_build_artifacts:
description: >-
Upload the packaged build output as a workflow artefact.
Prefers the m2repo when a deploy phase produced one, since
that is the layout publish and stage lanes consume; otherwise
takes the reactor's packaged output.
required: false
type: boolean
default: false
build_artifact_retention_days:
description: >-
Retention for the build artefact. Shorter than the SBOM's 45
days: build output is reproducible from the commit, so it is
a convenience rather than a record.
required: false
type: number
default: 7

# --- Gerrit-aware checkout (empty unless dispatched via Gerrit) ---
gerrit_refspec:
description: 'Gerrit refspec of the change under test'
Expand Down Expand Up @@ -299,6 +324,11 @@ jobs:
coverage: ${{ steps.build.outputs.coverage }}
branch_coverage: ${{ steps.build.outputs.branch_coverage }}
reports_found: ${{ steps.reports.outputs.found }}
# Surfaced so a caller's downstream job can locate the
# publish-ready repository layout without re-deriving the path.
m2repo_exists: ${{ steps.build.outputs.m2repo_exists }}
m2repo_path: ${{ steps.build.outputs.m2repo_path }}
artifact_count: ${{ steps.build.outputs.artifact_count }}
permissions:
contents: read
timeout-minutes: ${{ inputs.build_timeout_minutes }}
Expand Down Expand Up @@ -349,16 +379,16 @@ jobs:

# Detect Java/Maven project metadata: Java version (including
# maven.compiler.release plus parent/reactor resolution), module
# layout, version.properties and any releases/*.yaml. Surfaces a
# metadata summary and lets the build pick the Java version when
# the caller does not pin one.
# layout, version.properties and any releases/*.yaml. Lets the
# build pick the Java version when the caller does not pin one.
# The action takes no github_summary input; one was passed here
# until it was noticed in the run annotations, ignored throughout.
- name: 'Detect build metadata'
id: metadata
# yamllint disable-line rule:line-length
uses: lfreleng-actions/build-metadata-action@6c432669e6dec2949442da27d0aa270f2bca0ecb # v0.7.1
with:
path_prefix: ${{ inputs.path_prefix }}
github_summary: 'true'

- name: 'Build with Maven'
id: build
Expand Down Expand Up @@ -411,6 +441,86 @@ jobs:
path: junit-reports
if-no-files-found: warn

# Two shapes can come out of a Maven build and they are not
# interchangeable. A deploy phase writes a repository layout to
# m2repo (groupId/artifactId/version/...), which is what Nexus
# staging and publish consume. Any other phase leaves packaged
# output scattered through the reactor's target/ directories.
# Prefer the former when it exists and say which was taken, so a
# consumer downloading the artefact knows what it has.
- name: 'Collect build artefacts'
id: build-artifacts
if: ${{ always() && inputs.upload_build_artifacts }}
shell: bash
env:
PATH_PREFIX: ${{ inputs.path_prefix }}
M2REPO_EXISTS: ${{ steps.build.outputs.m2repo_exists }}
M2REPO_PATH: ${{ steps.build.outputs.m2repo_path }}
run: |
# Stage the build output under a single directory for upload.
set -euo pipefail
dest="build-artifacts"
shape="none"
count=0

# Take the location from the build action rather than
# re-deriving it, so the layout stays the action's business.
if [ "${M2REPO_EXISTS}" = "true" ] && [ -n "${M2REPO_PATH}" ] \
&& [ -d "${M2REPO_PATH}" ]; then
shape="m2repo"
mkdir -p "${dest}"
cp -R "${M2REPO_PATH}" "${dest}/m2repo"
count=$(find "${dest}/m2repo" -type f \
\( -name '*.jar' -o -name '*.war' -o -name '*.ear' \
-o -name '*.pom' \) | wc -l | tr -d ' ')
else
# No deploy phase ran. Take the reactor's packaged output:
# files sitting directly in a module's target/ directory,
# not the compiled classes or unpacked dependencies below it.
shape="reactor"
while IFS= read -r -d '' dir; do
for f in "${dir}"/*.jar "${dir}"/*.war "${dir}"/*.ear; do
[ -f "${f}" ] || continue
rel="${f#"${PATH_PREFIX}/"}"
mkdir -p "${dest}/$(dirname "${rel}")"
cp "${f}" "${dest}/${rel}"
count=$((count + 1))
done
done < <(find "${PATH_PREFIX}" -type d -name target -print0)
fi

if [ "${count}" -eq 0 ]; then
shape="none"
fi

{
echo "shape=${shape}"
echo "count=${count}"
} >> "$GITHUB_OUTPUT"

{
echo "### Build artefacts"
echo ""
echo "| Property | Value |"
echo "| -------- | ----- |"
echo "| Layout | ${shape} |"
echo "| Files | ${count} |"
} >> "$GITHUB_STEP_SUMMARY"

echo "Collected ${count} file(s) as '${shape}' layout"

- name: 'Upload build artefacts'
# yamllint disable-line rule:line-length
if: ${{ always() && inputs.upload_build_artifacts && steps.build-artifacts.outputs.count != '0' }}
# yamllint disable-line rule:line-length
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
# yamllint disable-line rule:line-length
name: maven-build-artifacts-${{ steps.build-artifacts.outputs.shape }}
path: build-artifacts
retention-days: ${{ inputs.build_artifact_retention_days }}
if-no-files-found: error

tests:
name: 'Tests'
runs-on: ubuntu-latest
Expand Down
26 changes: 26 additions & 0 deletions docs/BRIEF.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,32 @@ gerrit-validate ─┬─ repository-metadata (informational)
`*/target/*-reports/*.xml`, copies them under `junit-reports/`, and
sets `found`.
4. When reports exist, they upload as the `maven-junit-reports` artefact.
5. When `upload_build_artifacts` is set, a "Collect build artefacts"
step stages the packaged output and uploads it.

Build artefact publication is **opt-in**. The packaged output is large
and most verify lanes never read it back, so uploading it unconditionally
would charge storage to every consumer in order to serve a minority. The
cases that want it are real, though — publish and stage lanes, container
builds, CSIT, and a human pulling a jar out of a failed run — so the
workflow makes it a switch rather than omitting it.

Two shapes can come out of a Maven build, and they are not
interchangeable. A `deploy` phase writes a repository layout to `m2repo`
(`groupId/artifactId/version/...`), which is what Nexus staging and
`nexus-publish-action` consume; any other phase leaves packaged output
scattered through the reactor's `target/` directories. The collect step
prefers the former when it exists and records which it took in the
artefact name (`maven-build-artifacts-m2repo` or
`maven-build-artifacts-reactor`), so a consumer knows what it has
without inspecting the contents. Note the lane's default `mvn_phases` is
`clean install`, which produces the reactor shape; `m2repo` appears only
when a caller asks for `deploy`. The build job also surfaces
`m2repo_exists`, `m2repo_path` and `artifact_count` as job outputs.

Gradle has no `m2repo` equivalent in a plain build, so its lane collects
one shape — each module's `build/libs` output — and names the artefact
`gradle-build-artifacts`.

A global `settings.xml` (which commonly carries Nexus server credentials)
is never accepted as a plain input: the workflow declares a
Expand Down
6 changes: 6 additions & 0 deletions examples/gradle/build-test/github.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,9 @@ jobs:
# tests_timeout_minutes: 30 # test report rendering
# sbom_timeout_minutes: 30 # SBOM generation
# grype_timeout_minutes: 30 # Grype scan
#
# Keep the packaged build output from each module's build/libs
# directory. Off by default; turn it on where a later job or a
# human needs the jars.
# upload_build_artifacts: true
# build_artifact_retention_days: 7
6 changes: 6 additions & 0 deletions examples/maven/build-test/github.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ jobs:
# tests_timeout_minutes: 30 # test report rendering
# sbom_timeout_minutes: 30 # SBOM generation
# grype_timeout_minutes: 30 # Grype scan
#
# Keep the packaged build output. Off by default; turn it on where a
# later job or a human needs the jars. Prefers the m2repo layout
# when a deploy phase produced one, else the reactor's target/ jars.
# upload_build_artifacts: true
# build_artifact_retention_days: 7
# settings.xml can carry server credentials, so it is passed only as a
# masked secret (never a plain input). Synthesise it with
# maven-xml-settings-action and forward it here when your build needs a
Expand Down
Loading