Skip to content

fix(release): develop to main - #833

Merged
bedatty merged 31 commits into
mainfrom
develop
Sep 24, 2026
Merged

bedatty merged 31 commits into
mainfrom
develop

Conversation

@bedatty

@bedatty bedatty commented Sep 24, 2026

Copy link
Copy Markdown
Contributor
Lerian

GitHub Actions Shared Workflows


Description

Type of Change

  • feat: New workflow or new input/output/step in an existing workflow
  • fix: Bug fix in a workflow (incorrect behavior, broken step, wrong condition)
  • perf: Performance improvement (e.g. caching, parallelism, reduced steps)
  • refactor: Internal restructuring with no behavior change
  • docs: Documentation only (README, docs/, inline comments)
  • ci: Changes to self-CI (workflows under .github/workflows/ that run on this repo)
  • chore: Dependency bumps, config updates, maintenance
  • test: Adding or updating tests
  • BREAKING CHANGE: Callers must update their configuration after this PR

Breaking Changes

None.

Testing

  • YAML syntax validated locally
  • Triggered a real workflow run on a caller repository using @this-branch or the beta tag
  • Verified all existing inputs still work with default values
  • Confirmed no secrets or tokens are printed in logs
  • Checked that unrelated workflows are not affected

Caller repo / workflow run:

Related Issues

Closes #

