Workspaces
A workspace builds checkouts of several projects together, while they are
changed together: a library and the engine that uses it, say, before the
library's change is committed. Go has the same, as go.work.
The stiftwork
A workspace is a file, stiftwork, that names its members' directories,
relative to its own:
;;; -*- mode: lisp-data -*-
(:use ("cl-ffi" "cl-ft2" "cl-harfbuzz" "pyssel"))
Each member is a project with a stiftfile, whose :project names the
project whose requirements its checkout stands in for. A stiftwork may
also hold a :replace list, as a stiftfile does, with :local
directories relative to the stiftwork:
;;; -*- mode: lisp-data -*-
(:use ("cl-ffi" "pyssel")
:replace (("https://github.com/Shinmera/parachute.git" :local "parachute")))
A stiftwork is not meant to be committed: it describes one person's
checkouts. The engine's sits at the workspace's root, which is no
repository.
Making one
cd ~/code/lisp/engine
stift work init cl-ffi cl-ft2 cl-harfbuzz pyssel # write the stiftwork here
stift work use cl-icu # add a member
stift work drop cl-ft2 # remove one
work init writes the stiftwork in the current directory, and refuses
to replace one. work use and work drop edit the workspace stift finds,
and take directories relative to the current directory; work use leaves
a member that is one already as it is, and work drop takes a directory
that no longer exists. Each prints the members after the change.
Which workspace
stift finds the stiftwork in the directory a command runs in, or the
nearest one above it. STIFT_WORKSPACE names another stiftwork, or
off for none, and --no-workspace leaves the workspace out for one
command.
When the current directory is in a project that is not a member of the workspace found, stift stops, and says how to add it, or to leave the workspace out. At the workspace's root, or in a directory of it that is in no project, the command works for every member.
Building in a workspace
In a workspace, every member is a main project:
- the members' requirements, test requirements included, are chosen from together, as Choosing Commits describes for one project;
- every requirement on a member is met by its checkout, uncommitted changes and all, whatever commit it names;
- the members' stiftfiles need not be tidy: stift reads every requirement
met, as
tidydoes, rather than check one stiftfile; - the members'
:replaceand:excludecount. When two members replace one project differently, stift stops: choose one in thestiftwork's:replace, which wins over the members'. A member's replacement of another member does not count, since the member's checkout stands in for it, and thestiftworkcannot replace a member.
stift exec, setup, download, list, why and graph, in any member
or at the workspace's root, work with the workspace's build list. stift test in a member tests the member's systems; at the root, every member's.
stift list shows each member with workspace and its directory.
get, update, remove and tidy ignore the workspace. They change one
project's stiftfile from pins alone, so that what is committed builds
without the workspace, as CI builds it. stift --no-workspace list in a
member shows the build list CI would make, and stops if the member's
stiftfile is not tidy.
Moving the pins: work sync
Changing a library in a workspace leaves the pins on it behind: the
members that require it still name the commit they named before.
stift work sync moves them:
cd ~/code/lisp/engine/cl-ffi
git commit -am "Faster calls" && git push
stift work sync
fakehacker.cc/dxtr/cl-harfbuzz
moved fakehacker.cc/dxtr/cl-ffi 4f0c1a2b3c4d..7a6b5c4d3e2f branch main
fakehacker.cc/dxtr/pyssel
moved fakehacker.cc/dxtr/cl-ffi 4f0c1a2b3c4d..7a6b5c4d3e2f branch main indirect
Commit and push the stiftfile of fakehacker.cc/dxtr/cl-harfbuzz, then run stift work sync again: other members require it.
It moves every requirement of a member on another member, direct, indirect or for tests, to the commit the other member's checkout has checked out. A requirement keeps its branch when the branch contains the new commit, or takes the branch the checkout is on. Then it tidies each stiftfile it changed, from pins alone, so that it builds without the workspace. It lists the members in the order they depend on each other, those required first.
It refuses, names the member, and changes nothing, when a commit it would move a pin to:
- is on no branch or tag of the member's remote, the URL the members require it by: push it first;
- does not contain the commit required now, so that the pin would go back: bring the checkout up to date.
Committing and pushing stay with you. A member's own stiftfile change
reaches the members that require it only once it is committed and pushed,
so work sync names the members whose stiftfiles others require and have
changes that are not committed. Commit and push them, in the order given,
and run it again, until it says:
The members' requirements on each other are up to date.
work sync needs the network, to see what the members' remotes hold.