Skip to content

ci(workflows): pin flux, yq and helm-unittest installs - #4645

Merged
IvanHunters merged 1 commit into
mainfrom
ci/pin-ci-tool-installs
Oct 5, 2026
Merged

IvanHunters merged 1 commit into
mainfrom
ci/pin-ci-tool-installs

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Workflows no longer run the flux install script as root. Every step that needed the flux CLI downloaded https://fluxcd.io/install.sh and ran it with sudo, so whatever that host served at the moment ran on runners that later get registry credentials. PR #4442 pinned the version, but the script was still fetched and executed. All 12 of those steps now use fluxcd/flux2/action, pinned to the v2.8.6 commit with version: "2.8.6". The action downloads the release tarball, checks it against that release's checksums file, and adds the binary to PATH.

Two workflows still took yq from the floating latest release, nightly and the fork e2e lane. They now use the same install the promotion steps already have: yq v4.53.3 checked against a committed SHA-256. I verified that hash against the release's checksums file and against the binary itself.

The helm-unittest plugin was installed at whatever version was newest. The plugin embeds its own Helm, so a plugin release could change chart test results on a PR that touched no chart. It's pinned to v1.1.2 now, the version CI already runs. A runner that carries a different version gets it replaced.

A new contract test scans every workflow for these three tools, because an unpinned install usually comes back by copying an older step. On main it fails on the install script, the missing setup action, the latest-release yq and the unpinned plugin. I ran one mutation per guard, and each one failed only the test meant to catch it. The existing contract tests that checked FLUX_VERSION inside the old steps now check for the action step.

The e2e sandbox image still runs install.sh in its Dockerfile. That's an image build, not a workflow step, so I left it for a separate change.

Closes #3619

Closes #3640

Closes #4510

Screenshots

Not applicable, no UI changes.

Downstream repositories

