On this page
Advisory file lock using fcntl.flock to prevent concurrent rlsbl operations from mutating project state in both regular and monorepo modes.
#rlsbl.lock
#rlsbl.lock
Advisory file lock using fcntl.flock to prevent concurrent rlsbl operations from mutating project state in both regular and monorepo modes.
One lock file per state home -- .rlsbl/lock for a standalone project, .rlsbl-monorepo/lock for a workspace -- and every operation that mutates that state takes it: release run, monorepo release run, scaffold, release scrub, and the repository conversions. There is deliberately no second lock elsewhere (under .git/, say): a lock a release does not take excludes nothing, and the whole point of the conversion taking one is that it cannot run while a release is running.
Waiting is the default and refusing is the alternative. A short operation queueing behind another is ordinary; a long, destructive one (a conversion that rewrites history into a new repository) is better refused with the holder named than left blocked behind a release that may be waiting on CI, which is what wait=False is for.
#LockHeldError
The advisory lock is held by another process and waiting was refused.
#acquire_lock
def acquire_lock(lock_dir='.rlsbl', *, project_root, wait=True)Acquire an exclusive advisory lock on
If another process holds the lock and wait is True, prints a waiting message and blocks until the lock is available. Returns early if already locked (prevents fd leak on double-acquire).
lock_dir: directory for the lock file (default ".rlsbl"). In monorepo mode pass ".rlsbl-monorepo". project_root: when provided, lock_dir is resolved relative to it. wait: block until the lock is free (the default). With wait=False a held lock raises :class:LockHeldError naming the lock file instead, for an operation whose caller would rather be told than queued.
#release_lock
def release_lock()Release the advisory lock, close the file descriptor, and remove the lock file.
#is_stale
def is_stale(lock_path=None, *, project_root)Check if a lock file exists but no process holds it.
Returns True if the file exists and is not held (stale). Returns False if the file doesn't exist or is actively held.
project_root: when lock_path is None, build the default lock path relative to project_root.
#rlsbl_lock
def rlsbl_lock(lock_dir='.rlsbl', *, project_root, wait=True)Context manager that acquires the lock on enter and releases on exit.
wait=False refuses a held lock with :class:LockHeldError instead of queueing behind its holder -- see :func:acquire_lock.