mirror of
https://github.com/bestnite/quadlet-migrator-skill.git
synced 2026-04-04 00:13:28 +00:00
init
This commit is contained in:
177
references/compose-mapping.md
Normal file
177
references/compose-mapping.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# Compose Mapping
|
||||
|
||||
Use this file when converting `docker-compose.yml` or `compose.yaml` into Quadlet units.
|
||||
|
||||
## General approach
|
||||
|
||||
- Model each service first, then decide how to group all resulting containers 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.
|
||||
|
||||
## Field strategy
|
||||
|
||||
### `name`
|
||||
|
||||
- Use as an application prefix when it improves unit naming clarity.
|
||||
- Do not force a top-level project name into every filename if the user prefers shorter units.
|
||||
|
||||
### `services.<name>.image`
|
||||
|
||||
- Map to `Image=` in `[Container]`.
|
||||
- Prefer fully qualified image names when normalizing output.
|
||||
- If the source omits a registry and is using Docker Hub semantics, normalize it explicitly for Podman.
|
||||
- Use these rules when filling in Docker Hub references:
|
||||
- `redis:7` -> `docker.io/library/redis:7`
|
||||
- `nginx` -> `docker.io/library/nginx`
|
||||
- `langgenius/dify-api:latest` -> `docker.io/langgenius/dify-api:latest`
|
||||
- Do not guess `docker.io/library/...` for images that already include a namespace.
|
||||
|
||||
### `services.<name>.build`
|
||||
|
||||
- Prefer upstream published images over local builds when the project already documents supported registry images.
|
||||
- If the user wants declarative builds, create a `.build` unit and reference it from `Image=`.
|
||||
- If the build semantics are too custom, or if an equivalent upstream image is clearly available, keep this as a manual follow-up item instead of guessing.
|
||||
|
||||
### `container_name`
|
||||
|
||||
- Drop it.
|
||||
- Do not generate `ContainerName=`. Let Podman and systemd derive the runtime name.
|
||||
|
||||
### `ports`
|
||||
|
||||
- For a standalone service, map to `PublishPort=` on the `.container`.
|
||||
- For a pod-based topology, prefer `PublishPort=` on the `.pod` when the published ports belong to the pod boundary rather than one child container.
|
||||
|
||||
### `volumes`
|
||||
|
||||
- Bind mounts become `Volume=HOST:CONTAINER[:OPTIONS]`.
|
||||
- Normalize relative host paths against the Compose file directory and emit absolute paths in the final Quadlet output.
|
||||
- Named volumes can remain referenced by name, but when the user wants explicit infrastructure-as-code, create matching `.volume` units.
|
||||
- Ask the user which volume mode they want when the source does not make the intended persistence model obvious.
|
||||
|
||||
### `networks`
|
||||
|
||||
- If the source uses a default network only, you often do not need a `.network` unit.
|
||||
- If a custom network is intentional and needs to be managed declaratively, create a `.network` unit and reference it explicitly.
|
||||
- Containers in the same `.pod` can communicate over `127.0.0.1` / `localhost` because they share a network namespace.
|
||||
- Containers in different pods must not be treated as reachable via `127.0.0.1` / `localhost`.
|
||||
- When splitting services across multiple pods, use container networking and service-level addressing, or publish ports to the host boundary when that is the intended access pattern.
|
||||
|
||||
### `environment`
|
||||
|
||||
- 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.
|
||||
|
||||
### `profiles`
|
||||
|
||||
- Decide first which profiles are actually part of the desired deployment.
|
||||
- Do not try to preserve Compose profiles as a direct Quadlet concept.
|
||||
- Treat profiles as source selection inputs that decide which services become units.
|
||||
|
||||
### `depends_on`
|
||||
|
||||
- Translate to `Requires=` and `After=` when that reflects intent.
|
||||
- State clearly that this controls startup ordering, not application readiness.
|
||||
|
||||
### `restart`
|
||||
|
||||
- Map common cases to `[Service] Restart=`.
|
||||
- If the source policy has no direct systemd equivalent, choose the nearest safe mapping and explain it.
|
||||
|
||||
### `healthcheck`
|
||||
|
||||
- Prefer dedicated Quadlet health fields such as `HealthCmd=`, `HealthInterval=`, `HealthTimeout=`, `HealthRetries=` when representable.
|
||||
- If the Compose healthcheck is only partially representable, preserve the command intent and call out missing knobs.
|
||||
|
||||
### `command` and `entrypoint`
|
||||
|
||||
- `entrypoint` typically maps to `Entrypoint=`.
|
||||
- `command` typically maps to `Exec=`.
|
||||
|
||||
### `user`
|
||||
|
||||
- Map to `User=` and `Group=` in `[Container]` only when it is a container runtime user mapping, not a systemd service user.
|
||||
- Do not use systemd `User=` to try to make a rootless Quadlet run as another login user.
|
||||
|
||||
### unsupported or risky fields
|
||||
|
||||
Handle these conservatively and usually as migration notes:
|
||||
|
||||
- `deploy`
|
||||
- `profiles`
|
||||
- `extends`
|
||||
- advanced Compose merge behavior
|
||||
- readiness semantics hidden behind `depends_on`
|
||||
|
||||
## Minimal examples
|
||||
|
||||
### Single service to standalone container
|
||||
|
||||
Source intent:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- "8080:80"
|
||||
```
|
||||
|
||||
Reasonable result shape:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Image=docker.io/library/nginx:latest
|
||||
PublishPort=8080:80
|
||||
```
|
||||
|
||||
Use this when the deployment is truly a simple single-service case and pod semantics add little value.
|
||||
|
||||
### Small multi-service app to one pod
|
||||
|
||||
Source intent:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
api:
|
||||
image: ghcr.io/example/api:1.0
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
image: postgres:16
|
||||
```
|
||||
|
||||
Reasonable result shape:
|
||||
|
||||
- one `.pod` for the application boundary
|
||||
- one container unit for `api`
|
||||
- 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 when shared networking and a grouped lifecycle make the deployment easier to operate.
|
||||
|
||||
## Pod decision rule
|
||||
|
||||
Choose the simplest topology that preserves the source deployment intent.
|
||||
|
||||
Prefer a single `.pod` for multi-container applications when practical.
|
||||
|
||||
If the project is a simple single-container deployment with no real need for pod semantics, a standalone `.container` is acceptable.
|
||||
|
||||
If shared networking, shared published ports, or tightly coupled service lifecycle make pod semantics useful, prefer one or more `.pod` units.
|
||||
|
||||
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.
|
||||
|
||||
For large application stacks with optional services, ask the user to choose the desired service set before generating a minimized result.
|
||||
55
references/deployment-notes.md
Normal file
55
references/deployment-notes.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Deployment Notes
|
||||
|
||||
Use this file when the user wants deployment-ready instructions alongside generated Quadlet units.
|
||||
|
||||
## Directory choice
|
||||
|
||||
### Rootless
|
||||
|
||||
- primary default: `~/.config/containers/systemd/`
|
||||
- user-scoped management commands use `systemctl --user`
|
||||
|
||||
### Rootful
|
||||
|
||||
- primary default: `/etc/containers/systemd/`
|
||||
- system-scoped management commands use `systemctl`
|
||||
|
||||
See `podman-systemd.unit.5.md` for the full search-path matrix.
|
||||
|
||||
## Rootless operational notes
|
||||
|
||||
- Bind mounts may hit UID/GID mismatches.
|
||||
- For pod-based deployments that should preserve host ownership semantics, consider `UserNS=keep-id` on `[Pod]` when appropriate.
|
||||
- If the service must 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 `./...`.
|
||||
|
||||
## Recommended service defaults
|
||||
|
||||
Depending on the workload, consider adding:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
Restart=always
|
||||
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
|
||||
- explicit `.volume` or `.network` units when the user wants declarative infrastructure instead of implicit Podman objects
|
||||
|
||||
## 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.
|
||||
165
references/env-strategy.md
Normal file
165
references/env-strategy.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# Environment Strategy
|
||||
|
||||
Use this file whenever the migration includes `.env`, `env_file`, Compose interpolation, or inline `-e` flags.
|
||||
|
||||
## Goals
|
||||
|
||||
- preserve source-of-truth for variables
|
||||
- avoid leaking secrets into generated Quadlet files by default
|
||||
- keep resulting units readable
|
||||
- report missing variables explicitly
|
||||
- reduce large upstream env templates into a small set of user decisions
|
||||
|
||||
## Default rules
|
||||
|
||||
The agent should actively interpret the env template and its comments. Do not offload the entire env review back to the user.
|
||||
|
||||
### Prefer `Environment=` when
|
||||
|
||||
- there are only a few variables
|
||||
- values are stable and not sensitive
|
||||
- keeping everything in one file materially improves readability
|
||||
|
||||
### Prefer `EnvironmentFile=` when
|
||||
|
||||
- the source already uses `.env` or `env_file`
|
||||
- variables are numerous
|
||||
- variables contain secrets or deployment-specific values
|
||||
- the same env file is shared by multiple services
|
||||
|
||||
## Sensitive-value heuristic
|
||||
|
||||
Treat names containing these substrings as sensitive unless the user tells you otherwise:
|
||||
|
||||
- `PASSWORD`
|
||||
- `TOKEN`
|
||||
- `SECRET`
|
||||
- `KEY`
|
||||
- `PRIVATE`
|
||||
- `PASS`
|
||||
|
||||
Default behavior:
|
||||
|
||||
- do not inline them in `Environment=`
|
||||
- keep them in `EnvironmentFile=` or generate a placeholder sample file instead
|
||||
|
||||
## Compose interpolation
|
||||
|
||||
Common forms:
|
||||
|
||||
- `${VAR}`
|
||||
- `${VAR:-default}`
|
||||
- `${VAR-default}`
|
||||
|
||||
Strategy:
|
||||
|
||||
- if the actual value source is present, resolve it and document where it came from
|
||||
- if only a default is available, note that the value is default-derived
|
||||
- if the variable is missing, list it as unresolved
|
||||
|
||||
Do not fabricate values.
|
||||
|
||||
## Agent responsibility
|
||||
|
||||
When the source project ships a large `.env.example` or multiple env templates:
|
||||
|
||||
- read the comments and deployment docs yourself
|
||||
- determine which values can safely stay at documented defaults
|
||||
- determine which values are true deployment decisions and must be confirmed with the user
|
||||
- prepare a candidate `.env` or env delta instead of asking the user to read the whole template manually
|
||||
|
||||
The user should only need to answer the small number of high-impact questions that cannot be discovered locally.
|
||||
|
||||
## Minimal examples
|
||||
|
||||
### Inline stable values
|
||||
|
||||
Source intent:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
APP_ENV: production
|
||||
APP_PORT: "8080"
|
||||
```
|
||||
|
||||
Reasonable result shape:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Environment=APP_ENV=production
|
||||
Environment=APP_PORT=8080
|
||||
```
|
||||
|
||||
Use this when there are only a few non-sensitive structural values.
|
||||
|
||||
### Preserve env-file workflow
|
||||
|
||||
Source intent:
|
||||
|
||||
```yaml
|
||||
env_file:
|
||||
- .env
|
||||
```
|
||||
|
||||
Reasonable result shape:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
EnvironmentFile=/opt/myapp/.env
|
||||
```
|
||||
|
||||
Use this when the source already relies on an env file, especially for secrets or many variables.
|
||||
|
||||
### Large template reduced to a candidate env
|
||||
|
||||
Source intent:
|
||||
|
||||
- upstream ships `.env.example`
|
||||
- only a few values are true deployment decisions
|
||||
|
||||
Recommended behavior:
|
||||
|
||||
- keep documented defaults that are safe to preserve
|
||||
- ask the user only for high-impact values such as domain, storage path, or database password
|
||||
- generate a candidate `.env` or env delta with clear placeholders for the unresolved items
|
||||
|
||||
## Output patterns
|
||||
|
||||
### Small inline set
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Environment=APP_ENV=production
|
||||
Environment=APP_PORT=8080
|
||||
```
|
||||
|
||||
### External env file
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
EnvironmentFile=/opt/myapp/myapp.env
|
||||
```
|
||||
|
||||
### Mixed pattern
|
||||
|
||||
Use this when a few values are structural and the rest are secret or deployment-specific.
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
Environment=APP_ENV=production
|
||||
EnvironmentFile=/opt/myapp/myapp.env
|
||||
```
|
||||
|
||||
## Missing-variable reporting
|
||||
|
||||
When variables cannot be resolved, report them as a concrete checklist.
|
||||
|
||||
Example:
|
||||
|
||||
- missing `DB_PASSWORD`
|
||||
- missing `IMMICH_VERSION`
|
||||
- missing `UPLOAD_LOCATION`
|
||||
|
||||
If the user asks for scaffolding, generate a sample env file with obvious placeholders.
|
||||
|
||||
Even when the user does not explicitly ask for scaffolding, produce a candidate env result when that materially advances the migration.
|
||||
57
references/github-repo-intake.md
Normal file
57
references/github-repo-intake.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# GitHub Repository Intake
|
||||
|
||||
Use this file when the user provides a GitHub repository URL and expects you to find the deployment inputs yourself.
|
||||
|
||||
## Goal
|
||||
|
||||
Discover the canonical self-hosting assets before attempting any Quadlet conversion.
|
||||
|
||||
## Discovery order
|
||||
|
||||
1. Read the repository `README.md`.
|
||||
2. Look for self-hosting instructions and explicit references to Compose files.
|
||||
3. If documentation names a path, follow that path first.
|
||||
4. Search the repository tree for likely deployment files:
|
||||
- `docker-compose.yaml`
|
||||
- `docker-compose.yml`
|
||||
- `compose.yaml`
|
||||
- `compose.yml`
|
||||
- `.env.example`
|
||||
- `.env.sample`
|
||||
- `env.example`
|
||||
- `middleware.env.example`
|
||||
5. Inspect likely deployment directories when needed:
|
||||
- `docker/`
|
||||
- `deploy/`
|
||||
- `ops/`
|
||||
- `infra/`
|
||||
- `examples/`
|
||||
- `.devcontainer/`
|
||||
6. Read deployment-specific README files in those directories.
|
||||
7. Identify helper scripts that generate or sync compose files.
|
||||
|
||||
## What to extract
|
||||
|
||||
- canonical compose file path
|
||||
- companion env template path
|
||||
- additional compose files used for middleware or optional services
|
||||
- whether the compose file is generated
|
||||
- whether the source relies on profiles
|
||||
- whether startup requires preparatory steps such as copying `.env.example` to `.env`
|
||||
|
||||
## Heuristics
|
||||
|
||||
- Prefer the path explicitly linked from the main README over a randomly discovered file.
|
||||
- Do not hardcode assumptions like "the deployment entry point is always under `docker/`".
|
||||
- If the repo has both a template and a generated compose file, treat the generated file as the runnable source and the template as explanatory context.
|
||||
- If profiles control optional databases or vector stores, decide which profile set the user actually wants before generating Quadlet.
|
||||
- If env management is mandatory, preserve that pattern rather than flattening hundreds of variables into inline `Environment=` values.
|
||||
- If several candidate compose files exist, explain which one you selected and why.
|
||||
|
||||
## Migration posture for GitHub-sourced projects
|
||||
|
||||
When converting a GitHub-hosted project, report:
|
||||
|
||||
- which files you chose as source of truth
|
||||
- which optional files or profiles you ignored
|
||||
- which variables still require user decisions
|
||||
2556
references/podman-systemd.unit.5.md
Normal file
2556
references/podman-systemd.unit.5.md
Normal file
File diff suppressed because it is too large
Load Diff
70
references/validation.md
Normal file
70
references/validation.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Validation
|
||||
|
||||
Use this file when the user asks how to verify or troubleshoot generated Quadlet units.
|
||||
|
||||
## Basic deployment flow
|
||||
|
||||
### Rootless
|
||||
|
||||
```bash
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user start <unit>
|
||||
systemctl --user status <unit>
|
||||
```
|
||||
|
||||
### Rootful
|
||||
|
||||
```bash
|
||||
systemctl daemon-reload
|
||||
systemctl start <unit>
|
||||
systemctl status <unit>
|
||||
```
|
||||
|
||||
## Generator debugging
|
||||
|
||||
Use the Podman systemd generator dry run when units fail to appear or options look unsupported.
|
||||
|
||||
```bash
|
||||
/usr/lib/systemd/system-generators/podman-system-generator --dryrun
|
||||
```
|
||||
|
||||
For rootless debugging:
|
||||
|
||||
```bash
|
||||
/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
|
||||
```
|
||||
|
||||
## Systemd verification
|
||||
|
||||
```bash
|
||||
systemd-analyze verify <unit>.service
|
||||
```
|
||||
|
||||
For user units:
|
||||
|
||||
```bash
|
||||
systemd-analyze --user verify <unit>.service
|
||||
```
|
||||
|
||||
## Common failure causes
|
||||
|
||||
- unsupported Quadlet option for the installed Podman version
|
||||
- bind-mount source directory missing
|
||||
- wrong rootless or rootful unit directory
|
||||
- unresolved env file path
|
||||
- permissions on rootless bind mounts
|
||||
- readiness assumptions hidden behind `depends_on`
|
||||
|
||||
## Troubleshooting posture
|
||||
|
||||
When validation fails, report:
|
||||
|
||||
- what generated successfully
|
||||
- what failed to generate or start
|
||||
- whether the issue is syntax, unsupported feature, path resolution, or permissions
|
||||
|
||||
## Relationship to execution phase
|
||||
|
||||
Validation belongs after the files are written in the execution phase.
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user