Skip to content

Commit 7fa72f8

Browse files
ferr3ira-gabrielguimoreirarclaudefactory-droid[bot]
authored
release: enhanced Go workflows with monorepo support and Slack notifications (#24)
* feat: add GitOps Update workflow and comprehensive documentation - Add GitOps Update reusable workflow with multi-environment support (dev/stg/prd/sandbox) - Add self-release workflow for automated semantic versioning - Create comprehensive documentation for all workflows: - GitOps Update workflow guide - API Dog E2E Tests workflow guide - PR Security Scan workflow guide - Release workflow guide - Documentation index with examples and best practices - Add semantic release configuration (.releaserc.yml) - Update CONTRIBUTING.md with conventional commits guidelines and release process - Update README.md with versioning section and documentation links - Simplify release workflow to use .releaserc.yml configuration Key Features: - Multi-environment GitOps updates with automatic tag detection - Static and dynamic YAML key mapping support - Optional ArgoCD sync integration - Docker Hub login to avoid rate limits - Comprehensive documentation with examples and troubleshooting - Automated semantic versioning with conventional commits * chore: change dependabot target branch to develop * chore: add PR template * feat(gitops): add convention-based configuration with auto-generated paths and names - Add convention-based configuration that auto-generates values from repository name - Auto-generate app name, artifact pattern, commit prefix, and ArgoCD app name - Auto-generate GitOps file paths based on server and environment - Make Docker Hub login enabled by default to avoid rate limits - Simplify ArgoCD sync steps using the action module - Remove sandbox tag detection (sandbox only updates with production releases) - Update production releases to sync both prd and sandbox environments - Reduce required inputs from 11 to just 1 (yaml_key_mappings) - Update documentation with simplified examples and convention details * docs: replace real repository names and IDs with fictional examples - Replace real repository names (plugin-auth, plugin-crm, midaz) with generic examples - Replace real Apidog environment IDs with fictional values - Replace organization-specific secret names with generic examples - Move environment_id from input to secret in api-dog-e2e-tests workflow for better security - Update all documentation examples to use fictional data * chore: remove rc rule from .releaserc.yml * chore: pass dockerfile path as parameter * chore: pass npm secrets as parameter * chore: pass npm secrets as parameter * refactor: simplify GitOps workflow environment detection and file handling * fix: simplify ArgoCD app name handling in sync workflow * feat: enhanced Go workflows with monorepo support and Slack notifications (#23) * feat: add Go workflows and PR validation with GitHub token support Add comprehensive Go project workflows: - go-ci.yml: Multi-version CI with linting and cross-platform builds - go-security.yml: Security scanning with 8 tools (gosec, trivy, etc.) - go-release.yml: Automated releases with GoReleaser - go-unit-tests.yml: Fast unit testing with matrix support - go-coverage-check.yml: Coverage validation with PR comments - pr-validation.yml: PR validation with semantic titles and auto-labeling All workflows support GitHub token as optional secret parameter for API operations. Token defaults to GITHUB_TOKEN if not provided. Add release-candidate branch to semantic-release configuration. Update documentation: - Add workflow usage guides for all new workflows - Remove emojis from README - Update branch order to: develop, release-candidate, main * fix: rename github_token to manage_token to avoid reserved name collision GitHub Actions reserves the name 'github_token' as a system name, causing workflow_call to fail with collision error. Changes: - Rename secret 'github_token' to 'manage_token' in go-coverage-check.yml - Rename secret 'github_token' to 'manage_token' in pr-validation.yml - Update all references (9 total) across both workflows This aligns with the pattern used in gitops-update.yml and prevents the "secret name collides with system reserved name" error. * Upgrade CodeQL Action from v3 to v4 Updated the CodeQL action used for SARIF upload from the deprecated v3 to the current v4 version. Changes: - Update github/codeql-action/upload-sarif from v3 to v4 This addresses the deprecation warning: "CodeQL Action v3 will be deprecated in December 2026" Benefits of v4: - Improved performance and stability - Better permission handling with reusable workflows - Enhanced compatibility with GitHub's security features - Long-term support and active maintenance Reference: https://github.blog/changelog/2025-10-28-upcoming-deprecation-of-codeql-action-v3/ This should also resolve the "Resource not accessible by integration" errors when uploading SARIF files to GitHub Code Scanning. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: rename github_token to manage_token to avoid reserved name conflict (#21) * fix: update go-coverage-report version and remove all-checks-pass job - Update fgrosse/go-coverage-report from @v1 to @v1.2.0 (v1 doesn't exist) - Remove all-checks-pass job to prevent cascading failures - Individual checks are sufficient for PR validation Fixes issues in lerian-cli PR #1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(go-coverage-check): aggregate coverage by package instead of listing individual files ## Problem The "Coverage by Package" section in PR comments was empty or showing individual file entries instead of aggregated package-level coverage. The AWK script had two issues: 1. Used reserved keyword `func` as variable name (syntax error) 2. Listed individual files with coverage, not package aggregates ## Solution - Generate markdown table format for better readability - Extract package path by removing filename from file path - Accumulate coverage data across all functions in each package - Calculate average coverage percentage per package - Sort packages alphabetically for consistent output ## Changes - Replace bullet list with markdown table (| Package | Coverage |) - Fix AWK syntax by using `pkg` instead of reserved `func` - Add aggregation logic to sum coverage across package functions - Add debug output to show generated coverage report ## Output Example ### Before (Broken) \`\`\` ## Coverage by Package - **path/to/file.go**: 85.5% - **path/to/file.go**: 90.2% ... \`\`\` ### After (Fixed) \`\`\` ## Coverage by Package | Package | Coverage | |---------|----------| | \`github.com/org/project/internal/client\` | 95.6% | | \`github.com/org/project/internal/config\` | 88.7% | | \`github.com/org/project/internal/output\` | 98.8% | \`\`\` ## Testing Tested with Go 1.24 coverage files, verified correct aggregation across multiple packages with varying coverage levels. * feat(changed-paths): add reusable workflow for detecting changed paths Adds a new reusable workflow for monorepo CI/CD optimization that detects changed paths between commits with support for path filtering, path level trimming, and app name generation for matrix strategies. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat: add Go PR analysis reusable workflow New workflow for Go monorepo PR analysis that handles: - Change detection for specified paths - Matrix-based execution per changed app - GolangCI-Lint with configurable version - Security scanning (gosec, govulncheck) - Unit tests with coverage - Coverage threshold check with PR comments - Build verification All logic is centralized in this workflow for easy maintenance. Includes documentation at docs/go-pr-analysis-workflow.md * feat: upgrade golangci-lint-action to v7 for golangci-lint v2 support - golangci-lint v2.x requires golangci-lint-action v7 - v6 only supports golangci-lint v1.x Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat: add detailed coverage report with package breakdown - Generate coverage by package table in PR comments - Generate HTML coverage report as artifact - Add coverage summary to GitHub Actions summary - Update existing PR comments instead of creating duplicates Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: add debug logging for coverage comment posting Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat: add monorepo support with filter_paths for release workflow - Add filter_paths input to detect changes in monorepo apps - Add prepare_matrix job to build dynamic matrix from changed paths - If filter_paths provided: detects changes and releases only changed apps - If filter_paths empty: single app mode, releases from root - Support per-app working directories via matrix - Install @semantic-release/changelog for per-app changelogs Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat: add unified build workflow for Docker images - Support both DockerHub and GHCR registries - Optional change detection with filter_paths for monorepo - Platform strategy: beta/rc = amd64 only, release = amd64+arm64 - Semantic versioning tags - Optional app name prefix for monorepo apps - Build caching with GitHub Actions cache - Default runner: firmino-lxc-runners Also updated release.yml default runner to firmino-lxc-runners. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat: use changed-paths.yml internally for monorepo detection Updated build.yml, release.yml, and pr-security-scan.yml to use the local changed-paths.yml workflow instead of external action. - Removed dependency on LerianStudio/github-actions-changed-paths - All workflows now use 3-job pattern: detect_changes -> prepare -> main job - Changed filter_paths to JSON array format for consistency - Default runner: firmino-lxc-runners Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: improve GHCR authentication and image naming - Use github.actor for GHCR username (matches existing pipeline) - Require ghcr_token secret (no fallback) - Normalize repository owner to lowercase (GHCR requirement) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: use github-actions-changed-paths action in all workflows Switched from workflow call (./.github/workflows/changed-paths.yml) to using the action (LerianStudio/github-actions-changed-paths@main) as a step within jobs. This approach works because actions can be used from any repository, while workflow calls with relative paths fail when called from external repositories. - build.yml: use action in prepare job - release.yml: use action in prepare job - pr-security-scan.yml: already using action (no change) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: remove block scalar from filter_paths to fix action input The YAML block scalar (|-) was causing issues when passing filter_paths to the github-actions-changed-paths action. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: move working_directory to action input for semantic-release step working-directory is only valid for 'run' steps, not 'uses' steps. The cycjimmy/semantic-release-action accepts working_directory as an input. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: add Node.js setup step for release workflow Self-hosted runners may not have Node.js installed. Add actions/setup-node@v4 to ensure npm is available. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: extract version from prefixed tag for Docker metadata Monorepo tags like 'agent-v1.0.0-beta.1' need the prefix stripped to get a valid semver 'v1.0.0-beta.1' for docker/metadata-action. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: handle multi-hyphen app names in version extraction Use sed pattern that finds '-v' followed by digit to properly extract version from tags like 'control-plane-v1.0.0-beta.1'. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat(go-pr-analysis): add single-app mode when filter_paths is empty When filter_paths is not provided or empty, the workflow now treats the repository as a single-app repo and runs against the root directory instead of trying to detect changed directories. This simplifies usage for non-monorepo Go projects: - Monorepo: filter_paths: '["apps/api", "apps/worker"]' - Single-app: filter_paths not set (or empty) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat(build): add GitOps artifacts upload support - Add enable_gitops_artifacts input (default: false) - Upload tag artifacts for downstream gitops-update workflow - Artifacts follow pattern: gitops-tags-<app-name>/<app-name>.tag * feat(workflows): add Slack notifications to all reusable workflows - Created slack-notify.yml reusable workflow for centralized notifications - All workflows now notify on both success and failure - Messages include: repo name, author, failed jobs (on failure), commit, branch - Graceful degradation when SLACK_WEBHOOK_URL secret not configured - Uses secrets: inherit pattern for app repos (no per-repo configuration needed) Updated workflows: - build.yml - go-pr-analysis.yml - pr-validation.yml - pr-security-scan.yml - release.yml - gitops-update.yml - api-dog-e2e-tests.yml - go-release.yml Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat(workflows): migrate to secrets inherit pattern - Remove secrets declarations from workflow_call (workflows now use org secrets directly) - Update secret references to use uppercase org secret names: - DOCKER_USERNAME, DOCKER_PASSWORD - MANAGE_TOKEN - LERIAN_STUDIO_MIDAZ_PUSH_BOT_APP_ID, LERIAN_STUDIO_MIDAZ_PUSH_BOT_PRIVATE_KEY - LERIAN_CI_CD_USER_GPG_KEY, LERIAN_CI_CD_USER_GPG_KEY_PASSWORD - LERIAN_CI_CD_USER_NAME, LERIAN_CI_CD_USER_EMAIL - ARGOCD_GHUSER_TOKEN, ARGOCD_URL - TAP_GITHUB_TOKEN, GORELEASER_KEY, NPMRC_TOKEN - Keep slack-notify.yml with secret input (nested workflow requirement) - Keep api-dog-e2e-tests.yml unchanged (app-specific secrets, notifications commented out) App repos now use 'secrets: inherit' for simpler configuration. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * feat(pr-validation): add source branch validation - Add enforce_source_branches input to enable branch source checking - Add allowed_source_branches input for configuring allowed patterns - Add target_branches_for_source_check input for specifying protected branches - Add pr-source-branch job that validates and adds REQUEST_CHANGES review if invalid - Supports exact match (develop) and prefix match (hotfix/*) patterns * fix(pr-security-scan): scan only changed component folder instead of entire repo - Change scan-ref from '.' to matrix.working_dir - Only scans the component folder that has changes - Prevents scanning unrelated folders in monorepos * feat(go-pr-analysis): add support for private Go modules - Add go_private_modules input to specify GOPRIVATE pattern - Configure git authentication using MANAGE_TOKEN for private repos - Apply to all jobs: lint, security, tests, coverage, build * fix(go-pr-analysis): clarify coverage report title as Unit Test Coverage * chore: standardize runner_type parameter with firmino-lxc-runners default - Renamed runner to runner_type in build.yml and release.yml - Updated default from ubuntu-* to firmino-lxc-runners in all workflows: - build.yml - release.yml - go-pr-analysis.yml - pr-validation.yml - pr-security-scan.yml - slack-notify.yml - go-ci.yml - go-coverage-check.yml - go-release.yml - go-security.yml - go-unit-tests.yml - changed-paths.yml * fix: enable CGO for race detector in tests -race flag requires cgo to be enabled * fix: remove -race flag from tests (requires gcc not available on custom runners) * fix: restore -race flag with CGO_ENABLED=1 (gcc now available on runners) * docs: update documentation and remove duplicate workflows - Remove go-coverage-check.yml and go-unit-tests.yml (now in go-pr-analysis) - Add build-workflow.md documentation - Add slack-notify-workflow.md documentation - Update go-pr-analysis-workflow.md with private modules, CGO_ENABLED, secrets inherit - Update pr-validation-workflow.md with source branch validation feature - Update pr-security-scan-workflow.md with component-scoped scanning - Update release-workflow.md to use runner_type parameter - Update README.md with new workflows and remove duplicate entries Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> * fix: update self-release.yml to use runner_type and secrets inherit - Changed runner to runner_type parameter - Changed explicit secrets to secrets: inherit pattern * fix: use ubuntu-latest runner for self-release --------- Co-authored-by: Guilherme Moreira Rodrigues <gui.rodrigues@lerian.studio> Co-authored-by: Guilherme Moreira Rodrigues <30627541+guimoreirar@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
1 parent 8f93ac9 commit 7fa72f8

31 files changed

+7891
-286
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ updates:
66
schedule:
77
interval: "weekly"
88
day: "monday"
9-
target-branch: "main"
9+
target-branch: "develop"
1010
labels:
1111
- "dependencies"
1212
- "github-actions"
@@ -33,7 +33,7 @@ updates:
3333
schedule:
3434
interval: "weekly"
3535
day: "wednesday"
36-
target-branch: "main"
36+
target-branch: "develop"
3737
labels:
3838
- "dependencies"
3939
- "go"

.github/pull_request_template.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
## Description
2+
3+
<!-- Provide a brief description of the changes in this PR -->
4+
5+
## Type of Change
6+
7+
<!-- Mark the relevant option with an "x" -->
8+
9+
- [ ] `feat`: New feature or workflow
10+
- [ ] `fix`: Bug fix
11+
- [ ] `docs`: Documentation update
12+
- [ ] `refactor`: Code refactoring
13+
- [ ] `perf`: Performance improvement
14+
- [ ] `test`: Adding or updating tests
15+
- [ ] `ci`: CI/CD configuration changes
16+
- [ ] `chore`: Maintenance tasks
17+
- [ ] `BREAKING CHANGE`: Breaking change (requires major version bump)
18+
19+
## Affected Workflows
20+
21+
<!-- Mark all workflows affected by this PR -->
22+
23+
- [ ] GitOps Update
24+
- [ ] API Dog E2E Tests
25+
- [ ] PR Security Scan
26+
- [ ] Release Workflow
27+
- [ ] Other (specify): _______________
28+
29+
## Changes Made
30+
31+
<!-- List the main changes in this PR -->
32+
33+
-
34+
-
35+
-
36+
37+
## Breaking Changes
38+
39+
<!-- If this is a breaking change, describe what breaks and how to migrate -->
40+
41+
**None** / **Describe breaking changes here**
42+
43+
## Testing
44+
45+
<!-- Describe how you tested these changes -->
46+
47+
- [ ] Tested locally
48+
- [ ] Tested in development environment
49+
- [ ] Tested with example repository: _______________
50+
- [ ] All existing workflows still work
51+
52+
## Checklist
53+
54+
<!-- Ensure all items are completed before requesting review -->
55+
56+
- [ ] Code follows conventional commit format
57+
- [ ] Documentation updated (if applicable)
58+
- [ ] Examples updated (if applicable)
59+
- [ ] No hardcoded secrets or sensitive data
60+
- [ ] Backward compatible (or breaking changes documented)
61+
- [ ] Self-review completed
62+
- [ ] Comments added for complex logic
63+
64+
## Related Issues
65+
66+
<!-- Link any related issues -->
67+
68+
Closes #
69+
Related to #
70+
71+
## Additional Notes
72+
73+
<!-- Add any additional context, screenshots, or information -->

.github/workflows/api-dog-e2e-tests.yml

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ on:
1010
description: 'Node.js version to use'
1111
type: string
1212
default: '20'
13-
environment_id:
14-
description: 'Apidog environment ID (optional - will auto-detect from tag if not provided)'
15-
required: false
16-
type: string
1713
test_iterations:
1814
description: 'Number of test iterations'
1915
type: string
@@ -37,6 +33,9 @@ on:
3733
apidog_access_token:
3834
description: 'Apidog access token for authentication'
3935
required: true
36+
environment_id:
37+
description: 'Apidog environment ID (optional - will auto-detect from tag if not provided)'
38+
required: false
4039
dev_environment_id:
4140
description: 'Apidog dev environment ID (for beta tags)'
4241
required: false
@@ -82,7 +81,7 @@ jobs:
8281
- name: Set manual environment ID
8382
if: ${{ !inputs.auto_detect_environment }}
8483
run: |
85-
echo "ENVIRONMENT_ID=${{ inputs.environment_id }}" >> $GITHUB_ENV
84+
echo "ENVIRONMENT_ID=${{ secrets.environment_id }}" >> $GITHUB_ENV
8685
echo "TAG_TYPE=manual" >> $GITHUB_ENV
8786
8887
- name: Run Apidog Test Scenario
@@ -111,4 +110,17 @@ jobs:
111110
if: always()
112111
run: |
113112
npm uninstall -g apidog-cli || true
114-
rm -rf $(npm root -g)/apidog-cli || true
113+
rm -rf $(npm root -g)/apidog-cli || true
114+
115+
# TODO: Add Slack notification when ready
116+
# notify:
117+
# name: Notify
118+
# needs: [api-dog-e2e-tests]
119+
# if: always()
120+
# uses: ./.github/workflows/slack-notify.yml
121+
# with:
122+
# status: ${{ needs.api-dog-e2e-tests.result }}
123+
# workflow_name: "API Dog E2E Tests"
124+
# failed_jobs: ${{ needs.api-dog-e2e-tests.result == 'failure' && 'E2E Tests' || '' }}
125+
# secrets:
126+
# SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

.github/workflows/build.yml

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
name: "Build and Push Docker Images"
2+
3+
# This reusable workflow handles Docker image builds and pushes to registries
4+
# Supports both DockerHub and GHCR (GitHub Container Registry)
5+
#
6+
# Features:
7+
# - Monorepo support with optional change detection via LerianStudio/github-actions-changed-paths
8+
# - Multi-registry support (DockerHub and/or GHCR)
9+
# - Platform strategy: beta/rc builds amd64 only, release builds amd64+arm64
10+
# - Semantic versioning tags
11+
# - GitOps artifacts upload for downstream gitops-update workflow
12+
13+
on:
14+
workflow_call:
15+
inputs:
16+
runner_type:
17+
description: 'Runner to use for the workflow'
18+
type: string
19+
default: 'firmino-lxc-runners'
20+
filter_paths:
21+
description: 'Newline-separated list of path prefixes to filter. If not provided, builds from root.'
22+
type: string
23+
required: false
24+
default: ''
25+
path_level:
26+
description: 'Limits the path to the first N segments (e.g., 2 -> "apps/agent")'
27+
type: string
28+
default: '2'
29+
enable_dockerhub:
30+
description: 'Enable pushing to DockerHub'
31+
type: boolean
32+
default: true
33+
enable_ghcr:
34+
description: 'Enable pushing to GitHub Container Registry'
35+
type: boolean
36+
default: false
37+
dockerhub_org:
38+
description: 'DockerHub organization name'
39+
type: string
40+
default: 'lerianstudio'
41+
ghcr_org:
42+
description: 'GHCR organization name (defaults to repository owner)'
43+
type: string
44+
default: ''
45+
dockerfile_name:
46+
description: 'Name of the Dockerfile'
47+
type: string
48+
default: 'Dockerfile'
49+
app_name_prefix:
50+
description: 'Prefix for app names in monorepo (e.g., "midaz" results in "midaz-agent")'
51+
type: string
52+
default: ''
53+
build_context:
54+
description: 'Docker build context (defaults to repository root for monorepo)'
55+
type: string
56+
default: '.'
57+
enable_gitops_artifacts:
58+
description: 'Enable GitOps artifacts upload for downstream gitops-update workflow'
59+
type: boolean
60+
default: false
61+
62+
permissions:
63+
contents: read
64+
packages: write
65+
66+
jobs:
67+
prepare:
68+
runs-on: ${{ inputs.runner_type }}
69+
outputs:
70+
matrix: ${{ steps.set-matrix.outputs.matrix }}
71+
has_builds: ${{ steps.set-matrix.outputs.has_builds }}
72+
platforms: ${{ steps.set-platforms.outputs.platforms }}
73+
is_release: ${{ steps.set-platforms.outputs.is_release }}
74+
steps:
75+
- name: Get changed paths (monorepo)
76+
if: inputs.filter_paths != ''
77+
id: changed-paths
78+
uses: LerianStudio/github-actions-changed-paths@main
79+
with:
80+
filter_paths: ${{ inputs.filter_paths }}
81+
path_level: ${{ inputs.path_level }}
82+
get_app_name: 'true'
83+
app_name_prefix: ${{ inputs.app_name_prefix }}
84+
85+
- name: Set matrix
86+
id: set-matrix
87+
run: |
88+
if [ -z "${{ inputs.filter_paths }}" ]; then
89+
# Single app mode - build from root
90+
APP_NAME="${{ github.event.repository.name }}"
91+
echo "matrix=[{\"name\": \"${APP_NAME}\", \"working_dir\": \".\"}]" >> $GITHUB_OUTPUT
92+
echo "has_builds=true" >> $GITHUB_OUTPUT
93+
else
94+
MATRIX='${{ steps.changed-paths.outputs.matrix }}'
95+
if [ "$MATRIX" == "[]" ] || [ -z "$MATRIX" ]; then
96+
echo "matrix=[]" >> $GITHUB_OUTPUT
97+
echo "has_builds=false" >> $GITHUB_OUTPUT
98+
else
99+
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
100+
echo "has_builds=true" >> $GITHUB_OUTPUT
101+
fi
102+
fi
103+
104+
- name: Set platforms based on tag type
105+
id: set-platforms
106+
run: |
107+
TAG="${GITHUB_REF#refs/tags/}"
108+
109+
if [[ "$TAG" == *"-beta"* ]] || [[ "$TAG" == *"-rc"* ]]; then
110+
echo "platforms=linux/amd64" >> $GITHUB_OUTPUT
111+
echo "is_release=false" >> $GITHUB_OUTPUT
112+
echo "Building for amd64 only (beta/rc tag)"
113+
else
114+
echo "platforms=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT
115+
echo "is_release=true" >> $GITHUB_OUTPUT
116+
echo "Building for amd64 and arm64 (release tag)"
117+
fi
118+
119+
build:
120+
needs: prepare
121+
if: needs.prepare.outputs.has_builds == 'true'
122+
runs-on: ${{ inputs.runner_type }}
123+
name: Build ${{ matrix.app.name }}
124+
strategy:
125+
max-parallel: 2
126+
fail-fast: false
127+
matrix:
128+
app: ${{ fromJson(needs.prepare.outputs.matrix) }}
129+
130+
steps:
131+
- name: Checkout repository
132+
uses: actions/checkout@v4
133+
134+
- name: Set up QEMU
135+
if: needs.prepare.outputs.is_release == 'true'
136+
uses: docker/setup-qemu-action@v3
137+
138+
- name: Set up Docker Buildx
139+
uses: docker/setup-buildx-action@v3
140+
141+
- name: Log in to DockerHub
142+
if: inputs.enable_dockerhub
143+
uses: docker/login-action@v3
144+
with:
145+
username: ${{ secrets.DOCKER_USERNAME }}
146+
password: ${{ secrets.DOCKER_PASSWORD }}
147+
148+
- name: Log in to GHCR
149+
if: inputs.enable_ghcr
150+
uses: docker/login-action@v3
151+
with:
152+
registry: ghcr.io
153+
username: ${{ github.actor }}
154+
password: ${{ secrets.MANAGE_TOKEN }}
155+
156+
- name: Normalize repository owner to lowercase
157+
id: normalize
158+
run: |
159+
echo "owner_lower=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
160+
161+
- name: Set image names
162+
id: image-names
163+
run: |
164+
IMAGES=""
165+
GHCR_ORG="${{ inputs.ghcr_org }}"
166+
if [ -z "$GHCR_ORG" ]; then
167+
GHCR_ORG="${{ steps.normalize.outputs.owner_lower }}"
168+
else
169+
GHCR_ORG=$(echo "$GHCR_ORG" | tr '[:upper:]' '[:lower:]')
170+
fi
171+
172+
if [ "${{ inputs.enable_dockerhub }}" == "true" ]; then
173+
IMAGES="${{ inputs.dockerhub_org }}/${{ matrix.app.name }}"
174+
fi
175+
176+
if [ "${{ inputs.enable_ghcr }}" == "true" ]; then
177+
if [ -n "$IMAGES" ]; then
178+
IMAGES="${IMAGES},ghcr.io/${GHCR_ORG}/${{ matrix.app.name }}"
179+
else
180+
IMAGES="ghcr.io/${GHCR_ORG}/${{ matrix.app.name }}"
181+
fi
182+
fi
183+
184+
echo "images=$IMAGES" >> $GITHUB_OUTPUT
185+
186+
- name: Extract version from tag
187+
id: version
188+
run: |
189+
TAG="${GITHUB_REF#refs/tags/}"
190+
# Strip app prefix by finding -v and extracting from there
191+
# e.g., agent-v1.0.0-beta.1 -> v1.0.0-beta.1
192+
# e.g., control-plane-v1.0.0-beta.1 -> v1.0.0-beta.1
193+
VERSION=$(echo "$TAG" | sed 's/.*-\(v[0-9]\)/\1/')
194+
echo "version=$VERSION" >> $GITHUB_OUTPUT
195+
echo "Extracted version: $VERSION from tag: $TAG"
196+
197+
- name: Docker metadata
198+
id: meta
199+
uses: docker/metadata-action@v5
200+
with:
201+
images: ${{ steps.image-names.outputs.images }}
202+
tags: |
203+
type=semver,pattern={{version}},value=${{ steps.version.outputs.version }}
204+
type=semver,pattern={{major}}.{{minor}},value=${{ steps.version.outputs.version }}
205+
type=semver,pattern={{major}},value=${{ steps.version.outputs.version }},enable=${{ needs.prepare.outputs.is_release }}
206+
207+
- name: Build and push Docker image
208+
uses: docker/build-push-action@v6
209+
with:
210+
context: ${{ inputs.build_context }}
211+
file: ${{ matrix.app.working_dir }}/${{ inputs.dockerfile_name }}
212+
platforms: ${{ needs.prepare.outputs.platforms }}
213+
push: true
214+
tags: ${{ steps.meta.outputs.tags }}
215+
labels: ${{ steps.meta.outputs.labels }}
216+
cache-from: type=gha
217+
cache-to: type=gha,mode=max
218+
219+
# GitOps artifacts for downstream gitops-update workflow
220+
- name: Create GitOps tag artifact
221+
if: inputs.enable_gitops_artifacts
222+
run: |
223+
set -euo pipefail
224+
mkdir -p gitops-tags
225+
# Extract version without leading 'v'
226+
VERSION="${{ steps.version.outputs.version }}"
227+
VERSION="${VERSION#v}"
228+
printf "%s" "$VERSION" > "gitops-tags/${{ matrix.app.name }}.tag"
229+
echo "Created artifact: gitops-tags/${{ matrix.app.name }}.tag with version: $VERSION"
230+
231+
- name: Upload GitOps tag artifact
232+
if: inputs.enable_gitops_artifacts
233+
uses: actions/upload-artifact@v4
234+
with:
235+
name: gitops-tags-${{ matrix.app.name }}
236+
path: gitops-tags/
237+
238+
# Slack notification
239+
notify:
240+
name: Notify
241+
needs: [prepare, build]
242+
if: always() && needs.prepare.outputs.has_builds == 'true'
243+
uses: ./.github/workflows/slack-notify.yml
244+
with:
245+
status: ${{ needs.build.result }}
246+
workflow_name: "Build & Push Docker Images"
247+
failed_jobs: ${{ needs.build.result == 'failure' && 'Build' || '' }}
248+
secrets:
249+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

0 commit comments

Comments
 (0)