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
10 changes: 10 additions & 0 deletions .github/workflows/5_builderpackage_dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ on:
permissions:
id-token: write
contents: read
# Pull the prebuilt builder images from ghcr in Generate packages
packages: read

env:
CI_DEV_INTERNAL_BUCKET: ${{ secrets.CI_DEV_INTERNAL_BUCKET }}
Expand Down Expand Up @@ -438,6 +440,14 @@ jobs:
sudo nohup dockerd --storage-driver vfs > /var/log/dockerd.log 2>&1 &
timeout 60 bash -c 'until docker info &>/dev/null 2>&1; do sleep 1; done'

# build-packages.sh builds the builder images locally when it can't pull
# them, so a failed login must not stop the job.
- name: Login to GitHub Container Registry
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: echo "$GITHUB_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin

- name: Build package
run: |
cd ${{ github.workspace }}/dev-tools/build-packages
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Deletes the old versions of the dashboard package builder images in ghcr.
#
# Rule (dev-tools/build-packages/common/builder-image-retention.jq): untagged
# versions are deleted, and a tagged version is deleted only when all its tags
# are <version>-<Dockerfile hash> and none is the newest tag of its <version>.
# So the current image of every version branch is kept, and versions with any
# other tag are never touched.
#
# A branch whose tag is deleted anyway (e.g. a Dockerfile reverted to an older
# content) still works: build-packages.sh can't pull the tag and builds the
# image locally.

run-name: Clean up dashboard builder images${{ inputs.dry_run && ' (dry run)' || '' }}
name: (5.x) Clean up dashboard package builder images

on:
schedule:
- cron: '0 3 * * 1'
workflow_dispatch:
inputs:
dry_run:
description: 'Only list the versions that would be deleted'
type: boolean
required: false
default: false

permissions:
contents: read

jobs:
cleanup:
name: Clean up the builder images
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Test the retention filter
run: bash dev-tools/build-packages/common/test-builder-image-retention.sh

- name: Delete old versions
env:
GH_TOKEN: ${{ github.token }}
OWNER: ${{ github.repository_owner }}
# Scheduled runs have no inputs: they delete.
DRY_RUN: ${{ inputs.dry_run || false }}
FILTER: dev-tools/build-packages/common/builder-image-retention.jq
run: |
set -euo pipefail
packages=()
for image in base rpm deb; do
for arch in amd64 arm64; do
packages+=("pkg_dashboard_${image}_builder_${arch}")
done
done
for package in "${packages[@]}"; do
if [[ ! "${package}" =~ ^pkg_dashboard_(base|rpm|deb)_builder_(amd64|arm64)$ ]]; then
echo "::error::Invalid package name '${package}': nothing deleted"
exit 1
fi
done
echo "Deleting old builder image versions (dry run: ${DRY_RUN})"

err="${RUNNER_TEMP:-/tmp}/gh.err"
failed=0
for package in "${packages[@]}"; do
api="/orgs/${OWNER}/packages/container/${package}/versions"
if ! versions="$(gh api --paginate --slurp "${api}?per_page=100" 2>"${err}" | jq -c '[.[][]]')"; then
if grep -q 'HTTP 404' "${err}"; then
echo "::notice::${package} does not exist yet: nothing to delete"
continue
fi
cat "${err}"
echo "::error::Could not list the versions of ${package}"
failed=1
continue
fi
if ! delete="$(jq -r -f "${FILTER}" <<<"${versions}")"; then
echo "::error::Could not select the versions of ${package} to delete"
failed=1
continue
fi
echo "${package}: $(jq length <<<"${versions}") versions, $(grep -c . <<<"${delete}" || true) to delete"
while IFS=$'\t' read -r id reason; do
[ -n "${id}" ] || continue
if [[ ! "${id}" =~ ^[0-9]+$ ]]; then
echo "::error::Invalid version id '${id}' for ${package}"
failed=1
continue
fi
echo "Delete ${package} version ${id} (${reason})"
[ "${DRY_RUN}" = true ] && continue
if ! gh api -X DELETE "${api}/${id}" >/dev/null; then
echo "::error::Could not delete ${package} version ${id}"
failed=1
fi
done <<<"${delete}"
done
exit "${failed}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Publishes the dashboard package builder images (base, rpm, deb on amd64 and
# arm64) to ghcr, so `Generate packages` pulls them instead of building them.
#
# Tags are immutable: <version>-<short sha256 of the Dockerfile>, computed by
# dev-tools/build-packages/common/builder-image.sh. A tag that already exists
# is never pushed again.