The diff touches only workflow tool-install steps and hack/*.bats contract tests. Nothing in the trigger map reaches them.

Release note

ci(workflows): install flux through the commit-pinned upstream setup action instead of running fluxcd.io/install.sh as root, pin yq with checksum verification in the nightly and fork e2e workflows, and pin the helm-unittest plugin version.

Summary by CodeRabbit

  • Reliability
    • CI workflows now use a consistent, pinned Flux version. The yq and Helm plugin setup also verifies or enforces specific versions.
  • Tests
    • Added checks to ensure workflow tool versions are pinned and consistent, and that downloaded yq releases are checksum-verified.

@github-actions github-actions Bot added area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review size/L This PR changes 100-499 lines, ignoring generated files labels Oct 1, 2026
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration

Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 1ed5bb55-e78a-492c-a70a-923cf10cd26c

📥 Commits

Reviewing files that changed from the base of the PR and between 3e2e437 and 6df5d07.

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 7bd2cf00-2944-4bb3-af79-28d0a2ee9369

📥 Commits

Reviewing files that changed from the base of the PR and between 5415973 and 3e2e437.

📒 Files selected for processing (12)
  • .github/workflows/build-main.yaml
  • .github/workflows/build-release.yaml
  • .github/workflows/e2e-fork.yaml
  • .github/workflows/nightly.yaml
  • .github/workflows/promote-rc.yaml
  • .github/workflows/pull-requests-release.yaml
  • .github/workflows/pull-requests.yaml
  • .github/workflows/tags.yaml
  • hack/ci-tool-installs-contract.bats
  • hack/promote-packages-contract.bats
  • hack/stitch-multiarch_test.bats
  • hack/verify-promoted-packages.sh

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

GitHub workflows replace Flux installer scripts with the Flux setup action at version 2.8.6. Selected yq and Helm unittest installations now check versions, and yq downloads are checksum-verified. Bats contracts and verification guidance are updated to reflect these workflow configurations.

Changes

CI tool installation pins

Layer / File(s) Summary
Flux setup across workflows
.github/workflows/build-main.yaml, .github/workflows/build-release.yaml, .github/workflows/e2e-fork.yaml, .github/workflows/nightly.yaml, .github/workflows/promote-rc.yaml, .github/workflows/pull-requests-release.yaml, .github/workflows/pull-requests.yaml, .github/workflows/tags.yaml
Workflows replace Flux installer scripts with the Flux setup action at version 2.8.6. Some workflows move Flux setup to a separate step. The nightly mirror workflow also pins yq and verifies its checksum.
yq and Helm version checks
.github/workflows/e2e-fork.yaml, .github/workflows/pull-requests.yaml
The fork e2e workflow checks the yq version and verifies its checksum before installation. The unit-test workflow installs Helm unittest v1.1.2 when the installed version differs.
Installation contracts and guidance
hack/ci-tool-installs-contract.bats, hack/promote-packages-contract.bats, hack/stitch-multiarch_test.bats, hack/verify-promoted-packages.sh
Bats contracts validate Flux action pins, reject floating installer URLs, check yq version and checksum declarations, and require a Helm plugin version. Existing workflow assertions and verification guidance are updated for Flux action setup.

Priority: ⬆️ High

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Other · Severity of issue fixed: High

Merge Risk: ⚪ Minimal · up to 3e2e4

The changes pin CI tools and strengthen installation checks. No actionable merge-blocking issue is established; merge after normal CI checks pass.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 3e2e4

The changes reduce exposure to mutable installers and unversioned downloads without changing workflow permissions or credential configuration. Remaining uncertainty concerns the upstream setup action and recovery from interrupted tool installation.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — A compromise of tooling in the existing publication jobs could affect registry artifacts and downstream release consumers, rather than only one test run. Relevant authority includes package-write tokens and OCIR credentials. Their server-side scope was not inspected, and the PR does not change their workflow configuration.

Trust Boundaries and Controls

  • observed — Flux setup precedes explicit registry login in the inspected build, nightly and fork-publication jobs, and receives no registry-secret input. This ordering is not a sandbox boundary: installed tools remain trusted when later steps acquire credentials and publish artifacts.

Resilience and Maintainability Implications

  • observed — Helm plugin replacement uninstalls a mismatched version before installing v1.1.2; a matching reported version skips replacement. The following test step has no failure override, and the job has contents-read permission. Replacement is not an atomic rollback operation, and recovery from an interrupted installation remains unverified.

Hardening Proposals

  • proposed — As follow-up hardening, validate preinstalled yq bytes against the committed checksum instead of relying only on reported version. This would strengthen an existing runner-trust assumption, not remedy an established regression from this PR.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Flux requirements in [#3619] are met: the workflow installs use fluxcd/flux2/action at commit 04acaec6161ac4fb1a82ffafa88901c03271d34f, pass version: "2.8.6", and contract tests reject the old i… Update the yq setup in tags.yaml to use an explicit version and SHA-256 verification, or provide equivalent reviewable evidence that this workflow does not install an unpinned yq binary. Extend the contract coverage if required.
Out of Scope Changes check ⚠️ Warning The tool-install and contract-test changes are within scope. The PR also changes .github/workflows/promote-rc.yaml to describe the dispatch registry write as multiple steps. That registry-write chan… Remove the unrelated registry-write changes from .github/workflows/promote-rc.yaml, unless the change is required by a separately linked objective.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main changes: pinning Flux, yq, and helm-unittest installations in CI workflows.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 4 files. (8 skipped: 8 …
Full details: Linked Issues check

Explanation

Flux requirements in [#3619] are met: the workflow installs use fluxcd/flux2/action at commit 04acaec6161ac4fb1a82ffafa88901c03271d34f, pass version: "2.8.6", and contract tests reject the old installer and unpinned setup. The action checksum behavior is reported in the PR summary. The helm-unittest requirement in [#4510] is met by pinning version 1.1.2 and replacing a different installed version. The yq changes cover nightly.yaml and e2e-fork.yaml, while existing pins cover the promotion workflows. The tags.yaml yq installation identified by [#3640] is not changed to a version and checksum pin. Therefore one directly linked coding requirement remains unmet.

Full details: Out of Scope Changes check

Explanation

The tool-install and contract-test changes are within scope. The PR also changes .github/workflows/promote-rc.yaml to describe the dispatch registry write as multiple steps. That registry-write change has no demonstrated connection to the linked Flux, yq, or helm-unittest objectives. The e2e sandbox Dockerfile remains unchanged, as stated in the PR summary.

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Every flux install fetched fluxcd.io/install.sh and ran it as root, so
whatever that host served at the moment executed on runners that later
hold registry credentials. Use the upstream setup action instead, pinned
by commit and given an explicit version: it checks the release tarball
against that release's checksums file and runs no script fetched at run
time.

nightly and the fork e2e lane still took yq from the floating latest
release. They now use the pinned, checksum-verified install the
promotion steps already have.

The helm-unittest plugin embeds its own Helm and was installed at
whatever version was newest, so a plugin release could change chart
test results on a PR that touched no chart. Pin it, and replace a
different version a persistent runner may carry.

A contract test scans every workflow for these three tools, so an
unpinned flux, yq or helm-unittest install copied from an older step
fails the unit lane.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

CI tool-install hardening, and it holds up. I checked the three pins against upstream rather than taking them on faith:

  • flux: fluxcd/flux2/action@04acaec6161ac4fb1a82ffafa88901c03271d34f dereferences exactly from the annotated v2.8.6 tag, and the action's action.yml at that commit takes the version input, checksums the release tarball against flux_<ver>_checksums.txt, and puts the binary on $GITHUB_PATH for later steps. The version: "2.8.6" input is right.
  • yq: downloaded yq_linux_amd64 v4.53.3 and the sha256 matches fa52a4e7...eded4 byte for byte.
  • helm-unittest v1.1.2 exists; its plugin.yaml carries version: "1.1.2" (no v), so the [ "$installed" != "$HELM_UNITTEST_VERSION" ] guard is idempotent across reruns, and --version "v1.1.2" resolves to the git tag.

Coverage is complete: the merge base had 12 fluxcd.io/install.sh invocations across 8 files, all 12 are gone, and exactly 12 commit-pinned action steps replace them. No releases/latest/download left, no unpinned helm plugin install, every yq download pins v${YQ_VERSION} with the checksum. Splitting flux into its own step preserves each consuming step's if: guard and ordering. The new hack/ci-tool-installs-contract.bats passes (5/5), as do the updated promote-packages-contract.bats (7/7) and the edited stitch-multiarch_test.bats case.

On the two automated warnings: the tags.yaml yq flag is a false positive, that workflow never installs yq, it only asserts a preinstalled mikefarah build; and the promote-rc.yaml "out of scope" flag is the flux step split plus a one-word comment edit, which is intrinsic to this change.

One optional follow-up, not blocking: tags.yaml and the unit-test helm step still trust a runner-preinstalled yq/helm without a version check. That predates this PR and is out of its scope, but it is the same class of input this change is tightening elsewhere.

@IvanHunters
IvanHunters enabled auto-merge October 5, 2026 11:10
@IvanHunters
IvanHunters merged commit 9239555 into main Oct 5, 2026
57 checks passed
@IvanHunters
IvanHunters deleted the ci/pin-ci-tool-installs branch October 5, 2026 11:11
myasnikovdaniil added a commit that referenced this pull request Oct 5, 2026
#4751)

## What this PR does

Unit tests on main are red, and that blocks E2E on every open PR. Three
contracts broke the same way. In each case a check from one PR and a
line from another merged a few minutes apart on 2026-10-05, with no
textual conflict. Any one of them alone keeps the job red, so they are
fixed together.

The first is the pin on how a suite reads HelmRelease history. #4689
added it. It accepts `$(kubectl ...` and, since #3594, the bounded
`$(_cozy_guard_kubectl ...` wrapper. Nothing in front of kubectl is
allowed. The monitoring suite from #4678 bounds that read with
`$(timeout 30 kubectl ...`, and the bound is wanted. So the pin now
accepts an optional `timeout [-k N] N` in front of kubectl. The prefix
is spelled out on purpose. Any text would also let through `$(echo
kubectl ...)`, which names the shared expansion without running it, and
that form is now a refuse case. I checked it with mutations: the new
accept cases fail against the old pattern, and a pin that takes any
prefix fails a refuse case.

The second is the flux install in workflows. #4645 forbids running
fluxcd.io/install.sh and moved every workflow to the SHA-pinned
fluxcd/flux2 action. The isp-hosted lane from #4712 still ran the
script. It now uses the same action and version.

The third is the negated-assertion audit. It reads the find operand in
`\( ! -type d` in select-e2e_test.bats as a shell negation and fails.
The line now says `-not -type d`. This is the same change as #4748, so
this PR supersedes it. The audit bug itself is #4752.

After the Go fix, the full log of the unit job had only these two bats
failures left.

Closes #4750
Closes #4755

### Screenshots

### Downstream repositories

- [x] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

### Release note

```release-note
fix(ci): fix three unit-test contracts that went red on main after adjacent merges.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* History checks now recognize direct `kubectl` reads and reads through
the supported wrapper, including timeout-bounded commands with an
optional kill-grace setting. Commands that only include `kubectl` as an
argument, use similarly named commands, or place a timeout before the
wrapper are not treated as reads.
* **Chores**
* The hosted end-to-end workflow now uses a pinned Flux version, and
suite selection handles directory exclusions consistently.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

2 participants