Skip to content

Commit 5dc6016

Browse files
committed
Add support for sapling
Sapling is a somewhat novel SCM from Facebook / Meta. It is fully compatible with Git and provides a more powerful yet userfriendly frontend, modeled after mercurial. This adds Sapling support in alibuild, including: * Support for alidist sapling checkouts * Support for development packages which were cloned via sapling
1 parent 75a42fa commit 5dc6016

File tree

5 files changed

+83
-5
lines changed

5 files changed

+83
-5
lines changed

.github/workflows/pr-check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ jobs:
8282

8383
- name: Install test dependencies
8484
run: |
85-
brew install modules alisw/system-deps/o2-full-deps
85+
brew install modules alisw/system-deps/o2-full-deps sapling
8686
python3 -m pip install --upgrade tox tox-gh-actions coverage
8787
8888
- name: Run tests

alibuild_helpers/build.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from alibuild_helpers.utilities import yamlDump
1515
from alibuild_helpers.utilities import resolve_tag, resolve_version
1616
from alibuild_helpers.git import git, clone_speedup_options, Git
17+
from alibuild_helpers.sl import sapling, Sapling
1718
from alibuild_helpers.sync import (NoRemoteSync, HttpRemoteSync, S3RemoteSync,
1819
Boto3RemoteSync, RsyncRemoteSync)
1920
import yaml
@@ -62,6 +63,10 @@ def update_git_repos(args, specs, buildOrder, develPkgs):
6263

6364
def update_repo(package, git_prompt):
6465
specs[package]["scm"] = Git()
66+
if package in develPkgs:
67+
localCheckout = os.path.join(os.getcwd(), specs[package]["package"])
68+
if exists("%s/.sl" % localCheckout):
69+
specs[package]["scm"] = Sapling()
6570
updateReferenceRepoSpec(args.referenceSources, package, specs[package],
6671
fetch=args.fetchRepos,
6772
usePartialClone=not args.docker,
@@ -363,10 +368,19 @@ def doBuild(args, parser):
363368
if not exists(specDir):
364369
makedirs(specDir)
365370

366-
# By default Git is our SCM, so we find the commit hash of alidist
367-
# using it.
368-
scm = Git()
369-
os.environ["ALIBUILD_ALIDIST_HASH"] = scm.checkedOutCommitName(directory=args.configDir)
371+
# If the alidist workdir contains a .sl directory, we use Saplign as SCM
372+
# otherwise we default to git (without checking for the actual presence of
373+
# .git).
374+
# We do it this way, because we have one test which uses an embedded folder
375+
# in the alibuild source, which therefore does not contain a .git directory
376+
# and falls back to the alibuild git commit.
377+
scm = exists("%s/.sl" % args.configDir) and Sapling() or Git()
378+
try:
379+
checkedOutCommitName = scm.checkedOutCommitName(directory=args.configDir)
380+
except:
381+
error("Cannot find SCM directory in %s.", args.configDir)
382+
return 1
383+
os.environ["ALIBUILD_ALIDIST_HASH"] = checkedOutCommitName
370384

371385
debug("Building for architecture %s", args.architecture)
372386
debug("Number of parallel builds: %d", args.jobs)

alibuild_helpers/sl.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from shlex import quote # Python 3.3+
2+
from alibuild_helpers.cmd import getstatusoutput
3+
from alibuild_helpers.log import debug
4+
from alibuild_helpers.scm import SCM
5+
6+
SL_COMMAND_TIMEOUT_SEC = 120
7+
"""How many seconds to let any sl command execute before being terminated."""
8+
9+
# Sapling is a novel SCM by Meta (i.e. Facebook) that is fully compatible with
10+
# git, but has a different command line interface. Among the reasons why it's
11+
# worth suporting it is the ability to handle unnamed branches, the ability to
12+
# absorb changes to the correct commit without having to explicitly rebase and
13+
# the integration with github to allow for pull requests to be created from the
14+
# command line from each commit of a branch.
15+
class Sapling(SCM):
16+
name = "Sapling"
17+
def checkedOutCommitName(self, directory):
18+
return sapling(("whereami", ), directory)
19+
def branchOrRef(self, directory):
20+
# Format is <hash>[+] <branch>
21+
identity = sapling(("identify", ), directory)
22+
return identity.split(" ")[-1]
23+
def exec(self, *args, **kwargs):
24+
return sapling(*args, **kwargs)
25+
def parseRefs(self, output):
26+
return {
27+
sl_ref: sl_hash for sl_ref, sep, sl_hash
28+
in (line.partition("\t") for line in output.splitlines()) if sep
29+
}
30+
def listRefsCmd(self):
31+
return ["bookmark", "--list", "--remote", "-R"]
32+
def diffCmd(self, directory):
33+
return "cd %s && sl diff && sl status" % directory
34+
def checkUntracked(self, line):
35+
return line.startswith("? ")
36+
37+
def sapling(args, directory=".", check=True, prompt=True):
38+
debug("Executing sl %s (in directory %s)", " ".join(args), directory)
39+
# We can't use git --git-dir=%s/.git or git -C %s here as the former requires
40+
# that the directory we're inspecting to be the root of a git directory, not
41+
# just contained in one (and that breaks CI tests), and the latter isn't
42+
# supported by the git version we have on slc6.
43+
# Silence cd as shell configuration can cause the new directory to be echoed.
44+
err, output = getstatusoutput("""\
45+
set -e +x
46+
sl -R {directory} {args}
47+
""".format(
48+
directory=quote(directory),
49+
args=" ".join(map(quote, args)),
50+
), timeout=SL_COMMAND_TIMEOUT_SEC)
51+
if check and err != 0:
52+
raise RuntimeError("Error {} from sl {}: {}".format(err, " ".join(args), output))
53+
return output if check else (err, output)

tests/test_build.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ def dummy_exists(x):
181181
return {
182182
"/alidist": True,
183183
"/alidist/.git": True,
184+
"/alidist/.sl": False,
184185
"/sw": True,
185186
"/sw/SPECS": False,
186187
"/sw/MIRROR/root": True,

tox.ini

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ allowlist_externals =
4040
git
4141
test
4242
touch
43+
sl
44+
rm
4345
deps =
4446
py27: mock
4547
coverage
@@ -109,6 +111,14 @@ commands =
109111
touch zlib/foo
110112
coverage run --source={toxinidir} -a {toxinidir}/aliBuild -a {env:ARCHITECTURE} --no-system --disable GCC-Toolchain build zlib
111113
coverage run --source={envsitepackagesdir} -a -m unittest discover {toxinidir}/tests
114+
# On Darwin we also test sapling support
115+
darwin: sl clone https://github.com/alisw/alidist alidist-sapling
116+
darwin: coverage run --source={toxinidir} -a {toxinidir}/aliBuild -a {env:ARCHITECTURE} -c alidist-sapling --no-system --disable GCC-Toolchain build zlib
117+
darwin: rm -fr zlib
118+
darwin: sl clone https://github.com/alisw/zlib
119+
darwin: coverage run --source={toxinidir} -a {toxinidir}/aliBuild -a {env:ARCHITECTURE} --no-system --disable GCC-Toolchain build zlib
120+
touch zlib/foo
121+
darwin: coverage run --source={toxinidir} -a {toxinidir}/aliBuild -a {env:ARCHITECTURE} --no-system --disable GCC-Toolchain build zlib
112122

113123
[coverage:run]
114124
branch = True

0 commit comments

Comments
 (0)