run-name: Upload dashboard builder images from ${{ github.ref_name }}
name: (5.x) Upload dashboard package builder images

on:
push:
branches:
- 'main'
- '[0-9]+.[0-9]+.[0-9]+'
paths:
# The tag holds the version, so a version bump needs new tags too.
- 'VERSION.json'
- 'dev-tools/build-packages/base/Docker/Dockerfile'
- 'dev-tools/build-packages/rpm/Docker/Dockerfile'
- 'dev-tools/build-packages/deb/Docker/Dockerfile'
- 'dev-tools/build-packages/common/builder-image.sh'
- '.github/workflows/5_builderprecompiled_dashboard_builder-images-upload.yml'
workflow_dispatch:

permissions:
contents: read
packages: write

jobs:
# Computes every image reference first so the upload jobs can use it as
# their concurrency group.
get-references:
name: Get image references
runs-on: ubuntu-24.04
outputs:
matrix: ${{ steps.references.outputs.matrix }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Compute image references
id: references
run: |
entries=()
for image in base rpm deb; do
for arch in amd64 arm64; do
ref="$(bash dev-tools/build-packages/common/builder-image.sh "${image}" "${arch}")"
echo "${image} ${arch}: ${ref}"
entries+=("$(jq -nc --arg image "${image}" --arg arch "${arch}" --arg ref "${ref}" \
'{image: $image, arch: $arch, ref: $ref}')")
done
done
echo "matrix=$(printf '%s\n' "${entries[@]}" | jq -sc '{include: .}')" >> "$GITHUB_OUTPUT"

upload:
needs: get-references
name: Upload ${{ matrix.image }} builder image (${{ matrix.arch }})
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
concurrency:
group: builder-image-${{ matrix.ref }}
cancel-in-progress: false
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.get-references.outputs.matrix) }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Login to GitHub Container Registry
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: echo "$GITHUB_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin

- name: Build and push the image
env:
IMAGE_REF: ${{ matrix.ref }}
CONTEXT_DIR: dev-tools/build-packages/${{ matrix.image }}/Docker
SOURCE_URL: ${{ github.server_url }}/${{ github.repository }}
run: |
if output="$(docker manifest inspect "${IMAGE_REF}" 2>&1)"; then
echo "${IMAGE_REF} already exists. Tags are immutable: nothing to push."
exit 0
fi
# ghcr answers "denied" for a package that doesn't exist yet. Any
# other error could hide an existing tag, so don't overwrite it.
if ! grep -qiE 'no such manifest|manifest unknown|name unknown|not found|denied' <<< "${output}"; then
echo "::error::Could not check whether ${IMAGE_REF} exists: ${output}"
exit 1
fi
echo "${IMAGE_REF} doesn't exist yet. Building and pushing it."
# The source label links the package to this repository. No
# provenance: an attestation adds untagged versions that the
# cleanup workflow would delete.
docker build --provenance=false \
--label "org.opencontainers.image.source=${SOURCE_URL}" \
-t "${IMAGE_REF}" "${CONTEXT_DIR}"
docker push "${IMAGE_REF}"
12 changes: 3 additions & 9 deletions dev-tools/build-packages/base/Docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ RUN cd brotli && chmod +x ./bootstrap && ./bootstrap && ./configure --prefix=/us

