diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 19a810bd..cfd0c92e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ on: enable_ghcr: description: 'Enable pushing to GitHub Container Registry' type: boolean - default: false + default: true dockerhub_org: description: 'DockerHub organization name' type: string @@ -249,7 +249,7 @@ jobs: - name: Set up QEMU if: contains(needs.prepare.outputs.platforms, 'arm64') - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4 + uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4 diff --git a/.github/workflows/go-pr-analysis.yml b/.github/workflows/go-pr-analysis.yml index 3eb89c9d..c1b6d18c 100644 --- a/.github/workflows/go-pr-analysis.yml +++ b/.github/workflows/go-pr-analysis.yml @@ -48,7 +48,7 @@ on: fail_on_coverage_threshold: description: 'Fail the workflow if coverage is below threshold' type: boolean - default: false + default: true enable_lint: description: 'Enable GolangCI-Lint' type: boolean @@ -213,7 +213,11 @@ jobs: - name: Run GolangCI-Lint (direct) if: steps.detect-make.outputs.use_make != 'true' working-directory: ${{ matrix.app.working_dir }} - run: golangci-lint run ${{ inputs.golangci_lint_args }} + env: + GOLANGCI_LINT_ARGS: ${{ inputs.golangci_lint_args }} + run: | + read -ra golangci_args <<< "$GOLANGCI_LINT_ARGS" + golangci-lint run "${golangci_args[@]}" # ============================================ # SECURITY SCANNING @@ -291,7 +295,7 @@ jobs: - name: Run Gosec for SARIF id: gosec-sarif - uses: securego/gosec@4a3bd8af174872c778439083ded7adbf3747e770 # v2.26.1 + uses: securego/gosec@9e6a9843d7a4a6e3e9a8539b02612c8a4aa3f889 # v2.27.1 with: args: -no-fail -fmt sarif -out gosec-${{ matrix.app.name }}.sarif ./${{ matrix.app.working_dir }}/... diff --git a/.github/workflows/go-pr-validation.yml b/.github/workflows/go-pr-validation.yml index d0e49353..7018acb3 100644 --- a/.github/workflows/go-pr-validation.yml +++ b/.github/workflows/go-pr-validation.yml @@ -92,6 +92,14 @@ on: description: 'GolangCI-Lint version' type: string default: 'v1.62.2' + golangci_lint_args: + description: 'Extra arguments passed to golangci-lint (e.g. --timeout=5m)' + type: string + default: '--timeout=5m' + app_name_prefix: + description: 'Prefix used to namespace coverage/build artifacts' + type: string + default: '' coverage_threshold: description: 'Minimum coverage percentage required (0-100)' type: number @@ -99,7 +107,7 @@ on: fail_on_coverage_threshold: description: 'Fail the workflow if coverage is below threshold' type: boolean - default: false + default: true go_private_modules: description: 'GOPRIVATE pattern for private Go modules (e.g., github.com/LerianStudio/*)' type: string @@ -208,6 +216,8 @@ jobs: runner_type: ${{ inputs.runner_type }} go_version: ${{ inputs.go_version }} golangci_lint_version: ${{ inputs.golangci_lint_version }} + golangci_lint_args: ${{ inputs.golangci_lint_args }} + app_name_prefix: ${{ inputs.app_name_prefix }} coverage_threshold: ${{ inputs.coverage_threshold }} fail_on_coverage_threshold: ${{ inputs.fail_on_coverage_threshold }} go_private_modules: ${{ inputs.go_private_modules }} diff --git a/.github/workflows/go-release.yml b/.github/workflows/go-release.yml index 5ee852d2..ba3e8322 100644 --- a/.github/workflows/go-release.yml +++ b/.github/workflows/go-release.yml @@ -50,7 +50,7 @@ on: enable_ghcr: description: 'Enable pushing to GitHub Container Registry' type: boolean - default: false + default: true enable_gitops_artifacts: description: 'Enable GitOps artifacts upload for the downstream gitops-update job' type: boolean diff --git a/.github/workflows/go-security.yml b/.github/workflows/go-security.yml index 45d193c4..67f52ada 100644 --- a/.github/workflows/go-security.yml +++ b/.github/workflows/go-security.yml @@ -99,7 +99,7 @@ jobs: cache: true - name: Run Gosec Security Scanner - uses: securego/gosec@4a3bd8af174872c778439083ded7adbf3747e770 # v2.26.1 + uses: securego/gosec@9e6a9843d7a4a6e3e9a8539b02612c8a4aa3f889 # v2.27.1 with: args: '-no-fail -fmt sarif -out gosec-results.sarif ./...' @@ -192,7 +192,7 @@ jobs: fetch-depth: 0 - name: TruffleHog OSS - uses: trufflesecurity/trufflehog@37b77001d0174ebec2fcca2bd83ff83a6d45a3ab # v3.95.3 + uses: trufflesecurity/trufflehog@d411fff7b8879a62509f3fa98c07f247ac089a51 # v3.95.5 with: path: ./ base: ${{ github.event.repository.default_branch }} diff --git a/.github/workflows/helm-update-chart.yml b/.github/workflows/helm-update-chart.yml index fe05b9a7..2ecdc3bf 100644 --- a/.github/workflows/helm-update-chart.yml +++ b/.github/workflows/helm-update-chart.yml @@ -223,7 +223,7 @@ jobs: go build -o update-chart-version-readme update-chart-version-readme.go - name: Setup yq - uses: mikefarah/yq@751d8ad57b84f1794661bc70c0afb92a22ad7b3c # v4 + uses: mikefarah/yq@1b9b4ac5187171d2e5e3129be0cfa827c7f9d53d # v4 - name: Process all components id: process diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab4400c9..6bd076ff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,6 +53,33 @@ on: type: string default: 'openai/gpt-4o' + # ----------------- Backmerge ----------------- + backmerge_enabled: + description: 'Backmerge the release branch into the target branch after a successful release' + required: false + type: boolean + default: true + backmerge_source: + description: 'Release branch eligible for backmerge; backmerge runs only when the release ref matches this' + required: false + type: string + default: 'main' + backmerge_target: + description: 'Branch that receives the backmerge' + required: false + type: string + default: 'develop' + backmerge_mode: + description: 'Backmerge strategy: direct | pr | direct-with-pr-fallback' + required: false + type: string + default: 'direct-with-pr-fallback' + dry_run: + description: 'Run semantic-release in dry-run mode (no tags/releases created) and preview the backmerge instead of applying it' + required: false + type: boolean + default: false + permissions: contents: read @@ -170,22 +197,44 @@ jobs: npm install --save-dev \ @semantic-release/exec - # ----------------- Snapshot tags before release ----------------- - - name: Snapshot tags before release - id: pre-tags - uses: LerianStudio/github-actions-shared-workflows/src/config/release-tag-snapshot@v1 + # ----------------- Backmerge config preflight ----------------- + - name: Validate backmerge inputs + if: inputs.backmerge_enabled + env: + BACKMERGE_MODE: ${{ inputs.backmerge_mode }} + BACKMERGE_SOURCE: ${{ inputs.backmerge_source }} + BACKMERGE_TARGET: ${{ inputs.backmerge_target }} + run: | + case "$BACKMERGE_MODE" in + direct|pr|direct-with-pr-fallback) ;; + *) + echo "::error::Invalid backmerge_mode '$BACKMERGE_MODE' (must be: direct, pr, direct-with-pr-fallback)" + exit 1 + ;; + esac + if [ "$BACKMERGE_SOURCE" = "$BACKMERGE_TARGET" ]; then + echo "::error::backmerge_source and backmerge_target must differ (got '$BACKMERGE_SOURCE')" + exit 1 + fi + if ! git check-ref-format --branch "$BACKMERGE_SOURCE" >/dev/null 2>&1; then + echo "::error::Invalid backmerge_source ref: '$BACKMERGE_SOURCE'" + exit 1 + fi + if ! git check-ref-format --branch "$BACKMERGE_TARGET" >/dev/null 2>&1; then + echo "::error::Invalid backmerge_target ref: '$BACKMERGE_TARGET'" + exit 1 + fi - name: Semantic Release uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6 id: semantic - continue-on-error: true with: ci: false + dry_run: ${{ inputs.dry_run }} semantic_version: ${{ inputs.semantic_version }} working_directory: ${{ matrix.app.working_dir }} extra_plugins: | conventional-changelog-conventionalcommits@v7.0.2 - @saithodev/semantic-release-backmerge env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} GIT_AUTHOR_NAME: ${{ secrets.LERIAN_CI_CD_USER_NAME }} @@ -193,35 +242,23 @@ jobs: GIT_COMMITTER_NAME: ${{ secrets.LERIAN_CI_CD_USER_NAME }} GIT_COMMITTER_EMAIL: ${{ secrets.LERIAN_CI_CD_USER_EMAIL }} - # ----------------- Detect release via git tag ----------------- - - name: Detect if release was published - if: always() && steps.semantic.outcome == 'failure' - id: detect-release - uses: LerianStudio/github-actions-shared-workflows/src/config/release-tag-check@v1 - with: - previous-tag: ${{ steps.pre-tags.outputs.latest-tag }} - - # ----------------- Backmerge Fallback ----------------- - - name: Backmerge PR fallback + # ----------------- Backmerge ----------------- + - name: Backmerge ${{ inputs.backmerge_source }} → ${{ inputs.backmerge_target }} if: | - always() && steps.semantic.outcome == 'failure' && ( - steps.semantic.outputs.new_release_published == 'true' || - steps.detect-release.outputs.release-published == 'true' - ) - uses: LerianStudio/github-actions-shared-workflows/src/config/backmerge-pr@v1 + inputs.backmerge_enabled && + steps.semantic.outputs.new_release_published == 'true' && + github.ref_name == inputs.backmerge_source + uses: LerianStudio/github-actions-shared-workflows/src/config/backmerge-sync@v1 with: github-token: ${{ steps.app-token.outputs.token }} - source-branch: ${{ github.ref_name }} - version: ${{ steps.semantic.outputs.new_release_version || steps.detect-release.outputs.release-version }} - - - name: Fail if release itself failed - if: | - always() && steps.semantic.outcome == 'failure' && - steps.semantic.outputs.new_release_published != 'true' && - steps.detect-release.outputs.release-published != 'true' - run: | - echo "::error::Semantic release failed before publishing a new version" - exit 1 + source-branch: ${{ inputs.backmerge_source }} + target-branch: ${{ inputs.backmerge_target }} + mode: ${{ inputs.backmerge_mode }} + dry-run: ${{ inputs.dry_run }} + commit-message: "chore(release): backmerge ${source} into ${target} [skip ci]" + pr-title: "chore(release): backmerge ${source} → ${target} (v${{ steps.semantic.outputs.new_release_version }})" + git-user-name: ${{ secrets.LERIAN_CI_CD_USER_NAME }} + git-user-email: ${{ secrets.LERIAN_CI_CD_USER_EMAIL }} # ----------------- Per-leg release publish marker ----------------- # Matrix job outputs are last-writer-wins, so we persist each leg's @@ -230,12 +267,11 @@ jobs: if: always() env: SEMANTIC_PUBLISHED: ${{ steps.semantic.outputs.new_release_published }} - DETECT_PUBLISHED: ${{ steps.detect-release.outputs.release-published }} APP_NAME: ${{ matrix.app.name }} STATUS_DIR: ${{ runner.temp }}/publish-status run: | mkdir -p "$STATUS_DIR" - if [[ "$SEMANTIC_PUBLISHED" == "true" || "$DETECT_PUBLISHED" == "true" ]]; then + if [[ "$SEMANTIC_PUBLISHED" == "true" ]]; then echo "true" > "$STATUS_DIR/${APP_NAME}.txt" echo "✅ ${APP_NAME}: release published" else diff --git a/.github/workflows/slack-notify.yml b/.github/workflows/slack-notify.yml index cad19139..aef051c1 100644 --- a/.github/workflows/slack-notify.yml +++ b/.github/workflows/slack-notify.yml @@ -147,7 +147,7 @@ jobs: - name: Send Slack notification if: steps.check_webhook.outputs.skip != 'true' - uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2 + uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_COLOR: ${{ steps.settings.outputs.color }} diff --git a/.releaserc.yml b/.releaserc.yml index e6e89f14..118d0056 100644 --- a/.releaserc.yml +++ b/.releaserc.yml @@ -42,18 +42,9 @@ plugins: failComment: false labels: [] - # Plugin to perform the backmerge between the main and develop branches - - path: "@saithodev/semantic-release-backmerge" - backmergeBranches: - - from: main - to: develop - backmergeStrategy: merge - message: "chore(release): backmerge ${nextRelease.version} [skip ci]" - - path: "@semantic-release/exec" branches: - name: main - name: develop prerelease: beta - diff --git a/docs/build-workflow.md b/docs/build-workflow.md index 2bdb30a1..22b37981 100644 --- a/docs/build-workflow.md +++ b/docs/build-workflow.md @@ -99,7 +99,7 @@ jobs: | `filter_paths` | string | `''` | Newline-separated list of path prefixes. If empty, builds from root (single-app mode) | | `path_level` | string | `2` | Directory depth for app name extraction | | `enable_dockerhub` | boolean | `true` | Enable pushing to DockerHub | -| `enable_ghcr` | boolean | `false` | Enable pushing to GitHub Container Registry | +| `enable_ghcr` | boolean | `true` | Enable pushing to GitHub Container Registry (requires `MANAGE_TOKEN`) | | `dockerhub_org` | string | `lerianstudio` | DockerHub organization name | | `ghcr_org` | string | `''` | GHCR organization (defaults to repository owner) | | `dockerfile_name` | string | `Dockerfile` | Name of the Dockerfile | diff --git a/docs/go-pr-analysis-workflow.md b/docs/go-pr-analysis-workflow.md index a08a3dad..b24ac2ee 100644 --- a/docs/go-pr-analysis-workflow.md +++ b/docs/go-pr-analysis-workflow.md @@ -102,7 +102,7 @@ jobs: | `golangci_lint_version` | GolangCI-Lint version | No | `v1.62.2` | | `golangci_lint_args` | Additional golangci-lint arguments | No | `--timeout=5m` | | `coverage_threshold` | Minimum coverage percentage (0-100) | No | `80` | -| `fail_on_coverage_threshold` | Fail if coverage below threshold | No | `false` | +| `fail_on_coverage_threshold` | Fail if coverage below threshold | No | `true` | | `enable_lint` | Enable GolangCI-Lint | No | `true` | | `enable_security` | Enable security scanning (gosec, govulncheck) | No | `true` | | `enable_tests` | Enable unit tests | No | `true` | @@ -316,7 +316,7 @@ swagger.go 1. **Pin to version tag**: Use `@v1.0.0` instead of `@v1.0.0` for production stability 2. **Custom linting**: Place `.golangci.yml` in each app directory for app-specific rules -3. **Coverage threshold**: Start with `fail_on_coverage_threshold: false` and enable once baseline is established +3. **Coverage threshold**: Enforced by default (`fail_on_coverage_threshold: true`); set it to `false` to temporarily report coverage without blocking while establishing a baseline 4. **Security findings**: GoSec results appear in GitHub Security tab when SARIF upload succeeds 5. **Performance**: Jobs run in parallel per app - more apps = more parallelism 6. **Makefile consistency**: Use Makefiles to ensure local dev matches CI behavior diff --git a/docs/go-pr-validation.md b/docs/go-pr-validation.md index 73858d36..f8ec18d4 100644 --- a/docs/go-pr-validation.md +++ b/docs/go-pr-validation.md @@ -38,8 +38,10 @@ The `go-analysis`, `security` and `lib-version` pipelines each have a `*-gate` a | `target_branches_for_source_check` | Target branches requiring source validation | string | `main` | | `go_version` | Go version | string | `1.23` | | `golangci_lint_version` | GolangCI-Lint version | string | `v1.62.2` | +| `golangci_lint_args` | Extra arguments passed to golangci-lint (e.g. `--timeout=5m`) | string | `--timeout=5m` | +| `app_name_prefix` | Prefix used to namespace coverage/build artifacts | string | `''` | | `coverage_threshold` | Minimum coverage percentage (0-100) | number | `80` | -| `fail_on_coverage_threshold` | Fail when coverage is below threshold | boolean | `false` | +| `fail_on_coverage_threshold` | Fail when coverage is below threshold | boolean | `true` | | `go_private_modules` | GOPRIVATE pattern for private modules | string | `''` | | `enable_integration_tests` | Enable integration tests | boolean | `false` | | `system_packages` | apt packages to install for CGO repos | string | `''` | diff --git a/docs/go-release-workflow.md b/docs/go-release-workflow.md index 0129b521..f9863313 100644 --- a/docs/go-release-workflow.md +++ b/docs/go-release-workflow.md @@ -24,7 +24,7 @@ Umbrella reusable workflow for Go **service** repositories (deployable apps that | `enable_major_tag` | Force-update the floating major tag (e.g. `v1`) | boolean | `false` | | `stable_releases_only` | Only generate changelogs for stable releases | boolean | `true` | | `enable_dockerhub` | Push image to DockerHub | boolean | `true` | -| `enable_ghcr` | Push image to GitHub Container Registry | boolean | `false` | +| `enable_ghcr` | Push image to GitHub Container Registry (requires `MANAGE_TOKEN`) | boolean | `true` | | `enable_gitops_artifacts` | Upload GitOps artifacts for the downstream update | boolean | `false` | | `app_name` | Override app/image name (single-app mode) | string | `''` (repo name) | | `docker_build_args` | Newline-separated Docker build args | string | `''` | diff --git a/docs/release-workflow.md b/docs/release-workflow.md index cf39f0c4..49a61aaf 100644 --- a/docs/release-workflow.md +++ b/docs/release-workflow.md @@ -83,6 +83,11 @@ jobs: |-------|------|---------|-------------| | `semantic_version` | string | `23.0.8` | Semantic release version to use | | `runner_type` | string | `firmino-lxc-runners` | GitHub runner type | +| `backmerge_enabled` | boolean | `true` | Backmerge the release branch into the target branch after a successful release | +| `backmerge_source` | string | `main` | Release branch eligible for backmerge; backmerge runs only when the release ref matches this | +| `backmerge_target` | string | `develop` | Branch that receives the backmerge | +| `backmerge_mode` | string | `direct-with-pr-fallback` | Backmerge strategy: `direct`, `pr`, or `direct-with-pr-fallback` | +| `dry_run` | boolean | `false` | Run semantic-release in dry-run mode (no tags/releases) and preview the backmerge instead of applying it | ## Secrets @@ -205,11 +210,10 @@ plugins: - "@semantic-release/release-notes-generator" - "@semantic-release/changelog" - "@semantic-release/github" - - - "@saithodev/semantic-release-backmerge" - - backmergeBranches: [develop] - backmergeStrategy: merge ``` +> **Migration:** backmerge is now orchestrated by the workflow, not by semantic-release. Remove any `@saithodev/semantic-release-backmerge` plugin entry from your `.releaserc` and configure backmerge through the `backmerge_*` workflow inputs instead. + ## Workflow Steps 1. **Create GitHub App Token**: Generate authentication token with higher rate limits @@ -456,7 +460,8 @@ jobs: - **@semantic-release/github**: Creates GitHub releases - **@semantic-release/exec**: Executes custom scripts (installed automatically) - **conventional-changelog-conventionalcommits**: Conventional commits support -- **@saithodev/semantic-release-backmerge**: Automatic backmerging + +Backmerging is no longer handled by a semantic-release plugin. After a successful release, the workflow runs the `backmerge-sync` composite action (controlled by the `backmerge_*` inputs) to sync `backmerge_source` into `backmerge_target`. Behavior depends on `backmerge_mode`: `direct` (fail on conflict), `pr` (always open a PR), or `direct-with-pr-fallback` (attempt a direct merge, open a PR on conflict or rejected push). ### Custom Plugins