Merge pull request #278 from JanakaSandaruwan/test/gcp-logs-realistic… #184
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Copyright 2026 The OpenChoreo Authors | |
| # SPDX-License-Identifier: Apache-2.0 | |
| name: Build Images and Release Charts | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - "*/**" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| packages: write | |
| jobs: | |
| unit-test: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install yq | |
| uses: mikefarah/yq@v4 | |
| # TODO: Extend language setup for non-Go modules as they are added | |
| - name: Determine Go version | |
| id: go-version | |
| run: | | |
| # Find the highest Go version across all go.mod files | |
| GO_VERSION=$(find . -name 'go.mod' -exec grep '^go ' {} \; | awk '{print $2}' | sort -V | tail -1) | |
| echo "version=${GO_VERSION}" >> "$GITHUB_OUTPUT" | |
| - name: Set up Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: ${{ steps.go-version.outputs.version }} | |
| - name: Run unit tests for all modules | |
| run: | | |
| set -euo pipefail | |
| TEST_COUNT=0 | |
| FAILED=false | |
| for module_dir in */; do | |
| module="${module_dir%/}" | |
| # Skip if this module has no module.yaml | |
| if [ ! -f "${module}/module.yaml" ]; then | |
| continue | |
| fi | |
| # Skip modules without a Makefile | |
| if [ ! -f "${module}/Makefile" ]; then | |
| echo "Skipping ${module}: no Makefile" | |
| continue | |
| fi | |
| # Skip if Makefile has no unit-test target | |
| if ! grep -q '^unit-test:' "${module}/Makefile"; then | |
| continue | |
| fi | |
| echo "::group::Testing ${module}" | |
| make -C "${module}" unit-test | |
| echo "::endgroup::" | |
| TEST_COUNT=$((TEST_COUNT + 1)) | |
| done | |
| if [ "$FAILED" = "true" ]; then | |
| echo "" | |
| echo "One or more modules failed unit tests." | |
| exit 1 | |
| fi | |
| echo "" | |
| if [ "$TEST_COUNT" -eq 0 ]; then | |
| echo "No modules with unit tests found." | |
| else | |
| echo "Successfully tested ${TEST_COUNT} module(s). ✓" | |
| fi | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| use_oidc: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) }} | |
| files: ./*-coverage.out | |
| fail_ci_if_error: false | |
| build-and-release: | |
| needs: unit-test | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| packages: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install yq | |
| uses: mikefarah/yq@v4 | |
| - name: Install Helm | |
| uses: azure/setup-helm@v5 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Login to GHCR (Docker) | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Login to GHCR (Helm) | |
| run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io --username "${{ github.actor }}" --password-stdin | |
| - name: Build images and release charts | |
| run: | | |
| set -euo pipefail | |
| REPO_OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') | |
| GIT_SHA_SHORT=$(git rev-parse --short=8 HEAD) | |
| EVENT_NAME="${{ github.event_name }}" | |
| BEFORE_SHA="${{ github.event.before }}" | |
| # Determine changed files | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | |
| CHANGED_FILES=$(git ls-files) | |
| elif [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then | |
| CHANGED_FILES=$(git diff --name-only "$BEFORE_SHA" HEAD) | |
| else | |
| CHANGED_FILES=$(git ls-files) | |
| fi | |
| echo "Changed files:" | |
| echo "$CHANGED_FILES" | |
| echo "GIT_SHA_SHORT=${GIT_SHA_SHORT}" | |
| echo "" | |
| for module_dir in */; do | |
| module="${module_dir%/}" | |
| chart_dir="${module}/helm" | |
| # Skip directories without a helm chart | |
| if [ ! -f "${chart_dir}/Chart.yaml" ]; then | |
| continue | |
| fi | |
| # Per-module changed-file slice | |
| MODULE_FILES=$(echo "$CHANGED_FILES" | grep "^${module}/" || true) | |
| MODULE_CHANGED=false #Any file under the module changed | |
| VERSION_BUMPED=false #The module's VERSION file was changed | |
| if [ -n "$MODULE_FILES" ]; then | |
| MODULE_CHANGED=true | |
| fi | |
| # Detect a VERSION-file bump for this module | |
| VERSION_FILE="${module}/VERSION" | |
| RELEASE_VERSION="" | |
| if [ -f "$VERSION_FILE" ]; then | |
| RELEASE_VERSION=$(tr -d '[:space:]' < "$VERSION_FILE") | |
| OLD_VERSION="" | |
| if [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then | |
| OLD_VERSION=$(git show "${BEFORE_SHA}:${VERSION_FILE}" 2>/dev/null | tr -d '[:space:]' || echo "") | |
| fi | |
| if [ -n "$RELEASE_VERSION" ] && [ "$RELEASE_VERSION" != "$OLD_VERSION" ]; then | |
| VERSION_BUMPED=true | |
| fi | |
| fi | |
| # workflow_dispatch: force dev publish only, never formal | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | |
| MODULE_CHANGED=true | |
| VERSION_BUMPED=false | |
| fi | |
| if [ "$MODULE_CHANGED" = "false" ] && [ "$VERSION_BUMPED" = "false" ]; then | |
| echo "Skipping ${module}: no changes" | |
| continue | |
| fi | |
| echo "::group::${module} (changed=${MODULE_CHANGED} version_bumped=${VERSION_BUMPED} release_version=${RELEASE_VERSION:-none})" | |
| # ── Build container images ── | |
| if [ "$MODULE_CHANGED" = "true" ] || [ "$VERSION_BUMPED" = "true" ]; then | |
| if [ -f "${module}/module.yaml" ]; then | |
| image_count=$(yq '.images | length' "${module}/module.yaml") | |
| if [ "$image_count" != "0" ] && [ "$image_count" != "null" ]; then | |
| for i in $(seq 0 $((image_count - 1))); do | |
| image_name=$(yq ".images[${i}].name" "${module}/module.yaml") | |
| context=$(yq ".images[${i}].context" "${module}/module.yaml") | |
| dockerfile=$(yq ".images[${i}].dockerfile" "${module}/module.yaml") | |
| FULL_IMAGE="ghcr.io/${REPO_OWNER}/${image_name}" | |
| TAG_ARGS=() | |
| if [ "$MODULE_CHANGED" = "true" ]; then | |
| TAG_ARGS+=(-t "${FULL_IMAGE}:latest-dev") | |
| TAG_ARGS+=(-t "${FULL_IMAGE}:${GIT_SHA_SHORT}") | |
| fi | |
| if [ "$VERSION_BUMPED" = "true" ]; then | |
| TAG_ARGS+=(-t "${FULL_IMAGE}:${RELEASE_VERSION}") | |
| fi | |
| echo "Building and pushing ${FULL_IMAGE} with tags: ${TAG_ARGS[*]}" | |
| docker buildx build \ | |
| --platform linux/amd64,linux/arm64 \ | |
| --push \ | |
| "${TAG_ARGS[@]}" \ | |
| -f "${module}/${dockerfile}" \ | |
| "${module}/${context}" | |
| done | |
| fi | |
| fi | |
| fi | |
| # ── Package and publish the Helm chart ── | |
| if [ "$MODULE_CHANGED" = "true" ] || [ "$VERSION_BUMPED" = "true" ]; then | |
| chart_name=$(yq '.name' "${chart_dir}/Chart.yaml") | |
| # Add dependency repos and build (once per module) | |
| dep_count=$(yq '.dependencies | length' "${chart_dir}/Chart.yaml") | |
| if [ "$dep_count" != "null" ] && [ "$dep_count" != "0" ]; then | |
| for i in $(seq 0 $((dep_count - 1))); do | |
| dep_repo=$(yq ".dependencies[${i}].repository" "${chart_dir}/Chart.yaml") | |
| if [ -n "$dep_repo" ] && [ "$dep_repo" != "null" ]; then | |
| dep_name="dep-$(echo "$dep_repo" | md5sum | cut -d' ' -f1)" | |
| helm repo add "$dep_name" "$dep_repo" 2>/dev/null || true | |
| fi | |
| done | |
| helm dependency build "${chart_dir}" | |
| fi | |
| # Each version override bakes into a separate .tgz, so package + push per target | |
| package_and_push() { | |
| local target_version="$1" | |
| local target_app_version="$2" | |
| rm -rf .packages | |
| helm package "${chart_dir}" \ | |
| --version "${target_version}" \ | |
| --app-version "${target_app_version}" \ | |
| -d .packages | |
| echo "Pushing ${chart_name}:${target_version} to ghcr.io/${REPO_OWNER}/helm-charts..." | |
| helm push ".packages/${chart_name}-${target_version}.tgz" "oci://ghcr.io/${REPO_OWNER}/helm-charts" | |
| } | |
| if [ "$MODULE_CHANGED" = "true" ]; then | |
| package_and_push "0.0.0-latest-dev" "latest-dev" | |
| package_and_push "0.0.0-${GIT_SHA_SHORT}" "${GIT_SHA_SHORT}" | |
| fi | |
| if [ "$VERSION_BUMPED" = "true" ]; then | |
| package_and_push "${RELEASE_VERSION}" "${RELEASE_VERSION}" | |
| fi | |
| fi | |
| # ── Create and push git tag for the release ── | |
| if [ "$VERSION_BUMPED" = "true" ]; then | |
| TAG="${module}-${RELEASE_VERSION}" | |
| if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then | |
| echo "Tag ${TAG} already exists; skipping" | |
| else | |
| echo "Creating and pushing tag ${TAG}" | |
| git tag "${TAG}" HEAD | |
| git push origin "refs/tags/${TAG}" | |
| fi | |
| fi | |
| echo "::endgroup::" | |
| done |