Skip to content

feat: add .recur app bundles (install, pack, startup auto-register)#17

Merged
directedbits merged 2 commits into
mainfrom
feat/app-bundle
Jul 13, 2026
Merged

feat: add .recur app bundles (install, pack, startup auto-register)#17
directedbits merged 2 commits into
mainfrom
feat/app-bundle

Conversation

@directedbits

@directedbits directedbits commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

Adds app bundles: a .recur zip archive containing a recurfile (the single YAML file at its root) plus any local scripts an app needs, so an app travels as one file. Installing unpacks the bundle into ~/.config/recur/app/<name>/ and registers its recurfile; apps installed while the daemon is stopped are auto-registered on next start.

This is Part 1 of a two-part effort. The optional bundle dependency check (Part 2) is intentionally deferred pending a decision on approach, and nothing here depends on it.

  • Are new tests required? (added — unit + manual e2e)
  • Does documentation need to be updated? (user docs + requirements spec added in this PR)
  • Contains breaking changes?
  • Touches plugin contract, manifest format, or recurfile YAML schema? (deliberately no schema change)
  • Changes daemon config keys or plugin options?
  • Affects daemon startup/shutdown behavior? (startup app-folder scan)
  • Changes CLI flags or command signatures? (new recur app subcommands)

Changes

  • appbundle package (src/infra/fs/appbundle/): traversal-safe zip pack/unpack preserving layout and file modes; relaxed root-YAML recurfile selection (any single root YAML is the recurfile; a conventional recurfile.* breaks ties when several are present).
  • recur app install <bundle.recur | URL>: resolves the source (URL downloads reuse the plugin path's allowed_hosts gate), unpacks via a staging dir so a bad bundle never clobbers an installed app, derives the name (recurfile stem → bundle stem, --name overrides), prompts overwrite/abandon on collision (--force to skip), then registers with the daemon. Daemon-down installs just unpack and defer registration.
  • recur app list / remove / pack: the rest of the lifecycle, incl. an authoring command to build bundles.
  • Daemon startup scan (scanAppFolder, hooked into Run() after state replay): registers apps under ~/.config/recur/app/ not already registered, idempotent via new registry.hasRecurfilePath.
  • configyaml.AppDir(): single source of truth for the app directory path, shared by CLI and daemon.
  • Docs & spec: requirements/app-bundles.md (spec + acceptance criteria), recur app commands added to requirements/cli-commands.md, startup scan noted in requirements/daemon-lifecycle.md, a user guide at docs/content/docs/configuration/app-bundles.md, and a CLI reference section.

Test plan

  • Unit tests pass (task test) — new tests for bundle round-trip/mode-preservation/traversal/selection, CLI install/name-resolution/overwrite-abort/force/list/remove/pack, and daemon scan register/idempotent/skip-non-app.
  • Plugin tests pass (task test:plugins) — n/a, no plugin changes.
  • E2E tests pass (task test:e2e) — formal suite not run; manual e2e performed: installed a bundle with the daemon down → started the daemon → app auto-registered (app list shows "registered", daemon lists 1 group/1 trigger/1 action); list/remove/pack verified. task lint:go reports 0 issues.

Checklist

  • Tests added or updated for new/changed behavior
  • Spec in requirements/ updated — requirements/app-bundles.md plus cli-commands/daemon-lifecycle updates
  • User docs updated — docs/content/docs/configuration/app-bundles.md and the CLI reference
  • Small, buildable commits — feature + docs split into two commits, each compiles and passes tests
  • No breaking changes — additive only

Breaking changes

None.

Introduce app bundles: a .recur zip archive containing a recurfile (the
single YAML at its root) plus any local scripts an app needs.

- appbundle package: traversal-safe zip pack/unpack; relaxed root-YAML
  recurfile selection (conventional recurfile.* breaks ties).
- `recur app install <bundle|URL>`: resolve source (URL downloads gated by
  allowed_hosts), unpack to ~/.config/recur/app/<name>/ via a staging dir,
  derive name (yaml stem -> bundle stem, --name overrides), prompt on
  collision, then register with the daemon. Daemon-down installs just unpack.
- `recur app list` / `remove` / `pack` for the rest of the lifecycle.
- Daemon startup scan registers apps installed while it was stopped
  (idempotent via registry.hasRecurfilePath), so no manual register is needed.
- Shared configyaml.AppDir() as the single source of truth for the path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Binary Size Report

Binary Base PR Delta
recur 20MiB 20MiB +110KiB
recurd 17MiB 17MiB +12KiB

- requirements/app-bundles.md: spec with bundle format, install/list/remove/pack
  behavior, daemon startup scan, and acceptance criteria.
- requirements/cli-commands.md: add `recur app` commands to the hierarchy and
  per-command detail blocks.
- requirements/daemon-lifecycle.md: note the startup app-folder scan.
- docs: user guide (configuration/app-bundles.md) and CLI reference section for
  installing, managing, and creating .recur bundles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@directedbits
directedbits merged commit 36ecaee into main Jul 13, 2026
6 checks passed
@directedbits
directedbits deleted the feat/app-bundle branch July 13, 2026 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant