Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
ff16b50
chore(changelog): backmerge main into develop [skip ci]
lerian-studio Jun 10, 2026
40855fe
chore(deps): bump rtCamp/action-slack-notify
dependabot[bot] Jun 15, 2026
01485f5
chore(deps): bump docker/setup-qemu-action
dependabot[bot] Jun 15, 2026
5d09d74
chore(deps): bump mikefarah/yq in the utilities group
dependabot[bot] Jun 15, 2026
fd9e770
feat(go-pr-validation): expose golangci_lint_args and app_name_prefix
bedatty Jun 17, 2026
78419ae
fix(go-pr-analysis): map golangci_lint_args via env to prevent shell …
bedatty Jun 17, 2026
a3554fa
fix(go-pr-analysis): split golangci_lint_args via read -ra to satisfy…
bedatty Jun 17, 2026
590a988
feat(go-pr-analysis): enforce coverage threshold by default
bedatty Jun 17, 2026
c4be2b4
feat(release): orchestrate backmerge via backmerge-sync instead of se…
bedatty Jun 17, 2026
08dc880
fix(release): drop trailing blank line in .releaserc.yml (yamllint em…
bedatty Jun 17, 2026
a470506
feat(release): add dry_run input and preflight-validate backmerge config
bedatty Jun 17, 2026
6720647
feat(build): default enable_ghcr to true in go-release and build
bedatty Jun 17, 2026
5b60134
Merge pull request #428 from LerianStudio/feat/go-pr-validation-expos…
bedatty Jun 17, 2026
b03cf1f
Merge pull request #394 from LerianStudio/dependabot/github_actions/d…
bedatty Jun 17, 2026
ea2d903
chore(deps): bump the security-scanners group across 1 directory with…
dependabot[bot] Jun 15, 2026
05e9b1f
Merge pull request #404 from LerianStudio/dependabot/github_actions/d…
bedatty Jun 17, 2026
06a9b62
Merge pull request #426 from LerianStudio/dependabot/github_actions/d…
bedatty Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/go-pr-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 }}/...

Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/go-pr-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,22 @@ 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
default: 80
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
Expand Down Expand Up @@ -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 }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/go-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/go-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 ./...'

Expand Down Expand Up @@ -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 }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/helm-update-chart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
104 changes: 70 additions & 34 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -170,58 +197,68 @@ 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 }}
GIT_AUTHOR_EMAIL: ${{ secrets.LERIAN_CI_CD_USER_EMAIL }}
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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/slack-notify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
9 changes: 0 additions & 9 deletions .releaserc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

2 changes: 1 addition & 1 deletion docs/build-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
4 changes: 2 additions & 2 deletions docs/go-pr-analysis-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion docs/go-pr-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | `''` |
Expand Down
2 changes: 1 addition & 1 deletion docs/go-release-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | `''` |
Expand Down
13 changes: 9 additions & 4 deletions docs/release-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
Loading