RUN mkdir -p /usr/local/lib/wazuh

# Add the scripts to build the RPM package
ADD base-builder.sh /usr/local/bin/builder
ADD VERSION.json /usr/local/bin/VERSION.json
ADD plugins /usr/local/bin/plugins
ADD run-with-retry.sh /usr/local/lib/wazuh/run-with-retry.sh
RUN chmod +x /usr/local/bin/builder

# Set the entrypoint
ENTRYPOINT ["/usr/local/bin/builder"]
# The builder script and its inputs are mounted at run time by
# build-packages.sh, so this Dockerfile is the only input of the image.
ENTRYPOINT ["/bin/bash", "/usr/local/bin/builder"]
74 changes: 46 additions & 28 deletions dev-tools/build-packages/build-packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -e

current_path="$( cd $(dirname $0) ; pwd -P )"
root_dir="${current_path}/../.."
root_dir="$( cd "${current_path}/../.." ; pwd -P )"
app=""
base=""
revision="1"
Expand Down Expand Up @@ -30,6 +30,7 @@ RETRY_DELAY_SECONDS="${RETRY_DELAY_SECONDS:-15}"
export RETRY_MAX_ATTEMPTS RETRY_DELAY_SECONDS

source "${current_path}/common/run-with-retry.sh"
source "${current_path}/common/builder-image.sh"

trap clean INT
trap clean EXIT
Expand All @@ -47,13 +48,6 @@ clean() {
echo
# Clean the files
rm -rf ${tmp_dir}
rm -f ${current_path}/base/Docker/base-builder.sh
rm -f ${current_path}/base/Docker/plugins
rm -f ${current_path}/base/Docker/run-with-retry.sh
rm -f ${current_path}/rpm/Docker/rpm-builder.sh
rm -f ${current_path}/rpm/Docker/wazuh-dashboard.spec
rm -f ${current_path}/deb/Docker/deb-builder.sh
rm -rf ${current_path}/deb/Docker/debian
trap '' EXIT
exit ${exit_code}
}
Expand Down Expand Up @@ -94,6 +88,33 @@ get_packages(){
cd ..
}

builder_arch() {
if [ "${architecture}" = "arm64" ]; then
echo "arm64"
else
echo "amd64"
fi
}

# Pulls the published builder image and builds it locally when the pull fails
# (tag not published yet, no registry access, ...). Never pushes. The pull is
# tried twice only: a missing tag fails every attempt, and the local build is
# the fallback anyway.
prepare_builder_image() {
local image="$1"
local ref="$2"
log "Pulling builder image ${ref}..."
if RETRY_MAX_ATTEMPTS=2 RETRY_DELAY_SECONDS=5 run_with_retry docker pull "${ref}"; then
log "Using the published builder image ${ref}"
return 0
fi
echo "Notice: could not pull ${ref}. Building it locally."
if [ "${GITHUB_ACTIONS:-}" = "true" ]; then
echo "::warning::${ref} was not found in ghcr or couldn't be pulled; building it locally"
fi
run_with_retry docker build -t "${ref}" "${current_path}/${image}/Docker"
}

