Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .beads/issues.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
{"id":"quad-ops-284","content_hash":"93e3458e056754e5293e66cbac9a751078bd9c48e6ec638cf67d7e95c38e63d1","title":"Step 2: Scaffold QuadletWriter utility","description":"Introduce reusable INI writer abstraction without modifying renderer logic yet.\n\nIntent: Create a QuadletWriter struct to centralize INI section formatting and reduce duplication while keeping Renderer API intact.\n\nFiles affected:\n- internal/platform/systemd/quadlet.go (new file)\n- internal/platform/systemd/renderer.go (no changes yet)\n- internal/platform/systemd/helpers.go (preparation)\n\nChanges:\n- Create QuadletWriter struct with buf *strings.Builder\n- Implement NewQuadletWriter(section string) *QuadletWriter\n- Implement Set(key, value string) for single-value directives\n- Implement Append(key string, values ...string) for multi-value directives\n- Implement String() string to return formatted INI section\n- Add unit tests for QuadletWriter formatting behavior\n\nPreconditions: Step 1 merged; tests encode current outputs\nPostconditions: QuadletWriter exists, covered by unit tests to ensure formatting parity; renderer still uses legacy string building\nChecks: go test ./internal/platform/systemd/... including new quadlet tests","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-11T12:30:58.874785-05:00","updated_at":"2025-11-11T12:36:58.109594-05:00","closed_at":"2025-11-11T12:36:58.109594-05:00","source_repo":".","dependencies":[{"issue_id":"quad-ops-284","depends_on_id":"quad-ops-8p7","type":"blocks","created_at":"2025-11-11T12:30:58.875737-05:00","created_by":"daemon"}]}
{"id":"quad-ops-2bs","content_hash":"2b60b26bf656a806575e6c23d219a3715542f6e5d9a2595bdbb236516b2f30f9","title":"Add platform-specific feature validation and warnings","description":"Users should be warned when using features that don't work on their platform. macOS silently ignores Quadlet-specific features leading to feature loss and confusion.\n\nCurrent state:\n- Platform detection exists via runtime.GOOS in internal/validate/validate.go\n- Init containers disabled on macOS with no warning\n- macOS renderer silently ignores spec.Volumes, spec.Networks, and spec.Container.Build\n- No feature-level platform capability checking\n\nNeed to:\n- Add validation warnings during spec conversion when unsupported features are used\n- Detect when Quadlet-specific features (volumes, networks, builds) are used on macOS\n- Update compose reader to flag unsupported features per platform\n- Add platform capability checking in internal/validate/\n- Test validation works correctly for both platforms\n\nThis prevents user confusion and provides clear feedback about platform limitations.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-11-06T21:39:40.863822782-05:00","updated_at":"2025-11-11T20:45:05.228228-05:00","source_repo":".","labels":["cross-platform","user-experience","validation"],"dependencies":[{"issue_id":"quad-ops-2bs","depends_on_id":"quad-ops-6cz","type":"blocks","created_at":"2025-11-06T21:39:48.664022136-05:00","created_by":"daemon"}]}
{"id":"quad-ops-3","content_hash":"f634dad79c6a53702e1fa0b5d3df60c3ffe53cd57b4d148f37b59620c0166d73","title":"Phase 2: Create shared podman_args.go builder used by both systemd and launchd renderers","description":"Create shared podman argument builder to eliminate duplication between systemd and launchd renderers.\n\nCurrent problem:\n- internal/platform/systemd/podmanargs.go: ~200 lines of podman command building\n- internal/platform/launchd/podmanargs.go: ~200 lines of duplicate logic\n\nSolution:\n- Create internal/platform/podman_args.go (~150 lines) with unified PodmanArgsBuilder\n- Both systemd and launchd renderers import and use shared builder\n- Delete per-platform podmanargs.go files\n- Eliminates 200+ lines of duplication\n\nResponsibilities of shared builder:\n- BuildImage() - Container image specification\n- BuildCommand() - Command and args\n- BuildPorts() - Port mappings (--publish)\n- BuildMounts() - Volume mounts (--volume, --mount)\n- BuildEnvironment() - Environment variables (--env, --env-file)\n- BuildCapabilities() - Security options (--cap-add, --cap-drop, --privileged, --security-opt)\n- BuildResourceLimits() - Memory, CPU constraints (--memory, --cpus, --cpu-shares)\n- BuildNetworking() - Network mode and DNS (--network, --dns, --hostname)\n- Build() - Returns complete []string of podman run arguments\n\nReference: history/full-pipeline-simplification.md Section \"Eliminate Duplication\" and implementation-checklist.md Phase 2","acceptance_criteria":"✅ Created internal/platform/podman_args.go (≤150 lines) with PodmanArgsBuilder struct\n✅ Builder has methods for all podman argument categories (image, ports, mounts, env, security, resources, networking)\n✅ Systemd renderer updated to use shared builder (delete internal/platform/systemd/podmanargs.go)\n✅ Launchd renderer updated to use shared builder (delete internal/platform/launchd/podmanargs.go)\n✅ Table-driven unit tests for builder in podman_args_test.go\n✅ Both renderer test suites still pass (output unchanged)\n✅ No duplication between platform implementations\n✅ go test ./internal/platform/... passes","notes":"**NO BACKWARD COMPATIBILITY REQUIRED**: Internal platform package refactoring. Breaking changes to internal renderer APIs are acceptable.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-11T01:29:30.10291501-05:00","updated_at":"2025-11-11T12:20:39.174023-05:00","closed_at":"2025-11-11T12:20:39.174023-05:00","source_repo":".","dependencies":[{"issue_id":"quad-ops-3","depends_on_id":"quad-ops-9f5","type":"discovered-from","created_at":"2025-11-11T01:28:46.919269796-05:00","created_by":"daemon"},{"issue_id":"quad-ops-3","depends_on_id":"quad-ops-2","type":"blocks","created_at":"2025-11-11T10:09:01.914654-05:00","created_by":"daemon"}]}
{"id":"quad-ops-39uw","content_hash":"068fa7f75718ed5879883b5241ff3211f7a41ccd52cd474b3df26d7f6395f5ff","title":"Fix launchd test failures on macOS CI due to podman resolution","description":"Fix launchd tests that fail on CI environments without podman installed by using testOptions() instead of DefaultOptions().\nFixes https://github.com/trly/quad-ops/issues/58","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-18T22:01:25.038693-05:00","updated_at":"2025-11-18T22:01:51.395355-05:00","closed_at":"2025-11-18T22:01:51.395355-05:00","source_repo":"."}
{"id":"quad-ops-3aq","content_hash":"fa0ded88e9f3b15fc1c0ed67e8184385190a3e461945d71fdcfc07d40f7e659e","title":"Update documentation to clarify platform differences","description":"The current documentation claims full Docker Compose support but this is misleading for macOS. Need to clearly document platform differences.\n\nNeed to:\n- Update README.md to clarify platform-specific feature support\n- Add platform comparison table in documentation\n- Update feature claims to be platform-specific\n- Add migration guide for users moving between platforms\n- Update examples to show platform differences\n\nThis will set proper user expectations and prevent confusion about feature availability.","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-06T21:39:43.446055828-05:00","updated_at":"2025-11-06T21:39:43.446055828-05:00","source_repo":".","labels":["documentation","platform-differences","user-experience"],"dependencies":[{"issue_id":"quad-ops-3aq","depends_on_id":"quad-ops-2bs","type":"blocks","created_at":"2025-11-06T21:39:50.509568025-05:00","created_by":"daemon"}]}
{"id":"quad-ops-3i1","content_hash":"45349bd2bbd7e536078dd82e8f65672f28322e41b3ab97ae94d510a05dee4061","title":"x-podman-* extensions documented but not implemented","description":"README documents x-podman-buildargs and x-podman-volumes extensions but they're not wired up in code.\n\nEvidence: grep -r \"x-podman-\" internal/ finds nothing\n\nRisk: Documentation promises features that don't work","design":"Options:\n1. Implement extensions:\n - x-podman-buildargs: Read from service.Extensions, add to build.PodmanArgs\n - x-podman-volumes: Read from service.Extensions, parse into Mounts or Container.PodmanArgs\n \n2. Remove from README until implemented\n\nRecommendation: Remove from README for now, implement when users request it.\n\nIf implementing:\n- Parse extensions in spec_converter.go:convertService()\n- Check for map[\"x-podman-buildargs\"], map[\"x-podman-volumes\"]\n- Type assert and convert to appropriate fields\n- Add test coverage with compose files using extensions","acceptance_criteria":"Either:\nA) Extensions are implemented and tested, OR\nB) Extensions are removed from README with TODO note\n\nIf implementing:\n- x-podman-buildargs adds to build args\n- x-podman-volumes adds to mounts or podman args\n- Test coverage for extensions","status":"closed","priority":3,"issue_type":"bug","created_at":"2025-11-09T16:40:17.290625-05:00","updated_at":"2025-11-09T23:12:22.492706-05:00","closed_at":"2025-11-09T23:12:22.492706-05:00","source_repo":"."}
{"id":"quad-ops-3kb","content_hash":"bd6d11ccbeb9f8f983aada7040d63502fb4b4850adf021aaa470a7f115068fd6","title":"Fix volume name escaping: underscore vs hyphen mismatch","description":"Volume mount sources are prefixed with underscores in spec_converter.go (lines 236, 248) but volumes are created with hyphens via SanitizeName().\n\n**Location:** internal/compose/spec_converter.go lines 236 and 248\n\n**Current behavior:**\n```go\nmount.Source = fmt.Sprintf(\"%s_%s\", project.Name, v.Source) // Uses underscore\n```\n\n**Expected behavior:**\nShould use the Prefix() helper that uses hyphens:\n```go\nmount.Source = compose.Prefix(project.Name, v.Source) // Creates with hyphen\n```\n\n**Effect:** Volume references fail because quadlet looks for `miniflux_db_data` (underscores) when the actual volume is `miniflux-db-data` (hyphens).\n\n**Part of:** https://github.com/trly/quad-ops/discussions/52 (Quadlet generator analysis)","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-11-09T12:22:21.88470299-05:00","updated_at":"2025-11-09T12:24:15.593588877-05:00","closed_at":"2025-11-09T12:24:15.593588877-05:00","source_repo":".","dependencies":[{"issue_id":"quad-ops-3kb","depends_on_id":"quad-ops-712","type":"blocks","created_at":"2025-11-09T12:22:21.885666818-05:00","created_by":"daemon"}]}
Expand Down Expand Up @@ -158,6 +159,7 @@
{"id":"quad-ops-rgd","content_hash":"dffc52f6195e2891aa2ff38b7d642c435d310b2725e7c0a1663a51fcb60028f7","title":"Fix gofmt issue in renderer_test.go","description":"Task fmt modified internal/platform/systemd/renderer_test.go - code not properly formatted.\n\nRun `go fmt ./...` and commit the changes.","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-11-11T18:53:36.12947-05:00","updated_at":"2025-11-11T19:18:28.761185-05:00","closed_at":"2025-11-11T19:18:28.761185-05:00","source_repo":".","dependencies":[{"issue_id":"quad-ops-rgd","depends_on_id":"quad-ops-7","type":"discovered-from","created_at":"2025-11-11T18:53:36.129955-05:00","created_by":"daemon"}]}
{"id":"quad-ops-rpgk","content_hash":"39d6d0b601b574a24e0a63c364e5f6d02c8aa3350d692e59b2ecfc4d76049f3a","title":"Align project/service name validation with Compose spec","description":"**BLOCKER for quad-ops-dep1 completion**\n\nCurrent validation does not match Docker Compose specification exactly.\n\n**Compose Spec Requirements:**\n- **Project names**: lowercase letters, digits, dashes, underscores; must start with letter/digit. Pattern: ^[a-z0-9][a-z0-9_-]*$\n- **Service names**: YAML string keys (no strict character set enforcement beyond valid YAML)\n\n**Current Implementation:**\n- ValidateProjectName: ^[a-z0-9][a-z0-9_-]*$ ✅ CORRECT\n- ValidateServiceName: ^[A-Za-z0-9][A-Za-z0-9_.-]*$ ❌ TOO RESTRICTIVE\n\n**Problem:**\nService name validation is stricter than Compose spec requires. Compose allows any valid YAML string as service name, but we reject valid compose files.\n\n**Decision needed:**\n- Option A: Match Compose exactly (allow any YAML string for services)\n- Option B: Keep identifier-style validation but document as quad-ops restriction\n- Recommendation: Option A for maximum Compose compatibility\n\n**Constraint:**\nService names must be convertible to valid systemd unit names and launchd labels without sanitization (per quad-ops-dep0 philosophy: validate, don't sanitize)","design":"Research needed:\n1. Check what systemd unit naming requires (man systemd.unit)\n2. Check what launchd label naming requires\n3. Decide validation pattern that works for both platforms without sanitization\n\nFiles to update:\n- internal/compose/convert.go - ValidateServiceName()\n- history/sanitization-removal-summary.md\n- history/dependency-consolidation-summary.md \n- README.md naming requirements\n- Error messages","acceptance_criteria":"Pattern chosen matches Compose spec or documents divergence with rationale. Docs updated. Tests verify accepted/rejected names. Error messages show correct pattern.","status":"open","priority":0,"issue_type":"bug","created_at":"2025-11-12T14:18:24.827974-05:00","updated_at":"2025-11-12T14:18:24.827974-05:00","source_repo":".","dependencies":[{"issue_id":"quad-ops-rpgk","depends_on_id":"quad-ops-dep1","type":"blocks","created_at":"2025-11-12T14:18:24.82894-05:00","created_by":"daemon"}]}
{"id":"quad-ops-rqe","content_hash":"e0566460da5c65f3233cd08cbe0a303fc6ed4a6e1ad40f7d605c12f5a14fd0de","title":"Missing bind mount options: z/Z relabel flags","description":"SELinux relabel flags (z/Z) for bind mounts not supported in current implementation.","design":"## Problem\nDocker Compose bind mounts support SELinux relabeling via `bind.selinux: z|Z` which controls SELinux context labeling. `z` = shared label (multiple containers), `Z` = private label (single container). This option is currently ignored during conversion.\n\n## Source Data\n- compose-spec library: `types.ServiceVolumeConfig.Bind.SELinux` (string: \"z\" | \"Z\")\n- Location: `internal/compose/spec_converter.go:convertVolumeMounts()`\n\n## Target Format\nQuadlet Volume= directive supports z/Z options:\n```\nVolume=/host/path:/container/path:z # shared SELinux label\nVolume=/host/path:/container/path:Z # private SELinux label\n```\n\n## Implementation Plan\n1. Update `internal/service/models.go`:\n - Add `SELinux string` field to `BindOptions` struct (already exists, just needs to be populated)\n\n2. Update `internal/compose/spec_converter.go:convertVolumeMounts()`:\n - For bind-type mounts, check `v.Bind != nil \u0026\u0026 v.Bind.SELinux != \"\"`\n - Set `mount.BindOptions.SELinux = v.Bind.SELinux` \n - Already captures Propagation, just add SELinux field handling\n\n3. Update `internal/platform/systemd/renderer.go:addMounts()`:\n - When building mount string for bind mounts, check `m.BindOptions.SELinux`\n - Append `:z` or `:Z` to mount string based on value\n\n4. Add test cases:\n - `internal/compose/testdata/golden/` - add compose file with bind mount z/Z\n - `internal/platform/systemd/renderer_test.go` - test Volume= with z and Z options","acceptance_criteria":"- z/Z options in bind mounts are preserved\n- Rendered correctly in Quadlet Volume= directive\n- SELinux relabeling works as expected","status":"closed","priority":3,"issue_type":"bug","created_at":"2025-11-09T13:44:34.795669-05:00","updated_at":"2025-11-09T16:03:50.735425-05:00","closed_at":"2025-11-09T16:03:50.735425-05:00","source_repo":".","dependencies":[{"issue_id":"quad-ops-rqe","depends_on_id":"quad-ops-hlf","type":"parent-child","created_at":"2025-11-09T13:44:34.796066-05:00","created_by":"daemon"}]}
{"id":"quad-ops-rtx7","content_hash":"fb7fe3e5062852f70ce5dc268045cdb56055164462453aeb4a74d17beda85988","title":"Security: Update golang.org/x/crypto to v0.43.0 to fix DoS vulnerability","description":"Update the indirect dependency `golang.org/x/crypto` from v0.41.0 to v0.43.0 (or later) to fix the known DoS vulnerability GO-2025-4116.\n\nThe vulnerability is triggered through the use of `go-git/v5` which imports cryptographic functions from the vulnerable version.\n\n**Remediation Plan:**\n\n1. Update dependency:\n ```bash\n go get -u golang.org/x/crypto@v0.43.0\n ```\n\n2. Verify the fix:\n ```bash\n go install golang.org/x/vuln/cmd/govulncheck@latest\n govulncheck -tags exclude_graphdriver_btrfs,exclude_graphdriver_devicemapper,containers_image_openpgp ./...\n ```\n\n3. Verify backward compatibility:\n ```bash\n go test ./...\n ```\n\nReference: https://github.com/trly/quad-ops/issues/56","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-11-18T19:53:52.265378-05:00","updated_at":"2025-11-18T20:01:04.388765-05:00","closed_at":"2025-11-18T20:01:04.388765-05:00","source_repo":"."}
{"id":"quad-ops-s9l","content_hash":"8ee76ac798fea820efdd842ed9d1584a27f8d962c31f9e107404b8968142b7ca","title":"Fix init container volume mounting in spec converter","description":"Init containers defined via x-quad-ops-init extension need to inherit volumes from the main service to access shared data directories. Currently, init containers don't get volume mounts, causing failures when they try to write to shared volumes like /shared/data.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-11-06T23:51:14.6100226-05:00","updated_at":"2025-11-06T23:54:13.232121169-05:00","closed_at":"2025-11-06T23:54:13.232121169-05:00","source_repo":".","labels":["init-containers","spec-converter","volumes"]}
{"id":"quad-ops-sgd","content_hash":"22856d51040298b23f5dcbe6aea96e32fea36efb9f18c2ded4043c46f6ca3ef6","title":"issue with github workflow build.yml","description":"\nError: Failed to run: Error: Command failed: /home/runner/golangci-lint-2.6.1-linux-amd64/golangci-lint config verify\njsonschema: does not validate with /additionalProperties: additional properties 'linters-settings' not allowed\nThe command is terminated due to an error: the configuration contains invalid elements\n, Error: Command failed: /home/runner/golangci-lint-2.6.1-linux-amd64/golangci-lint config verify\njsonschema: does not validate with /additionalProperties: additional properties 'linters-settings' not allowed\nThe command is terminated due to an error: the configuration contains invalid elements\n\n at genericNodeError (node:internal/errors:984:15)\n at wrappedFn (node:internal/errors:538:14)\n at ChildProcess.exithandler (node:child_process:422:12)\n at ChildProcess.emit (node:events:524:28)\n at maybeClose (node:internal/child_process:1104:16)\n at ChildProcess._handle.onexit (node:internal/child_process:304:5)\nError: Command failed: /home/runner/golangci-lint-2.6.1-linux-amd64/golangci-lint config verify\njsonschema: does not validate with /additionalProperties: additional properties 'linters-settings' not allowed","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-11-06T21:53:02.978723976-05:00","updated_at":"2025-11-06T21:54:17.737045479-05:00","closed_at":"2025-11-06T21:54:17.737045479-05:00","source_repo":"."}
{"id":"quad-ops-slm","content_hash":"0ae28aeac9514a36d3152c850e1c79f32735d3986b585a5ccdc87e9a99be00d6","title":"Implement x-podman-env-secrets extension support","description":"The multi-service example uses x-podman-env-secrets extension to map secrets to environment variables. This extension is used but not implemented.\n\nNeed to:\n- Process x-podman-env-secrets from project.Extensions in spec_converter.go\n- Add support for mapping secrets to environment variables in the service model\n- Ensure the renderer outputs appropriate Podman arguments or systemd environment setup\n\nExtension format:\n```yaml\nx-podman-env-secrets:\n db_password: WORDPRESS_DB_PASSWORD\n```\n\nThis should make the secret available as an environment variable in the container.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-06T21:36:26.634415865-05:00","updated_at":"2025-11-06T21:45:44.413052281-05:00","closed_at":"2025-11-06T21:45:44.413052281-05:00","source_repo":"."}
Expand Down
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@

# Use bd merge for beads JSONL files
.beads/beads.jsonl merge=beads
.beads/issues.jsonl merge=beads
Loading
Loading