viv lock

Lock file maintenance: translate an existing composer.lock into viv.lock without re-solving, write composer.lock back from viv.lock, or merge a lock as a git merge driver.

Usage

Lock file maintenance: translate an existing `composer.lock` into chapter 1's `viv.lock` (#273), without re-solving, or merge one as a git merge driver (#275)

Usage: viv lock [OPTIONS] <COMMAND>

Commands:
  convert  Translate an existing `composer.lock` into `viv.lock` (#273): reads `DIR/composer.lock` and `DIR/composer.json`, and writes `DIR/viv.lock`, without re-solving anything
  export   Write `DIR/composer.lock` from `DIR/viv.lock` and `DIR/composer.json` (#344), through the same `lock_writer::write` a solve feeds: a team that stops committing `composer.lock` regenerates it this way in CI or on checkout
  merge    A git merge driver for `composer.lock`/`viv.lock` (#275): merges the three inputs by name-keyed package record instead of by text line, writes the result over `<ours>`, and exits 1 when a divergent name's own re-solve doesn't finish (a human decision then, not this chunk's job — see `docs/research.md` chapter 1). Git's merge-driver convention: `%O %A %B` (`git help gitattributes`'s "Defining a custom merge driver")
  help     Print this message or the help of the given subcommand(s)

Options:
  -v, --verbose                Raise logging to debug
      --cache-dir <CACHE_DIR>  Store location (default `$XDG_CACHE_HOME/vivace`, or `~/.cache/vivace`)
      --offline                Fail fast on any request instead of connecting: install errors, naming every package not already in the store; update solves from cached repository metadata only, erroring on an uncached package. Also set by `COMPOSER_DISABLE_NETWORK` (any value but unset, empty or `0`; Composer's own git-priming `prime` value is not special-cased here, since neither `install` nor `update` touch a git source)
  -h, --help                   Print help

viv lock convert

Reads DIR/composer.lock and DIR/composer.json, and writes DIR/viv.lock, without re-solving anything. --stdout prints the result instead of writing it.

Translate an existing `composer.lock` into `viv.lock` (#273): reads `DIR/composer.lock` and `DIR/composer.json`, and writes `DIR/viv.lock`, without re-solving anything

Usage: viv lock convert [OPTIONS]

Options:
  -d, --project-dir <PROJECT_DIR>  Project directory holding composer.json and composer.lock [default: .]
  -v, --verbose                    Raise logging to debug
      --cache-dir <CACHE_DIR>      Store location (default `$XDG_CACHE_HOME/vivace`, or `~/.cache/vivace`)
      --stdout                     Print the translated lock to stdout instead of writing `viv.lock` to disk
      --offline                    Fail fast on any request instead of connecting: install errors, naming every package not already in the store; update solves from cached repository metadata only, erroring on an uncached package. Also set by `COMPOSER_DISABLE_NETWORK` (any value but unset, empty or `0`; Composer's own git-priming `prime` value is not special-cased here, since neither `install` nor `update` touch a git source)
  -h, --help                       Print help

viv lock export

Writes DIR/composer.lock from DIR/viv.lock and DIR/composer.json, through the same writer a solve feeds. When composer.json names isolated plugins in extra.viv.isolate, the export adds that map to composer.lock as a top-level extra.viv.isolate key, which Composer ignores. --check writes nothing and reports whether the existing composer.lock already matches.

Write `DIR/composer.lock` from `DIR/viv.lock` and `DIR/composer.json` (#344), through the same `lock_writer::write` a solve feeds: a team that stops committing `composer.lock` regenerates it this way in CI or on checkout

Usage: viv lock export [OPTIONS]

Options:
  -d, --project-dir <PROJECT_DIR>  Project directory holding viv.lock and composer.json [default: .]
  -v, --verbose                    Raise logging to debug
      --cache-dir <CACHE_DIR>      Store location (default `$XDG_CACHE_HOME/vivace`, or `~/.cache/vivace`)
      --check                      Write nothing; exit 0 when the existing composer.lock already equals the export, 1 with a one-line diff summary otherwise
      --offline                    Fail fast on any request instead of connecting: install errors, naming every package not already in the store; update solves from cached repository metadata only, erroring on an uncached package. Also set by `COMPOSER_DISABLE_NETWORK` (any value but unset, empty or `0`; Composer's own git-priming `prime` value is not special-cased here, since neither `install` nor `update` touch a git source)
  -h, --help                       Print help

viv lock merge

A git merge driver for composer.lock/viv.lock: merges the three inputs (%O %A %B, see git help gitattributes) by name-keyed package record instead of by text line, and writes the result over ours. Configure it as a merge= driver in .gitattributes and git config, not run by hand day-to-day.

A git merge driver for `composer.lock`/`viv.lock` (#275): merges the three inputs by name-keyed package record instead of by text line, writes the result over `<ours>`, and exits 1 when a divergent name's own re-solve doesn't finish (a human decision then, not this chunk's job — see `docs/research.md` chapter 1). Git's merge-driver convention: `%O %A %B` (`git help gitattributes`'s "Defining a custom merge driver")

Usage: viv lock merge [OPTIONS] <BASE> <OURS> <THEIRS>

Arguments:
  <BASE>
          The common ancestor's version of the lock (`%O`)

  <OURS>
          This side's version (`%A`); overwritten with the merge result

  <THEIRS>
          The other side's version (`%B`)

Options:
  -d, --project-dir <PROJECT_DIR>
          Project directory whose composer.json supplies the root requirements and the repositories a divergent name's re-solve fetches against; for composer.lock, also the `content-hash`, and for viv.lock (#295), the sibling composer.lock the pinned set's `require` is read off

          [default: .]

  -v, --verbose
          Raise logging to debug

      --cache-dir <CACHE_DIR>
          Store location (default `$XDG_CACHE_HOME/vivace`, or `~/.cache/vivace`)

      --no-resolve
          Skip the re-solve and go straight to chunk 1's conflict markers for any divergent name, in either format. For tests and offline use, where a network re-solve isn't wanted at all

      --as-of <TIMESTAMP>
          Resolve the divergent closure as the registry stood at this RFC 3339 timestamp: a released version Packagist's own `time` puts later than this is dropped from the pool, so a version that did not exist yet cannot be chosen. `dev-*` branch versions are never filtered this way — Packagist serves only a branch's current head, not a historical revision of one, so they always resolve to today's

      --offline
          Fail fast on any request instead of connecting: install errors, naming every package not already in the store; update solves from cached repository metadata only, erroring on an uncached package. Also set by `COMPOSER_DISABLE_NETWORK` (any value but unset, empty or `0`; Composer's own git-priming `prime` value is not special-cased here, since neither `install` nor `update` touch a git source)

      --max-scope <MAX_SCOPE>
          How far a divergent name's re-solve may escalate before giving up on markers (#296): `closure` is chunk 2's original scope (the divergent names plus their own locked closure), `dependents` adds pinned packages that directly require one of those, `seeded` (the default) is a full solve that prefers every non-divergent locked version but pins none of them hard

          Possible values:
          - closure:    Rung 1: the divergent names plus their own locked transitive closure, everything else pinned hard (chunk 2's original behaviour)
          - dependents: Rung 2: rung 1 plus any pinned package that directly requires a name in the closure
          - seeded:     Rung 3: a full solve over the merged manifest with every non-divergent locked version passed as `--minimal-changes`'s `preferred` pin set. Never a plain update: nothing is pinned hard, but a satisfying solution that keeps a preferred version is always preferred over one that doesn't

          [default: seeded]

      --offline-rung
          Try one further, offline rung (#314) once the registry escalation above has already failed at every rung `--max-scope` allowed: one parent's own pinned record per divergent name (`ours` first, then `theirs`), checked against the two locks' own `require`/`conflict`/`replace`/`provide`/platform data with no registry fetch at all. Only ever a last resort before conflict markers, so it can never disagree with a registry answer that also finished — it only ever fires when there wasn't one. Off by default; a no-op for `viv.lock`, whose records carry none of the fields the check needs

  -h, --help
          Print help (see a summary with '-h')

Reads and writes

  • Reads: composer.json, composer.lock, viv.lock; merge also reads the three merge inputs and, unless --no-resolve, fetches repository metadata over the network for a divergent package's re-solve.
  • Writes: viv.lock (convert), composer.lock (export), the ours file in place (merge).

Exit codes

  • 0 — converted, exported, or merged cleanly (including export --check finding the file already up to date).
  • 1 — export --check finding a difference, or a divergent name's re-solve in merge not finishing (conflict markers were written instead), or a filesystem error.
  • 2 — as update, when merge's re-solve is a genuine dependency-resolution failure rather than a divergent-name conflict.

See also

viv update-lock, Files viv writes