feat(ci): add Renovate, release workflow, and self-contained examples#4
feat(ci): add Renovate, release workflow, and self-contained examples#4
Conversation
Custom regex managers track cozy-installer OCI tags and update both roles/cozystack/defaults/main.yml and galaxy.yml in a single PR. Built-in ansible-galaxy manager handles k3s-ansible updates via git-tags. Automerge enabled with squash strategy. Override default ignorePaths to include tests/ and examples/ directories which contain version-pinned dependencies. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
Creates a GitHub release with auto-generated notes when galaxy.yml version changes on main. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
k3s.orchestration is only used by example playbooks, not by the collection itself. Move it to tests/requirements.yml and pin to tag 1.1.0 instead of a commit hash. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
Each example directory now includes its own requirements.yml listing all collections explicitly. Users copying an example get a complete setup without guessing dependencies. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
Temporarily set cozystack_chart_version and galaxy.yml version to 1.1.0 so Renovate creates an upgrade PR to 1.1.2 as a real-world validation of the setup. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds a GitHub Actions "Release" workflow driven by Changes
Sequence Diagram(s)sequenceDiagram
participant Repo as Repo (files: galaxy.yml)
participant Runner as GH Actions Runner
participant GHAPI as GitHub API / gh CLI
Repo->>Runner: push to main (galaxy.yml changed)
Runner->>Runner: extract version from `galaxy.yml` (output: version)
Runner->>GHAPI: gh release view v<version>
alt release exists
GHAPI-->>Runner: release found
Runner-->>Repo: skip create release
else release missing
GHAPI-->>Runner: no release
Runner->>GHAPI: gh release create v<version> --title ... --notes ...
GHAPI-->>Runner: release created
end
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment Tip CodeRabbit can enforce grammar and style rules using `languagetool`.Configure the |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the project's automation and dependency management. It introduces Renovate for automated dependency updates and a GitHub Actions workflow for streamlined releases. Furthermore, it refines the project's dependency structure by isolating CI-specific requirements and making example directories self-sufficient, ensuring better maintainability and clearer dependency declarations. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Ignored Files
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces Renovate for automatic dependency updates, a new release workflow, and makes the examples self-contained. The changes to support Renovate, like downgrading versions for testing purposes and restructuring dependencies, are well-aligned with the goal. My main suggestion is to improve the new example requirements.yml files by pinning all dependencies to specific versions instead of using main, which will make the examples more stable and reproducible for users.
| # WARNING: never use 'main' in production — always pin to a release tag (e.g. v1.1.2) | ||
| version: main |
There was a problem hiding this comment.
Using version: main for cozystack.installer makes this example non-deterministic and it may fail unexpectedly if there are breaking changes in the main branch. While there is a warning comment, it's a better practice for examples to be stable and reproducible out-of-the-box. Please consider pinning this to a specific version tag.
# Pinning to a specific version ensures the example is reproducible.
version: 1.1.0| # WARNING: never use 'main' in production — always pin to a release tag (e.g. v1.1.2) | ||
| version: main |
There was a problem hiding this comment.
Using version: main for cozystack.installer makes this example non-deterministic and it may fail unexpectedly if there are breaking changes in the main branch. While there is a warning comment, it's a better practice for examples to be stable and reproducible out-of-the-box. Please consider pinning this to a specific version tag.
# Pinning to a specific version ensures the example is reproducible.
version: 1.1.0| # WARNING: never use 'main' in production — always pin to a release tag (e.g. v1.1.2) | ||
| version: main |
There was a problem hiding this comment.
Using version: main for cozystack.installer makes this example non-deterministic and it may fail unexpectedly if there are breaking changes in the main branch. While there is a warning comment, it's a better practice for examples to be stable and reproducible out-of-the-box. Please consider pinning this to a specific version tag.
# Pinning to a specific version ensures the example is reproducible.
version: 1.1.0Pin cozystack.installer to 1.1.0 in example requirements.yml instead of 'main'. Add regex custom manager so all cozystack version references share the same depName and arrive in a single Renovate PR. Disable built-in ansible-galaxy manager for cozystack.installer to avoid duplicate updates. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yml:
- Around line 4-8: The workflow currently triggers on any edit to galaxy.yml
(on: push paths: [galaxy.yml]) which is too broad; add a gating step before
creating a release that compares the version value in the current commit vs the
previous commit (for example by extracting the `version` key from `git show
HEAD:galaxy.yml` and `git show HEAD^:galaxy.yml` with a YAML parser like yq or
simple grep) and only proceed if the values differ; update the release job/step
that invokes `gh release view` to be conditional on this version-check step's
result so the workflow skips release actions when the version hasn't changed.
- Around line 26-43: Add a concurrency block to the workflow (job that contains
the "Check if release already exists" step with id check and the "Create
release" step) to serialize release runs and avoid the check-then-create race;
set cancel-in-progress: false and use a stable key that includes the release
identifier (for example using the version output) so only one run for the same
release executes at a time, leaving the existing check (steps.check) and create
logic unchanged.
In `@examples/rhel/requirements.yml`:
- Around line 10-14: Replace the mutable branch reference for the dependency
cozystack.installer with a specific immutable release tag: update the version
field in the requirements entry for cozystack.installer from "main" to a pinned
tag such as "v1.0.2" (or another chosen release like "v1.0.0-rc.1") so installs
are deterministic; ensure the change is applied to the requirements.yml entry
referencing source https://github.com/cozystack/ansible-cozystack.git and that
the version value is a release tag string.
In `@examples/suse/requirements.yml`:
- Around line 10-14: Replace the floating branch pin for the role
cozystack.installer by changing the version field from "main" to a specific
immutable tag (e.g. "v1.0.2") so the requirements.yml uses a reproducible
release; update the version value for the entry whose name is
cozystack.installer to a concrete tag.
In `@examples/ubuntu/requirements.yml`:
- Around line 13-14: The requirements.yml currently pins cozystack.installer to
a non-existent release ("version: main"); update the version field for the
cozystack.installer entry to the actual released tag v1.0.2 (replace "version:
main" with "version: v1.0.2") so installations reference a real release and
won't fail.
In `@renovate.json`:
- Line 29: The regex "version:\\s*(?<currentValue>\\S+)" is unanchored and can
match unintended keys; update the Renovate regex string to explicitly anchor the
pattern to the line boundaries by adding ^ at the start and $ at the end (e.g.,
change the "version:\\s*(?<currentValue>\\S+)" entry to use a line-anchored form
like "^version:\\s*(?<currentValue>\\S+)$") so only a top-level version: line in
galaxy.yml is matched and keys like collection_version are ignored.
In `@roles/cozystack/defaults/main.yml`:
- Line 13: The default variable cozystack_chart_version was hard-set to "1.1.0"
which will cause fresh installs from main to deploy an older chart; change the
declaration of cozystack_chart_version in roles/cozystack/defaults/main.yml to
avoid downgrading users by either reverting it to the previous (or current)
chart version, removing the hardcoded value so the installer uses the
release-provided version, or set it to an explicit semver range or null (e.g.,
leave undefined) so consumers or the installer pick the intended latest chart
instead of forcing 1.1.0. Ensure you only modify the cozystack_chart_version
variable and keep the key name intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 628858bd-2daa-4c5f-9459-c7482836d495
📒 Files selected for processing (14)
.ansible-lint.github/workflows/release.yml.github/workflows/test.ymlexamples/rhel/requirements.ymlexamples/rhel/site.ymlexamples/suse/requirements.ymlexamples/suse/site.ymlexamples/ubuntu/requirements.ymlexamples/ubuntu/site.ymlgalaxy.ymlrenovate.jsonrequirements.ymlroles/cozystack/defaults/main.ymltests/requirements.yml
💤 Files with no reviewable changes (2)
- .ansible-lint
- requirements.yml
| on: | ||
| push: | ||
| branches: [main] | ||
| paths: | ||
| - galaxy.yml |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In GitHub Actions, does on.push.paths: [galaxy.yml] trigger for any modification in that file regardless of which YAML key changed?
💡 Result:
Yes. on: push: paths: [galaxy.yml] is a path-based filter: GitHub Actions decides whether to run by comparing the list of changed files in the push against the paths globs. If galaxy.yml appears in the changed-files list, the workflow runs—regardless of which YAML key/line changed. It does not inspect file contents/keys. [1]
🏁 Script executed:
cat .github/workflows/release.ymlRepository: cozystack/ansible-cozystack
Length of output: 1218
Trigger condition is broader than "version changed".
Lines 4-8 run the workflow for any galaxy.yml edit. The path filter does not inspect file contents—it only checks if the file was modified at all, regardless of which YAML key changed. While the workflow includes a downstream check (gh release view) that prevents duplicate releases, the workflow still runs unnecessarily on edits to non-version fields, wasting CI/CD resources. To match the stated behavior of creating a release only when the version actually changes, compare the version value against the previous commit (e.g., via git show HEAD:galaxy.yml) to gate the release creation step.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 4 - 8, The workflow currently
triggers on any edit to galaxy.yml (on: push paths: [galaxy.yml]) which is too
broad; add a gating step before creating a release that compares the version
value in the current commit vs the previous commit (for example by extracting
the `version` key from `git show HEAD:galaxy.yml` and `git show
HEAD^:galaxy.yml` with a YAML parser like yq or simple grep) and only proceed if
the values differ; update the release job/step that invokes `gh release view` to
be conditional on this version-check step's result so the workflow skips release
actions when the version hasn't changed.
| - name: Check if release already exists | ||
| id: check | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| if gh release view "v${{ steps.version.outputs.version }}" > /dev/null 2>&1; then | ||
| echo "exists=true" >> "${GITHUB_OUTPUT}" | ||
| else | ||
| echo "exists=false" >> "${GITHUB_OUTPUT}" | ||
| fi | ||
|
|
||
| - name: Create release | ||
| if: steps.check.outputs.exists == 'false' | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| gh release create "v${{ steps.version.outputs.version }}" \ | ||
| --title "v${{ steps.version.outputs.version }}" \ |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What is the gh release create behavior/exit status when a release tag already exists, and is this the recommended idempotent pattern in GitHub Actions?
💡 Result:
What happens if the tag already exists?
There are two distinct “already exists” cases:
- Git tag exists, but no GitHub Release exists yet
gh release create <tag>will create the Release for that existing tag.- If the tag does not exist,
gh release createwill (by default) create the tag from the default branch unless you use--verify-tagto force “tag must already exist”. [1]
- A GitHub Release for that tag already exists
gh release create <tag>fails with a GitHub API validation error like:
Validation Failed {"resource":"Release","code":"already_exists","field":"tag_name"}[3]- GitHub CLI exit status in this situation is 1 (generic failure). [2]
Recommended idempotent pattern in GitHub Actions
No—calling gh release create repeatedly and hoping it’s “idempotent” is not the recommended pattern, because “release already exists” is a hard failure (exit 1). [2], [3]
Use an explicit check-then-create / else-edit pattern, e.g.:
tag="$GITHUB_REF_NAME"
if gh release view "$tag" >/dev/null 2>&1; then
gh release edit "$tag" --notes-file notes.md # and/or gh release upload ... --clobber
else
gh release create "$tag" --verify-tag --notes-file notes.md
fiThis matches the common automation approach of querying whether the release exists, then running gh release create or gh release edit accordingly. [4]
Add concurrency control to prevent race conditions in release creation.
Lines 26-43 create a race window: a second concurrent run can pass the check and attempt creation while the first is still running, causing both to fail. Add workflow/job concurrency with cancel-in-progress: false to serialize release creation:
Proposed diff
jobs:
release:
name: Create Release
+ concurrency:
+ group: release-${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: false
runs-on: ubuntu-latestWith concurrency control in place, the existing check-then-create pattern is sufficient and handles the race safely.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 26 - 43, Add a concurrency block
to the workflow (job that contains the "Check if release already exists" step
with id check and the "Create release" step) to serialize release runs and avoid
the check-then-create race; set cancel-in-progress: false and use a stable key
that includes the release identifier (for example using the version output) so
only one run for the same release executes at a time, leaving the existing check
(steps.check) and create logic unchanged.
| # WARNING: never use 'main' in production — always pin to a release tag (e.g. v1.1.2) | ||
| version: main |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
import json, urllib.request
url = "https://api.github.com/repos/cozystack/ansible-cozystack/tags"
tags = json.load(urllib.request.urlopen(url))
for t in tags[:30]:
print(t["name"])
PYRepository: cozystack/ansible-cozystack
Length of output: 2652
🏁 Script executed:
# First, check if the file exists and examine its content
git ls-files | grep -E "requirements\.yml" | head -20Repository: cozystack/ansible-cozystack
Length of output: 205
🏁 Script executed:
# Look for the specific file
cat -n examples/ubuntu/requirements.yml 2>/dev/null || echo "File not found in examples/ubuntu/"Repository: cozystack/ansible-cozystack
Length of output: 497
🌐 Web query:
cozystack/ansible-cozystack releases tags
💡 Result:
cozystack/ansible-cozystack currently has no GitHub Releases published. [2]
Tags:
- v1.0.2 — tagged Mar 4, 2026 (“sync with Cozystack v1.0.2, Helm 3.20.0”) [1]
- v1.0.0-rc.1 — tagged Feb 23, 2026 (“initial Ansible collection for Cozystack installation”) [1]
Sources: [1] [2]
Use a real release tag for cozystack.installer instead of a non-existent version.
Line 13 pins to version: 1.1.0, which is not an actual release tag for this repository. The latest available release is v1.0.2 (tagged March 4, 2026). Using a non-existent version tag will cause installation failures. Update to an actual released version.
Proposed fix
- version: 1.1.0
+ version: v1.0.2🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/ubuntu/requirements.yml` around lines 13 - 14, The requirements.yml
currently pins cozystack.installer to a non-existent release ("version: main");
update the version field for the cozystack.installer entry to the actual
released tag v1.0.2 (replace "version: main" with "version: v1.0.2") so
installations reference a real release and won't fail.
| "customType": "regex", | ||
| "managerFilePatterns": ["/^galaxy\\.yml$/"], | ||
| "matchStrings": [ | ||
| "version:\\s*(?<currentValue>\\S+)" |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Renovate custom regex managers, is (?m)^version:\s*(?\S+)$a validmatchStringspattern, and does anchoring prevent matching keys likecollection_version?
💡 Result:
- As-written, it’s not a good/“valid for your intent”
matchStringsfor Renovate’s regex manager. Renovate’s regex manager runs matches per file, so^and$match only the start/end of the entire file, not each line. That means^version:\s*(?<currentValue>\S+)$would only match if the whole file is exactly that oneversion:line. (docs.renovatebot.com) - Also, Renovate’s regex manager uses RE2 (via node-re2) with ECMAScript/JavaScript flavor; Renovate’s docs do not describe supporting inline modifiers like
(?m)inmatchStrings, and they explicitly recommend matching line boundaries manually instead. (docs.renovatebot.com)
To match a version: key on its own line (and avoid collection_version:), don’t rely on (?m)^…$; instead, include a real line-boundary prefix, e.g. something like:
(?:^|\r\n|\r|\n)version:\s*(?<currentValue>\S+)
This effectively anchors version: to the beginning of a line, so it won’t match the version: substring inside collection_version:. (docs.renovatebot.com)
Citations:
- 1: https://docs.renovatebot.com/modules/manager/regex/
- 2: https://docs.renovatebot.com/modules/manager/regex/
- 3: https://docs.renovatebot.com/modules/manager/regex/
Anchor the galaxy.yml version regex to avoid accidental matches.
Line 29 is currently unanchored. If additional keys containing version: are added later, Renovate may capture the wrong value. However, the proposed use of (?m)^…$ won't work as intended—Renovate's regex manager runs per-file and does not support inline multiline modifiers. Instead, use an explicit line-boundary prefix:
- "version:\\s*(?<currentValue>\\S+)"
+ "(?:^|\\r\\n|\\r|\\n)version:\\s*(?<currentValue>\\S+)"This prevents matching version: inside keys like collection_version:.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "version:\\s*(?<currentValue>\\S+)" | |
| "(?:^|\\r\\n|\\r|\\n)version:\\s*(?<currentValue>\\S+)" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@renovate.json` at line 29, The regex "version:\\s*(?<currentValue>\\S+)" is
unanchored and can match unintended keys; update the Renovate regex string to
explicitly anchor the pattern to the line boundaries by adding ^ at the start
and $ at the end (e.g., change the "version:\\s*(?<currentValue>\\S+)" entry to
use a line-anchored form like "^version:\\s*(?<currentValue>\\S+)$") so only a
top-level version: line in galaxy.yml is matched and keys like
collection_version are ignored.
|
|
||
| # Helm chart version (synced with Cozystack release) | ||
| cozystack_chart_version: "1.1.2" | ||
| cozystack_chart_version: "1.1.0" |
There was a problem hiding this comment.
Default chart rollback on main can cause unintended user downgrades.
Line 13 changes the installer default to 1.1.0, so fresh installs from main will deploy an older chart until a follow-up bump lands. That’s a user-facing regression risk.
Proposed adjustment
- cozystack_chart_version: "1.1.0"
+ cozystack_chart_version: "1.1.2"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| cozystack_chart_version: "1.1.0" | |
| cozystack_chart_version: "1.1.2" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@roles/cozystack/defaults/main.yml` at line 13, The default variable
cozystack_chart_version was hard-set to "1.1.0" which will cause fresh installs
from main to deploy an older chart; change the declaration of
cozystack_chart_version in roles/cozystack/defaults/main.yml to avoid
downgrading users by either reverting it to the previous (or current) chart
version, removing the hardcoded value so the installer uses the release-provided
version, or set it to an explicit semver range or null (e.g., leave undefined)
so consumers or the installer pick the intended latest chart instead of forcing
1.1.0. Ensure you only modify the cozystack_chart_version variable and keep the
key name intact.
Summary
galaxy.ymlversion changes on maink3s.orchestrationfrom rootrequirements.ymltotests/requirements.yml(CI-only dependency)requirements.ymlto each example directory with all dependencies listed explicitlyTest plan
renovate-config-validatorpassesrenovate --platform=localconfirms detection of cozystack 1.1.0→1.1.2 and k3s-ansible 1.1.0→1.2.0Summary by CodeRabbit
New Features
Updates
Chores