1
0
mirror of https://github.com/bestnite/quadlet-migrator-skill.git synced 2026-04-26 20:41:54 +00:00

Refactor skill docs into core workflow and references

This commit is contained in:
2026-04-10 02:08:09 +10:00
parent 108e92be4f
commit 349b8ed436
6 changed files with 199 additions and 427 deletions
+37 -52
View File
@@ -2,19 +2,27 @@
Use this file when converting `docker-compose.yml` or `compose.yaml` into Quadlet units.
## General approach
## Contents
- Model each service first, then decide how to group all resulting containers into one or more `.pod` units.
- General defaults
- Field mapping
- Topology guidance
- Risky or lossy areas
## General Defaults
- Model each service first, then decide how to group the result into one or more `.pod` units.
- Prefer maintainable Quadlet output over mechanical one-to-one translation.
- Keep names stable and predictable. Generated filenames should use a shared project prefix.
- Keep filenames stable and predictable. Use a shared project prefix for generated artifacts.
- Do not add explicit runtime naming directives such as `PodName=`, `ServiceName=`, `ContainerName=`, or `NetworkName=` by default. Let Quadlet and Podman derive runtime names unless the user explicitly asks for custom naming or a reviewed requirement depends on it.
- When one deliverable set includes multiple Quadlet files, keep that shared prefix consistent so helper shell scripts can match them by shared-prefix globbing such as `<prefix>*`, instead of hardcoding exact filenames or assuming a fixed file count.
- Do not add `User=`, `Group=`, or `UserNS=keep-id` by default. Preserve or introduce runtime identity mapping only when the source explicitly requires it or when the user is working through permission or ownership behavior.
- For env-specific decisions, use `references/env-strategy.md` instead of expanding the env rules here.
## Field strategy
## Field Mapping
### `name`
- Use as an application prefix when it improves unit naming clarity.
- Use it as an application prefix when it improves naming clarity.
- Do not force a top-level project name into every filename if the user prefers shorter units.
### `services.<name>.image`
@@ -37,7 +45,7 @@ Use this file when converting `docker-compose.yml` or `compose.yaml` into Quadle
### `container_name`
- Drop it.
- Do not generate `ContainerName=`. Let Podman and systemd derive the runtime name.
- Do not generate `ContainerName=`.
### `ports`
@@ -61,33 +69,21 @@ Use this file when converting `docker-compose.yml` or `compose.yaml` into Quadle
- If the source uses a default network only, you often do not need a `.network` unit at all.
- If the source uses bridge networking for containers that can reasonably live in one pod, collapse that topology into one `.pod` so the containers share one network namespace.
- Create a `.network` unit only when services must be split across pods, or when explicit network isolation or custom network management is materially required.
- Do not add `NetworkName=` by default. Let Quadlet derive the network name unless the user explicitly asks for a custom network name or a reviewed requirement depends on it.
- Do not add `NetworkName=` by default.
- Containers in the same `.pod` can communicate over `127.0.0.1` / `localhost` because they share a network namespace.
- When services in the same `.pod` must accept connections from sibling containers, ensure they listen on `127.0.0.1` or `0.0.0.0`; if they listen only on another interface, sibling containers in the pod may not be able to reach them.
- When the upstream service supports configuring the listen address via environment variables or equivalent runtime settings, preserve or generate the necessary configuration instead of assuming the default bind address is correct.
- When `Pod=` is set, never generate `AddHost=` entries whose purpose is sibling-container discovery inside that pod; intra-pod communication should use `127.0.0.1` / `localhost` instead.
- `AddHost=` is a host-to-IP override, not an intra-pod service-discovery mechanism. Upstream Quadlet documents `AddHost=` in both `[Container]` and `[Pod]`, so do not describe `Pod=` as a blanket prohibition on `AddHost=` unless the upstream reference explicitly requires that for the case at hand.
- When `Pod=` is set, never generate `AddHost=` entries whose purpose is sibling-container discovery inside that pod. Intra-pod communication should use `127.0.0.1` / `localhost` instead.
- `AddHost=` is a host-to-IP override, not an intra-pod service-discovery mechanism. Do not describe `Pod=` as a blanket prohibition on `AddHost=` unless the upstream reference explicitly requires that for the case at hand.
- Containers in different pods must not be treated as reachable via `127.0.0.1` / `localhost`.
- When splitting services across multiple pods or preserving a shared bridge network, use container names, pod names, or explicit `NetworkAlias=` values on the shared network, or publish ports to the host boundary when that is the intended access pattern.
- Do not add `ServiceName=` or `PodName=` by default. If they are omitted, use Quadlet's derived names unless the user explicitly asks for custom runtime naming or a reviewed requirement depends on it.
- Do not add `ServiceName=` or `PodName=` by default.
- `ServiceName=` controls the generated systemd unit name only and must not be used as an application-facing network address.
- `PodName=` controls the Podman pod name only; it can participate in the chosen addressing strategy, but it does not determine the systemd service name.
### `environment`
### `environment`, `env_file`, and `.env` interpolation
- Small stable values can become one or more `Environment=` lines.
- Sensitive values should be moved to `EnvironmentFile=` unless the user explicitly wants them inline.
### `env_file`
- Prefer `EnvironmentFile=`.
- If there are multiple env files, preserve order and explain precedence if the user asks.
### `.env` interpolation
- Resolve only when you have the actual source values.
- If variables are missing, surface a missing-variable list.
- Never silently replace unknown values with blanks.
- Use `references/env-strategy.md` for detailed env handling, interpolation, sensitivity defaults, completeness checks, and missing-variable reporting.
### `profiles`
@@ -113,21 +109,32 @@ Use this file when converting `docker-compose.yml` or `compose.yaml` into Quadle
### `user`
- Do not add `User=` or `Group=` by default. Map them in `[Container]` only when the source explicitly requires a container runtime user mapping or when the user is addressing permission or ownership behavior, and not as a systemd service user substitute.
- Map `User=` and `Group=` only when the source explicitly requires a container runtime user mapping or when the user is addressing permission or ownership behavior.
- Do not use systemd `User=` to try to make a rootless Quadlet run as another login user.
- Do not add `UserNS=keep-id` by default. Consider it only when the user is working through rootless permission or ownership behavior and the reviewed topology benefits from preserving host identity semantics.
- Consider `UserNS=keep-id` only when the user is working through rootless permission or ownership behavior and the reviewed topology benefits from preserving host identity semantics.
### unsupported or risky fields
## Topology Guidance
Choose the simplest topology that preserves the source deployment intent.
- Prefer a single `.pod` for multi-container applications when practical.
- If one logical service contains multiple containers, default to putting them in the same `.pod` so they share networking and lifecycle.
- If the project is a simple single-container deployment with no real need for pod semantics, a standalone `.container` is the preferred result.
- If one pod is not practical because of port conflicts or clearly incompatible groupings, split the result into a small number of pods rather than forcing an awkward topology.
- Avoid preserving bridge networks by default when pod grouping already expresses the intended communication pattern well.
- For large application stacks with optional services, ask the user to choose the desired service set before generating a minimized result.
## Risky Or Lossy Areas
Handle these conservatively and usually as migration notes:
- `deploy`
- `profiles`
- `extends`
- advanced Compose merge behavior
- readiness semantics hidden behind `depends_on`
- any mapping that changes the source network or storage behavior in a way that matters
## Minimal examples
## Minimal Examples
### Single service to standalone container
@@ -149,8 +156,6 @@ Image=docker.io/library/nginx:latest
PublishPort=8080:80
```
Use this when the deployment is truly a simple single-service case. A single container should usually stay a standalone `.container` rather than being wrapped in a pod.
### Small multi-service app to one pod
Source intent:
@@ -172,23 +177,3 @@ Reasonable result shape:
- one container unit for `db`
- `api` may reach `db` over `127.0.0.1` / `localhost` because both containers share the pod network namespace
- ordering hints for startup, while explicitly noting that `depends_on` does not guarantee readiness
Use this as the default shape for a small multi-container service unless port conflicts or incompatible grouping force a split into multiple pods.
## Pod decision rule
Choose the simplest topology that preserves the source deployment intent.
Prefer a single `.pod` for multi-container applications when practical.
If one logical service contains multiple containers, default to putting them in the same `.pod` so they share networking and lifecycle.
If the project is a simple single-container deployment with no real need for pod semantics, a standalone `.container` is the preferred result.
If one pod is not practical because of port conflicts or clearly incompatible groupings, split the result into a small number of pods rather than forcing an awkward topology.
When services are split across multiple pods, do not rely on `127.0.0.1` / `localhost` for cross-pod communication. Use container names, pod names, or explicit `NetworkAlias=` values on the shared network instead.
Avoid preserving bridge networks by default when pod grouping already expresses the intended communication pattern well.
For large application stacks with optional services, ask the user to choose the desired service set before generating a minimized result.
+41 -62
View File
@@ -2,22 +2,31 @@
Use this file when the user wants deployment-ready instructions alongside generated Quadlet units.
## Delivery flow
## Contents
- Delivery flow
- Apply target directories
- Helper-script expectations
- Operational notes
- Optional enhancements
## Delivery Flow
For `design` mode, stop after the user reviews the finalize snapshot in conversation.
For `generate` mode, continue only after the user has reviewed and confirmed the finalize snapshot or requested edits.
Recommended apply flow:
1. Generate the reviewable artifacts in the current working directory by default, or in another user-requested output directory.
2. Resolve high-impact planning questions with the user before freezing the design.
3. Review the finalized design snapshot together with the generated Quadlet files, env files, helper scripts, and any required repo-local support files or directories.
4. Use `install.sh` to copy only the reviewed Quadlet unit files into the chosen Quadlet directory.
5. Run the appropriate `daemon-reload` command.
6. Use `start.sh`, `stop.sh`, and `restart.sh` to manage the deployed services.
7. Use `uninstall.sh` when the user wants to remove the installed reviewed Quadlet unit files without broad Podman cleanup.
2. Review the finalized design together with the generated Quadlet files, env files, helper scripts, and required repo-local support files.
3. Use `install.sh` to copy only the reviewed Quadlet unit files into the chosen Quadlet directory.
4. Run the appropriate `daemon-reload` command.
5. Use `start.sh`, `stop.sh`, and `restart.sh` to manage services.
6. Use `uninstall.sh` only when the user wants to remove the installed reviewed Quadlet unit files without broad Podman cleanup.
Do not start execution until the user has reviewed and confirmed the finalize snapshot or requested edits.
Keep installation separate from service-management scripts so the user can review generated files before applying them.
## Apply target directory
## Apply Target Directories
### Rootless
@@ -31,67 +40,42 @@ Do not start execution until the user has reviewed and confirmed the finalize sn
See `podman-systemd.unit.5.md` for the full search-path matrix.
## Helper scripts
## Helper-Script Expectations
- `install.sh`: canonical apply script; copy only reviewed Quadlet unit files into the selected Quadlet target directory
- do not generate a separate `apply.sh` by default; reserve that alternate name only when the user explicitly asks for it
- helper shell scripts must discover the reviewed Quadlet files through their shared generated prefix, using shared-prefix glob matching such as `<prefix>*` instead of hardcoding exact filenames or assuming a fixed file count
- `install.sh` must not start, stop, restart, or reload services as a side effect
- `uninstall.sh`: remove the installed reviewed Quadlet unit files from the selected Quadlet target directory
- `uninstall.sh` should stop affected services before removing their installed unit files, and should not delete support files from the current-directory deliverable set, unrelated files, shared directories, named volumes, images, or Podman objects unless the user explicitly asks for broader cleanup
- `reload.sh`: run only the appropriate `daemon-reload` command after installation changes
- `start.sh`, `stop.sh`, `restart.sh`: manage services only and must not silently install or overwrite reviewed files
- when a generated topology includes `<name>.pod` plus child containers linked with `Pod=<name>.pod`, helper scripts should use the pod service as the lifecycle entrypoint; derive that service name from `ServiceName=` when present on the `.pod`, otherwise use Quadlet's default generated pod service name. Do not add `ServiceName=` merely to simplify helper scripts, and do not duplicate per-container lifecycle commands for child units
- `install.sh` is the canonical apply script. It copies only reviewed Quadlet unit files into the selected Quadlet target directory.
- Do not generate a separate `apply.sh` by default. Use that alternate name only when the user explicitly asks for it.
- Helper shell scripts must discover the reviewed Quadlet files through their shared generated prefix, using shared-prefix glob matching such as `<prefix>*` instead of hardcoding exact filenames or assuming a fixed file count.
- `install.sh` must not start, stop, restart, or reload services as a side effect.
- `uninstall.sh` removes only the installed reviewed Quadlet unit files from the selected Quadlet target directory.
- `uninstall.sh` should stop affected services before removing their installed unit files, and should not delete support files, unrelated files, shared directories, named volumes, images, or Podman objects unless the user explicitly asks for broader cleanup.
- `reload.sh` runs only the appropriate `daemon-reload` command after installation changes.
- `start.sh`, `stop.sh`, and `restart.sh` manage services only and must not silently install or overwrite reviewed files.
- When a generated topology includes `<name>.pod` plus child containers linked with `Pod=<name>.pod`, helper scripts should use the pod service as the lifecycle entrypoint. Derive that service name from `ServiceName=` when present on the `.pod`, otherwise use Quadlet's default generated pod service name. Do not add `ServiceName=` merely to simplify helper scripts.
Keep installation separate from service-management scripts so the user can review generated files before applying them.
Do not add explicit runtime naming directives such as `PodName=`, `ServiceName=`, `ContainerName=`, or `NetworkName=` by default. Use Quadlet's derived names unless the user explicitly asks for custom naming or a reviewed requirement depends on it.
Do not use `ServiceName=` as an application connection target. It controls the generated systemd unit name only. When services communicate over a shared network outside a single pod namespace, prefer container names, pod names, or explicit `NetworkAlias=` values.
Within a single pod, use `127.0.0.1` / `localhost` for container-to-container communication instead of generating `AddHost=` entries whose purpose is sibling-container discovery.
If a service inside the pod must accept connections from sibling containers, ensure its effective listen address is reachable within the shared pod namespace, typically `127.0.0.1` or `0.0.0.0`. When the upstream service exposes this through environment variables or similar runtime configuration, preserve or generate that setting explicitly.
## Operational Notes
## Review checklist before install
Review not only the Quadlet unit files but also:
- env files referenced by `EnvironmentFile=`
- repo-local mounted config files and directory trees
- initialization files such as `init.sql`, seed data, or bootstrap assets
- repo-local entrypoint and helper scripts referenced by `Entrypoint=`, `Exec=`, docs, or wrapper scripts
Do not treat the deliverable as complete if these support files are still missing from the reviewable artifact set.
Execution checklist template before install:
- [ ] all reviewed artifacts are present in the current-directory deliverable tree
- [ ] required support files and directories are included alongside the Quadlet and env artifacts
- [ ] every `EnvironmentFile=` path resolves to an actual reviewed file
- [ ] support files remain in the current-directory deliverable tree at the absolute paths referenced by mounts and scripts
- [ ] startup-critical env keys are present, or explicitly marked as unresolved placeholders
- [ ] any unresolved values are clearly marked as intentionally non-runnable placeholders
- [ ] service-management scripts operate on the same reviewed artifact set that finalize approved
- [ ] runnable-output gate passed before describing the result as runnable
## Rootless operational notes
- Bind mounts may hit UID/GID mismatches.
- Review not only Quadlet unit files but also env files, mounted config, initialization assets, entrypoint scripts, and other support files required at runtime.
- Do not add explicit runtime naming directives such as `PodName=`, `ServiceName=`, `ContainerName=`, or `NetworkName=` by default. Use Quadlet's derived names unless the user explicitly asks for custom naming or a reviewed requirement depends on it.
- Do not use `ServiceName=` as an application connection target. It controls the generated systemd unit name only.
- Within a single pod, use `127.0.0.1` / `localhost` for container-to-container communication instead of generating `AddHost=` entries for sibling-container discovery.
- If a service inside the pod must accept connections from sibling containers, ensure its effective listen address is reachable within the shared pod namespace, typically `127.0.0.1` or `0.0.0.0`.
- Bind mounts may hit UID/GID mismatches, especially in rootless deployments.
- Do not add `User=`, `Group=`, or `UserNS=keep-id` by default. Consider them only when the user is working through container permission or ownership behavior, or when the source explicitly requires that runtime identity mapping.
- If the service must survive logout, mention lingering:
- For rootless long-running services that should survive logout, mention lingering:
```bash
sudo loginctl enable-linger <username>
```
## Paths and bind mounts
- Ensure bind-mount source directories exist before first start.
- Normalize relative source paths against the source Compose file directory or the directory the user specifies.
- Emit absolute host paths in generated Quadlet files when using bind mounts.
- Explain the resolved absolute path if the source used `./...`.
- If the source project bind-mounts repo-local files or directories, make sure the reviewed current-directory deliverable set preserves the required contents and that the generated Quadlet files reference their absolute paths correctly.
## Recommended service defaults
## Optional Enhancements
Depending on the workload, consider adding:
- `AutoUpdate=registry` for opt-in automatic image refresh workflows when the approved image strategy uses fully qualified registry images
- explicit `.volume` or `.network` units when the user wants declarative infrastructure instead of implicit Podman objects
- service defaults such as:
```ini
[Service]
@@ -101,11 +85,6 @@ TimeoutStartSec=900
Use the timeout especially when first start may need to pull large images or build locally.
## Useful optional enhancements
- `AutoUpdate=registry` for opt-in automatic image refresh workflows when the approved image strategy uses fully qualified registry images
- explicit `.volume` or `.network` units when the user wants declarative infrastructure instead of implicit Podman objects
## Output language
## Output Language
If you generate a README, deployment note, or operator-facing document as part of the migration, write it in the user's language unless the user explicitly asks for another language.
+9
View File
@@ -2,6 +2,15 @@
Use this file whenever the migration includes `.env`, `env_file`, Compose interpolation, or inline `-e` flags.
## Contents
- Goals
- Default rules
- Sensitive values
- Interpolation
- Completeness validation
- Examples and checklists
## Goals
- preserve source-of-truth for variables
+11 -2
View File
@@ -2,6 +2,15 @@
Use this file when the user provides a GitHub repository URL and expects you to find the deployment inputs yourself.
## Contents
- Goal
- Discovery order
- What to extract
- Heuristics
- Support-file checklist
- Reporting expectations
## Goal
Discover the canonical self-hosting assets before attempting any Quadlet conversion.
@@ -56,7 +65,7 @@ Discover the canonical self-hosting assets before attempting any Quadlet convers
- Do not reduce runnable output to only Quadlet plus env when the source project depends on additional repo-local assets.
- If several candidate compose files exist, explain which one you selected and why.
## Support-file checklist template
## Support-file checklist
Before choosing the final source of truth, confirm:
@@ -68,7 +77,7 @@ Before choosing the final source of truth, confirm:
Use this checklist to prevent reducing the deliverable to Quadlet plus env when the source project depends on more than that.
## Migration posture for GitHub-sourced projects
## Reporting expectations
When converting a GitHub-hosted project, report:
+24 -16
View File
@@ -2,10 +2,22 @@
Use this file when the user asks how to verify or troubleshoot generated Quadlet units.
## Basic deployment flow
## Contents
1. Review the generated files in the current working directory by default, or in another user-requested output directory, and confirm the expected Quadlet units, env files, helper scripts, and required repo-local support files exist.
2. Confirm the user has already reviewed the finalize snapshot and approved execution, or requested edits that were incorporated before writing runnable artifacts.
- Validation flow
- Verification commands
- Runnable-output checks
- Common failure causes
- Troubleshooting posture
## Validation Flow
Validation belongs after execution, after the reviewed Quadlet files have been applied to a valid Quadlet directory, and while the referenced support files still exist at the host paths used by the generated units.
Recommended validation flow:
1. Confirm the user already reviewed the finalize snapshot and approved execution.
2. Confirm the generated deliverable set contains the expected Quadlet units, env files, helper scripts, and required support files.
3. Run `install.sh` to copy only the reviewed Quadlet unit files into the target Quadlet directory.
4. Run the appropriate reload command.
5. Start the relevant units and inspect their status.
@@ -13,6 +25,8 @@ Use this file when the user asks how to verify or troubleshoot generated Quadlet
If the user requested an alternate apply script name explicitly, substitute that name where needed, but treat `install.sh` as the default documentation path.
## Verification Commands
### Rootless
```bash
@@ -29,7 +43,7 @@ systemctl start <unit>
systemctl status <unit>
```
## Generator debugging
### Generator debugging
Use the Podman systemd generator dry run when units fail to appear or options look unsupported.
@@ -43,7 +57,7 @@ For rootless debugging:
/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
```
## Systemd verification
### Systemd verification
```bash
systemd-analyze verify <unit>.service
@@ -55,16 +69,16 @@ For user units:
systemd-analyze --user verify <unit>.service
```
## Support-file and env checks
## Runnable-Output Checks
Before calling the result runnable, verify that:
- every referenced `EnvironmentFile=` exists at the path referenced by the installed unit
- required env keys are actually present in the final env sources, or are explicitly surfaced as unresolved placeholders
- required env keys are present in the final env sources, or are explicitly surfaced as unresolved placeholders
- bind-mounted files and directories exist at the absolute paths referenced by the generated Quadlet files
- bind-mounted file-versus-directory shape still matches the source input
- if `AutoUpdate=registry` is enabled, the generated unit uses a fully qualified image reference
- when sibling containers in the same pod must connect to a service, its effective listen address is reachable within the pod namespace (`127.0.0.1` or `0.0.0.0`, unless upstream docs require another reachable bind address)
- when sibling containers in the same pod must connect to a service, its effective listen address is reachable within the pod namespace (`127.0.0.1` or `0.0.0.0`, unless upstream docs require another reviewed bind address)
- repo-local entrypoint or helper scripts referenced by the container exist and are executable when needed
- initialization assets such as `init.sql`, seeds, bootstrap files, or config templates are present where the deployment expects them
- service-management scripts operate on the same reviewed artifact set that finalize approved
@@ -86,7 +100,7 @@ Runnable-output gate checklist template:
Do not call the result runnable until every item above is checked.
## Common failure causes
## Common Failure Causes
- unsupported Quadlet option for the installed Podman version
- `AutoUpdate=registry` was enabled but the image reference is not fully qualified
@@ -100,7 +114,7 @@ Do not call the result runnable until every item above is checked.
- permissions on rootless bind mounts
- readiness assumptions hidden behind `depends_on`
## Troubleshooting posture
## Troubleshooting Posture
When validation fails, report:
@@ -108,9 +122,3 @@ When validation fails, report:
- what was applied successfully
- what failed to generate, apply, or start
- whether the issue is syntax, unsupported feature, path resolution, installation path, missing support files, missing env keys, or permissions
## Relationship to execution phase
Validation belongs after the files are written in the execution phase, the Quadlet units are applied to a valid Quadlet directory, and the referenced support files remain available at the absolute host paths used by the generated units.
Before execution, the skill should already have completed planning and finalize review with the user. Do not treat validation as a substitute for design review.