feat(skills): add cozy-external-app skill for scaffolding external apps#3
feat(skills): add cozy-external-app skill for scaffolding external apps#3kitsunoff wants to merge 31 commits intocozystack:mainfrom
Conversation
Add a new skill that generates the complete package structure for Cozystack external apps, including chart skeleton, values with cozyvalues-gen annotations, CozystackResourceDefinition registration, and dependency integration via managed CNPG clusters or external secret references. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 28 minutes and 3 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds a new Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant CLI as CLI Parser
participant Preflight as Preflight Validator
participant DepResolver as Dependency Resolver
participant Generator as Template Generator
participant FS as File System
participant SchemaVal as Schema Validator
User->>CLI: invoke cozy-external-app (app-name, flags)
CLI->>Preflight: parse args & run repo/tool checks
Preflight-->>CLI: preflight result
CLI->>DepResolver: resolve dependencies (local/gh/kubectl)
DepResolver-->>CLI: dependency contracts & wiring options
CLI->>User: interactive prompts for app & wiring
User-->>CLI: approve plan
CLI->>Generator: generate chart skeleton & templates
Generator->>FS: write Chart.yaml, values, templates, platform entries
FS-->>Generator: confirm files written
Generator->>SchemaVal: validate schemas & manifests
SchemaVal-->>CLI: validation results
CLI->>User: summary & next steps
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Code Review
This pull request introduces the cozy-external-app skill, which provides a structured workflow for scaffolding external application packages within the Cozystack ecosystem. The skill automates the creation of Helm charts, templates, and platform registration files while handling dependency patterns like managed Postgres clusters. The review feedback highlights several opportunities to strengthen the skill's logic, specifically by ensuring all required metadata for resource definitions is collected, clarifying the process for gathering operator chart details, providing explicit template examples for upstream Helm charts, and maintaining correct YAML formatting and variable consistency during the registration phase.
| 2. **Container image**: image reference (e.g., `ghcr.io/immich-app/immich-server:v1.120.0`). | ||
| 3. **Public port**: does the app expose an HTTP port? If yes, which port number? Should an Ingress template be generated? | ||
| 4. **Persistent storage**: does the app need a PVC? If yes, default size (e.g., `10Gi`). | ||
| 5. **Icon**: path to an SVG file for the dashboard. If not available yet, note it — Phase 6 will create a `logos/` placeholder and Phase 9 will fail until the user provides one. |
There was a problem hiding this comment.
Phase 3 is missing several metadata fields required for the CozystackResourceDefinition in Phase 8. Specifically, the skill needs to gather the app's display name, description, category, tags, and the Kubernetes Kind/Plural names for the resource.
| 5. **Icon**: path to an SVG file for the dashboard. If not available yet, note it — Phase 6 will create a `logos/` placeholder and Phase 9 will fail until the user provides one. | |
| 5. **Icon**: path to an SVG file for the dashboard. If not available yet, note it — Phase 6 will create a `logos/` placeholder and Phase 9 will fail until the user provides one. | |
| 6. **Metadata**: Display Name (e.g., `Immich`), Description (e.g., `Self-hosted photo and video management solution`), Category (e.g., `Media`), and Tags (e.g., `photo, video`). | |
| 7. **Resource Definition**: Kind (e.g., `Immich`) and Plural (e.g., `immichs`) for the CozystackResourceDefinition. |
|
|
||
| Skip if `--operator` was not passed and the app does not need a custom operator. | ||
|
|
||
| If an operator is needed, create `packages/system/$APP_NAME-operator/`: |
There was a problem hiding this comment.
Phase 5 uses $OPERATOR_CHART_NAME and $OPERATOR_CHART_VERSION in the generated Makefile, but these variables are never gathered from the user. If an operator is required, the skill should ask for the specific chart name and version within the repository.
| If an operator is needed, create `packages/system/$APP_NAME-operator/`: | |
| If an operator is needed, use `AskUserQuestion` to collect the operator's chart name and version from the provided repository URL. Then create `packages/system/$APP_NAME-operator/`: |
|
|
||
| ### Main workload — $APP_NAME.yaml | ||
|
|
||
| Generate the primary workload template. If the user chose "upstream Helm chart", wrap it in a Flux HelmRelease (like harbor does). If "custom templates", create a Deployment or StatefulSet directly. |
There was a problem hiding this comment.
The skill mentions wrapping upstream charts in a Flux HelmRelease but only provides a template for a direct Deployment. Providing a HelmRelease template example will help the AI generate the correct structure for the 'upstream chart' case.
| Generate the primary workload template. If the user chose "upstream Helm chart", wrap it in a Flux HelmRelease (like harbor does). If "custom templates", create a Deployment or StatefulSet directly. | |
| **For a Flux HelmRelease wrapper example:** | |
| ```yaml | |
| apiVersion: helm.toolkit.fluxcd.io/v2 | |
| kind: HelmRelease | |
| metadata: | |
| name: {{ .Release.Name }} | |
| spec: | |
| interval: 1h | |
| chart: | |
| spec: | |
| chart: $UPSTREAM_CHART_NAME | |
| version: $UPSTREAM_CHART_VERSION | |
| sourceRef: | |
| kind: HelmRepository | |
| name: $UPSTREAM_REPO_NAME | |
| values: | |
| # Map values from .Values to upstream chart values |
| chart: ./packages/system/$APP_NAME-operator | ||
| sourceRef: | ||
| kind: GitRepository | ||
| name: external-apps |
There was a problem hiding this comment.
The sourceRef.name is hardcoded to external-apps, but Phase 8 later includes logic to extract the actual name from init.yaml. The template should use the variable extracted from init.yaml to ensure consistency if the user has renamed their repository.
| name: external-apps | |
| name: $GIT_REPO_NAME |
| application: | ||
| kind: $APP_KIND | ||
| openAPISchema: | | ||
| <contents of values.schema.json> |
There was a problem hiding this comment.
…ase 3 Phase 8 renders a CozystackResourceDefinition that requires display name, description, category, tags, Kind, and Plural. Phase 3 previously collected none of these, forcing the skill to improvise in a later phase. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…ase 5 The generated Makefile interpolates $OPERATOR_CHART_NAME and $OPERATOR_CHART_VERSION when pulling the operator chart, but the skill never asked the user for them. Add an explicit AskUserQuestion step so the variables are populated before the Makefile is written. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
Phase 7 instructs the skill to wrap upstream charts in a Flux HelmRelease but only showed a direct Deployment example. Add an explicit HelmRelease template so the upstream chart case has a concrete shape to follow. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
The operator HelmRelease template hardcoded sourceRef.name to "external-apps" while the CozystackResourceDefinition already used $GIT_REPO_NAME. Extract the value from init.yaml once at the top of phase 8 and reuse it in both templates so the skill works for users who renamed their repository. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
The CozystackResourceDefinition embeds values.schema.json under a YAML literal block scalar. Without indenting the JSON by six spaces the YAML parser silently absorbs the schema as plain text. Call out the rule and provide a sed one-liner for the manual path. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
The CRD kind CozystackResourceDefinition does not exist in Cozystack v1.x — it was renamed to ApplicationDefinition. The skill, plugin metadata, and marketplace entry all referenced the stale name, so every generated manifest would have been rejected by the API server. Switch to the current kind everywhere. The filename cozyrds.yaml is kept unchanged, matching upstream convention in external-apps-example. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
Three schema-level issues made the generated manifest invalid: metadata.namespace was set on a cluster-scoped kind; release.chart.sourceRef does not exist in the CRD (required field is release.chartRef with kind HelmChart/ExternalArtifact/OCIRepository); keysOrder was missing and the dashboard UI would render fields in arbitrary order. Align the template with the CRD schema and the minecraft-server reference in external-apps-example. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…ackage The reference external-apps layout registers operator charts via a flux HelmRepository (typically oci://) and references them from the HelmRelease with sourceRef.kind: HelmRepository. The skill instead created a stub packages/system/<app>-operator/ chart with a make update target that pre-pulled the upstream into charts/ — a pattern that diverges from the reference and leaves the HelmRelease pointing at an empty stub Chart.yaml. Rewrite Phase 5 to gather operator specification only (type, URL, chart name, version) and switch the Phase 8 operator HelmRelease template to sourceRef.kind: HelmRepository. Also fix the misplaced dependsOn paragraph: the application HelmRelease is controller-generated from the ApplicationDefinition and is not user-authored, so dependsOn only makes sense on the operator HelmRelease. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
The reference layout has five platform template files, not three. helmcharts.yaml must have one entry per app (the source referenced by ApplicationDefinition.spec.release.chartRef), and helmrepositories.yaml must have one entry per operator chart. Without these the chartRef has no source and flux cannot produce the chart artifact. Add both subsections to phase 8, expand the phase 2 pre-flight to assert all five files, and update summary/guardrails to match. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
Two problems made the generated app Makefile unusable against external-apps repos. It lacked export NAME and export NAMESPACE, so every downstream target inherited from scripts/package.mk failed with "env NAME is not set!". And it invoked ../../../hack/update-crd.sh, a script that only exists in the cozystack monorepo — not in external-apps-example. Add the required exports, drop the hack/update-crd.sh call, switch cozyvalues-gen to its long flags, and strip the phase 8 paragraphs that promoted a script users do not have. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
Phase 4 previously hardcoded only the CNPG/postgres pattern. A redis or mongodb dependency would have left the assistant to improvise CR kinds, secret naming, and credential keys — a guaranteed source of silent wiring bugs. Introduce a dependency catalog documenting four common patterns verified against the cozystack monorepo (CNPG for postgres, Spotahome RedisFailover for redis, Percona PSMDB for mongodb, Strimzi for kafka) plus a research procedure for anything not listed. The catalog explicitly distinguishes operator-emitted secrets (CNPG) from chart-created ones (RedisFailover), since the wiring differs. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
… wiring Split Phase 4 Pattern A into three explicit steps: research the dependency (CR kind, output Secret owner/name/keys, app-side wiring), collect CR-spec values, collect env mapping. The earlier flow jumped straight to hardcoded CNPG defaults and would have silently produced wrong Secret references for redis or mongodb. Env mapping now uses facts captured during research — catalog-driven examples show how wiring differs between operator-emitted secrets (CNPG) and chart-created ones (Spotahome RedisFailover). Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…ase 7 Phase 7 previously shipped a single hardcoded database.yaml for postgres/CNPG, implying the same wiring for every dependency. Rework the section into "per-dep creation templates": one file per Pattern A dependency, shape dictated by the facts captured during Phase 4 research. Add a full redis.yaml example (Secret + RedisFailover with auth.secretPath) to illustrate that chart-created secrets require a fundamentally different wiring from CNPG. Tighten guardrails to forbid copying postgres wiring onto other dependencies. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…chart source Phase 5 was scoped to operator-only chart sources, but flux HelmRepository registration is equally needed when the app wraps an upstream helm chart (Gitea, Immich, Nextcloud — all have first-party maintained charts). Rewrite the phase around a $SOURCE_ROLE (main | operator) so either case registers a HelmRepository; phase 8 can then reuse one code path for both. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
For apps with a maintained first-party helm chart, wrapping in a Flux HelmRelease is strictly better than hand-rolling a Deployment: upgrades, init jobs, probes, PDBs, and CVE mitigations all stay the responsibility of the upstream maintainers. Rewrite Phase 3 question 1 to bias toward the wrapper pattern, and rework Phase 7 main workload so the HelmRelease example is the primary shape — complete with dependsOn against pattern C sibling HelmReleases, bundled-subchart disablement, and valuesFrom lifting passwords from cozystack-created Secrets into the upstream chart value schema. The custom Deployment example is demoted to a fallback for apps without a viable upstream chart. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…apps Pattern A (in-chart operator CR) bypasses the cozystack abstraction — dependencies never appear in the dashboard, WorkloadMonitor/backup conventions get reinvented per app, and platform upgrades do not propagate. For tenant-facing external apps the correct default is Pattern C: the app chart creates a sibling cozystack CR (Postgres, Redis, MariaDB, etc.), the cozystack controller reconciles it into its own HelmRelease, and the app reads the Secret/Service that downstream chart produces. Add pattern C to phase 4 as the recommended path and demote pattern A to a system-style escape hatch. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…tgres and redis Extract the sibling-CR naming contracts (HelmRelease prefix, credentials Secret template, Service names) from cozystack packages/system/postgres-rd and redis-rd cozyrds files and publish them as catalog entries. Each entry ships a ready-to-adapt templates/<dep>.yaml snippet and a matching main-HelmRelease values/valuesFrom block — the two pieces a user needs to wire a pattern C dependency end to end. For mariadb, mongodb, kafka, and friends, point readers at packages/system/<dep>-rd/cozyrds/<dep>.yaml as the authoritative source rather than publishing unverified copies. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
Phase 7 now ships ready-to-adapt pattern C templates (apps.cozystack.io Postgres and Redis) as the preferred shape, with pattern A CNPG and RedisFailover examples kept for the escape-hatch case. Phase 10 summary surfaces the chosen pattern per dependency (A/B/C) plus the chart-source mode (upstream wrapper vs custom templates) so a quick glance at the summary tells a reader exactly how the generated package is wired. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
After phase 5 was generalized, phase 8 still referenced the old $OPERATOR_* variable names and assumed a single chart source per app. Rewrite the namespaces, helmrepositories, and helmreleases subsections to iterate over every $SOURCE_* entry gathered in phase 5 (main and/or operator) and emit the corresponding resource once per source. Also call out that the main upstream chart HelmRelease lives inside the app chart itself (one per tenant instance) and must not appear in the platform-level helmreleases.yaml. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…k at runtime Replace the "ask user to list deps" flow with a chart-driven, dynamically-resolved procedure. Step 1 introspects the upstream chart (helm show chart/values/readme + keyword scan of value paths) to surface candidate deps along with the exact targetPath the app expects. Step 2 resolves each dep against cozystack — local checkout, then gh api, then live cluster via kubectl — and records a $DEP_CONTRACT with kind, prefix, secret/service templates, and openAPISchema. Steps 3-5 pick the integration pattern, collect spec values from the resolved schema, and write the wiring record. Patterns A/B/C are preserved but now consume $DEP_CONTRACT instead of ad-hoc lookups, and pattern C is only offered when resolution succeeded. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
Phase 4 now resolves dependency contracts at runtime from cozystack itself, so the catalog appendix stops trying to enumerate every supported dep. Rewrite the intro as an interpretation guide that shows how to read an ApplicationDefinition into a $DEP_CONTRACT record, keep postgres and redis as worked examples so the assistant can self-check its parsing, and delete the "other dependencies" hand-wave that had become dead weight. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…ight Phase 2 now detects which of the three contract sources (local cozystack checkout, gh api, live cluster) is available and records $COZYSTACK_CONTRACT_SOURCE. Pattern C is marked unavailable up front when no source is reachable, preventing the skill from silently falling back to invented contracts. Tighten guardrails to forbid skipping either resolution step, and add the source-priority pointer to the References section. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
The skill previously gated file writes with a per-phase AskUserQuestion in phases 6, 7, and 8 — the user never saw the full picture before files started landing on disk. Phase 5.5 assembles everything decided in phases 1-5 (app summary, chart source, contract source, exact file list, wiring table, open items) into one plan and asks for a single approve/edit/abort. Once approved via $PLAN_APPROVED, phases 6-8 skip their individual confirmations and run through without extra prompts. The per-phase gates remain as a fallback for runs where 5.5 was not reached or the plan was rejected. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
kitsunoff
left a comment
There was a problem hiding this comment.
Self-review after two parallel analyses. Three concrete correctness issues will bite anyone running the skill end-to-end and should be fixed before merge; four smaller items are non-blocking. Overall architecture (contract-driven Pattern C, runtime resolution, Phase 5.5 plan gate) checks out against the actual cozystack and external-apps-example sources.
Blocker scope: generated charts would fail helm template (missing values), silently rotate DB passwords on every reconcile (random regeneration without lookup), or pick the wrong GitRepository name from init.yaml. All three are one-line fixes.
| {{- end }} | ||
| users: | ||
| {{ .Values.database.user }}: | ||
| password: {{ .Values.database.password | default (randAlphaNum 32) | quote }} |
There was a problem hiding this comment.
Blocker: randAlphaNum 32 produces a fresh random value every helm template or Flux reconcile. When .Values.database.password is empty (the default — Phase 6 never declares the field), each render writes a different password into the Postgres CR, which cascades into the downstream postgres-<cr>-credentials Secret and breaks the running app whenever its password rotates.
Evidence: cozystack/packages/apps/postgres/templates/init-script.yaml solves this with Sprig's lookup — pulls the existing password from the already-rendered Secret and reuses it on subsequent renders. The Pattern A redis example in this skill uses the same idiom.
Fix: wrap the password with lookup so subsequent renders reuse the previously-rendered value:
{{- $existing := lookup "v1" "Secret" .Release.Namespace (printf "postgres-%s-db-credentials" .Release.Name) }}
{{- $pwd := "" }}
{{- if $existing }}
{{- $pwd = index $existing.data .Values.database.user | b64dec }}
{{- end }}
{{- $pwd = $pwd | default (.Values.database.password | default (randAlphaNum 32)) }}
...
password: {{ $pwd | quote }}
Same fix needed in the Dependency catalog Pattern C postgres snippet around line 1024.
| host: "" | ||
| port: 5432 | ||
| secretName: "" | ||
| ``` |
There was a problem hiding this comment.
Blocker: Phase 7 Pattern C postgres.yaml (line ~581) reads .Values.database.{size,replicas,user,name,password} and Pattern C redis.yaml (line ~620) reads .Values.redis.{size,replicas}. This values.yaml example only documents Pattern A (database.{size,replicas} without user/name) and Pattern B. A user who follows the skill end-to-end on Pattern C — the documented default for external apps — ends up with a chart that fails helm template: <no value> in every wired gitea.config.database.* path, plus the Postgres CR spec renders with empty users: and databases: maps.
Fix: add a Pattern C values section alongside Pattern A and Pattern B. Minimum fields for postgres: user, name, size, replicas with sane defaults. For redis: size, replicas. Document that password is optional and generated when empty (ties to blocker #1).
Test: generate a chart for /cozy-external-app gitea --depends-on=postgres,redis on Pattern C defaults and run helm template test . — must render without <no value> anywhere.
| Before generating any YAML in this phase, extract the GitRepository name from `init.yaml` — it is referenced as `sourceRef.name` in both the operator HelmRelease and the ApplicationDefinition below: | ||
|
|
||
| ```bash | ||
| GIT_REPO_NAME=$(yq -r '.metadata.name' $REPO_DIR/init.yaml | head -1) |
There was a problem hiding this comment.
Blocker: yq -r '.metadata.name' $REPO_DIR/init.yaml | head -1 treats any metadata.name from any document as authoritative. The reference init.yaml in cozystack/external-apps-example contains both a GitRepository and a HelmRelease both named external-apps — the extraction works by coincidence. If a user renames their repo so the two differ (e.g., GitRepository my-apps, HelmRelease my-apps-release, which is a valid layout), head -1 silently grabs the HelmRelease name and every Phase 8 sourceRef points at a non-existent source.
The skill already documents the safe form directly below (line 758) as a "fallback for multi-document init.yaml":
yq -r 'select(.kind == "GitRepository") | .metadata.name' $REPO_DIR/init.yaml
Fix: promote that form to the only/primary path. It handles the single-document case equally well.
|
|
||
| Generate via: | ||
| ```bash | ||
| cd $REPO_DIR/packages/apps/$APP_NAME && cozyvalues-gen -v values.yaml -s values.schema.json -r README.md |
There was a problem hiding this comment.
Observation (non-blocking): this bash snippet uses short flags (-v -s -r) while the Makefile generated a few lines up (line 311) uses long flags (--values --schema --readme). The global convention is long-flag-only. Since users literally copy this command, consistency with the Makefile matters. Normalize to long form here.
| spec: | ||
| interval: 5m | ||
| url: $SOURCE_REPO_URL | ||
| # If $SOURCE_REPO_TYPE is `oci`, uncomment the next line: |
There was a problem hiding this comment.
Observation (non-blocking): a literal-minded assistant may emit the # type: oci line as a comment rather than uncommenting it. The reference external-apps-example's helmrepositories.yaml has type: oci set plainly when the source is OCI. Consider two explicit variants in the skill (HTTPS source vs OCI source) or a Go-template conditional so the emitted YAML never needs post-processing.
|
|
||
| For each dependency from Step 1, resolve a `$DEP_CONTRACT` from cozystack. Try these sources in order; stop at the first success: | ||
|
|
||
| 1. **Local cozystack checkout** — when `$COZYSTACK_REPO` is set (or detectable as a sibling dir of `$REPO_DIR`). Read: |
There was a problem hiding this comment.
Observation (non-blocking): the clause "or detectable as a sibling dir of $REPO_DIR" is never defined. Phase 2 Step 4 only reads $COZYSTACK_REPO from env. Either specify the probe (e.g., check ../cozystack/packages/system/ for cozyrds directories) or drop the clause.
| redis: | ||
| host: rfs-redis-{{ .Release.Name }}-redis | ||
| port: 26379 | ||
| sentinelMaster: mymaster |
There was a problem hiding this comment.
Observation (non-blocking): functionally correct — mymaster is Spotahome's default sentinel monitor name and cozystack's packages/apps/redis/templates/redisfailover.yaml does not override it. But the skill's own guardrail says "never guess a dependency's Secret/config names" and this value is not derivable from $DEP_CONTRACT. Add a one-line comment citing the Spotahome default so an assistant or reviewer can tell verified from guessed.
The pattern C Postgres CR used randAlphaNum 32 as the default password, which regenerates on every helm template or flux reconcile. Since the downstream cozystack apps/postgres chart already preserves passwords via Sprig lookup against its own postgres-<release>-credentials Secret (templates/init-script.yaml), the external chart should simply not pass a password unless the user explicitly supplied one. Omitting the field lets the downstream chart own password lifecycle end-to-end. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
Phase 7 pattern C templates read .Values.database.{user,name,password,size,replicas} and .Values.redis.{size,replicas}, but phase 6 only documented pattern A and pattern B values sections. A user following the skill end-to-end on pattern C (the documented default for external apps) ended up with a chart that fails helm template — <no value> in every wired gitea.config.database.* path and an empty users map in the Postgres CR. Add a pattern C section ahead of A and B so the values.yaml skeleton actually covers the recommended path.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…parse The phase 8 yq extraction used .metadata.name + head -1 as primary path, picking whichever kind happened to come first in init.yaml. The external-apps-example layout has a GitRepository and a HelmRelease both named external-apps, so the old extraction worked by coincidence; if a user ever renames the repo and the HelmRelease ends up with a different name (a valid layout), head -1 silently grabs the HelmRelease name and every phase 8 sourceRef points at nonexistent source. Make kind=GitRepository the deterministic primary selector. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@skills/cozy-external-app/skills/cozy-external-app/SKILL.md`:
- Around line 474-484: Replace the hardcoded dependsOn and subchart toggles (the
dependsOn block plus values keys postgresql, postgresql-ha, redis,
redis-cluster) with logic that constructs those entries from the Phase 4
resolved dependency list and the detected upstream subcharts: iterate the
resolved dependencies and emit a dependsOn entry for each backing service (using
the release name/namespace templating currently used), and iterate detected
upstream subcharts to set values.<chart>.enabled = false only for the subcharts
that actually exist; skip others so HelmRelease output matches actual deps.
Ensure the code that builds the HelmRelease uses the Phase 4
resolvedDependencies data structure and the upstreamSubcharts detection result
rather than fixed names.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 27c70102-e082-451b-b46d-8b402aa35531
📒 Files selected for processing (4)
.claude-plugin/marketplace.jsonREADME.mdskills/cozy-external-app/.claude-plugin/plugin.jsonskills/cozy-external-app/skills/cozy-external-app/SKILL.md
| dependsOn: | ||
| - name: postgres-{{ .Release.Name }}-db | ||
| namespace: {{ .Release.Namespace }} | ||
| - name: redis-{{ .Release.Name }}-redis | ||
| namespace: {{ .Release.Namespace }} | ||
| # Disable the upstream chart's bundled subcharts — we provide backing services via Pattern C. | ||
| values: | ||
| postgresql: { enabled: false } | ||
| postgresql-ha: { enabled: false } | ||
| redis: { enabled: false } | ||
| redis-cluster: { enabled: false } |
There was a problem hiding this comment.
Make dependency wiring dynamic instead of hardcoded postgres/redis.
Lines 474-484 hardcode dependsOn and subchart toggles for postgres/redis. For apps without those deps (or with different deps), this generates incorrect HelmRelease output. Build these blocks from the Phase 4 resolved dependency list and detected upstream subcharts.
Suggested adjustment
- dependsOn:
- - name: postgres-{{ .Release.Name }}-db
- namespace: {{ .Release.Namespace }}
- - name: redis-{{ .Release.Name }}-redis
- namespace: {{ .Release.Namespace }}
+ # Emit dependsOn entries only for dependencies selected in Phase 4 (Pattern C/A),
+ # using each dependency's resolved release prefix and CR name template.
- values:
- postgresql: { enabled: false }
- postgresql-ha: { enabled: false }
- redis: { enabled: false }
- redis-cluster: { enabled: false }
+ values:
+ # Disable only subcharts that were actually detected in Phase 4 Step 1
+ # and mapped to external/sibling dependency wiring.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@skills/cozy-external-app/skills/cozy-external-app/SKILL.md` around lines 474
- 484, Replace the hardcoded dependsOn and subchart toggles (the dependsOn block
plus values keys postgresql, postgresql-ha, redis, redis-cluster) with logic
that constructs those entries from the Phase 4 resolved dependency list and the
detected upstream subcharts: iterate the resolved dependencies and emit a
dependsOn entry for each backing service (using the release name/namespace
templating currently used), and iterate detected upstream subcharts to set
values.<chart>.enabled = false only for the subcharts that actually exist; skip
others so HelmRelease output matches actual deps. Ensure the code that builds
the HelmRelease uses the Phase 4 resolvedDependencies data structure and the
upstreamSubcharts detection result rather than fixed names.
- normalize cozyvalues-gen invocation to long flags, matching Makefile - split helmrepositories.yaml into explicit HTTPS and OCI variants so the skill never emits a commented-out type line - drop undefined sibling-dir detection clause from Phase 4 Step 2; Phase 2 Step 4 only reads $COZYSTACK_REPO - cite Spotahome RedisFailover default next to every sentinelMaster: mymaster so the value is traceable, not a guess - frame the Phase 7 HelmRelease example as a two-dep illustration and warn against copying unrelated dependsOn or subchart toggles Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@skills/cozy-external-app/skills/cozy-external-app/SKILL.md`:
- Around line 214-215: The contract for $SOURCE_REPO_TYPE is inconsistent: Phase
5 documentation says non-OCI should be left empty while Phase 8 (and the
HelmRepository template condition) expects "https", which causes wrong variant
selection; update the Phase 8 text/examples and any condition checks that
reference $SOURCE_REPO_TYPE (the Phase 8 rendering logic and the HelmRepository
template branch) to treat non-OCI as empty (or conversely make Phase 5 use
"https" everywhere) so both collection and rendering agree—pick the
empty-as-default approach and replace "https" examples/conditions in Phase 8 and
related template branches with empty/default handling for $SOURCE_REPO_TYPE.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 18cdd063-21b3-4ebf-8db6-8669b1769f83
📒 Files selected for processing (1)
skills/cozy-external-app/skills/cozy-external-app/SKILL.md
Blockers:
- main HelmRelease no longer bakes $APP_DB_USER / $APP_DB_NAME at
generation time; use {{ .Values.database.user }} / .name so the
values block and valuesFrom.valuesKey stay in sync with templates/
postgres.yaml when a tenant overrides database.user at deploy time
- Pattern A Spotahome RedisFailover Secret now inlines the full
lookup-then-randAlphaNum idiom from cozystack/packages/apps/redis/
templates/redisfailover.yaml, preventing the same password-churn
class previously fixed for Pattern C postgres
Observations:
- Phase 3 collects Display Name plural separately; Phase 8 cozyrds
now renders plural: $APP_DISPLAY_PLURAL instead of reusing the
singular, matching the cozystack external-apps-example reference
- cozyrds tags template shows two placeholders with a comment
requiring one list item per collected tag
- $SOURCE_REPO_TYPE is now https (not empty) for HTTPS sources so
Phase 8's variant selector reads the exact value it documents
- Phase 3 icon note and Phase 5.5 open-items line reference Phase 8
base64 as the actual failure point, not Phase 9 validation
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: ZverGuy <maximbel2003@gmail.com>
Main picked up drbd-recovery (PR cozystack#4) while this branch was open. Both plugins coexist in alphabetical order: cozy-deploy, cozy-external-app, drbd-recovery. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
…e number Line-number ranges like `sts.yaml:142-168` drift on any upstream refactor. Anchor the three cozystack references by the named template block they point at (`KC_DB_*` env block in keycloak sts.yaml; `database.external` values block in harbor.yaml) so the guidance stays valid across cozystack releases. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ZverGuy <maximbel2003@gmail.com>
Summary
cozy-external-appskill that scaffolds a complete Cozystack external app packageChanges
skills/cozy-external-app/.claude-plugin/plugin.json— plugin metadataskills/cozy-external-app/skills/cozy-external-app/SKILL.md— 10-phase skill with Guardrails and References.claude-plugin/marketplace.json— added plugin entryREADME.md— added table rowSummary by CodeRabbit
New Features
Documentation