build_tar() {
log
log "Building base package..."
Expand All @@ -102,35 +123,33 @@ build_tar() {
cp -r ${config_dir} ${tmp_dir}
cp -r ${current_path}/credentials ${tmp_dir}
cd ./base
dockerfile_path="${current_path}/base/Docker"
container_name="dashboard-base-builder"
cp ./base-builder.sh ${dockerfile_path}
cp ./plugins ${dockerfile_path}
cp ${root_dir}/VERSION.json ${dockerfile_path}
cp ${current_path}/common/run-with-retry.sh ${dockerfile_path}
run_with_retry docker build -t "${container_name}" "${dockerfile_path}" || return 1
builder_image="$(builder_image_ref base "$(builder_arch)")" || return 1
prepare_builder_image base "${builder_image}" || return 1
run_with_retry docker run -t --rm \
-e "RETRY_MAX_ATTEMPTS=${RETRY_MAX_ATTEMPTS}" \
-e "RETRY_DELAY_SECONDS=${RETRY_DELAY_SECONDS}" \
-v "${tmp_dir}/:/tmp:Z" -v "${output_dir}/:/output:Z" \
"${container_name}" "${version}" "${revision}" "${architecture}" "${verbose}" || return 1
-v "${current_path}/base/base-builder.sh:/usr/local/bin/builder:ro,z" \
-v "${current_path}/base/plugins:/usr/local/bin/plugins:ro,z" \
-v "${root_dir}/VERSION.json:/usr/local/bin/VERSION.json:ro,z" \
-v "${current_path}/common/run-with-retry.sh:/usr/local/lib/wazuh/run-with-retry.sh:ro,z" \
"${builder_image}" "${version}" "${revision}" "${architecture}" "${verbose}" || return 1
cd ..
}

build_rpm() {
log "Building rpm package..."
cd ./rpm
dockerfile_path="${current_path}/rpm/Docker"
container_name="dashboard-rpm-builder"
cp -r ${package_config_dir} ${tmp_dir}
cp ./rpm-builder.sh ${dockerfile_path}
cp ./wazuh-dashboard.spec ${dockerfile_path}
run_with_retry docker build -t "${container_name}" "${dockerfile_path}" || return 1
builder_image="$(builder_image_ref rpm "$(builder_arch)")" || return 1
prepare_builder_image rpm "${builder_image}" || return 1
run_with_retry docker run -t --rm \
-e "RETRY_MAX_ATTEMPTS=${RETRY_MAX_ATTEMPTS}" \
-e "RETRY_DELAY_SECONDS=${RETRY_DELAY_SECONDS}" \
-v "${tmp_dir}/:/tmp:Z" -v "${output_dir}/:/output:Z" \
"${container_name}" "${version}" "${revision}" "${architecture}" \
-v "${current_path}/rpm/rpm-builder.sh:/usr/local/bin/builder:ro,z" \
-v "${current_path}/rpm/wazuh-dashboard.spec:/usr/local/src/wazuh-dashboard.spec:ro,z" \
"${builder_image}" "${version}" "${revision}" "${architecture}" \
"${commit_sha}" "${production}" "${verbose}" || return 1
cd ../
}
Expand All @@ -139,17 +158,16 @@ build_rpm() {
build_deb() {
log "Building deb package..."
cd ./deb
dockerfile_path="${current_path}/deb/Docker"
container_name="dashboard-deb-builder"
cp -r ${package_config_dir} ${tmp_dir}
cp ./deb-builder.sh ${dockerfile_path}
cp -r ./debian ${dockerfile_path}
run_with_retry docker build -t "${container_name}" "${dockerfile_path}" || return 1
builder_image="$(builder_image_ref deb "$(builder_arch)")" || return 1
prepare_builder_image deb "${builder_image}" || return 1
run_with_retry docker run -t --rm \
-e "RETRY_MAX_ATTEMPTS=${RETRY_MAX_ATTEMPTS}" \
-e "RETRY_DELAY_SECONDS=${RETRY_DELAY_SECONDS}" \
-v "${tmp_dir}/:/tmp:Z" -v "${output_dir}/:/output:Z" \
"${container_name}" "${version}" "${revision}" "${architecture}" \
-v "${current_path}/deb/deb-builder.sh:/usr/local/bin/builder:ro,z" \
-v "${current_path}/deb/debian:/usr/local/src/debian:ro,z" \
"${builder_image}" "${version}" "${revision}" "${architecture}" \
"${commit_sha}" "${production}" "${verbose}" || return 1
cd ..
}
Expand Down
Loading
Loading