dependabot Bot and others added 24 commits September 21, 2026 11:10
Bumps the security-scanners group with 1 update: [trufflesecurity/trufflehog](https://github.com/trufflesecurity/trufflehog).


Updates `trufflesecurity/trufflehog` from 3.97.4 to 3.97.5
- [Release notes](https://github.com/trufflesecurity/trufflehog/releases)
- [Commits](trufflesecurity/trufflehog@363923b...f714bf4)

---
updated-dependencies:
- dependency-name: trufflesecurity/trufflehog
  dependency-version: 3.97.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: security-scanners
...

Signed-off-by: dependabot[bot] <support@github.com>
The default ignore glob .github/* is matched with bash case, where * crosses /, so a pull request that changed only .github/workflows/pr-validation.yml was classified docs-only and skipped the Go pipeline it was editing. Matcher hit it on 2026-09-21 while moving its coverage gate config. A file under .github/workflows/ now always counts as code, before the glob loop; CODEOWNERS, templates and bot config keep their docs classification.
A pull request made only of deletions produced an empty changed-file
list, because the PR branch filtered out `status == "removed"`. The
classification loop then left `code=false`, go-pr-validation.yml and
js-pr-validation.yml skipped their analysis jobs, and result-gate read
`skipped` as a pass — so the required contexts reported green with
lint, tests, coverage and the custom checks never having run.

Deleting a file is a change to it: a removed `.go` file can orphan its
callers and drop coverage below the floor. Removed files are now
classified by path like any other, so a deleted `docs/x.md` is still
documentation and a deleted `.go` file is code. An empty diff keeps
returning `code=false` rather than being treated as a code change.

Adds a behavioural harness replaying the shipped Bash against
`pulls/{n}/files` fixtures with real jq and no network, wired into
self-pr-validation.yml like the other per-action test suites.
…image charts

The component image tag was always written to .<values_key>.image.tag. A
single-service chart that keeps its image at the values root (e.g.
product-console: .image.tag) got a bogus top-level '<component>:' block that the
chart never reads, while the real root image.tag stayed stale — every automated
bump PR reintroduced it (helm #2157/#2187/#2193).

Pick the path the chart actually uses: root .image.tag only when the chart is
annotated lerian.studio/chart-type: single-service AND a root .image.tag already
exists; otherwise keep the nested .<values_key>.image.tag (unchanged for every
multi-component chart and for single-service charts that nest, e.g.
tracer/bank-transfer). Also self-heal any orphan top-level '<values_key>:' block
left by older runs, but only when it holds nothing but the image mapping.

Backward-compatible: only a single-service root-image chart changes behavior.
Bumps the docker group with 3 updates in the / directory: [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action), [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) and [docker/build-push-action](https://github.com/docker/build-push-action).


Updates `docker/setup-qemu-action` from 4.3.0 to 4.4.0
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](docker/setup-qemu-action@1f40c72...9901266)

Updates `docker/setup-buildx-action` from 4.3.0 to 4.4.1
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](docker/setup-buildx-action@37fe631...f87e599)

Updates `docker/build-push-action` from 7.3.0 to 7.4.0
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](docker/build-push-action@53b7df9...c3c9e26)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 7.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docker
- dependency-name: docker/setup-buildx-action
  dependency-version: 4.4.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docker
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docker
...

Signed-off-by: dependabot[bot] <support@github.com>
An extra_builds group that sets enable_dockerhub, enable_ghcr or
enable_gitops_artifacts to false now gets false. Expression loose
equality coerces null and false both to 0, so the old `== null` test
treated an explicit false as omitted: enable_dockerhub and enable_ghcr
inherited the top-level input, and enable_gitops_artifacts fell back to
true, re-enabling a publish target the caller had turned off.

The inherit/default case is now detected with toJSON(...) == 'null',
the same form require_build_identity uses. An omitted key still
inherits the top-level input (dockerhub, ghcr) or defaults to true
(gitops artifacts).

Signed-off-by: Fred Amaral <fred@fredamaral.com.br>
…t false

Fails when a per-group enable_dockerhub, enable_ghcr, require_build_identity or enable_gitops_artifacts forwarding drops the toJSON(...) == 'null' inherit check, or when any per-group key is compared against null again.

Signed-off-by: Fred Amaral <fred@fredamaral.com.br>
Lints and runs tests/go-release on every pull request, alongside the build identity suites.

Signed-off-by: Fred Amaral <fred@fredamaral.com.br>
go-release.yml dropped its GoReleaser job when it became the service
release pipeline, leaving Go repositories that ship binaries instead of
container images with no supported path: their only option was to freeze
the caller at the last release that still carried the job, which never
receives a security fix and fights the tier-channel model.

Bring the binary lane back behind enable_goreleaser (default false, so
every current caller behaves exactly as it does now). The job mirrors the
container build's gate exactly — tag push honouring tag_prefix, plus the
same-run build_on_release path — so it builds from the tag
semantic-release just published, and runs GoReleaser against the
repository's own .goreleaser.yml.

When enable_goreleaser is on and both registries are off, the container
build is skipped entirely: a CLI-shaped repository has no Dockerfile to
build. update_gitops and s3_upload gate on the build producing images and
stand down with it.
The GoReleaser job checked out with persist-credentials: false and then
ran a separate `git fetch --force --tags`, which has no token to
authenticate with on a private repository and would fail before
GoReleaser ever runs. checkout's fetch-depth: 0 already fetches every
branch and tag, so the step was redundant as well as fragile.

Also stop publishing on a dry run: the umbrella honors dry_run in every
lane it can reach from this file, and this one uploads release assets.
It now reports the resolved configuration via ::notice:: and leaves
GoReleaser unexecuted.

Docs: correct the release.mode guidance — GoReleaser defaults to
keep-existing, which already preserves the semantic-release notes and
only uploads the assets; append adds GoReleaser's own changelog rather
than merely uploading.
The lane copied the container build's gate verbatim, inheriting two gaps
that matter more here than they do there.

With build_on_release and build_on_release_include_prerelease both on, a
beta/rc tag satisfies the tag-push path AND the same-run branch path, so
GoReleaser could publish the same tag twice. build.yml absorbs that with
on_existing_tag: skip; GoReleaser has no equivalent, and the second run
would try to re-upload the same release assets. The tag push therefore
stops being a prerelease path whenever the branch rescue is enabled,
leaving exactly one.

tag_prefix was also only applied to the pushed tag, so in a repository
that uses it to stay clear of another component's tags the same-run path
escaped the filter. It is now checked against the tag semantic-release
computed as well.

Both gaps exist in the build job too, but fixing them there changes
behaviour for callers in production — a separate change, not this one.
…tdated

resolve_latest runs inside a command substitution, so the `log` call on
the unreadable-repository branch wrote its `::warning` text to stdout and
that text became the resolved version. The string compare then read it as
newer than the current pin, so a private library without
LERIAN_LIB_READ_TOKEN was counted as OUTDATED and failed the job with a
false "behind latest stable".

Warnings raised inside resolve_latest are now queued in a file and
flushed by the caller once the substitution has returned, the same
mechanism already used for the release date. The annotation still shows
up in the job log, and the empty result routes the library to the
existing UNKNOWN bucket.

Tests cover the path with a curl stub that answers 404 the way an
unauthenticated request to a private LerianStudio repo does.
…peline

A pipeline switched off by its caller flag left `needs.<job>.result` as
`skipped`, which result-gate treats as a pass. `Security` and `Lib Version`
then reported green while asserting nothing -- and those names are exactly
what a repository reaches for when filling in required status checks.

The gate still passes: a caller that disabled a pipeline on purpose must not
be blocked by it. What changes is the check NAME. Each flag-guarded aggregator
now renames itself to `<name> (disabled)`, so the plain name is never published
by a job structurally incapable of failing. result-gate gains an `enabled`
input (default true) that says so in the log instead of printing a verdict it
never reached.

This keeps the legitimate skip intact: when the change detector ran and found
nothing to analyse, the check keeps its plain name and stays green. That one
is a real verdict.

Compatibility note: a repository that requires `Go Analysis`, `Security`,
`Lib Version`, `Frontend Analysis` or `Socket` and sets the matching `run_*`
flag to false will see that check go missing rather than green, leaving pull
requests pending until the name is removed from the required set.
@bedatty
bedatty requested a review from a team as a code owner September 24, 2026 18:12
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: LerianStudio/github-actions-shared-workflows/.coderabbit.yml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: a73392d7-bf0a-4981-a60d-622b4a736fc3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@lerian-studio

lerian-studio commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

🔍 PR Validation Summary

🚫 PR Blocked — 2 blocking failures

Check Status Blocking
Source Branch ✅ success yes
PR Title ✅ success yes
PR Description ✅ success yes
Breaking Change Guard ✅ success yes
Blocking Checks Runtime ❌ failure yes
Commit Signatures ❌ failure yes
PR Size ⏭️ skipped no
Auto Labels ⏭️ skipped no
PR Metadata ⏭️ skipped no
Commit Signatures

3 unsigned or unverified commits

Commit Author Reason
1b4b8ac bedatty unknown_key
6bdaa20 bedatty unknown_key
52c8f65 bedatty unknown_key

How to fix

Configure commit signing, then re-sign the commits in this branch:

# 1. Make sure signing is configured (SSH or GPG key registered on GitHub)
git config --local commit.gpgsign true

# 2. Re-sign every commit of this branch on top of its base
git rebase --exec 'git commit --amend --no-edit -S' origin/main

# 3. Update the pull request
git push --force-with-lease

Docs: https://docs.github.com/en/authentication/managing-commit-signature-verification

Fix the blocking checks above before merge.


🔍 View workflow run

@lerian-studio

lerian-studio commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

🔍 Lint Analysis

Check Files Scanned Status
YAML Lint 18 file(s) ✅ success
Action Lint 15 file(s) ✅ success
Pinned Actions 18 file(s) ✅ success
Markdown Link Check 10 file(s) ✅ success
Spelling Check 31 file(s) ✅ success
Shell Check 18 file(s) ✅ success
README Check 18 file(s) ✅ success
Composite Schema 3 file(s) ✅ success
Deployment Matrix no changes ⏭️ skipped

🔍 View full scan logs

@lerian-studio

lerian-studio commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

🛡️ CodeQL Analysis Results

Languages analyzed: actions

Found 1 issue(s): 1 Medium

Severity Rule File Message
🟡 Medium actions/untrusted-checkout/medium .github/workflows/build.yml:323 Potential unsafe checkout of untrusted pull request on non-privileged workflow.

1 finding(s) hidden (dismissed or fixed). See the Security tab for the full list.


🔍 View full scan logs | 🛡️ Security tab

bedatty and others added 4 commits September 24, 2026 16:05
go-pr-validation, js-pr-validation, go-release and js-release already skip
their expensive pipelines when a push or pull request touches nothing but
documentation. The reusable workflows that repositories call directly did
not, so ~22 repos on release.yml, ~19 on pr-security-scan.yml, 4 on
typescript-release.yml and the go-/typescript-ci pair still burned runners —
and cut release tags — on a README commit.

Each workflow now exposes enable_doc_gate (default true) and ignore_globs,
backed by the same src/config/non-doc-changes classifier, so the whole family
agrees on what counts as documentation. The wiring follows each workflow's
existing skip contract rather than inventing a parallel one:

  release.yml, typescript-release.yml  folds the verdict into the `should_skip`
                                       output of `prepare`, which every
                                       downstream job already honours
  pr-security-scan.yml                 collapses the scan matrix to '[]', the
                                       value the scan jobs and `checks_passed`
                                       already treat as nothing-to-do
  go-ci.yml, typescript-ci.yml         a `changes` job the root jobs depend on

Three boundaries are deliberate. Tag pushes are never gated: a tag carries no
diff to classify and a tag-driven release is explicit intent. The `changes`
job in the CI workflows is unconditional, with the step inside it doing the
gating and an absent verdict falling back to 'true' — a job skipped by its own
`if` also skips everything that needs it, which would take the whole pipeline
down when a caller disables the gate. And '.github/workflows/**' always counts
as code, so a pipeline edit still runs its own pipeline.

The four umbrella workflows now pass enable_doc_gate: false to the reusables
they call, since they classify the same event upstream; without it every
consumer would pay for a second job and a second API call to reach a verdict
already in hand.

For a repository whose documentation is the product, enable_doc_gate: false
restores the previous behaviour.
The zero-dependency verdict assumes the caller is a service, where depending on
at least one Lerian library is a company-standards rule. It is a false positive
for a repository that legitimately has none: a library whose public API is
standard-library only, a template, a generator. Those had one way out —
run_lib_version_check: false on the umbrella — which also switched off the
outdated-version guard the check exists for, and switched it off permanently,
including after the repository grows its first Lerian dependency.

Adds require_lerian_libs (composite: require-lerian-libs), default true, so
nothing changes for anyone who does not set it. When false, "no Lerian
libraries" is reported as a warning and the check exits clean instead of
failing.

Deliberately narrow, matching outdated_non_blocking: it softens only that one
verdict. A missing or unreadable go.mod, an outdated dependency and every
infrastructure error keep failing, so a repository that opts out cannot turn a
broken pipeline into a pass.

Found on lib-boilerplate, the Go library template: its example depends on the
standard library only, so the umbrella failed with "No github.com/LerianStudio/*
dependencies in go.mod" and the template had to disable the whole check.
… as docs

Four findings from CodeRabbit on #836. Two of them are pre-existing production
bugs in src/config/non-doc-changes, not regressions of this PR — but this PR
takes the classifier from the go-/js- umbrellas to ~45 repositories, so they
are fixed at the source rather than worked around per workflow.

In a bash `case` pattern `*` matches `/`, unlike a GitHub `paths-ignore`
filter where it does not. The default `.github/*` glob therefore swallowed
every nested path, and the single `.github/workflows/*` exception did not
cover `.github/actions/` or `.github/scripts/`: a push touching only a
composite action or a pipeline helper script was classified as documentation
and went unbuilt and unreleased. Both are now exceptions alongside workflows.

The compare endpoint caps `files` at 300 entries and signals the cap only by
hitting it. A push of 400 files whose first 300 were documentation classified
as docs-only and skipped the pipeline. A list at the ceiling is now treated as
what it is — not a verdict — and assumes code.

Six tests cover the two, and fail against the previous action.yml via
NON_DOC_ACTION_PATH.

The gate steps added by this PR are now continue-on-error. A caller granting
less than `pull-requests: read` cannot list PR files, and a reusable workflow
cannot elevate what its caller restricted; failing there would block the
pipeline the gate only means to shorten. No verdict already reads as
"assume code", so the fallback needed no new logic.

go-ci's check-docs left the gate entirely. It asserts README/LICENSE/SECURITY
are present and their links resolve, so documentation is its input, not a
reason to skip — gating it let a PR deleting README.md skip the check that
exists to catch exactly that.
bedatty and others added 3 commits September 24, 2026 16:47
…e opt-out

The `Lerian Lib Version Advisory Tests` check was failing on this branch. The
new REQUIRE_LERIAN_LIBS expansion runs under `set -euo pipefail`, and the test
harness did not define it, so the script aborted at the first expansion and
every verdict test read as a pass of the wrong kind. The harness now mirrors
the action's declared default, which is what it does for every other input.

The opt-out was `!= "true"`, so any unrecognised value waived the rule —
`require-lerian-libs: tru` from a direct composite caller silently turned a
compliance check off. Only the literal "false" opts out now, and a value that
is neither emits a warning, so the typo is visible instead of enforcing a rule
the caller believed it had disabled. The reusable workflow coerces a boolean
and can only ever send "true" or "false", so this only affects direct callers.

Seven tests cover the input, including the typo and empty-value paths and the
three verdicts the exception must not widen to.

Also corrected an overstated guarantee, in all four places it was written: the
new text claimed every infrastructure error keeps failing, but an unresolvable
releases API has always reported ⚠️ Unknown and passed. The failure-modes table
additionally still carried an unqualified "App has no Lerian libs → Fail" row
that the three new rows below it contradicted, plus two claims that a go.mod
with no Lerian libraries always fails, which is now `require_lerian_libs`'s
call rather than `outdated_non_blocking`'s.
The previous fixture spelled the invalid value "tru", which the Spelling Check
job flagged — correctly, since it cannot tell a deliberate fixture from a real
misspelling. Replaced with two values that are likelier to reach the action for
real: "FALSE", where only the case is wrong, and a bare "no", which YAML 1.1
reads as a boolean but which arrives at an action input spelled as written.
Better coverage than the invented typo it replaces.
@bedatty
bedatty merged commit bdf1d46 into main Sep 24, 2026
47 of 48 checks passed

This branch was successfully deployed

1 active deployment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants