CLI tool functions for querying Patcho topic versions.
Setup - add to deps.edn: {:aliases {:patcho {:extra-deps {dev.gersak/patcho {:mvn/version "0.4.2"}} :exec-ns patcho.cli}}}
Usage: clj -X:patcho version :topic :myapp/database :require myapp.patches clj -X:patcho versions :require myapp.patches clj -X:patcho topics :require myapp.patches
The version command returns a plain string for easy shell capture:
VERSION=$(clj -X:patcho version :topic :myapp/database :require myapp.patches)
The versions and topics commands return EDN for programmatic use:
(def versions (let [{:keys [out]} (b/process {:command-args ["clj" "-X:patcho" "versions" ":require" "myapp.patches"] :out :capture})] (edn/read-string out)))
CLI tool functions for querying Patcho topic versions.
Setup - add to deps.edn:
{:aliases
{:patcho {:extra-deps {dev.gersak/patcho {:mvn/version "0.4.2"}}
:exec-ns patcho.cli}}}
Usage:
clj -X:patcho version :topic :myapp/database :require myapp.patches
clj -X:patcho versions :require myapp.patches
clj -X:patcho topics :require myapp.patches
The `version` command returns a plain string for easy shell capture:
VERSION=$(clj -X:patcho version :topic :myapp/database :require myapp.patches)
The `versions` and `topics` commands return EDN for programmatic use:
(def versions
(let [{:keys [out]} (b/process
{:command-args ["clj" "-X:patcho" "versions"
":require" "myapp.patches"]
:out :capture})]
(edn/read-string out)))Module lifecycle management with dependency resolution.
Provides runtime lifecycle management for modules with explicit dependency declaration and automatic startup ordering.
This namespace provides a lightweight registry for managing module lifecycle:
(ns my.module
(:require [patcho.lifecycle :as lifecycle]))
(lifecycle/register-module! :my/module
{:depends-on [:other/module]
:setup (fn []
(create-tables!))
:cleanup (fn []
(drop-tables!))
:start (fn []
(start-connections!)
(subscribe-to-events!))
:stop (fn []
(unsubscribe!)
(close-connections!))})
(ns my.app
(:require
my.database ; Registers :my/database
my.cache ; Registers :my/cache (depends on :my/database)
my.api ; Registers :my/api (depends on :my/cache)
[patcho.lifecycle :as lifecycle]))
;; Set default store once (typically in main or init)
(lifecycle/set-store! (lifecycle/->FileLifecycleStore ".lifecycle"))
;; Simple approach: Just start! (auto-runs setup if needed)
(lifecycle/start! :my/api)
;; → Setups (if needed) and starts: :my/database → :my/cache → :my/api
;; Setup is idempotent - only runs once, tracked via lifecycle store
;; OR explicit control: Run setup separately (optional)
(lifecycle/setup! :my/api) ; One-time: setup all dependencies
(lifecycle/start! :my/api) ; Runtime: start all dependencies
;; Visualize dependencies
(lifecycle/print-dependency-tree :my/api)
;; :my/api
;; └── :my/cache
;; └── :my/database
;; Later: stop (RECURSIVE - stops dependents first)
(lifecycle/stop! :my/database)
;; → Stops: :my/api → :my/cache → :my/database
;; Surgical stop (only this module)
(lifecycle/stop-only! :my/api)
;; Cleanup (RECURSIVE with reference checking)
(lifecycle/cleanup! :my/api)
;; → Cleans :my/api
;; → Cleans :my/cache (if no other modules depend on it)
;; → Cleans :my/database (if no other modules depend on it)
Lifecycle management is complementary to version management:
They work together:
Note: start! automatically runs setup for modules that have a setup function, making the system easy to use while maintaining explicit control when needed.
Module lifecycle management with dependency resolution.
Provides runtime lifecycle management for modules with explicit dependency
declaration and automatic startup ordering.
## Design Philosophy
This namespace provides a lightweight registry for managing module lifecycle:
- Modules register themselves at namespace load time
- Dependencies are declared explicitly
- Operations are RECURSIVE - automatically handle dependencies
- Cleanup uses reference counting (claimed-by semantics)
- State tracking prevents double-start/stop
- Setup phase is separate from start/stop (one-time vs runtime)
## Usage
### Registration (at namespace load time)
(ns my.module
(:require [patcho.lifecycle :as lifecycle]))
(lifecycle/register-module! :my/module
{:depends-on [:other/module]
:setup (fn []
(create-tables!))
:cleanup (fn []
(drop-tables!))
:start (fn []
(start-connections!)
(subscribe-to-events!))
:stop (fn []
(unsubscribe!)
(close-connections!))})
### Application Startup
(ns my.app
(:require
my.database ; Registers :my/database
my.cache ; Registers :my/cache (depends on :my/database)
my.api ; Registers :my/api (depends on :my/cache)
[patcho.lifecycle :as lifecycle]))
;; Set default store once (typically in main or init)
(lifecycle/set-store! (lifecycle/->FileLifecycleStore ".lifecycle"))
;; Simple approach: Just start! (auto-runs setup if needed)
(lifecycle/start! :my/api)
;; → Setups (if needed) and starts: :my/database → :my/cache → :my/api
;; Setup is idempotent - only runs once, tracked via lifecycle store
;; OR explicit control: Run setup separately (optional)
(lifecycle/setup! :my/api) ; One-time: setup all dependencies
(lifecycle/start! :my/api) ; Runtime: start all dependencies
;; Visualize dependencies
(lifecycle/print-dependency-tree :my/api)
;; :my/api
;; └── :my/cache
;; └── :my/database
;; Later: stop (RECURSIVE - stops dependents first)
(lifecycle/stop! :my/database)
;; → Stops: :my/api → :my/cache → :my/database
;; Surgical stop (only this module)
(lifecycle/stop-only! :my/api)
;; Cleanup (RECURSIVE with reference checking)
(lifecycle/cleanup! :my/api)
;; → Cleans :my/api
;; → Cleans :my/cache (if no other modules depend on it)
;; → Cleans :my/database (if no other modules depend on it)
## Relationship to patcho.patch
Lifecycle management is complementary to version management:
- patcho.patch: Version migrations (data/schema state transitions)
- patcho.lifecycle: Runtime management (start/stop with dependencies)
They work together:
1. Apply patches to reach target version (patch/level!)
2. Start runtime services (lifecycle/start!) - auto-runs setup if needed
Note: start! automatically runs setup for modules that have a setup function,
making the system easy to use while maintaining explicit control when needed.
## Design Decisions
- Registry pattern: Modules stored in atom (like Patcho's patches)
- Keyword topics: Same style as Patcho (:my/module)
- Explicit dependencies: Clear, validated at runtime
- RECURSIVE operations: start/setup/cleanup handle deps automatically
- Auto-setup: start! runs setup if needed (idempotent via lifecycle store)
- Reference counting: cleanup only removes if not claimed by others
- State tracking: Prevents double-start bugs
- Functions not protocols: Simple, direct
- Minimal dependencies: Just clojure.coreA simple version migration system for Clojure applications.
Patcho provides a declarative way to define version-based patches that can be applied to migrate between different versions of your application or modules.
Key features:
Basic usage: (require '[patcho.patch :as patch])
; Define current version (patch/current-version ::my-app "2.0.0")
; Define upgrade patches (patch/upgrade ::my-app "1.0.0" (println "Initial setup"))
(patch/upgrade ::my-app "2.0.0" (println "Major upgrade"))
; Apply patches (patch/apply ::my-app "1.0.0") ; Runs 2.0.0 upgrade (patch/apply ::my-app nil) ; Runs all upgrades
A simple version migration system for Clojure applications.
Patcho provides a declarative way to define version-based patches that can be
applied to migrate between different versions of your application or modules.
Key features:
- Automatic patch sequencing based on semantic versioning
- Support for both upgrades and downgrades
- Topic-based organization for modular systems
- Simple macro-based API
Basic usage:
(require '[patcho.patch :as patch])
; Define current version
(patch/current-version ::my-app "2.0.0")
; Define upgrade patches
(patch/upgrade ::my-app "1.0.0"
(println "Initial setup"))
(patch/upgrade ::my-app "2.0.0"
(println "Major upgrade"))
; Apply patches
(patch/apply ::my-app "1.0.0") ; Runs 2.0.0 upgrade
(patch/apply ::my-app nil) ; Runs all upgradescljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |