From 53e00edd234d32d3f821f2b6927e8d1ee3988226 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 10:57:59 +0100 Subject: [PATCH 01/16] [EMB-115] Migrate package release from Cloudsmith to GAR This commit migrates the Debian package release process from Cloudsmith to Google Artifact Registry (GAR). The `release_pkg.yml` workflow has been updated to: - Authenticate with Google Cloud using Workload Identity Federation. - Use `gcloud artifacts apt upload` to publish packages. - Automatically extract package metadata from the `.deb` file. - Generate a detailed job summary with instructions on how to install the package from the new GAR repository. The now-unused `cloudsmith.Dockerfile` has been removed. --- .github/workflows/prepare_env.yml | 2 +- .github/workflows/release_pkg.yml | 163 +++++++++++++++++++++++++----- Dockerfiles/cloudsmith.Dockerfile | 9 -- 3 files changed, 138 insertions(+), 36 deletions(-) delete mode 100644 Dockerfiles/cloudsmith.Dockerfile diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index d38504d..ad66493 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -11,7 +11,7 @@ on: description: "The package version for this build" value: ${{ jobs.Prepare.outputs.RELEASE_VERSION }} RELEASE_REPO: - description: "The Cloudsmith repository to release the package" + description: "The GAR repository to release the package" value: ${{ jobs.Prepare.outputs.RELEASE_REPO }} env: # All branches starting with any of the items in the list below will be diff --git a/.github/workflows/release_pkg.yml b/.github/workflows/release_pkg.yml index aa156d0..a00276a 100644 --- a/.github/workflows/release_pkg.yml +++ b/.github/workflows/release_pkg.yml @@ -2,7 +2,7 @@ on: workflow_call: inputs: RELEASE_REPO: - description: "The Cloudsmith repository to release the package" + description: "The GAR repository to release the package" required: true type: string @@ -10,48 +10,159 @@ jobs: Release_Package: name: 'Release Package' runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' env: - RELEASE_REPO: ${{ inputs.RELEASE_REPO }} + EMB_APT_REPO: ${{ inputs.RELEASE_REPO }} + EMB_APT_SERVICE_ACCOUNT: ${{ vars.EMB_APT_SERVICE_ACCOUNT }} + EMB_GCP_LOCATION: ${{ vars.EMB_GCP_LOCATION }} + EMB_GCP_PROJECT: ${{ vars.EMB_GCP_PROJECT }} + EMB_WI_PROVIDER: ${{ vars.EMB_WI_PROVIDER }} steps: - name: Cloning Embedded-Workflows on default branch uses: actions/checkout@v4 with: repository: Ultimaker/embedded-workflows - token: ${{ secrets.ULTIMAKER_CI_PAT }} + token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Remove PAT (limit long-lived secrets) fetch-depth: 0 path: embedded-workflows - + - uses: actions/download-artifact@v4 with: name: build-package - - - name: Display structure of downloaded files - shell: bash - run: ls -lh - - - name: Build Cloudsmith Docker image - shell: bash + + - id: auth + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@v2' + with: + token_format: 'access_token' + workload_identity_provider: ${{ env.EMB_WI_PROVIDER }} + service_account: ${{ env.EMB_APT_SERVICE_ACCOUNT }} + + - name: 'Set up Cloud SDK' + uses: 'google-github-actions/setup-gcloud@v2' + + - name: 'Extract package metadata' + id: meta run: | - docker build embedded-workflows/Dockerfiles/ -f embedded-workflows/Dockerfiles/cloudsmith.Dockerfile -t cloudsmith-cli:latest - - - name: Upload Debian Packages - shell: bash + set -euo pipefail + # Find all .deb files and store the first one for summary + DEB_FILE=$(find ./dist -name "*.deb" -print -quit) + if [[ -z "${DEB_FILE:-}" ]]; then + echo "No .deb file found in ./dist" >&2 + exit 1 + fi + echo "deb_file=${DEB_FILE}" >> "$GITHUB_OUTPUT" + + # Extract package name and version from filename: _-.deb or _.deb + DEB_BASENAME=$(basename "$DEB_FILE") + PKG_NAME=${DEB_BASENAME%%_*} + PKG_VERSION_PART=${DEB_BASENAME#*_} + PKG_VERSION=${PKG_VERSION_PART%%.deb} + echo "pkg_name=${PKG_NAME}" >> "$GITHUB_OUTPUT" + echo "pkg_version=${PKG_VERSION}" >> "$GITHUB_OUTPUT" + + - name: 'Publish to GAR' + id: publish run: | + set -euo pipefail + # Upload all .deb packages found in ./dist + cd ./dist for package in *.deb; do - echo " " - echo "########## Uploading ${package} ##########" - echo " " - docker run -v "$(pwd):/build" cloudsmith-cli:latest cloudsmith push deb ultimaker/${{ env.RELEASE_REPO }}/debian/buster /build/${package} -k ${{ secrets.CLOUDSMITH_API_KEY }} - docker run -v "$(pwd):/build" cloudsmith-cli:latest cloudsmith push deb ultimaker/${{ env.RELEASE_REPO }}/debian/bookworm /build/${package} -k ${{ secrets.CLOUDSMITH_API_KEY }} - done; - + if [[ -f "$package" ]]; then + echo "Uploading ${package} to GAR..." + gcloud artifacts apt upload "$EMB_APT_REPO" \ + --project="$EMB_GCP_PROJECT" \ + --location="$EMB_GCP_LOCATION" \ + --source="$package" + fi + done + + - name: 'Publish summary' + if: ${{ success() }} + env: + DEB_FILE: ${{ steps.meta.outputs.deb_file }} + PKG_NAME: ${{ steps.meta.outputs.pkg_name }} + PKG_VERSION: ${{ steps.meta.outputs.pkg_version }} + APT_REPO: ${{ env.EMB_APT_REPO }} + GCP_PROJECT: ${{ env.EMB_GCP_PROJECT }} + GCP_LOCATION: ${{ env.EMB_GCP_LOCATION }} + run: | + set -euo pipefail + + echo "# Release summary" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "Published .deb package to Google Artifact Registry (APT):" >> "$GITHUB_STEP_SUMMARY" + echo "- File: \`${DEB_FILE}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- Package: \`${PKG_NAME}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- Version: \`${PKG_VERSION}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- Repository: \`${APT_REPO}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- Project: \`${GCP_PROJECT}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- Location: \`${GCP_LOCATION}\`" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Distribution Support" >> "$GITHUB_STEP_SUMMARY" + echo "This package supports multiple Debian distributions. The distribution is determined by the package metadata set during the build process. Supported distributions are:" >> "$GITHUB_STEP_SUMMARY" + echo "- \`buster\` (Debian 10)" >> "$GITHUB_STEP_SUMMARY" + echo "- \`bookworm\` (Debian 12)" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "The GAR repository automatically organizes packages by distribution based on the metadata in the .deb file. See **Build Configuration** below for how to set distribution metadata." >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## How to use" >> "$GITHUB_STEP_SUMMARY" + echo "You can install from the GAR APT repository using the following steps on Ubuntu/Debian:" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "1. Install the Artifact Registry APT transport helper:" >> "$GITHUB_STEP_SUMMARY" + echo '```bash' >> "$GITHUB_STEP_SUMMARY" + echo "sudo apt-get update" >> "$GITHUB_STEP_SUMMARY" + echo "sudo apt-get install -y artifact-registry-apt-transport" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "2. Authenticate with gcloud and configure your project (if needed):" >> "$GITHUB_STEP_SUMMARY" + echo '```bash' >> "$GITHUB_STEP_SUMMARY" + echo "gcloud auth login" >> "$GITHUB_STEP_SUMMARY" + echo "gcloud config set project ${GCP_PROJECT}" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "3. Add the GAR APT repository (replace \`buster\` with your target distribution):" >> "$GITHUB_STEP_SUMMARY" + echo '```bash' >> "$GITHUB_STEP_SUMMARY" + echo "echo 'deb [signed-by=/usr/share/keyrings/artifact-registry-keyring.gpg] https://${GCP_LOCATION}-apt.pkg.dev/${GCP_PROJECT}/${APT_REPO} buster main' | sudo tee /etc/apt/sources.list.d/${APT_REPO}.list" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "4. Update and install:" >> "$GITHUB_STEP_SUMMARY" + echo '```bash' >> "$GITHUB_STEP_SUMMARY" + echo "sudo apt-get update" >> "$GITHUB_STEP_SUMMARY" + echo "sudo apt-get install -y ${PKG_NAME}" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "5. Alternatively, install directly from the downloaded .deb file:" >> "$GITHUB_STEP_SUMMARY" + echo '```bash' >> "$GITHUB_STEP_SUMMARY" + echo "sudo apt-get install -y ./dist/${PKG_NAME}_${PKG_VERSION}.deb" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Build Configuration" >> "$GITHUB_STEP_SUMMARY" + echo "The distribution metadata for the Debian package is set during the build process. This ensures the package is automatically organized by distribution in the GAR repository." >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "### Setting Distribution in CMake (CPack)" >> "$GITHUB_STEP_SUMMARY" + echo "If using CMake with CPack, set the distribution in your \`CMakeLists.txt\`:" >> "$GITHUB_STEP_SUMMARY" + echo '```cmake' >> "$GITHUB_STEP_SUMMARY" + echo 'set(CPACK_DEBIAN_PACKAGE_DISTRIBUTION "buster") # or "bookworm"' >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "### Setting Distribution in Debian Control File" >> "$GITHUB_STEP_SUMMARY" + echo "Add the distribution to your \`debian/control\` file:" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "Distribution: buster" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "The package metadata is embedded in the .deb file and used by GAR to automatically organize packages by distribution, eliminating the need for manual distribution tagging during upload." >> "$GITHUB_STEP_SUMMARY" + - name: Dump GitHub context if: ${{ always() }} env: - GITHUB_CONTEXT: ${{ toJson(github) }} - JOB_CONTEXT: ${{ toJson(job) }} - STEPS_CONTEXT: ${{ toJson(steps) }} - RUNNER_CONTEXT: ${{ toJson(runner) }} + GITHUB_CONTEXT: ${{ toJson(github) }} + JOB_CONTEXT: ${{ toJson(job) }} + STEPS_CONTEXT: ${{ toJson(steps) }} + RUNNER_CONTEXT: ${{ toJson(runner) }} shell: bash run: | echo "${GITHUB_CONTEXT}" diff --git a/Dockerfiles/cloudsmith.Dockerfile b/Dockerfiles/cloudsmith.Dockerfile deleted file mode 100644 index 1c88b41..0000000 --- a/Dockerfiles/cloudsmith.Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM python:3.12.2-slim-bookworm - -LABEL Description="Image with Cloudsmith CLI tool" - -ENV PIP_REQUEREMENTS \ - cloudsmith-cli - -# install any python requirements -RUN pip3 install ${PIP_REQUEREMENTS} From 75da0c80e577ae4d386662a6da4c48da0740bbb6 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 12:15:14 +0100 Subject: [PATCH 02/16] [EMB-115] Implement SemVer 2.0 versioning strategy This commit refactors the CI versioning logic to fully support Semantic Versioning 2.0. The version generation logic has been extracted from the `prepare_env.yml` workflow into a dedicated, reusable, and testable shell script. Key changes include: - A new script `generate_semver_version.sh` that determines the `RELEASE_VERSION` and `RELEASE_REPO` based on the Git context (branch, tag, PR). - Support for `alpha`, `beta`, and `rc` pre-release tags, in addition to final release tags. - Automatic patch version bumping for development and nightly builds based on the latest Git tag. - Nightly builds for master branch merges now produce versions like `X.Y.Z-alpha.0+YYYYMMDD.branch.sha`. - Development builds for pull requests and feature branches generate versions like `X.Y.Z-alpha.0+sha`. - A comprehensive test script, `test_generate_semver_version.sh`, is introduced to validate the versioning logic across various scenarios. - The `prepare_env` workflow now includes a summary step that clearly documents the generated version, target repository, and the versioning strategy for better visibility in GitHub Actions. --- .github/workflows/prepare_env.yml | 147 +++++++++++----------- scripts/generate_semver_version.sh | 156 ++++++++++++++++++++++++ scripts/test_generate_semver_version.sh | 95 +++++++++++++++ 3 files changed, 322 insertions(+), 76 deletions(-) create mode 100755 scripts/generate_semver_version.sh create mode 100755 scripts/test_generate_semver_version.sh diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index ad66493..78161bd 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -2,10 +2,15 @@ on: workflow_call: inputs: BUILD_DOCKER_CACHE: - description: "If true, will build the docker image cache" + description: 'If true, will build the docker image cache' type: boolean required: false default: false + embedded_workflows_branch: + description: 'Branch to checkout embedded-workflows repo' + type: string + required: false + default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged outputs: RELEASE_VERSION: description: "The package version for this build" @@ -18,8 +23,10 @@ env: # considered a master branch, e.g. "master/s-line" starts with "master" # from the list below and it is then a master branch. MASTER_BRANCH_LIST: "main master stable" - VERSION_REGEX: 'v[0-9]{1,4}\.[0-9]{1,4}\.[0-9]{1,9}(-(dev|[0-9]{1,3}))?' - + # Regex to capture full prerelease identifiers including numeric suffixes + # Examples: v1.2.3, v1.2.3-alpha, v1.2.3-beta.1, v1.2.3-rc.2 + VERSION_REGEX: 'v[0-9]{1,4}\.[0-9]{1,4}\.[0-9]{1,9}(-(alpha|beta|rc)(\.?[0-9]+)?)?' + jobs: Prepare: name: 'Prepare Environment' @@ -27,90 +34,78 @@ jobs: outputs: RELEASE_VERSION: ${{ steps.vars.outputs.RELEASE_VERSION }} RELEASE_REPO: ${{ steps.vars.outputs.RELEASE_REPO }} - env: - CURRENT_BRANCH_REF: ${{ github.ref_type == 'branch' && github.ref || github.event.base_ref }} steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Remove PAT (limit long-lived secrets) + fetch-depth: 0 # Full history needed for git describe + submodules: recursive # Needed for docker cache build + + - name: Checkout Embedded-workflows repo + uses: actions/checkout@v4 + with: + repository: Ultimaker/embedded-workflows + path: embedded-workflows + ref: ${{ inputs.embedded_workflows_branch }} + - name: Generate Variables id: vars run: | - echo "GITHUB_REF ${GITHUB_REF}" - echo "GITHUB_REF_NAME ${GITHUB_REF_NAME}" - echo "GITHUB_REF_TYPE: ${GITHUB_REF_TYPE}" - RELEASE_VERSION="999.999.999" - RELEASE_REPO="none" + set -euo pipefail - # Check if this is a commit/tag in the master branch - IS_MASTER_BRANCH="no" - CURRENT_BRANCH="${CURRENT_BRANCH_REF##refs/heads/}" - for master_branch in $MASTER_BRANCH_LIST; do - # Try to remove "master_branch" from "CURRENT_BRANCH" and check if it changes - if [[ "${CURRENT_BRANCH##${master_branch}}" != "${CURRENT_BRANCH}" ]]; then - IS_MASTER_BRANCH="yes" - break - fi; - done; - echo "CURRENT_BRANCH: ${CURRENT_BRANCH}" - echo "IS_MASTER_BRANCH: ${IS_MASTER_BRANCH}" + # Use dedicated script for version generation + # This script can be tested locally and reused across workflows + VERSION_OUTPUT=$(./embedded-workflows/scripts/generate_semver_version.sh) - # Check what triggered this action: A Pull Request, a tag push or branch push - TRIGGER="pull_request" - if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then - if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then - TRIGGER="tag" - else - TRIGGER="branch" - fi - fi - - echo "TRIGGER: ${TRIGGER}" + # Parse output and set GitHub outputs + RELEASE_VERSION=$(echo "$VERSION_OUTPUT" | grep "^RELEASE_VERSION=" | cut -d= -f2) + RELEASE_REPO=$(echo "$VERSION_OUTPUT" | grep "^RELEASE_REPO=" | cut -d= -f2) - if [[ "${TRIGGER}" == "branch" && "${IS_MASTER_BRANCH}" == "yes" ]]; then - echo "This is a merge to master, lets make the Nightly Release" - - # Lets prepare the package name from the branch name. There are a few rules for debian package version and we must follow them: - # - Only lowercase letters: So lets convert all uppercase letter to lower case: - NIGHTLY_PACKAGE_VERSION_SUFFIX="${GITHUB_REF_NAME@L}" - - # - Other accepted chars: numbers (0-9), '.', '+' and '-'. Lets convert all other chars to '-': - NIGHTLY_PACKAGE_VERSION_SUFFIX=${NIGHTLY_PACKAGE_VERSION_SUFFIX//[^0-9a-z+.-]/-} - - echo "Nightly Package Version Suffix: '${NIGHTLY_PACKAGE_VERSION_SUFFIX}'" - RELEASE_REPO="nightly-builds" - RELEASE_VERSION="$(date +%Y.%m.%d)-merge-${NIGHTLY_PACKAGE_VERSION_SUFFIX}" - - elif [[ "${TRIGGER}" == "tag" ]]; then - echo "This is a tag push, lets parse the tag >${GITHUB_REF_NAME}< and check if we should release" - VERSION=$(echo "${GITHUB_REF_NAME}" | grep -o -E -e "${VERSION_REGEX}") || true # Return true if grep finds nothing - RELEASE_VERSION="${VERSION#v}" # Remove the initial "v" leaving only the numbers and optional "-dev" - - if [[ -z "${RELEASE_VERSION}" ]]; then - echo "Failed to parse the tag, it does not follow the standard" - elif [[ "${RELEASE_VERSION//dev/}" == "${RELEASE_VERSION}" ]]; then ## True if there is no "dev" in the tag - echo "Success, this is a official release" - RELEASE_REPO="packages-released" - else - echo "Success, this is a development release" - RELEASE_REPO="packages-dev" - fi - fi - - if [[ "${RELEASE_REPO}" == "none" ]]; then - echo "This is an ordinary commit, do not release" - RELEASE_VERSION="999.999.999" - fi - - echo "RELEASE_VERSION: ${RELEASE_VERSION}" - echo "RELEASE_REPO: ${RELEASE_REPO}" echo RELEASE_VERSION="${RELEASE_VERSION}" >> $GITHUB_OUTPUT echo RELEASE_REPO="${RELEASE_REPO}" >> $GITHUB_OUTPUT - - name: Checkout Repository - if: ${{ inputs.BUILD_DOCKER_CACHE }} - uses: actions/checkout@v4 - with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} - submodules: recursive + - name: Generate Summary + run: | + set -euo pipefail + + echo "# Environment Preparation Summary" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Release Information" >> "$GITHUB_STEP_SUMMARY" + echo "- **Version**: \`${{ steps.vars.outputs.RELEASE_VERSION }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Repository**: \`${{ steps.vars.outputs.RELEASE_REPO }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Trigger**: \`${GITHUB_EVENT_NAME}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Ref Type**: \`${GITHUB_REF_TYPE}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Ref Name**: \`${GITHUB_REF_NAME}\`" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Target GAR Repository" >> "$GITHUB_STEP_SUMMARY" + echo "The package will be published to: \`\${EMB_APT_REPO}-${{ steps.vars.outputs.RELEASE_REPO }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Version Strategy (SemVer 2.0)" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "All versions follow **Semantic Versioning 2.0**: \`major.minor.patch-prerelease.0+build_metadata\`" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "**Prerelease progression**: \`alpha\` → \`beta\` → \`rc\` → release" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + + if [[ "${{ steps.vars.outputs.RELEASE_REPO }}" == "nightly-builds" ]]; then + echo "- **Type**: Beta build from master branch merge" >> "$GITHUB_STEP_SUMMARY" + echo "- **Format**: \`X.Y.Z-beta.0+YYYYMMDD.branch.sha\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Example**: \`1.2.4-beta.0+20260118.main.abc1234\`" >> "$GITHUB_STEP_SUMMARY" + elif [[ "${{ steps.vars.outputs.RELEASE_REPO }}" == "packages-released" ]]; then + echo "- **Type**: Official release from git tag" >> "$GITHUB_STEP_SUMMARY" + echo "- **Format**: \`X.Y.Z+sha\` (from tag \`vX.Y.Z\`)" >> "$GITHUB_STEP_SUMMARY" + echo "- **Example**: \`1.2.3+abc1234\`" >> "$GITHUB_STEP_SUMMARY" + elif [[ "${{ steps.vars.outputs.RELEASE_REPO }}" == "packages-dev" ]]; then + echo "- **Type**: Development/pre-release" >> "$GITHUB_STEP_SUMMARY" + echo "- **Format**: \`X.Y.Z-{alpha|beta|rc}.0+sha\` (auto-bumped from latest tag)" >> "$GITHUB_STEP_SUMMARY" + echo "- **Example**: \`1.2.4-alpha.0+abc1234\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Supported tags**: \`vX.Y.Z-alpha\`, \`vX.Y.Z-beta\`, \`vX.Y.Z-rc\`, \`vX.Y.Z-dev\`" >> "$GITHUB_STEP_SUMMARY" + fi + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "**Build metadata** (\`+...\`) includes commit SHA for full traceability." >> "$GITHUB_STEP_SUMMARY" + # TODO: Use Workload Identity Pool - name: "Build Docker Image Cache" if: ${{ inputs.BUILD_DOCKER_CACHE }} run: | diff --git a/scripts/generate_semver_version.sh b/scripts/generate_semver_version.sh new file mode 100755 index 0000000..1157a39 --- /dev/null +++ b/scripts/generate_semver_version.sh @@ -0,0 +1,156 @@ +#!/bin/bash +# Generate SemVer 2.0 compliant version based on git history and context +# Usage: generate_semver_version.sh [options] +# +# This script generates semantic versions following SemVer 2.0: +# Format: major.minor.patch-prerelease.0+build_metadata +# +# Prerelease progression: alpha → beta → rc → release +# +# Environment variables: +# GITHUB_EVENT_NAME - Type of GitHub event (push, pull_request, etc.) +# GITHUB_REF_TYPE - Type of ref (tag, branch) +# GITHUB_REF_NAME - Name of the ref (tag name or branch name) +# GITHUB_REF - Full ref (refs/heads/main, refs/tags/v1.0.0) +# GITHUB_EVENT_BASE_REF - Base ref for pull requests +# MASTER_BRANCH_LIST - Space-separated list of master branch prefixes +# +# Outputs (printed to stdout in key=value format): +# RELEASE_VERSION=X.Y.Z-pre.0+metadata +# RELEASE_REPO=repository-name + +set -euo pipefail + +# Default values +MASTER_BRANCH_LIST="${MASTER_BRANCH_LIST:-main master stable}" +# Regex to capture full prerelease identifiers including numeric suffixes +# Examples: v1.2.3, v1.2.3-alpha, v1.2.3-beta.1, v1.2.3-rc.2 +VERSION_REGEX='v[0-9]{1,4}\.[0-9]{1,4}\.[0-9]{1,9}(-(alpha|beta|rc)(\.?[0-9]+)?)?' + +# Function to get latest version from git tags and bump patch +# Returns version in format: major.minor.patch +get_bumped_version() { + # Find the latest semver tag reachable from current branch + LATEST_TAG=$(git describe --tags --abbrev=0 --match "v[0-9]*.[0-9]*.[0-9]*" 2>/dev/null || echo "v0.0.0") + echo "Latest reachable tag: ${LATEST_TAG}" >&2 + + # Parse the version (remove 'v' prefix and any suffix) + VERSION_ONLY="${LATEST_TAG#v}" + VERSION_ONLY="${VERSION_ONLY%%-*}" # Remove anything after first dash + + # Split into major.minor.patch + IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION_ONLY" + MAJOR=${MAJOR:-0} + MINOR=${MINOR:-0} + PATCH=${PATCH:-0} + + # Bump patch version + PATCH=$((PATCH + 1)) + + echo "${MAJOR}.${MINOR}.${PATCH}" +} + +# Main logic +main() { + echo "=== SemVer Version Generator ===" >&2 + echo "GITHUB_REF: ${GITHUB_REF:-}" >&2 + echo "GITHUB_REF_NAME: ${GITHUB_REF_NAME:-}" >&2 + echo "GITHUB_REF_TYPE: ${GITHUB_REF_TYPE:-}" >&2 + echo "GITHUB_EVENT_NAME: ${GITHUB_EVENT_NAME:-}" >&2 + + # Get short commit SHA for build metadata + COMMIT_SHA=$(git rev-parse --short HEAD) + echo "COMMIT_SHA: ${COMMIT_SHA}" >&2 + + # Determine current branch for master branch detection + if [[ "${GITHUB_REF_TYPE:-branch}" == "branch" ]]; then + CURRENT_BRANCH_REF="${GITHUB_REF:-refs/heads/main}" + else + CURRENT_BRANCH_REF="${GITHUB_EVENT_BASE_REF:-refs/heads/main}" + fi + + # Check if this is a commit/tag in the master branch + IS_MASTER_BRANCH="no" + CURRENT_BRANCH="${CURRENT_BRANCH_REF##refs/heads/}" + for master_branch in $MASTER_BRANCH_LIST; do + # Try to remove "master_branch" from "CURRENT_BRANCH" and check if it changes + if [[ "${CURRENT_BRANCH##${master_branch}}" != "${CURRENT_BRANCH}" ]]; then + IS_MASTER_BRANCH="yes" + break + fi + done + echo "CURRENT_BRANCH: ${CURRENT_BRANCH}" >&2 + echo "IS_MASTER_BRANCH: ${IS_MASTER_BRANCH}" >&2 + + # Check what triggered this action: A Pull Request, a tag push or branch push + TRIGGER="pull_request" + if [[ "${GITHUB_EVENT_NAME:-pull_request}" == "push" ]]; then + if [[ "${GITHUB_REF_TYPE:-branch}" == "tag" ]]; then + TRIGGER="tag" + else + TRIGGER="branch" + fi + fi + echo "TRIGGER: ${TRIGGER}" >&2 + + RELEASE_VERSION="" + RELEASE_REPO="packages-dev" # Default to packages-dev, always release at minimum here + + if [[ "${TRIGGER}" == "branch" && "${IS_MASTER_BRANCH}" == "yes" ]]; then + echo "This is a merge to master, lets make the Alpha nightly build" >&2 + + # Get base version and prepare alpha nightly build following SemVer 2.0 + BASE_VERSION=$(get_bumped_version) + + # Prepare the branch name suffix following Debian and SemVer rules + # - Only lowercase letters, numbers, '.', '+' and '-' + BRANCH_SUFFIX="${GITHUB_REF_NAME@L}" + BRANCH_SUFFIX=${BRANCH_SUFFIX//[^0-9a-z+.-]/-} + + # SemVer 2.0 format: major.minor.patch-prerelease.0+build_metadata + # Nightly alpha format: X.Y.Z-alpha.0+YYYYMMDD.branch.sha + BUILD_DATE=$(date +%Y%m%d) + RELEASE_REPO="nightly-builds" + RELEASE_VERSION="${BASE_VERSION}-alpha.0+${BUILD_DATE}.${BRANCH_SUFFIX}.${COMMIT_SHA}" + + echo "Alpha nightly build version: '${RELEASE_VERSION}'" >&2 + + elif [[ "${TRIGGER}" == "tag" ]]; then + echo "This is a tag push, lets parse the tag >${GITHUB_REF_NAME}< and check if we should release" >&2 + VERSION=$(echo "${GITHUB_REF_NAME}" | grep -o -E -e "${VERSION_REGEX}") || true # Return true if grep finds nothing + RELEASE_VERSION="${VERSION#v}" # Remove the initial "v" leaving only the numbers and optional "-alpha", "-beta", "-rc" + + if [[ -z "${RELEASE_VERSION}" ]]; then + echo "Failed to parse the tag, it does not follow the standard" >&2 + # Fall through to generate version from git tags + elif [[ "${RELEASE_VERSION}" =~ (alpha|beta|rc) ]]; then + echo "Success, this is a pre-release (alpha/beta/rc)" >&2 + RELEASE_REPO="packages-dev" + # Tag-based releases use the tag as-is, no build metadata added + else + echo "Success, this is an official release" >&2 + RELEASE_REPO="packages-released" + # Tag-based releases use the tag as-is, no build metadata added + fi + fi + + # If no valid version yet, generate one from git history + # SemVer 2.0 format: major.minor.patch-pre.0+build_metadata + if [[ -z "${RELEASE_VERSION}" ]]; then + echo "Generating version from git tags following SemVer 2.0" >&2 + BASE_VERSION=$(get_bumped_version) + # Format: X.Y.Z-alpha.0+sha (alpha is earliest pre-release stage) + RELEASE_VERSION="${BASE_VERSION}-alpha.0+${COMMIT_SHA}" + echo "Generated version: ${RELEASE_VERSION}" >&2 + fi + + echo "RELEASE_VERSION: ${RELEASE_VERSION}" >&2 + echo "RELEASE_REPO: ${RELEASE_REPO}" >&2 + + # Output in format that can be sourced or parsed + echo "RELEASE_VERSION=${RELEASE_VERSION}" + echo "RELEASE_REPO=${RELEASE_REPO}" +} + +# Run main function +main "$@" diff --git a/scripts/test_generate_semver_version.sh b/scripts/test_generate_semver_version.sh new file mode 100755 index 0000000..1df3beb --- /dev/null +++ b/scripts/test_generate_semver_version.sh @@ -0,0 +1,95 @@ +#!/bin/bash +# Test script for generate_semver_version.sh +# Demonstrates different scenarios and how to test locally + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VERSION_SCRIPT="${SCRIPT_DIR}/generate_semver_version.sh" + +echo "==========================================" +echo "Testing SemVer Version Generation Script" +echo "==========================================" +echo "" + +# Test 1: Pull Request / Regular Commit (alpha version) +echo "Test 1: Pull Request / Regular Commit" +echo "Expected: X.Y.Z-alpha.0+sha" +export GITHUB_EVENT_NAME="pull_request" +export GITHUB_REF_TYPE="branch" +export GITHUB_REF_NAME="feature/test" +export GITHUB_REF="refs/heads/feature/test" +unset GITHUB_EVENT_BASE_REF +OUTPUT=$("$VERSION_SCRIPT") +echo "$OUTPUT" +echo "" + +# Test 2: Master Branch Merge (nightly build - alpha) +echo "Test 2: Master Branch Merge (nightly build - alpha)" +echo "Expected: X.Y.Z-alpha.0+YYYYMMDD.main.sha" +export GITHUB_EVENT_NAME="push" +export GITHUB_REF_TYPE="branch" +export GITHUB_REF_NAME="main" +export GITHUB_REF="refs/heads/main" +OUTPUT=$("$VERSION_SCRIPT") +echo "$OUTPUT" +echo "" + +# Test 3: Official Release Tag +echo "Test 3: Official Release Tag" +echo "Expected: X.Y.Z" +export GITHUB_EVENT_NAME="push" +export GITHUB_REF_TYPE="tag" +export GITHUB_REF_NAME="v1.2.3" +export GITHUB_REF="refs/tags/v1.2.3" +OUTPUT=$("$VERSION_SCRIPT") +echo "$OUTPUT" +echo "" + +# Test 4: Alpha Release Tag +echo "Test 4: Alpha Release Tag" +echo "Expected: X.Y.Z-alpha" +export GITHUB_EVENT_NAME="push" +export GITHUB_REF_TYPE="tag" +export GITHUB_REF_NAME="v1.2.3-alpha" +export GITHUB_REF="refs/tags/v1.2.3-alpha" +OUTPUT=$("$VERSION_SCRIPT") +echo "$OUTPUT" +echo "" + +# Test 5: Beta Release Tag +echo "Test 5: Beta Release Tag" +echo "Expected: X.Y.Z-beta.1" +export GITHUB_EVENT_NAME="push" +export GITHUB_REF_TYPE="tag" +export GITHUB_REF_NAME="v1.2.3-beta.1" +export GITHUB_REF="refs/tags/v1.2.3-beta.1" +OUTPUT=$("$VERSION_SCRIPT") +echo "$OUTPUT" +echo "" + +# Test 6: Release Candidate Tag +echo "Test 6: Release Candidate Tag" +echo "Expected: X.Y.Z-rc.1" +export GITHUB_EVENT_NAME="push" +export GITHUB_REF_TYPE="tag" +export GITHUB_REF_NAME="v1.2.3-rc.1" +export GITHUB_REF="refs/tags/v1.2.3-rc.1" +OUTPUT=$("$VERSION_SCRIPT") +echo "$OUTPUT" +echo "" + +# Test 7: Invalid Tag Format +echo "Test 7: Invalid Tag Format (should fall back to alpha)" +echo "Expected: X.Y.Z-alpha.0+sha" +export GITHUB_EVENT_NAME="push" +export GITHUB_REF_TYPE="tag" +export GITHUB_REF_NAME="random-tag" +export GITHUB_REF="refs/tags/random-tag" +OUTPUT=$("$VERSION_SCRIPT") +echo "$OUTPUT" +echo "" + +echo "==========================================" +echo "All tests completed!" +echo "==========================================" From e99b251503f52e9987457351140be3e6890eea08 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 12:18:57 +0100 Subject: [PATCH 03/16] [EMB-115] Document SemVer generation script and workflow --- scripts/generate_semver_version.md | 446 +++++++++++++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 scripts/generate_semver_version.md diff --git a/scripts/generate_semver_version.md b/scripts/generate_semver_version.md new file mode 100644 index 0000000..9c7b642 --- /dev/null +++ b/scripts/generate_semver_version.md @@ -0,0 +1,446 @@ +# SemVer Version Generation Scripts + +This directory contains scripts for generating Semantic Versioning 2.0 compliant version numbers based on git history and workflow context. + +## Understanding Semantic Versioning 2.0 + +[Semantic Versioning 2.0](https://semver.org/) is a versioning scheme that conveys meaning about the underlying changes. + +### Version Format + +``` +major.minor.patch-prerelease+build_metadata +``` + +**Core Version** (`major.minor.patch`): +- **MAJOR**: Incremented for incompatible API changes +- **MINOR**: Incremented for backward-compatible new functionality +- **PATCH**: Incremented for backward-compatible bug fixes + +**Prerelease** (`-alpha`, `-beta`, `-rc`): +- Optional identifier indicating the software is not yet stable +- Prerelease versions have **lower precedence** than the associated release +- Examples: `1.0.0-alpha.1`, `1.0.0-beta.2`, `1.0.0-rc.1` + +**Build Metadata** (`+sha`, `+date.branch.sha`): +- Optional metadata for traceability (commit SHA, build date, etc.) +- Ignored when determining version precedence +- Examples: `1.0.0+abc1234`, `1.0.0-beta.1+20260118.main.abc1234` + +### Prerelease Types and When to Use Them + +This project follows a standard prerelease progression: + +**`alpha`** → **`beta`** → **`rc`** (release candidate) → **release** + +#### Alpha (α) +- **Purpose**: Early testing, unstable, may have incomplete features +- **Audience**: Internal developers, early testers, continuous integration +- **Changes**: Frequent breaking changes expected +- **Example**: `1.2.0-alpha.0+abc1234` or `1.2.4-alpha.0+20260118.main.abc1234` +- **When generated**: + - Pull requests and feature branch commits + - **Automatic nightly builds from master/main branch merges** (with date and branch metadata) + - Invalid or unparseable tags + - Default for any commit not on a release branch +- **Note**: Auto-generated alpha versions use a naive patch bump (e.g., `v1.2.3` → `1.2.4-alpha.0`), regardless of whether the actual changes warrant a major or minor version increment. This is intentional - nightly builds from master represent ongoing development, not stabilization. For intentional version control or beta/RC releases, create an explicit git tag. + +#### Beta (β) +- **Purpose**: Feature-complete but may have bugs, ready for wider testing +- **Audience**: Beta testers, QA teams +- **Changes**: API should be stable, only bug fixes and minor tweaks +- **Example**: `1.2.0-beta.1` +- **When generated**: + - **Explicit release branches only**: Tags like `v1.2.0-beta` or `v1.2.0-beta.1` + - **NOT from automatic master/main merges** - Those use alpha instead +- **Note**: Beta versions require explicit git tags on release branches. Auto-generated builds from master/main branch merges use alpha (see below) to indicate ongoing development, not stabilization. + +#### RC (Release Candidate) +- **Purpose**: Potentially final version, undergoing final validation +- **Audience**: QA teams, staging environments +- **Changes**: Only critical bug fixes, no new features +- **Example**: `1.2.0-rc.1+abc1234` +- **When generated**: + - Tags like `v1.2.0-rc.1`, `v1.2.0-rc.2` + - Indicates release is imminent + +#### Release +- **Purpose**: Stable, production-ready version +- **Audience**: All users, production environments +- **Changes**: No breaking changes within same major version +- **Example**: `1.2.0+abc1234` +- **When generated**: + - Tags like `v1.2.0`, `v2.0.0` (no prerelease suffix) + +### Version Precedence Examples + +According to SemVer 2.0, versions are compared as follows: + +``` +1.0.0-alpha.0 < 1.0.0-alpha.1 < 1.0.0-beta.0 < 1.0.0-beta.1 < 1.0.0-rc.1 < 1.0.0 < 1.0.1 +``` + +Key points: +- Prerelease versions always sort **before** the associated release +- Numeric suffixes are compared numerically: `alpha.0` < `alpha.1` < `alpha.10` +- Build metadata (`+...`) does **not** affect precedence + +## Scripts + +### `generate_semver_version.sh` + +Main script that generates version numbers following SemVer 2.0 format. + +#### Usage + +```bash +./generate_semver_version.sh +``` + +The script reads environment variables and outputs version information to stdout in `key=value` format: + +``` +RELEASE_VERSION=1.2.4-alpha.0+abc1234 +RELEASE_REPO=packages-dev +``` + +#### Environment Variables + +Required for GitHub Actions context: +- `GITHUB_EVENT_NAME` - Type of GitHub event (push, pull_request, etc.) +- `GITHUB_REF_TYPE` - Type of ref (tag, branch) +- `GITHUB_REF_NAME` - Name of the ref (tag name or branch name) +- `GITHUB_REF` - Full ref (refs/heads/main, refs/tags/v1.0.0) +- `GITHUB_EVENT_BASE_REF` - Base ref for pull requests + +Optional: +- `MASTER_BRANCH_LIST` - Space-separated list of master branch prefixes (default: "main master stable") + +#### Version Generation Logic + +The script automatically determines the appropriate version based on the Git context: + +| Trigger | Condition | Version Format | Example | Repository | Use Case | +|---------|-----------|----------------|---------|------------|----------| +| Pull Request | Any feature branch | `X.Y.Z-alpha.0+sha` | `1.2.4-alpha.0+abc1234` | `packages-dev` | Development testing | +| Push | Master/main branch | `X.Y.Z-alpha.0+YYYYMMDD.branch.sha` | `1.2.4-alpha.0+20260118.main.abc1234` | `nightly-builds` | Nightly/continuous builds | +| Tag | `vX.Y.Z-alpha` | `X.Y.Z-alpha` | `1.2.3-alpha` | `packages-dev` | Early alpha release | +| Tag | `vX.Y.Z-alpha.N` | `X.Y.Z-alpha.N` | `1.2.3-alpha.2` | `packages-dev` | Specific alpha iteration | +| Tag | `vX.Y.Z-beta` | `X.Y.Z-beta` | `1.2.3-beta` | `packages-dev` | Beta release (release branch) | +| Tag | `vX.Y.Z-beta.N` | `X.Y.Z-beta.N` | `1.2.3-beta.1` | `packages-dev` | Specific beta iteration (release branch) | +| Tag | `vX.Y.Z-rc.N` | `X.Y.Z-rc.N` | `1.2.3-rc.1` | `packages-dev` | Release candidate | +| Tag | `vX.Y.Z` | `X.Y.Z` | `1.2.3` | `packages-released` | Official release | +| Invalid tag | Any unparseable tag | `X.Y.Z-alpha.0+sha` | `1.2.4-alpha.0+abc1234` | `packages-dev` | Fallback for invalid tags | + +**Note**: `X.Y.Z` is automatically determined by finding the latest reachable semver tag and bumping the patch version. + +⚠️ **Version Bumping Strategy**: During development (PRs, feature branches, and master merges), this script performs a **naive patch version bump** from the latest tag. This means even if you're working on a major or minor feature release, the auto-generated version will only bump the patch number (e.g., `v1.2.3` → `1.2.4-alpha.0+sha`). This is a limitation of the automated approach - the script cannot determine the context or scope of the work being done. For proper major/minor version increments, you must create an explicit git tag with the desired version number. + +## Release Workflow: When to Use Alpha vs Beta + +To understand when versions are alpha vs beta, think about the **development stage**: + +``` +Development Branch/PR → Nightly Master Merge → Release Branch/Tag + (alpha) (alpha) (beta/rc/release) +``` + +### Development Stage Progression + +1. **Feature Development** (`alpha`) + - Feature branches, pull requests + - Any commit not on master/release branch + - Version: `X.Y.Z-alpha.0+sha` + - Repository: `packages-dev` + - When to use: Normal development work + +2. **Nightly/Continuous Builds** (`alpha`) + - Automatic builds when PR is merged to master/main + - Represents latest development state + - Version: `X.Y.Z-alpha.0+YYYYMMDD.branch.sha` + - Repository: `nightly-builds` + - When to use: Integration testing, CI validation + - **Note**: Still alpha because master is the development branch, not a release candidate + +3. **Beta/Release Preparation** (`beta`, `rc`) + - **Only happens via explicit git tags** on release branches + - Beta: `vX.Y.Z-beta.1` → `X.Y.Z-beta.1` + - RC: `vX.Y.Z-rc.1` → `X.Y.Z-rc.1` + - Repository: `packages-dev` + - When to use: When preparing for a release, create a release branch and tag it with the desired version + +4. **Official Release** (`release`) + - **Only happens via explicit git tag** with no prerelease suffix + - Version: `vX.Y.Z` → `X.Y.Z` + - Repository: `packages-released` + - When to use: When release is validated and approved + +#### Workflow Decision Tree + +```mermaid +graph TD + A[Git Event: push/PR/tag] --> B{Event Type?} + + B -->|Pull Request| C[PR/Feature Branch] + B -->|Push to Branch| D{Master Branch?} + B -->|Tag Push| E{Valid Tag?} + + C --> F[Generate: X.Y.Z-alpha.0+sha] + F --> G[Repository: packages-dev] + + D -->|Yes| H[Master Merge] + D -->|No| C + + H --> I[Generate: X.Y.Z-alpha.0+YYYYMMDD.branch.sha] + I --> J[Repository: nightly-builds] + + E -->|No - Invalid| K[Fallback to alpha] + K --> F + + E -->|Yes| L{Tag Type?} + + L -->|vX.Y.Z| M[Official Release] + L -->|vX.Y.Z-alpha
vX.Y.Z-beta| N[Prerelease] + L -->|vX.Y.Z-rc.N| O[Release Candidate] + + M --> P[Generate: X.Y.Z] + P --> Q[Repository: packages-released] + + N --> R[Generate: X.Y.Z-prerelease] + R --> S[Repository: packages-dev] + + O --> T[Generate: X.Y.Z-rc.N] + T --> S + + style F fill:#fff3cd + style I fill:#fff3cd + style P fill:#d1e7dd + style R fill:#fff3cd + style T fill:#f8d7da +``` + +**Legend:** +- 🟡 Alpha versions (development & nightly builds) +- 🟢 Release versions (stable) +- 🔴 RC versions (release candidates) + +#### Target Repositories + +The `RELEASE_REPO` output determines which Google Artifact Registry repository the package will be published to: + +- **`packages-dev`**: Development and prerelease versions (alpha, beta, rc) +- **`nightly-builds`**: Automated builds from master branch merges +- **`packages-released`**: Official stable releases (no prerelease suffix) + +Combined with `EMB_APT_REPO` organization variable, the full repository name becomes: +``` +${EMB_APT_REPO}-${RELEASE_REPO} +``` +Example: `um-embedded-apt-packages-dev`, `um-embedded-apt-packages-released` + +### `test_generate_semver_version.sh` + +Test script that demonstrates various scenarios and validates the version generation logic. + +#### Usage + +```bash +cd /path/to/embedded-workflows/scripts +./test_generate_semver_version.sh +``` + +The script will run through 7 different test scenarios: +1. Pull Request / Regular Commit (alpha version) +2. Master Branch Merge (beta version) +3. Official Release Tag +4. Alpha Release Tag +5. Beta Release Tag +6. Release Candidate Tag +7. Invalid Tag Format (fallback to alpha) + +#### Example Output + +``` +========================================== +Testing SemVer Version Generation Script +========================================== + +Test 1: Pull Request / Regular Commit +Expected: X.Y.Z-alpha.0+sha +RELEASE_VERSION=1.2.4-alpha.0+abc1234 +RELEASE_REPO=packages-dev + +Test 2: Master Branch Merge +Expected: X.Y.Z-beta.0+YYYYMMDD.main.sha +RELEASE_VERSION=1.2.4-beta.0+20260118.main.abc1234 +RELEASE_REPO=nightly-builds + +... +``` + +## Local Testing + +To test the version generation locally outside of GitHub Actions: + +```bash +# Navigate to the scripts directory +cd /path/to/embedded-workflows/scripts + +# Set up minimal environment +export GITHUB_EVENT_NAME="pull_request" +export GITHUB_REF_TYPE="branch" +export GITHUB_REF_NAME="feature/my-feature" +export GITHUB_REF="refs/heads/feature/my-feature" + +# Run the script +./generate_semver_version.sh + +# Or run the full test suite +./test_generate_semver_version.sh +``` + +## Practical Examples + +### Example 1: Feature Development Workflow + +```bash +# Developer working on feature branch +git checkout -b feature/new-api +git commit -m "Add new API endpoint" +git push origin feature/new-api + +# Opens PR → Triggers workflow +# Generated version: 1.2.4-alpha.0+abc1234 +# Published to: um-embedded-apt-packages-dev +``` + +### Example 2: Master Branch Integration (Nightly Build) + +```bash +# PR merged to main +git checkout main +git merge feature/new-api +git push origin main + +# Push to main → Triggers workflow (nightly build) +# Generated version: 1.2.4-alpha.0+20260118.main.def5678 +# Published to: um-embedded-apt-nightly-builds +# Note: Uses alpha, not beta - nightly builds from master represent ongoing development +``` + +### Example 3: Releasing a Beta + +```bash +# Team decides to create a beta release +git tag v1.3.0-beta.1 +git push origin v1.3.0-beta.1 + +# Tag push → Triggers workflow +# Generated version: 1.3.0-beta.1 +# Published to: um-embedded-apt-packages-dev +``` + +### Example 4: Release Candidate + +```bash +# Beta testing complete, creating RC +git tag v1.3.0-rc.1 +git push origin v1.3.0-rc.1 + +# Tag push → Triggers workflow +# Generated version: 1.3.0-rc.1 +# Published to: um-embedded-apt-packages-dev +``` + +### Example 5: Official Release + +```bash +# RC validated, creating official release +git tag v1.3.0 +git push origin v1.3.0 + +# Tag push → Triggers workflow +# Generated version: 1.3.0 +# Published to: um-embedded-apt-packages-released +``` + +### Example 6: Hotfix Release + +```bash +# Critical bug found in production +git checkout v1.3.0 +git checkout -b hotfix/critical-bug +git commit -m "Fix critical security issue" +git tag v1.3.1 +git push origin v1.3.1 + +# Tag push → Triggers workflow +# Generated version: 1.3.1 +# Published to: um-embedded-apt-packages-released +``` + +## Local Testing + +To test the version generation locally outside of GitHub Actions: + +```bash +# Navigate to the scripts directory +cd /path/to/embedded-workflows/scripts + +# Set up minimal environment +export GITHUB_EVENT_NAME="pull_request" +export GITHUB_REF_TYPE="branch" +export GITHUB_REF_NAME="feature/my-feature" +export GITHUB_REF="refs/heads/feature/my-feature" + +# Run the script +./generate_semver_version.sh + +# Or run the full test suite +./test_generate_semver_version.sh +``` + +## Integration with Workflows + +The script is used in GitHub Actions workflows via: + +```yaml +- name: Generate Variables + id: vars + run: | + set -euo pipefail + + # Use dedicated script for version generation + VERSION_OUTPUT=$(./embedded-workflows/scripts/generate_semver_version.sh) + + # Parse output and set GitHub outputs + RELEASE_VERSION=$(echo "$VERSION_OUTPUT" | grep "^RELEASE_VERSION=" | cut -d= -f2) + RELEASE_REPO=$(echo "$VERSION_OUTPUT" | grep "^RELEASE_REPO=" | cut -d= -f2) + + echo RELEASE_VERSION="${RELEASE_VERSION}" >> $GITHUB_OUTPUT + echo RELEASE_REPO="${RELEASE_REPO}" >> $GITHUB_OUTPUT +``` + +## SemVer 2.0 Compliance + +All versions follow [Semantic Versioning 2.0](https://semver.org/): + +- **Major.Minor.Patch**: Core version numbers +- **Prerelease identifiers**: `-alpha`, `-beta`, `-rc` (with numeric suffixes like `.0`, `.1`) +- **Build metadata**: `+sha` or `+date.branch.sha` for traceability + +### Version Precedence + +``` +1.2.3-alpha.0 < 1.2.3-alpha.1 < 1.2.3-beta.0 < 1.2.3-rc.1 < 1.2.3 +``` + +Prerelease versions have lower precedence than the associated normal version. + +## Benefits + +1. **Testable**: Can be run and validated locally without GitHub Actions +2. **Reusable**: Same script can be used across multiple workflows +3. **Maintainable**: Logic is centralized in one place +4. **Standards-compliant**: Strictly follows SemVer 2.0 +5. **Traceable**: Every version includes commit SHA in build metadata From fd3a250c495ef4bcaf283c7cbb359983d7d9480a Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 12:27:15 +0100 Subject: [PATCH 04/16] [EMB-115] Remove versioning strategy from job summary --- .github/workflows/prepare_env.yml | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index 78161bd..aac7dbe 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -77,33 +77,6 @@ jobs: echo "- **Trigger**: \`${GITHUB_EVENT_NAME}\`" >> "$GITHUB_STEP_SUMMARY" echo "- **Ref Type**: \`${GITHUB_REF_TYPE}\`" >> "$GITHUB_STEP_SUMMARY" echo "- **Ref Name**: \`${GITHUB_REF_NAME}\`" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "## Target GAR Repository" >> "$GITHUB_STEP_SUMMARY" - echo "The package will be published to: \`\${EMB_APT_REPO}-${{ steps.vars.outputs.RELEASE_REPO }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "## Version Strategy (SemVer 2.0)" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "All versions follow **Semantic Versioning 2.0**: \`major.minor.patch-prerelease.0+build_metadata\`" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "**Prerelease progression**: \`alpha\` → \`beta\` → \`rc\` → release" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - - if [[ "${{ steps.vars.outputs.RELEASE_REPO }}" == "nightly-builds" ]]; then - echo "- **Type**: Beta build from master branch merge" >> "$GITHUB_STEP_SUMMARY" - echo "- **Format**: \`X.Y.Z-beta.0+YYYYMMDD.branch.sha\`" >> "$GITHUB_STEP_SUMMARY" - echo "- **Example**: \`1.2.4-beta.0+20260118.main.abc1234\`" >> "$GITHUB_STEP_SUMMARY" - elif [[ "${{ steps.vars.outputs.RELEASE_REPO }}" == "packages-released" ]]; then - echo "- **Type**: Official release from git tag" >> "$GITHUB_STEP_SUMMARY" - echo "- **Format**: \`X.Y.Z+sha\` (from tag \`vX.Y.Z\`)" >> "$GITHUB_STEP_SUMMARY" - echo "- **Example**: \`1.2.3+abc1234\`" >> "$GITHUB_STEP_SUMMARY" - elif [[ "${{ steps.vars.outputs.RELEASE_REPO }}" == "packages-dev" ]]; then - echo "- **Type**: Development/pre-release" >> "$GITHUB_STEP_SUMMARY" - echo "- **Format**: \`X.Y.Z-{alpha|beta|rc}.0+sha\` (auto-bumped from latest tag)" >> "$GITHUB_STEP_SUMMARY" - echo "- **Example**: \`1.2.4-alpha.0+abc1234\`" >> "$GITHUB_STEP_SUMMARY" - echo "- **Supported tags**: \`vX.Y.Z-alpha\`, \`vX.Y.Z-beta\`, \`vX.Y.Z-rc\`, \`vX.Y.Z-dev\`" >> "$GITHUB_STEP_SUMMARY" - fi - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "**Build metadata** (\`+...\`) includes commit SHA for full traceability." >> "$GITHUB_STEP_SUMMARY" # TODO: Use Workload Identity Pool - name: "Build Docker Image Cache" From 371dc23fbf4ec34ae13b1bb9613b7be2fe535750 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 12:30:15 +0100 Subject: [PATCH 05/16] [EMB-115] Search for deb packages in root instead of dist The `release_pkg` workflow was hardcoded to find `.deb` packages in the `./dist` directory. This change removes that assumption, making the workflow search for packages in the current working directory. This allows for more flexibility in the preceding build job, which no longer needs to place artifacts in a `dist` folder. The documentation in the job summary has been updated accordingly. --- .github/workflows/release_pkg.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release_pkg.yml b/.github/workflows/release_pkg.yml index a00276a..517513a 100644 --- a/.github/workflows/release_pkg.yml +++ b/.github/workflows/release_pkg.yml @@ -48,9 +48,9 @@ jobs: run: | set -euo pipefail # Find all .deb files and store the first one for summary - DEB_FILE=$(find ./dist -name "*.deb" -print -quit) + DEB_FILE=$(find . -name "*.deb" -print -quit) if [[ -z "${DEB_FILE:-}" ]]; then - echo "No .deb file found in ./dist" >&2 + echo "No .deb file found in ." >&2 exit 1 fi echo "deb_file=${DEB_FILE}" >> "$GITHUB_OUTPUT" @@ -67,8 +67,7 @@ jobs: id: publish run: | set -euo pipefail - # Upload all .deb packages found in ./dist - cd ./dist + # Upload all .deb packages found in . for package in *.deb; do if [[ -f "$package" ]]; then echo "Uploading ${package} to GAR..." @@ -136,7 +135,7 @@ jobs: echo "" >> "$GITHUB_STEP_SUMMARY" echo "5. Alternatively, install directly from the downloaded .deb file:" >> "$GITHUB_STEP_SUMMARY" echo '```bash' >> "$GITHUB_STEP_SUMMARY" - echo "sudo apt-get install -y ./dist/${PKG_NAME}_${PKG_VERSION}.deb" >> "$GITHUB_STEP_SUMMARY" + echo "sudo apt-get install -y ./${PKG_NAME}_${PKG_VERSION}.deb" >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "## Build Configuration" >> "$GITHUB_STEP_SUMMARY" From fded57c239c9db2f2e3adc4a675ff4dbabf24a8b Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 12:52:53 +0100 Subject: [PATCH 06/16] [EMB-115] Remove GH Action Debug steps Not needed anymore and only clutter the logs --- .github/workflows/build.yml | 19 +++---------------- .github/workflows/prepare_env.yml | 13 ------------- .github/workflows/release_docker_img.yml | 12 ++---------- .github/workflows/release_pkg.yml | 14 -------------- .github/workflows/shellcheck.yml | 4 ++-- .github/workflows/unit_test.yml | 15 +-------------- 6 files changed, 8 insertions(+), 69 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 649a694..2afdf12 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,9 +16,9 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} + token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Remove PAT (limit long-lived secrets) submodules: recursive - + - name: Build Package id: build run: | @@ -35,22 +35,9 @@ jobs: echo "RELEASE_VERSION: ${RELEASE_VERSION}" ./build_for_ultimaker.sh -a build - + - name: Upload Artifact (Built package) uses: actions/upload-artifact@v4 with: name: build-package path: "./*.deb" - - - name: Dump GitHub context - if: ${{ always() }} - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - JOB_CONTEXT: ${{ toJson(job) }} - STEPS_CONTEXT: ${{ toJson(steps) }} - RUNNER_CONTEXT: ${{ toJson(runner) }} - run: | - echo "${GITHUB_CONTEXT}" - echo "${JOB_CONTEXT}" - echo "${STEPS_CONTEXT}" - echo "${RUNNER_CONTEXT}" diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index aac7dbe..fb0093f 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -84,16 +84,3 @@ jobs: run: | echo "${{ secrets.ULTIMAKER_CI_PAT }}" | docker login ghcr.io -u $ --password-stdin ./build_for_ultimaker.sh -a build_docker_cache - - - name: Dump GitHub context - if: ${{ always() }} - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - JOB_CONTEXT: ${{ toJson(job) }} - STEPS_CONTEXT: ${{ toJson(steps) }} - RUNNER_CONTEXT: ${{ toJson(runner) }} - run: | - echo "${GITHUB_CONTEXT}" - echo "${JOB_CONTEXT}" - echo "${STEPS_CONTEXT}" - echo "${RUNNER_CONTEXT}" diff --git a/.github/workflows/release_docker_img.yml b/.github/workflows/release_docker_img.yml index bee968a..b235f6d 100644 --- a/.github/workflows/release_docker_img.yml +++ b/.github/workflows/release_docker_img.yml @@ -17,7 +17,7 @@ on: env: DOCKER_IMAGE_NAME: ghcr.io/ultimaker/${{ inputs.DOCKER_IMAGE_NAME }} DOCKER_TAG_PREFIX: ${{ inputs.DOCKER_TAG_PREFIX }} - + jobs: Build: name: 'Build and Release Docker Image' @@ -33,7 +33,7 @@ jobs: id: ghcr_login run: | echo "${{ secrets.ULTIMAKER_CI_PAT }}" | docker login ghcr.io -u $ --password-stdin - + - name: Build Docker Image id: build run: | @@ -55,11 +55,3 @@ jobs: echo "DOCKER_IMAGE_NAME: ${DOCKER_IMAGE_NAME}" echo "DOCKER_IMAGE_VERSION: ${DOCKER_IMAGE_VERSION}" docker push "${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_VERSION}" - - - name: Dump GitHub context - if: ${{ always() }} - run: | - echo "${{ toJson(github) }}" - echo "${{ toJson(steps) }}" - echo "${{ toJson(runner) }}" - diff --git a/.github/workflows/release_pkg.yml b/.github/workflows/release_pkg.yml index 517513a..aa13f2b 100644 --- a/.github/workflows/release_pkg.yml +++ b/.github/workflows/release_pkg.yml @@ -154,17 +154,3 @@ jobs: echo '```' >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "The package metadata is embedded in the .deb file and used by GAR to automatically organize packages by distribution, eliminating the need for manual distribution tagging during upload." >> "$GITHUB_STEP_SUMMARY" - - - name: Dump GitHub context - if: ${{ always() }} - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - JOB_CONTEXT: ${{ toJson(job) }} - STEPS_CONTEXT: ${{ toJson(steps) }} - RUNNER_CONTEXT: ${{ toJson(runner) }} - shell: bash - run: | - echo "${GITHUB_CONTEXT}" - echo "${JOB_CONTEXT}" - echo "${STEPS_CONTEXT}" - echo "${RUNNER_CONTEXT}" diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index ccf4faa..51c0d10 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -1,6 +1,6 @@ on: workflow_call: - + jobs: Shellcheck: name: 'Shell Check' @@ -12,7 +12,7 @@ jobs: token: ${{ secrets.ULTIMAKER_CI_PAT }} submodules: recursive fetch-depth: 0 - + - name: Shell Check id: shellcheck run: | diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 7b44ae8..9e0a7ae 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -11,21 +11,8 @@ jobs: with: token: ${{ secrets.ULTIMAKER_CI_PAT }} submodules: recursive - + - name: Unit Test id: unittest run: | ./build_for_ultimaker.sh -a unittest - - - name: Dump GitHub context - if: ${{ always() }} - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - JOB_CONTEXT: ${{ toJson(job) }} - STEPS_CONTEXT: ${{ toJson(steps) }} - RUNNER_CONTEXT: ${{ toJson(runner) }} - run: | - echo "${GITHUB_CONTEXT}" - echo "${JOB_CONTEXT}" - echo "${STEPS_CONTEXT}" - echo "${RUNNER_CONTEXT}" From 3a01e304ba47805c096b9e7506bad111039b94ec Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 13:53:14 +0100 Subject: [PATCH 07/16] [EMB-115] Migrate workflows to use centralized build script This commit refactors the GitHub Actions workflows to use a new centralized build script located in the `embedded-workflows` repository. Key changes include: - Updating `build.yml`, `unit_test.yml`, `shellcheck.yml`, and `prepare_env.yml` to check out the `embedded-workflows` repository and call the new script. - Adding authentication steps for Google Artifact Registry (GAR) using Workload Identity Federation, removing the dependency on PATs for Docker operations. - Overhauling the `release_docker_img.yml` workflow to use modern Docker actions (`docker/metadata-action`, `docker/build-push-action`) for building and pushing images to GAR. - Introducing a new reusable workflow `docker_build.yml` for standardized Docker image builds. - Adding the centralized `build_for_ultimaker.sh` script. --- .github/workflows/build.yml | 41 +++++- .github/workflows/docker_build.yml | 157 +++++++++++++++++++++++ .github/workflows/prepare_env.yml | 37 +++++- .github/workflows/release_docker_img.yml | 116 ++++++++++++++--- .github/workflows/shellcheck.yml | 16 ++- .github/workflows/unit_test.yml | 43 ++++++- scripts/build_for_ultimaker.sh | 149 +++++++++++++++++++++ 7 files changed, 531 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/docker_build.yml create mode 100644 scripts/build_for_ultimaker.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2afdf12..a836173 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,13 +5,24 @@ on: description: "The version to build the package" required: true type: string + embedded_workflows_branch: + description: 'Branch to checkout embedded-workflows repo' + type: string + required: false + default: 'main' jobs: Build: name: 'Build Package' runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' env: RELEASE_VERSION: ${{ inputs.RELEASE_VERSION }} + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} + DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -19,6 +30,33 @@ jobs: token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Remove PAT (limit long-lived secrets) submodules: recursive + - name: Checkout Embedded-workflows repo + uses: actions/checkout@v4 + with: + repository: Ultimaker/embedded-workflows + path: embedded-workflows + ref: ${{ inputs.embedded_workflows_branch }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # Authenticate to Google Cloud using Workload Identity Federation + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} + service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} + + # Login to Google Artifact Registry for Docker operations + - name: Login to GAR + uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + - name: Build Package id: build run: | @@ -34,7 +72,8 @@ jobs: export RELEASE_VERSION="${{ env.RELEASE_VERSION }}" echo "RELEASE_VERSION: ${RELEASE_VERSION}" - ./build_for_ultimaker.sh -a build + # Use the centralized build script from embedded-workflows + ./embedded-workflows/scripts/build_for_ultimaker.sh -a build - name: Upload Artifact (Built package) uses: actions/upload-artifact@v4 diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml new file mode 100644 index 0000000..94bc771 --- /dev/null +++ b/.github/workflows/docker_build.yml @@ -0,0 +1,157 @@ +name: Build and Push Docker Image + +on: + workflow_call: + inputs: + DOCKER_IMAGE_NAME: + description: "The name of the docker image (without registry/project prefix)" + required: true + type: string + DOCKER_CONTEXT: + description: "Docker build context path" + required: false + default: '.' + type: string + DOCKERFILE_PATH: + description: "Path to Dockerfile" + required: false + default: 'docker_env/Dockerfile' + type: string + BUILD_ARGS: + description: "Additional build arguments (multiline string)" + required: false + default: '' + type: string + PUSH_IMAGE: + description: "Whether to push the image to registry" + required: false + default: true + type: boolean + embedded_workflows_branch: + description: 'Branch to checkout embedded-workflows repo' + type: string + required: false + default: 'main' + outputs: + IMAGE_TAG: + description: "Full image tag that was built" + value: ${{ jobs.Build_Docker.outputs.IMAGE_TAG }} + IMAGE_DIGEST: + description: "Image digest" + value: ${{ jobs.Build_Docker.outputs.IMAGE_DIGEST }} + +env: + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} + DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} + +jobs: + Build_Docker: + name: 'Build and Push Docker Image' + runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' + outputs: + IMAGE_TAG: ${{ steps.meta.outputs.tags }} + IMAGE_DIGEST: ${{ steps.build.outputs.digest }} + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.ULTIMAKER_CI_PAT }} + submodules: recursive + + - name: Checkout Embedded-workflows repo + uses: actions/checkout@v4 + with: + repository: Ultimaker/embedded-workflows + path: embedded-workflows + ref: ${{ inputs.embedded_workflows_branch }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + image=moby/buildkit:latest + network=host + + # Authenticate to Google Cloud using Workload Identity Federation + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} + service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} + + # Login to Google Artifact Registry + - name: Login to GAR + uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + + # Generate Docker metadata (tags and labels) + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ inputs.DOCKER_IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix={{branch}}-,format=short + flavor: | + latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + + # Build and optionally push the Docker image + - name: Build and push Docker image + id: build + uses: docker/build-push-action@v5 + with: + context: ${{ inputs.DOCKER_CONTEXT }} + file: ${{ inputs.DOCKERFILE_PATH }} + push: ${{ inputs.PUSH_IMAGE && github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + ${{ inputs.BUILD_ARGS }} + GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + cache-from: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ inputs.DOCKER_IMAGE_NAME }}:buildcache + cache-to: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ inputs.DOCKER_IMAGE_NAME }}:buildcache,mode=max + provenance: false # Disable provenance attestation for compatibility + + # Generate build summary + - name: Generate Summary + run: | + set -euo pipefail + + echo "# Docker Build Summary" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Image Information" >> "$GITHUB_STEP_SUMMARY" + echo "- **Image Name**: \`${{ inputs.DOCKER_IMAGE_NAME }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Registry**: \`${{ env.DOCKER_REGISTRY }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Project**: \`${{ env.DOCKER_PROJECT }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Repository**: \`${{ env.DOCKER_REPOSITORY }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Build Details" >> "$GITHUB_STEP_SUMMARY" + echo "- **Pushed**: \`${{ inputs.PUSH_IMAGE && github.event_name != 'pull_request' }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Digest**: \`${{ steps.build.outputs.digest }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Tags" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "${{ steps.meta.outputs.tags }}" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Security Best Practices Applied" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ Workload Identity Federation (no long-lived credentials)" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ OAuth2 access token authentication" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ Multi-platform buildx support" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ Layer caching for faster builds" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ Automatic semantic versioning" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ Build provenance disabled for compatibility" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index fb0093f..536e04f 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -10,7 +10,7 @@ on: description: 'Branch to checkout embedded-workflows repo' type: string required: false - default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged + default: 'main' outputs: RELEASE_VERSION: description: "The package version for this build" @@ -31,6 +31,13 @@ jobs: Prepare: name: 'Prepare Environment' runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' + env: + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} + DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} outputs: RELEASE_VERSION: ${{ steps.vars.outputs.RELEASE_VERSION }} RELEASE_REPO: ${{ steps.vars.outputs.RELEASE_REPO }} @@ -78,9 +85,31 @@ jobs: echo "- **Ref Type**: \`${GITHUB_REF_TYPE}\`" >> "$GITHUB_STEP_SUMMARY" echo "- **Ref Name**: \`${GITHUB_REF_NAME}\`" >> "$GITHUB_STEP_SUMMARY" - # TODO: Use Workload Identity Pool + # Authenticate to Google Cloud using Workload Identity Federation + - name: Authenticate to Google Cloud + if: ${{ inputs.BUILD_DOCKER_CACHE }} + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} + service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} + + # Login to Google Artifact Registry + - name: Login to GAR + if: ${{ inputs.BUILD_DOCKER_CACHE }} + uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + + - name: Set up Docker Buildx + if: ${{ inputs.BUILD_DOCKER_CACHE }} + uses: docker/setup-buildx-action@v3 + - name: "Build Docker Image Cache" if: ${{ inputs.BUILD_DOCKER_CACHE }} run: | - echo "${{ secrets.ULTIMAKER_CI_PAT }}" | docker login ghcr.io -u $ --password-stdin - ./build_for_ultimaker.sh -a build_docker_cache + # Use the centralized build script from embedded-workflows + ./embedded-workflows/scripts/build_for_ultimaker.sh -a build_docker_cache diff --git a/.github/workflows/release_docker_img.yml b/.github/workflows/release_docker_img.yml index b235f6d..3168e06 100644 --- a/.github/workflows/release_docker_img.yml +++ b/.github/workflows/release_docker_img.yml @@ -7,21 +7,31 @@ on: description: "The name of the docker image. E.g.: um-kernel" required: true type: string - DOCKER_TAG_PREFIX: description: "The prefix used to tag the image. It will be used to extract the version. E.g.: docker_img-" required: false default: 'docker_img-' type: string + embedded_workflows_branch: + description: 'Branch to checkout embedded-workflows repo' + type: string + required: false + default: 'main' env: - DOCKER_IMAGE_NAME: ghcr.io/ultimaker/${{ inputs.DOCKER_IMAGE_NAME }} + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} + DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} + DOCKER_IMAGE_NAME: ${{ inputs.DOCKER_IMAGE_NAME }} DOCKER_TAG_PREFIX: ${{ inputs.DOCKER_TAG_PREFIX }} jobs: Build: name: 'Build and Release Docker Image' runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -29,29 +39,93 @@ jobs: token: ${{ secrets.ULTIMAKER_CI_PAT }} submodules: recursive - - name: Login in GitHub Container Registry - id: ghcr_login - run: | - echo "${{ secrets.ULTIMAKER_CI_PAT }}" | docker login ghcr.io -u $ --password-stdin + - name: Checkout Embedded-workflows repo + uses: actions/checkout@v4 + with: + repository: Ultimaker/embedded-workflows + path: embedded-workflows + ref: ${{ inputs.embedded_workflows_branch }} - - name: Build Docker Image - id: build + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # Authenticate to Google Cloud using Workload Identity Federation + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} + service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} + + # Login to Google Artifact Registry + - name: Login to GAR + uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + + # Extract version from tag + - name: Extract Version + id: version run: | # Remove the tag prefix to make the version export DOCKER_IMAGE_VERSION="${GITHUB_REF_NAME##${DOCKER_TAG_PREFIX}}" - export DOCKER_IMAGE_NAME - echo "DOCKER_IMAGE_NAME: ${DOCKER_IMAGE_NAME}" + echo "DOCKER_IMAGE_VERSION=${DOCKER_IMAGE_VERSION}" >> $GITHUB_OUTPUT echo "DOCKER_IMAGE_VERSION: ${DOCKER_IMAGE_VERSION}" - - # Just generate a new docker image, so pass action = none - ./build_for_ultimaker.sh -a docker_build - docker images - - name: Release Docker Image to Github Packages - id: release + # Generate Docker metadata (tags and labels) + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ env.DOCKER_IMAGE_NAME }} + tags: | + type=semver,pattern={{version}},value=${{ steps.version.outputs.DOCKER_IMAGE_VERSION }} + type=semver,pattern={{major}}.{{minor}},value=${{ steps.version.outputs.DOCKER_IMAGE_VERSION }} + type=semver,pattern={{major}},value=${{ steps.version.outputs.DOCKER_IMAGE_VERSION }} + flavor: | + latest=auto + + # Build and push the Docker image + - name: Build and Push Docker Image + id: build + uses: docker/build-push-action@v5 + with: + context: . + file: docker_env/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + cache-from: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache + cache-to: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache,mode=max + provenance: false # Disable provenance attestation for compatibility + + # Generate release summary + - name: Generate Summary run: | - export DOCKER_IMAGE_VERSION="${GITHUB_REF_NAME##${DOCKER_TAG_PREFIX}}" - export DOCKER_IMAGE_NAME - echo "DOCKER_IMAGE_NAME: ${DOCKER_IMAGE_NAME}" - echo "DOCKER_IMAGE_VERSION: ${DOCKER_IMAGE_VERSION}" - docker push "${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_VERSION}" + set -euo pipefail + + echo "# Docker Image Release Summary" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Image Information" >> "$GITHUB_STEP_SUMMARY" + echo "- **Image Name**: \`${{ env.DOCKER_IMAGE_NAME }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Version**: \`${{ steps.version.outputs.DOCKER_IMAGE_VERSION }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Registry**: \`${{ env.DOCKER_REGISTRY }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Digest**: \`${{ steps.build.outputs.digest }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Tags" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "${{ steps.meta.outputs.tags }}" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "## Security Features" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ Workload Identity Federation (no long-lived secrets)" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ OAuth2 access token authentication" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ Semantic versioning from git tags" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ Layer caching for efficient builds" >> "$GITHUB_STEP_SUMMARY" + echo "- ✅ Multi-platform buildx support" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 51c0d10..0c82f8d 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -1,5 +1,11 @@ on: workflow_call: + inputs: + embedded_workflows_branch: + description: 'Branch to checkout embedded-workflows repo' + type: string + required: false + default: 'main' jobs: Shellcheck: @@ -13,7 +19,15 @@ jobs: submodules: recursive fetch-depth: 0 + - name: Checkout Embedded-workflows repo + uses: actions/checkout@v4 + with: + repository: Ultimaker/embedded-workflows + path: embedded-workflows + ref: ${{ inputs.embedded_workflows_branch }} + - name: Shell Check id: shellcheck run: | - ./build_for_ultimaker.sh -a shellcheck + # Use the centralized build script from embedded-workflows + ./embedded-workflows/scripts/build_for_ultimaker.sh -a shellcheck diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 9e0a7ae..713419b 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -1,10 +1,23 @@ on: workflow_call: + inputs: + embedded_workflows_branch: + description: 'Branch to checkout embedded-workflows repo' + type: string + required: false + default: 'main' jobs: Unit_Test: name: 'Unit Test' runs-on: ubuntu-latest + permissions: + contents: 'read' + id-token: 'write' + env: + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} + DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -12,7 +25,35 @@ jobs: token: ${{ secrets.ULTIMAKER_CI_PAT }} submodules: recursive + - name: Checkout Embedded-workflows repo + uses: actions/checkout@v4 + with: + repository: Ultimaker/embedded-workflows + path: embedded-workflows + ref: ${{ inputs.embedded_workflows_branch }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # Authenticate to Google Cloud using Workload Identity Federation + - name: Authenticate to Google Cloud + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} + service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} + + # Login to Google Artifact Registry for Docker operations + - name: Login to GAR + uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: oauth2accesstoken + password: ${{ steps.auth.outputs.access_token }} + - name: Unit Test id: unittest run: | - ./build_for_ultimaker.sh -a unittest + # Use the centralized build script from embedded-workflows + ./embedded-workflows/scripts/build_for_ultimaker.sh -a unittest diff --git a/scripts/build_for_ultimaker.sh b/scripts/build_for_ultimaker.sh new file mode 100644 index 0000000..fe96974 --- /dev/null +++ b/scripts/build_for_ultimaker.sh @@ -0,0 +1,149 @@ +#!/bin/bash +# +# Copyright (C) 2019 Ultimaker B.V. +# +# Centralized build script for embedded firmware projects +# This script orchestrates Docker-based builds with proper authentication + +set -eu + +ARCH="armhf" + +SRC_DIR="$(pwd)" +RELEASE_VERSION="${RELEASE_VERSION:-0.0.1-alpha.0}" +DOCKER_WORK_DIR="/build" +BUILD_DIR_TEMPLATE="_build_${ARCH}" +BUILD_DIR="${BUILD_DIR:-${SRC_DIR}/${BUILD_DIR_TEMPLATE}}" + +# Docker configuration +DOCKER_IMAGE_NAME="${DOCKER_IMAGE_NAME:-opinicus}" +DOCKER_REGISTRY="${DOCKER_REGISTRY:-europe-west1-docker.pkg.dev}" +DOCKER_PROJECT="${DOCKER_PROJECT:-}" +DOCKER_REPOSITORY="${DOCKER_REPOSITORY:-}" + +run_env_check="yes" +run_verification="yes" +action="none" + +env_check() +{ + run_in_docker "./docker_env/buildenv_check.sh" +} + +run_build() +{ + run_in_docker "./build.sh" "$@" +} + +run_verification() +{ + echo "Testing!" + # These verifications should never fail! See .gitlab-ci.yml + ./ci/local/run_all.sh +} + +run_shellcheck() +{ + docker run \ + --rm \ + -v "$(pwd):${DOCKER_WORK_DIR}" \ + -w "${DOCKER_WORK_DIR}" \ + "registry.hub.docker.com/koalaman/shellcheck-alpine:stable" \ + "./run_shellcheck.sh" +} + +run_unittest() +{ + echo "Running unit tests!" + run_in_docker "./ci/local/run_unit_test.sh" +} + +usage() +{ + echo "Usage: ${0} [OPTIONS]" + echo " -a ACTION Specify action (build|build_docker|build_docker_cache|shellcheck|unittest)" + echo " -c Skip build environment checks" + echo " -h Print usage" + echo " -s Skip code verification" +} + +while getopts ":a:chls" options; do + case "${options}" in + a) + action="${OPTARG}" + ;; + c) + run_env_check="no" + ;; + h) + usage + exit 0 + ;; + s) + run_verification="no" + ;; + :) + echo "Option -${OPTARG} requires an argument." + exit 1 + ;; + ?) + echo "Invalid option: -${OPTARG}" + exit 1 + ;; + esac +done +shift "$((OPTIND - 1))" + +if ! command -V docker; then + echo "Docker not found, docker-less builds are not supported." + exit 1 +fi + +case "${action}" in + shellcheck) + run_shellcheck + exit 0 + ;; + build) + source ./docker_env/make_docker.sh "" + run_build "$@" + exit 0 + ;; + build_docker) + # Build Docker image without loading it locally + # This is typically used in CI/CD pipelines + source ./docker_env/make_docker.sh "" + exit 0 + ;; + build_docker_cache) + export DOCKER_BUILD_ONLY_CACHE="yes" + source ./docker_env/make_docker.sh "" + exit 0 + ;; + unittest) + source ./docker_env/make_docker.sh "" + run_unittest + exit 0 + ;; + none) + ;; + *) + echo "Invalid action: ${action}" + exit 1 + ;; +esac + +# Make sure to pass an empty argument to make_docker, else any arguments passed to build_for_ultimaker is passed to make_docker instead! +source ./docker_env/make_docker.sh "" + +if [ "${run_env_check}" = "yes" ]; then + env_check +fi + +run_build "$@" + +if [ "${run_verification}" = "yes" ]; then + run_verification +fi + +exit 0 From 6c61585cf67e239edbec20ed8997d2d50b034b2d Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 15:24:43 +0100 Subject: [PATCH 08/16] [EMB-115] Standardize embedded-workflows checkout This commit standardizes the checkout process for the `embedded-workflows` repository across all reusable workflows. - An `embedded_workflows_branch` input is added to the `release_pkg` workflow to align it with other workflows. - The default branch for checking out `embedded-workflows` is temporarily set to `EMB-115_migrate_to_gar` across all workflows to facilitate ongoing development. - The checkout step in `release_pkg` is cleaned up by removing an unnecessary PAT and fetch-depth. - A redundant "Security Best Practices" summary is removed from the `docker_build` workflow. --- .github/workflows/build.yml | 2 +- .github/workflows/docker_build.yml | 9 +-------- .github/workflows/prepare_env.yml | 2 +- .github/workflows/release_docker_img.yml | 2 +- .github/workflows/release_pkg.yml | 10 +++++++--- .github/workflows/shellcheck.yml | 2 +- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a836173..16bf5c1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ on: description: 'Branch to checkout embedded-workflows repo' type: string required: false - default: 'main' + default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged jobs: Build: diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index 94bc771..d118c9c 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -31,7 +31,7 @@ on: description: 'Branch to checkout embedded-workflows repo' type: string required: false - default: 'main' + default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged outputs: IMAGE_TAG: description: "Full image tag that was built" @@ -148,10 +148,3 @@ jobs: echo "${{ steps.meta.outputs.tags }}" >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "## Security Best Practices Applied" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ Workload Identity Federation (no long-lived credentials)" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ OAuth2 access token authentication" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ Multi-platform buildx support" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ Layer caching for faster builds" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ Automatic semantic versioning" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ Build provenance disabled for compatibility" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index 536e04f..5fb4f7b 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -10,7 +10,7 @@ on: description: 'Branch to checkout embedded-workflows repo' type: string required: false - default: 'main' + default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged outputs: RELEASE_VERSION: description: "The package version for this build" diff --git a/.github/workflows/release_docker_img.yml b/.github/workflows/release_docker_img.yml index 3168e06..11a7f69 100644 --- a/.github/workflows/release_docker_img.yml +++ b/.github/workflows/release_docker_img.yml @@ -16,7 +16,7 @@ on: description: 'Branch to checkout embedded-workflows repo' type: string required: false - default: 'main' + default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged env: DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} diff --git a/.github/workflows/release_pkg.yml b/.github/workflows/release_pkg.yml index aa13f2b..ca507ff 100644 --- a/.github/workflows/release_pkg.yml +++ b/.github/workflows/release_pkg.yml @@ -5,6 +5,11 @@ on: description: "The GAR repository to release the package" required: true type: string + embedded_workflows_branch: + description: 'Branch to checkout embedded-workflows repo' + type: string + required: false + default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged jobs: Release_Package: @@ -20,13 +25,12 @@ jobs: EMB_GCP_PROJECT: ${{ vars.EMB_GCP_PROJECT }} EMB_WI_PROVIDER: ${{ vars.EMB_WI_PROVIDER }} steps: - - name: Cloning Embedded-Workflows on default branch + - name: Checkout Embedded-workflows repo uses: actions/checkout@v4 with: repository: Ultimaker/embedded-workflows - token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Remove PAT (limit long-lived secrets) - fetch-depth: 0 path: embedded-workflows + ref: ${{ inputs.embedded_workflows_branch }} - uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 0c82f8d..6d4c0f1 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -5,7 +5,7 @@ on: description: 'Branch to checkout embedded-workflows repo' type: string required: false - default: 'main' + default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged jobs: Shellcheck: From cf8d7f3de116b8a0b06837f115368f8ec54a00a2 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 15:28:38 +0100 Subject: [PATCH 09/16] [EMB-115] Pass encryption key directly to build script The `UMLM_ENCRYPTION_KEY` is now passed as a temporary environment variable directly to the `build_for_ultimaker.sh` script instead of being exported for the entire step. This improves security by limiting the scope of the secret. Additionally, `TODO` comments in the CI workflows have been standardized. --- .github/workflows/build.yml | 9 +++------ .github/workflows/docker_build.yml | 2 +- .github/workflows/prepare_env.yml | 2 +- .github/workflows/release_docker_img.yml | 2 +- .github/workflows/shellcheck.yml | 2 +- .github/workflows/unit_test.yml | 2 +- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 16bf5c1..a77e87d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Remove PAT (limit long-lived secrets) + token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 submodules: recursive - name: Checkout Embedded-workflows repo @@ -65,15 +65,12 @@ jobs: # an encrypted firmware module using Ultimaker/umlicensemanager. # Other firmware components ignore this. export BUILDTYPE=PRODUCTION - # Encryption key for UMMOD file in "PRODUCTION" build mode. - # See umlicensemanager documentation - export UMLM_ENCRYPTION_KEY="${{ secrets.UMLM_ENCRYPTION_KEY }}" - export RELEASE_VERSION="${{ env.RELEASE_VERSION }}" echo "RELEASE_VERSION: ${RELEASE_VERSION}" # Use the centralized build script from embedded-workflows - ./embedded-workflows/scripts/build_for_ultimaker.sh -a build + # Pass encryption key directly to the build script, not as global export + UMLM_ENCRYPTION_KEY="${{ secrets.UMLM_ENCRYPTION_KEY }}" ./embedded-workflows/scripts/build_for_ultimaker.sh -a build - name: Upload Artifact (Built package) uses: actions/upload-artifact@v4 diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index d118c9c..577a54c 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -59,7 +59,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} + token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 submodules: recursive - name: Checkout Embedded-workflows repo diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index 5fb4f7b..1f3199f 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -45,7 +45,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Remove PAT (limit long-lived secrets) + token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 fetch-depth: 0 # Full history needed for git describe submodules: recursive # Needed for docker cache build diff --git a/.github/workflows/release_docker_img.yml b/.github/workflows/release_docker_img.yml index 11a7f69..7748b1a 100644 --- a/.github/workflows/release_docker_img.yml +++ b/.github/workflows/release_docker_img.yml @@ -36,7 +36,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} + token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 submodules: recursive - name: Checkout Embedded-workflows repo diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 6d4c0f1..1fa8307 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -15,7 +15,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} + token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 submodules: recursive fetch-depth: 0 diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 713419b..6965568 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} + token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 submodules: recursive - name: Checkout Embedded-workflows repo From e2c8cba357be469faf19341cee3f98522815b841 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 15:29:57 +0100 Subject: [PATCH 10/16] [EMB-115] Use secrets for passing GITHUB_TOKEN to docker build --- .github/workflows/docker_build.yml | 3 ++- .github/workflows/release_docker_img.yml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index 577a54c..670f105 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -121,7 +121,8 @@ jobs: labels: ${{ steps.meta.outputs.labels }} build-args: | ${{ inputs.BUILD_ARGS }} - GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + secrets: | + github_token=${{ secrets.GITHUB_TOKEN }} cache-from: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ inputs.DOCKER_IMAGE_NAME }}:buildcache cache-to: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ inputs.DOCKER_IMAGE_NAME }}:buildcache,mode=max provenance: false # Disable provenance attestation for compatibility diff --git a/.github/workflows/release_docker_img.yml b/.github/workflows/release_docker_img.yml index 7748b1a..bb195bb 100644 --- a/.github/workflows/release_docker_img.yml +++ b/.github/workflows/release_docker_img.yml @@ -99,8 +99,8 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - build-args: | - GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + secrets: | + github_token=${{ secrets.GITHUB_TOKEN }} cache-from: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache cache-to: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache,mode=max provenance: false # Disable provenance attestation for compatibility From 39607b9f10f6efc1616ff62ed1d9dd16d03ef055 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Sun, 18 Jan 2026 15:30:42 +0100 Subject: [PATCH 11/16] [EMB-115] Harden static analysis tool containers The static analysis Docker images (clang-tidy, clang-format, cppcheck) are hardened by creating and running as a non-privileged user. Additionally, the shellcheck script is updated to mount the source directory as read-only to prevent accidental modifications. --- Dockerfiles/clang-format/13/Dockerfile | 6 ++++++ Dockerfiles/clang-tidy/11/Dockerfile | 6 ++++++ Dockerfiles/clang-tidy/14/Dockerfile | 6 ++++++ Dockerfiles/cppcheck/2.3/Dockerfile | 6 ++++++ scripts/build_for_ultimaker.sh | 2 +- 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Dockerfiles/clang-format/13/Dockerfile b/Dockerfiles/clang-format/13/Dockerfile index 2d8f778..d2525f4 100644 --- a/Dockerfiles/clang-format/13/Dockerfile +++ b/Dockerfiles/clang-format/13/Dockerfile @@ -16,4 +16,10 @@ RUN apt-get update && \ # clang-format with pip RUN pip3 install clang-format==13.0.0 +# Create non-privileged user for security +RUN useradd -m -u 1000 -s /bin/bash builder && \ + chown -R builder:builder /work + +USER builder + COPY Dockerfile Dockerfile diff --git a/Dockerfiles/clang-tidy/11/Dockerfile b/Dockerfiles/clang-tidy/11/Dockerfile index 465eb97..994d91f 100644 --- a/Dockerfiles/clang-tidy/11/Dockerfile +++ b/Dockerfiles/clang-tidy/11/Dockerfile @@ -11,4 +11,10 @@ RUN apt-get update && \ rm -rf /var/lib/apt/* && \ ln -s /usr/bin/clang-tidy-11 /usr/bin/clang-tidy +# Create non-privileged user for security +RUN useradd -m -u 1000 -s /bin/bash builder && \ + chown -R builder:builder /work + +USER builder + COPY Dockerfile Dockerfile diff --git a/Dockerfiles/clang-tidy/14/Dockerfile b/Dockerfiles/clang-tidy/14/Dockerfile index 2af4230..d79dab0 100644 --- a/Dockerfiles/clang-tidy/14/Dockerfile +++ b/Dockerfiles/clang-tidy/14/Dockerfile @@ -11,4 +11,10 @@ RUN apt-get update && \ rm -rf /var/lib/apt/* && \ ln -s /usr/bin/clang-tidy-14 /usr/bin/clang-tidy +# Create non-privileged user for security +RUN useradd -m -u 1000 -s /bin/bash builder && \ + chown -R builder:builder /work + +USER builder + COPY Dockerfile Dockerfile diff --git a/Dockerfiles/cppcheck/2.3/Dockerfile b/Dockerfiles/cppcheck/2.3/Dockerfile index 4d5963b..7530a99 100644 --- a/Dockerfiles/cppcheck/2.3/Dockerfile +++ b/Dockerfiles/cppcheck/2.3/Dockerfile @@ -10,4 +10,10 @@ RUN apt-get update && \ rm -rf /var/cache/apt/* && \ rm -rf /var/lib/apt/* +# Create non-privileged user for security +RUN useradd -m -u 1000 -s /bin/bash builder && \ + chown -R builder:builder /work + +USER builder + COPY Dockerfile Dockerfile diff --git a/scripts/build_for_ultimaker.sh b/scripts/build_for_ultimaker.sh index fe96974..6f8f5eb 100644 --- a/scripts/build_for_ultimaker.sh +++ b/scripts/build_for_ultimaker.sh @@ -46,7 +46,7 @@ run_shellcheck() { docker run \ --rm \ - -v "$(pwd):${DOCKER_WORK_DIR}" \ + -v "$(pwd):${DOCKER_WORK_DIR}:ro" \ -w "${DOCKER_WORK_DIR}" \ "registry.hub.docker.com/koalaman/shellcheck-alpine:stable" \ "./run_shellcheck.sh" From 3172128b4566c5f0e808960ba4ca7b9873674c93 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 19 Jan 2026 13:11:13 +0100 Subject: [PATCH 12/16] [EMB-115] Remove fallback for DOCKER_REGISTRY variable --- .github/workflows/build.yml | 2 +- .github/workflows/docker_build.yml | 2 +- .github/workflows/prepare_env.yml | 2 +- .github/workflows/release_docker_img.yml | 2 +- .github/workflows/unit_test.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a77e87d..6d94237 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: id-token: 'write' env: RELEASE_VERSION: ${{ inputs.RELEASE_VERSION }} - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} steps: diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index 670f105..bb33897 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -41,7 +41,7 @@ on: value: ${{ jobs.Build_Docker.outputs.IMAGE_DIGEST }} env: - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index 1f3199f..d7033d3 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -35,7 +35,7 @@ jobs: contents: 'read' id-token: 'write' env: - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} outputs: diff --git a/.github/workflows/release_docker_img.yml b/.github/workflows/release_docker_img.yml index bb195bb..dbc511e 100644 --- a/.github/workflows/release_docker_img.yml +++ b/.github/workflows/release_docker_img.yml @@ -19,7 +19,7 @@ on: default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged env: - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} DOCKER_IMAGE_NAME: ${{ inputs.DOCKER_IMAGE_NAME }} diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 6965568..0d0e10f 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -15,7 +15,7 @@ jobs: contents: 'read' id-token: 'write' env: - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY || 'europe-west1-docker.pkg.dev' }} + DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} steps: From 8b8d9964551e503eaeefd87246250d6046c5e243 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 19 Jan 2026 14:07:02 +0100 Subject: [PATCH 13/16] [EMB-115] Grant write permissions to prepare environment job --- .github/workflows/prepare_env.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index d7033d3..fbbf1e2 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -32,7 +32,7 @@ jobs: name: 'Prepare Environment' runs-on: ubuntu-latest permissions: - contents: 'read' + contents: 'write' id-token: 'write' env: DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} From 96349267b3f56d7e238a2dd102d70a5412161f1b Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 19 Jan 2026 14:10:09 +0100 Subject: [PATCH 14/16] Revert "[EMB-115] Grant write permissions to prepare environment job" This reverts commit 8b8d9964551e503eaeefd87246250d6046c5e243. --- .github/workflows/prepare_env.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index fbbf1e2..d7033d3 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -32,7 +32,7 @@ jobs: name: 'Prepare Environment' runs-on: ubuntu-latest permissions: - contents: 'write' + contents: 'read' id-token: 'write' env: DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} From 2e7df8b5918e66f3ff12b65a405a02a8ab46eed4 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Mon, 19 Jan 2026 14:11:21 +0100 Subject: [PATCH 15/16] [EMB-115] Make build_for_ultimaker.sh executable --- scripts/build_for_ultimaker.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/build_for_ultimaker.sh diff --git a/scripts/build_for_ultimaker.sh b/scripts/build_for_ultimaker.sh old mode 100644 new mode 100755 From fa2e916b99a6c36c949d6685008e66d151e65833 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Tue, 20 Jan 2026 08:55:21 +0100 Subject: [PATCH 16/16] [EMB-115] Remove dependency on embedded-workflows This commit removes the dependency on the `embedded-workflows` repository by deleting the centralized `build_for_ultimaker.sh` script and the `docker_build.yml` workflow. The CI workflows (`build.yml`, `release_pkg.yml`, `unit_test.yml`, `shellcheck.yml`, `prepare_env.yml`, and `release_docker_img.yml`) are updated to no longer check out the `embedded-workflows` repository. Instead, they now rely on local scripts and configurations. Key changes include: - Deleting `scripts/build_for_ultimaker.sh` and `.github/workflows/docker_build.yml`. - Updating workflow files to remove the checkout step for `embedded-workflows`. - Simplifying Docker authentication and build steps, removing logic related to Google Artifact Registry (GAR) in favor of direct Docker commands and GitHub Container Registry (`ghcr.io`) login where needed. - Adjusting build, test, and shellcheck jobs to call local scripts directly. - Cleaning up unused inputs and environment variables from the workflows. --- .github/workflows/build.yml | 38 +----- .github/workflows/docker_build.yml | 151 ----------------------- .github/workflows/prepare_env.yml | 35 +----- .github/workflows/release_docker_img.yml | 113 ++++------------- .github/workflows/release_pkg.yml | 26 +--- .github/workflows/shellcheck.yml | 16 +-- .github/workflows/unit_test.yml | 43 +------ scripts/build_for_ultimaker.sh | 149 ---------------------- 8 files changed, 36 insertions(+), 535 deletions(-) delete mode 100644 .github/workflows/docker_build.yml delete mode 100755 scripts/build_for_ultimaker.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d94237..87034f7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,11 +5,6 @@ on: description: "The version to build the package" required: true type: string - embedded_workflows_branch: - description: 'Branch to checkout embedded-workflows repo' - type: string - required: false - default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged jobs: Build: @@ -20,9 +15,6 @@ jobs: id-token: 'write' env: RELEASE_VERSION: ${{ inputs.RELEASE_VERSION }} - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} - DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} - DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} steps: - name: Checkout Repository uses: actions/checkout@v4 @@ -30,33 +22,6 @@ jobs: token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 submodules: recursive - - name: Checkout Embedded-workflows repo - uses: actions/checkout@v4 - with: - repository: Ultimaker/embedded-workflows - path: embedded-workflows - ref: ${{ inputs.embedded_workflows_branch }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # Authenticate to Google Cloud using Workload Identity Federation - - name: Authenticate to Google Cloud - id: auth - uses: google-github-actions/auth@v2 - with: - token_format: 'access_token' - workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} - service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} - - # Login to Google Artifact Registry for Docker operations - - name: Login to GAR - uses: docker/login-action@v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: oauth2accesstoken - password: ${{ steps.auth.outputs.access_token }} - - name: Build Package id: build run: | @@ -68,9 +33,8 @@ jobs: export RELEASE_VERSION="${{ env.RELEASE_VERSION }}" echo "RELEASE_VERSION: ${RELEASE_VERSION}" - # Use the centralized build script from embedded-workflows # Pass encryption key directly to the build script, not as global export - UMLM_ENCRYPTION_KEY="${{ secrets.UMLM_ENCRYPTION_KEY }}" ./embedded-workflows/scripts/build_for_ultimaker.sh -a build + UMLM_ENCRYPTION_KEY="${{ secrets.UMLM_ENCRYPTION_KEY }}" ./build_for_ultimaker.sh -a build - name: Upload Artifact (Built package) uses: actions/upload-artifact@v4 diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml deleted file mode 100644 index bb33897..0000000 --- a/.github/workflows/docker_build.yml +++ /dev/null @@ -1,151 +0,0 @@ -name: Build and Push Docker Image - -on: - workflow_call: - inputs: - DOCKER_IMAGE_NAME: - description: "The name of the docker image (without registry/project prefix)" - required: true - type: string - DOCKER_CONTEXT: - description: "Docker build context path" - required: false - default: '.' - type: string - DOCKERFILE_PATH: - description: "Path to Dockerfile" - required: false - default: 'docker_env/Dockerfile' - type: string - BUILD_ARGS: - description: "Additional build arguments (multiline string)" - required: false - default: '' - type: string - PUSH_IMAGE: - description: "Whether to push the image to registry" - required: false - default: true - type: boolean - embedded_workflows_branch: - description: 'Branch to checkout embedded-workflows repo' - type: string - required: false - default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged - outputs: - IMAGE_TAG: - description: "Full image tag that was built" - value: ${{ jobs.Build_Docker.outputs.IMAGE_TAG }} - IMAGE_DIGEST: - description: "Image digest" - value: ${{ jobs.Build_Docker.outputs.IMAGE_DIGEST }} - -env: - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} - DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} - DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} - -jobs: - Build_Docker: - name: 'Build and Push Docker Image' - runs-on: ubuntu-latest - permissions: - contents: 'read' - id-token: 'write' - outputs: - IMAGE_TAG: ${{ steps.meta.outputs.tags }} - IMAGE_DIGEST: ${{ steps.build.outputs.digest }} - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 - submodules: recursive - - - name: Checkout Embedded-workflows repo - uses: actions/checkout@v4 - with: - repository: Ultimaker/embedded-workflows - path: embedded-workflows - ref: ${{ inputs.embedded_workflows_branch }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - driver-opts: | - image=moby/buildkit:latest - network=host - - # Authenticate to Google Cloud using Workload Identity Federation - - name: Authenticate to Google Cloud - id: auth - uses: google-github-actions/auth@v2 - with: - token_format: 'access_token' - workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} - service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} - - # Login to Google Artifact Registry - - name: Login to GAR - uses: docker/login-action@v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: oauth2accesstoken - password: ${{ steps.auth.outputs.access_token }} - - # Generate Docker metadata (tags and labels) - - name: Docker metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: | - ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ inputs.DOCKER_IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha,prefix={{branch}}-,format=short - flavor: | - latest=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - - # Build and optionally push the Docker image - - name: Build and push Docker image - id: build - uses: docker/build-push-action@v5 - with: - context: ${{ inputs.DOCKER_CONTEXT }} - file: ${{ inputs.DOCKERFILE_PATH }} - push: ${{ inputs.PUSH_IMAGE && github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: | - ${{ inputs.BUILD_ARGS }} - secrets: | - github_token=${{ secrets.GITHUB_TOKEN }} - cache-from: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ inputs.DOCKER_IMAGE_NAME }}:buildcache - cache-to: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ inputs.DOCKER_IMAGE_NAME }}:buildcache,mode=max - provenance: false # Disable provenance attestation for compatibility - - # Generate build summary - - name: Generate Summary - run: | - set -euo pipefail - - echo "# Docker Build Summary" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "## Image Information" >> "$GITHUB_STEP_SUMMARY" - echo "- **Image Name**: \`${{ inputs.DOCKER_IMAGE_NAME }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- **Registry**: \`${{ env.DOCKER_REGISTRY }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- **Project**: \`${{ env.DOCKER_PROJECT }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- **Repository**: \`${{ env.DOCKER_REPOSITORY }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "## Build Details" >> "$GITHUB_STEP_SUMMARY" - echo "- **Pushed**: \`${{ inputs.PUSH_IMAGE && github.event_name != 'pull_request' }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- **Digest**: \`${{ steps.build.outputs.digest }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "## Tags" >> "$GITHUB_STEP_SUMMARY" - echo '```' >> "$GITHUB_STEP_SUMMARY" - echo "${{ steps.meta.outputs.tags }}" >> "$GITHUB_STEP_SUMMARY" - echo '```' >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/prepare_env.yml b/.github/workflows/prepare_env.yml index d7033d3..e3d0bd5 100644 --- a/.github/workflows/prepare_env.yml +++ b/.github/workflows/prepare_env.yml @@ -6,7 +6,7 @@ on: type: boolean required: false default: false - embedded_workflows_branch: + EMBEDDED_WORKFLOWS_BRANCH: description: 'Branch to checkout embedded-workflows repo' type: string required: false @@ -34,10 +34,6 @@ jobs: permissions: contents: 'read' id-token: 'write' - env: - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} - DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} - DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} outputs: RELEASE_VERSION: ${{ steps.vars.outputs.RELEASE_VERSION }} RELEASE_REPO: ${{ steps.vars.outputs.RELEASE_REPO }} @@ -54,7 +50,7 @@ jobs: with: repository: Ultimaker/embedded-workflows path: embedded-workflows - ref: ${{ inputs.embedded_workflows_branch }} + ref: ${{ inputs.EMBEDDED_WORKFLOWS_BRANCH }} - name: Generate Variables id: vars @@ -85,31 +81,8 @@ jobs: echo "- **Ref Type**: \`${GITHUB_REF_TYPE}\`" >> "$GITHUB_STEP_SUMMARY" echo "- **Ref Name**: \`${GITHUB_REF_NAME}\`" >> "$GITHUB_STEP_SUMMARY" - # Authenticate to Google Cloud using Workload Identity Federation - - name: Authenticate to Google Cloud - if: ${{ inputs.BUILD_DOCKER_CACHE }} - id: auth - uses: google-github-actions/auth@v2 - with: - token_format: 'access_token' - workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} - service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} - - # Login to Google Artifact Registry - - name: Login to GAR - if: ${{ inputs.BUILD_DOCKER_CACHE }} - uses: docker/login-action@v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: oauth2accesstoken - password: ${{ steps.auth.outputs.access_token }} - - - name: Set up Docker Buildx - if: ${{ inputs.BUILD_DOCKER_CACHE }} - uses: docker/setup-buildx-action@v3 - - name: "Build Docker Image Cache" if: ${{ inputs.BUILD_DOCKER_CACHE }} run: | - # Use the centralized build script from embedded-workflows - ./embedded-workflows/scripts/build_for_ultimaker.sh -a build_docker_cache + echo "${{ secrets.ULTIMAKER_CI_PAT }}" | docker login ghcr.io -u $ --password-stdin + ./build_for_ultimaker.sh -a build_docker_cache diff --git a/.github/workflows/release_docker_img.yml b/.github/workflows/release_docker_img.yml index dbc511e..39dfa70 100644 --- a/.github/workflows/release_docker_img.yml +++ b/.github/workflows/release_docker_img.yml @@ -7,22 +7,15 @@ on: description: "The name of the docker image. E.g.: um-kernel" required: true type: string + DOCKER_TAG_PREFIX: description: "The prefix used to tag the image. It will be used to extract the version. E.g.: docker_img-" required: false default: 'docker_img-' type: string - embedded_workflows_branch: - description: 'Branch to checkout embedded-workflows repo' - type: string - required: false - default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged env: - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} - DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} - DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} - DOCKER_IMAGE_NAME: ${{ inputs.DOCKER_IMAGE_NAME }} + DOCKER_IMAGE_NAME: ghcr.io/ultimaker/${{ inputs.DOCKER_IMAGE_NAME }} DOCKER_TAG_PREFIX: ${{ inputs.DOCKER_TAG_PREFIX }} jobs: @@ -39,93 +32,29 @@ jobs: token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 submodules: recursive - - name: Checkout Embedded-workflows repo - uses: actions/checkout@v4 - with: - repository: Ultimaker/embedded-workflows - path: embedded-workflows - ref: ${{ inputs.embedded_workflows_branch }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # Authenticate to Google Cloud using Workload Identity Federation - - name: Authenticate to Google Cloud - id: auth - uses: google-github-actions/auth@v2 - with: - token_format: 'access_token' - workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} - service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} - - # Login to Google Artifact Registry - - name: Login to GAR - uses: docker/login-action@v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: oauth2accesstoken - password: ${{ steps.auth.outputs.access_token }} + - name: Login in GitHub Container Registry + id: ghcr_login + run: | + echo "${{ secrets.ULTIMAKER_CI_PAT }}" | docker login ghcr.io -u $ --password-stdin - # Extract version from tag - - name: Extract Version - id: version + - name: Build Docker Image + id: build run: | # Remove the tag prefix to make the version export DOCKER_IMAGE_VERSION="${GITHUB_REF_NAME##${DOCKER_TAG_PREFIX}}" - echo "DOCKER_IMAGE_VERSION=${DOCKER_IMAGE_VERSION}" >> $GITHUB_OUTPUT + export DOCKER_IMAGE_NAME + echo "DOCKER_IMAGE_NAME: ${DOCKER_IMAGE_NAME}" echo "DOCKER_IMAGE_VERSION: ${DOCKER_IMAGE_VERSION}" + + # Just generate a new docker image, so pass action = none + ./build_for_ultimaker.sh -a docker_build + docker images - # Generate Docker metadata (tags and labels) - - name: Docker metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: | - ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ env.DOCKER_IMAGE_NAME }} - tags: | - type=semver,pattern={{version}},value=${{ steps.version.outputs.DOCKER_IMAGE_VERSION }} - type=semver,pattern={{major}}.{{minor}},value=${{ steps.version.outputs.DOCKER_IMAGE_VERSION }} - type=semver,pattern={{major}},value=${{ steps.version.outputs.DOCKER_IMAGE_VERSION }} - flavor: | - latest=auto - - # Build and push the Docker image - - name: Build and Push Docker Image - id: build - uses: docker/build-push-action@v5 - with: - context: . - file: docker_env/Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - secrets: | - github_token=${{ secrets.GITHUB_TOKEN }} - cache-from: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache - cache-to: type=registry,ref=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_PROJECT }}/${{ env.DOCKER_REPOSITORY }}/${{ env.DOCKER_IMAGE_NAME }}:buildcache,mode=max - provenance: false # Disable provenance attestation for compatibility - - # Generate release summary - - name: Generate Summary + - name: Release Docker Image to Github Packages + id: release run: | - set -euo pipefail - - echo "# Docker Image Release Summary" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "## Image Information" >> "$GITHUB_STEP_SUMMARY" - echo "- **Image Name**: \`${{ env.DOCKER_IMAGE_NAME }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- **Version**: \`${{ steps.version.outputs.DOCKER_IMAGE_VERSION }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- **Registry**: \`${{ env.DOCKER_REGISTRY }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- **Digest**: \`${{ steps.build.outputs.digest }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "## Tags" >> "$GITHUB_STEP_SUMMARY" - echo '```' >> "$GITHUB_STEP_SUMMARY" - echo "${{ steps.meta.outputs.tags }}" >> "$GITHUB_STEP_SUMMARY" - echo '```' >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "## Security Features" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ Workload Identity Federation (no long-lived secrets)" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ OAuth2 access token authentication" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ Semantic versioning from git tags" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ Layer caching for efficient builds" >> "$GITHUB_STEP_SUMMARY" - echo "- ✅ Multi-platform buildx support" >> "$GITHUB_STEP_SUMMARY" + export DOCKER_IMAGE_VERSION="${GITHUB_REF_NAME##${DOCKER_TAG_PREFIX}}" + export DOCKER_IMAGE_NAME + echo "DOCKER_IMAGE_NAME: ${DOCKER_IMAGE_NAME}" + echo "DOCKER_IMAGE_VERSION: ${DOCKER_IMAGE_VERSION}" + docker push "${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_VERSION}" diff --git a/.github/workflows/release_pkg.yml b/.github/workflows/release_pkg.yml index ca507ff..a8c97b9 100644 --- a/.github/workflows/release_pkg.yml +++ b/.github/workflows/release_pkg.yml @@ -5,11 +5,6 @@ on: description: "The GAR repository to release the package" required: true type: string - embedded_workflows_branch: - description: 'Branch to checkout embedded-workflows repo' - type: string - required: false - default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged jobs: Release_Package: @@ -24,14 +19,8 @@ jobs: EMB_GCP_LOCATION: ${{ vars.EMB_GCP_LOCATION }} EMB_GCP_PROJECT: ${{ vars.EMB_GCP_PROJECT }} EMB_WI_PROVIDER: ${{ vars.EMB_WI_PROVIDER }} - steps: - - name: Checkout Embedded-workflows repo - uses: actions/checkout@v4 - with: - repository: Ultimaker/embedded-workflows - path: embedded-workflows - ref: ${{ inputs.embedded_workflows_branch }} + steps: - uses: actions/download-artifact@v4 with: name: build-package @@ -88,9 +77,6 @@ jobs: DEB_FILE: ${{ steps.meta.outputs.deb_file }} PKG_NAME: ${{ steps.meta.outputs.pkg_name }} PKG_VERSION: ${{ steps.meta.outputs.pkg_version }} - APT_REPO: ${{ env.EMB_APT_REPO }} - GCP_PROJECT: ${{ env.EMB_GCP_PROJECT }} - GCP_LOCATION: ${{ env.EMB_GCP_LOCATION }} run: | set -euo pipefail @@ -100,9 +86,9 @@ jobs: echo "- File: \`${DEB_FILE}\`" >> "$GITHUB_STEP_SUMMARY" echo "- Package: \`${PKG_NAME}\`" >> "$GITHUB_STEP_SUMMARY" echo "- Version: \`${PKG_VERSION}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- Repository: \`${APT_REPO}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- Project: \`${GCP_PROJECT}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- Location: \`${GCP_LOCATION}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- Repository: \`${EMB_APT_REPO}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- Project: \`${EMB_GCP_PROJECT}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- Location: \`${EMB_GCP_LOCATION}\`" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "## Distribution Support" >> "$GITHUB_STEP_SUMMARY" echo "This package supports multiple Debian distributions. The distribution is determined by the package metadata set during the build process. Supported distributions are:" >> "$GITHUB_STEP_SUMMARY" @@ -123,12 +109,12 @@ jobs: echo "2. Authenticate with gcloud and configure your project (if needed):" >> "$GITHUB_STEP_SUMMARY" echo '```bash' >> "$GITHUB_STEP_SUMMARY" echo "gcloud auth login" >> "$GITHUB_STEP_SUMMARY" - echo "gcloud config set project ${GCP_PROJECT}" >> "$GITHUB_STEP_SUMMARY" + echo "gcloud config set project ${EMB_GCP_PROJECT}" >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "3. Add the GAR APT repository (replace \`buster\` with your target distribution):" >> "$GITHUB_STEP_SUMMARY" echo '```bash' >> "$GITHUB_STEP_SUMMARY" - echo "echo 'deb [signed-by=/usr/share/keyrings/artifact-registry-keyring.gpg] https://${GCP_LOCATION}-apt.pkg.dev/${GCP_PROJECT}/${APT_REPO} buster main' | sudo tee /etc/apt/sources.list.d/${APT_REPO}.list" >> "$GITHUB_STEP_SUMMARY" + echo "echo 'deb [signed-by=/usr/share/keyrings/artifact-registry-keyring.gpg] https://${EMB_GCP_LOCATION}-apt.pkg.dev/${EMB_GCP_PROJECT}/${EMB_APT_REPO} buster main' | sudo tee /etc/apt/sources.list.d/${EMB_APT_REPO}.list" >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "4. Update and install:" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 1fa8307..e1148da 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -1,11 +1,5 @@ on: workflow_call: - inputs: - embedded_workflows_branch: - description: 'Branch to checkout embedded-workflows repo' - type: string - required: false - default: 'EMB-115_migrate_to_gar' # TODO: Change to `main` once merged jobs: Shellcheck: @@ -19,15 +13,7 @@ jobs: submodules: recursive fetch-depth: 0 - - name: Checkout Embedded-workflows repo - uses: actions/checkout@v4 - with: - repository: Ultimaker/embedded-workflows - path: embedded-workflows - ref: ${{ inputs.embedded_workflows_branch }} - - name: Shell Check id: shellcheck run: | - # Use the centralized build script from embedded-workflows - ./embedded-workflows/scripts/build_for_ultimaker.sh -a shellcheck + ./build_for_ultimaker.sh -a shellcheck diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 0d0e10f..43e045e 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -1,11 +1,5 @@ on: workflow_call: - inputs: - embedded_workflows_branch: - description: 'Branch to checkout embedded-workflows repo' - type: string - required: false - default: 'main' jobs: Unit_Test: @@ -14,46 +8,15 @@ jobs: permissions: contents: 'read' id-token: 'write' - env: - DOCKER_REGISTRY: ${{ vars.EMB_DOCKER_REGISTRY }} - DOCKER_PROJECT: ${{ vars.EMB_GCP_PROJECT }} - DOCKER_REPOSITORY: ${{ vars.EMB_DOCKER_REPOSITORY }} + steps: - name: Checkout Repository uses: actions/checkout@v4 with: - token: ${{ secrets.ULTIMAKER_CI_PAT }} # TODO: Replace with GITHUB_TOKEN - See https://ultimaker.atlassian.net/browse/EMB-120 + token: ${{ secrets.ULTIMAKER_CI_PAT }} submodules: recursive - - name: Checkout Embedded-workflows repo - uses: actions/checkout@v4 - with: - repository: Ultimaker/embedded-workflows - path: embedded-workflows - ref: ${{ inputs.embedded_workflows_branch }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # Authenticate to Google Cloud using Workload Identity Federation - - name: Authenticate to Google Cloud - id: auth - uses: google-github-actions/auth@v2 - with: - token_format: 'access_token' - workload_identity_provider: ${{ vars.EMB_WI_PROVIDER }} - service_account: ${{ vars.EMB_DOCKER_SERVICE_ACCOUNT }} - - # Login to Google Artifact Registry for Docker operations - - name: Login to GAR - uses: docker/login-action@v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: oauth2accesstoken - password: ${{ steps.auth.outputs.access_token }} - - name: Unit Test id: unittest run: | - # Use the centralized build script from embedded-workflows - ./embedded-workflows/scripts/build_for_ultimaker.sh -a unittest + ./build_for_ultimaker.sh -a unittest diff --git a/scripts/build_for_ultimaker.sh b/scripts/build_for_ultimaker.sh deleted file mode 100755 index 6f8f5eb..0000000 --- a/scripts/build_for_ultimaker.sh +++ /dev/null @@ -1,149 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2019 Ultimaker B.V. -# -# Centralized build script for embedded firmware projects -# This script orchestrates Docker-based builds with proper authentication - -set -eu - -ARCH="armhf" - -SRC_DIR="$(pwd)" -RELEASE_VERSION="${RELEASE_VERSION:-0.0.1-alpha.0}" -DOCKER_WORK_DIR="/build" -BUILD_DIR_TEMPLATE="_build_${ARCH}" -BUILD_DIR="${BUILD_DIR:-${SRC_DIR}/${BUILD_DIR_TEMPLATE}}" - -# Docker configuration -DOCKER_IMAGE_NAME="${DOCKER_IMAGE_NAME:-opinicus}" -DOCKER_REGISTRY="${DOCKER_REGISTRY:-europe-west1-docker.pkg.dev}" -DOCKER_PROJECT="${DOCKER_PROJECT:-}" -DOCKER_REPOSITORY="${DOCKER_REPOSITORY:-}" - -run_env_check="yes" -run_verification="yes" -action="none" - -env_check() -{ - run_in_docker "./docker_env/buildenv_check.sh" -} - -run_build() -{ - run_in_docker "./build.sh" "$@" -} - -run_verification() -{ - echo "Testing!" - # These verifications should never fail! See .gitlab-ci.yml - ./ci/local/run_all.sh -} - -run_shellcheck() -{ - docker run \ - --rm \ - -v "$(pwd):${DOCKER_WORK_DIR}:ro" \ - -w "${DOCKER_WORK_DIR}" \ - "registry.hub.docker.com/koalaman/shellcheck-alpine:stable" \ - "./run_shellcheck.sh" -} - -run_unittest() -{ - echo "Running unit tests!" - run_in_docker "./ci/local/run_unit_test.sh" -} - -usage() -{ - echo "Usage: ${0} [OPTIONS]" - echo " -a ACTION Specify action (build|build_docker|build_docker_cache|shellcheck|unittest)" - echo " -c Skip build environment checks" - echo " -h Print usage" - echo " -s Skip code verification" -} - -while getopts ":a:chls" options; do - case "${options}" in - a) - action="${OPTARG}" - ;; - c) - run_env_check="no" - ;; - h) - usage - exit 0 - ;; - s) - run_verification="no" - ;; - :) - echo "Option -${OPTARG} requires an argument." - exit 1 - ;; - ?) - echo "Invalid option: -${OPTARG}" - exit 1 - ;; - esac -done -shift "$((OPTIND - 1))" - -if ! command -V docker; then - echo "Docker not found, docker-less builds are not supported." - exit 1 -fi - -case "${action}" in - shellcheck) - run_shellcheck - exit 0 - ;; - build) - source ./docker_env/make_docker.sh "" - run_build "$@" - exit 0 - ;; - build_docker) - # Build Docker image without loading it locally - # This is typically used in CI/CD pipelines - source ./docker_env/make_docker.sh "" - exit 0 - ;; - build_docker_cache) - export DOCKER_BUILD_ONLY_CACHE="yes" - source ./docker_env/make_docker.sh "" - exit 0 - ;; - unittest) - source ./docker_env/make_docker.sh "" - run_unittest - exit 0 - ;; - none) - ;; - *) - echo "Invalid action: ${action}" - exit 1 - ;; -esac - -# Make sure to pass an empty argument to make_docker, else any arguments passed to build_for_ultimaker is passed to make_docker instead! -source ./docker_env/make_docker.sh "" - -if [ "${run_env_check}" = "yes" ]; then - env_check -fi - -run_build "$@" - -if [ "${run_verification}" = "yes" ]; then - run_verification -fi - -exit 0