git-cinnabar
497 строк · 15.3 Кб
1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5import hashlib
6import json
7import os
8import subprocess
9import sys
10
11BASE_DIR = os.path.dirname(__file__)
12sys.path.append(BASE_DIR)
13sys.path.append(os.path.join(BASE_DIR, ".."))
14
15from itertools import chain
16
17import osx # noqa: F401
18from tasks import (
19Task,
20TaskEnvironment,
21Tool,
22action,
23parse_version,
24)
25from tools import (
26ALL_MERCURIAL_VERSIONS,
27GIT_VERSION,
28MERCURIAL_VERSION,
29SOME_MERCURIAL_VERSIONS,
30Build,
31Git,
32Hg,
33nproc,
34)
35from variables import * # noqa: F403
36
37
38def git_rev_parse(committish):
39return subprocess.check_output(
40["git", "rev-parse", committish], text=True, cwd=os.path.join(BASE_DIR, "..")
41).strip()
42
43
44def is_old_hg(version):
45# `version` is a sha1 for trunk, which means it's >= 3.6
46if len(version) == 40:
47return False
48try:
49version = [int(x) for x in version.split(".")]
50except ValueError:
51# Assume that an invalid version per the conversion above is
52# newer.
53return False
54return version < [3, 6]
55
56
57UPGRADE_FROM = () # ('0.5.0',)
58
59
60class TestTask(Task):
61coverage = []
62
63def __init__(self, **kwargs):
64git = kwargs.pop("git", GIT_VERSION)
65hg = kwargs.pop("hg", MERCURIAL_VERSION)
66hg_clone = kwargs.pop("hg_clone", None)
67commit = kwargs.pop("commit", None)
68task_env = kwargs.pop("task_env", "linux")
69variant = kwargs.pop("variant", None)
70build = kwargs.pop("build", None)
71clone = kwargs.pop("clone", TC_COMMIT)
72desc = kwargs.pop("description", None)
73short_desc = kwargs.pop("short_desc", "test")
74extra_desc = kwargs.pop("extra_desc", None)
75pre_command = kwargs.pop("pre_command", None)
76if build is None:
77build = "{}.{}".format(task_env, variant) if variant else task_env
78build = Build.by_name(build)
79kwargs.setdefault("mounts", []).append(build.mount())
80build = build.install()
81if variant:
82kwargs.setdefault("env", {})["VARIANT"] = variant
83env = TaskEnvironment.by_name("{}.test".format(task_env))
84command = []
85if pre_command:
86command.extend(pre_command)
87if hg:
88hg_task = Hg.by_name("{}.{}".format(task_env, hg))
89kwargs.setdefault("mounts", []).append(hg_task.mount())
90command.extend(hg_task.install())
91command.append("hg --version")
92if is_old_hg(hg):
93kwargs.setdefault("env", {})["NO_CLONEBUNDLES"] = "1"
94if git:
95git_task = Git.by_name("{}.{}".format(task_env, git))
96kwargs.setdefault("mounts", []).append(git_task.mount())
97command.extend(git_task.install())
98command.append("git --version")
99command.extend(Task.checkout(commit=commit))
100command.extend(build)
101if clone:
102kwargs.setdefault("mounts", []).append(
103{"file:bundle.git": Clone.by_name(clone)}
104)
105command.extend(
106[
107"git init repo/hg.old.git",
108"git -C repo/hg.old.git fetch ../../bundle.git refs/*:refs/*",
109"git -C repo/hg.old.git remote add origin hg:${REPO#https:}",
110"git -C repo/hg.old.git symbolic-ref HEAD"
111" refs/heads/branches/default/tip",
112]
113)
114kwargs.setdefault("env", {})["REPO"] = REPO
115command.extend(("repo/git-cinnabar --version",))
116if "command" not in kwargs or hg_clone:
117command += [
118"hg init repo/hg.pure.hg",
119"hg -R repo/hg.pure.hg unbundle bundle.hg",
120]
121kwargs.setdefault("mounts", []).append(
122{"file:bundle.hg": HgClone.by_name(MERCURIAL_VERSION)}
123)
124if "command" in kwargs:
125kwargs["command"] = command + kwargs["command"]
126else:
127if commit:
128# Always use the current CI scripts
129command.append(
130"git -C repo -c core.autocrlf=input checkout {} CI".format(
131TC_COMMIT
132)
133)
134output_sync = " --output-sync=target"
135if env.os == "macos":
136output_sync = ""
137kwargs["command"] = command + [
138"make -C repo -f CI/tests.mk -j$({}){}".format(nproc(env), output_sync),
139]
140
141if variant == "coverage":
142kwargs["command"].extend(
143[
144"shopt -s nullglob",
145"cd repo",
146"zip $ARTIFACTS/coverage.zip" ' $(find . -name "*.gcda")',
147"cd ..",
148"shopt -u nullglob",
149]
150)
151artifact = kwargs.pop("artifact", None)
152artifacts = kwargs.setdefault("artifacts", [])
153assert not (artifacts and artifact)
154if artifact:
155artifacts.push(artifact)
156artifacts.append("coverage.zip")
157self.coverage.append(self)
158elif variant == "asan" and task_env == "linux":
159kwargs["caps"] = ["SYS_PTRACE"]
160if not desc:
161desc = "{} w/ git-{} hg-{}".format(
162short_desc, git, "r" + hg if len(hg) == 40 else hg
163)
164if variant and variant != "coverage":
165desc = " ".join((desc, variant))
166if extra_desc:
167desc = " ".join((desc, extra_desc))
168if task_env != "linux":
169desc = " ".join((desc, env.os, env.cpu))
170kwargs["description"] = desc
171Task.__init__(self, task_env=env, **kwargs)
172
173
174class Clone(TestTask, metaclass=Tool):
175PREFIX = "clone"
176
177def __init__(self, version):
178sha1 = git_rev_parse(version)
179expireIn = "26 weeks"
180kwargs = {}
181if version == TC_COMMIT or len(version) == 40:
182if version == TC_COMMIT:
183build = Build.by_name("linux")
184else:
185build = Build.by_name("linux.old:{}".format(version))
186kwargs.setdefault("mounts", []).append(build.mount())
187download = build.install()
188expireIn = "26 weeks"
189elif parse_version(version) < parse_version("0.6.0"):
190download = ["repo/git-cinnabar download"]
191if parse_version(version) < parse_version("0.5.7"):
192kwargs["git"] = "2.30.2"
193else:
194download = ["repo/download.py"]
195if REPO == DEFAULT_REPO:
196index = "bundle.{}".format(sha1)
197else:
198index = "bundle.{}.{}".format(hashlib.sha1(REPO).hexdigest(), sha1)
199TestTask.__init__(
200self,
201hg=MERCURIAL_VERSION,
202hg_clone=True,
203description="clone w/ {}".format(version),
204index=index,
205expireIn=expireIn,
206build=download,
207commit=sha1,
208clone=False,
209command=[
210"PATH=$PWD/repo:$PATH"
211" git -c fetch.prune=true clone -n hg::$PWD/repo/hg.pure.hg"
212" hg.old.git",
213"git -C hg.old.git bundle create $ARTIFACTS/bundle.git --all",
214],
215artifact="bundle.git",
216priority="high",
217**kwargs,
218)
219
220
221class HgClone(Task, metaclass=Tool):
222PREFIX = "hgclone"
223
224def __init__(self, version):
225if REPO == DEFAULT_REPO:
226index = "hgclone.{}".format(version)
227else:
228index = "hgclone.{}.{}".format(hashlib.sha1(REPO).hexdigest(), version)
229hg_task = Hg.by_name(f"linux.{version}")
230Task.__init__(
231self,
232task_env=TaskEnvironment.by_name("linux.test"),
233description=f"hg clone w/ {version}",
234index=index,
235expireIn="26 weeks",
236command=hg_task.install()
237+ [
238"hg clone -U --stream $REPO repo",
239"hg -R repo bundle -t none-v1 -a $ARTIFACTS/bundle.hg",
240],
241mounts=[hg_task.mount()],
242artifact="bundle.hg",
243env={
244"REPO": REPO,
245},
246priority="high",
247)
248
249
250@action("decision")
251def decision():
252for env in ("linux", "mingw64", "osx", "arm64-osx"):
253# Can't spawn osx workers from pull requests.
254if env.endswith("osx") and not TC_IS_PUSH:
255continue
256
257TestTask(
258task_env=env,
259variant="coverage" if env == "linux" else None,
260)
261
262task_env = TaskEnvironment.by_name("{}.test".format(env))
263git = Git.by_name("{}.{}".format(env, GIT_VERSION))
264build = Build.by_name(env)
265bin = os.path.basename(build.artifacts[0])
266Task(
267task_env=task_env,
268description="download build {} {}".format(task_env.os, task_env.cpu),
269command=list(
270chain(
271git.install(),
272Task.checkout(),
273build.install(),
274[
275f"python3 repo/CI/test_download.py repo/{bin}",
276],
277)
278),
279mounts=[
280git.mount(),
281build.mount(),
282],
283)
284
285# Because nothing is using the arm64 linux build, we need to manually
286# touch it.
287Build.by_name("arm64-linux")
288
289for upgrade in UPGRADE_FROM:
290TestTask(
291short_desc="upgrade tests",
292extra_desc="from-{}".format(upgrade),
293variant="coverage",
294clone=upgrade,
295env={
296"UPGRADE_FROM": upgrade,
297},
298hg="5.4.2",
299)
300
301for git in ("1.8.5", "2.7.4"):
302TestTask(git=git, env={"GIT_OLD_VERSION": "1"})
303
304for hg in SOME_MERCURIAL_VERSIONS:
305if hg != MERCURIAL_VERSION:
306do_hg_version(hg)
307
308TestTask(
309task_env="linux",
310variant="asan",
311)
312
313for env in ("linux", "mingw64", "osx", "arm64-osx"):
314# Can't spawn osx workers from pull requests.
315if env.endswith("osx") and not TC_IS_PUSH:
316continue
317
318TestTask(
319task_env=env,
320variant="coverage" if env == "linux" else None,
321short_desc="graft tests",
322env={
323"GRAFT": "1",
324},
325)
326
327for env, variant in (
328("linux", "coverage"),
329("linux", "asan"),
330("osx", None),
331("arm64-osx", None),
332):
333# Can't spawn osx workers from pull requests.
334if env.endswith("osx") and not TC_IS_PUSH:
335continue
336
337pre_command = []
338if env != "linux":
339pre_command.append("pip install cram==0.7")
340
341TestTask(
342task_env=env,
343variant=variant,
344short_desc="cram",
345clone=False,
346command=pre_command
347+ [
348"cram --verbose repo/tests",
349],
350env={
351"GIT_CINNABAR_CHECK": "no-version-check",
352},
353)
354
355
356def do_hg_version(hg):
357TestTask(hg=hg)
358# Don't run cram tests for version < 3.6, which would need
359# different tests because of server-side changes in behavior
360# wrt bookmarks.
361if not is_old_hg(hg):
362TestTask(
363short_desc="cram",
364clone=False,
365hg=hg,
366command=[
367"cram --verbose repo/tests",
368],
369env={
370"GIT_CINNABAR_CHECK": "no-version-check",
371},
372)
373
374
375@action(
376"more-hg-versions",
377title="More hg versions",
378description="Trigger tests against more mercurial versions",
379)
380def more_hg_versions():
381for hg in ALL_MERCURIAL_VERSIONS:
382if hg != MERCURIAL_VERSION and hg not in SOME_MERCURIAL_VERSIONS:
383do_hg_version(hg)
384
385
386@action(
387"hg-trunk",
388title="Test w/ hg trunk",
389description="Trigger tests against current mercurial trunk",
390)
391def hg_trunk():
392import requests
393
394r = requests.get("https://www.mercurial-scm.org/repo/hg/?cmd=branchmap")
395trunk = None
396for l in r.text.splitlines():
397fields = l.split()
398if fields[0] == "default":
399trunk = fields[-1]
400if not trunk:
401raise Exception("Cannot find mercurial trunk changeset")
402do_hg_version(trunk)
403
404
405def main():
406try:
407func = action.by_name[TC_ACTION or "decision"].func
408except AttributeError:
409raise Exception("Unsupported action: %s", TC_ACTION or "decision")
410
411func()
412
413merge_coverage = []
414
415if TestTask.coverage and TC_IS_PUSH and TC_BRANCH:
416coverage_mounts = [
417{f"file:cov-{task.id}.zip": task} for task in TestTask.coverage
418]
419task = Build.by_name("linux.coverage")
420coverage_mounts.append(
421{
422"file:gcno-build.zip": {
423"artifact": task.artifacts[1],
424"taskId": task.id,
425}
426}
427)
428
429merge_coverage.extend(
430[
431"grcov -s repo -t lcov -o repo/coverage.lcov gcno-build.zip "
432+ " ".join(f"cov-{task.id}.zip" for task in TestTask.coverage),
433]
434)
435
436if merge_coverage:
437Task(
438task_env=TaskEnvironment.by_name("linux.codecov"),
439description="upload coverage",
440scopes=["secrets:get:project/git-cinnabar/codecov"],
441mounts=coverage_mounts,
442command=list(
443chain(
444Task.checkout(),
445[
446"set +x",
447(
448"export CODECOV_TOKEN=$(curl -sL "
449f"{PROXY_URL}/api/secrets/v1/secret/project/git-cinnabar"
450"/codecov | python3"
451' -c "import json, sys; print(json.load(sys.stdin)'
452'[\\"secret\\"][\\"token\\"])")'
453),
454"set -x",
455],
456merge_coverage,
457[
458"cd repo",
459'codecov -Z --name "taskcluster" -C {} -B {}'.format(
460TC_COMMIT, TC_BRANCH
461),
462],
463)
464),
465)
466
467for t in Task.by_id.values():
468t.submit()
469
470if not TC_ACTION and "TC_GROUP_ID" in os.environ:
471actions = {
472"version": 1,
473"actions": [],
474"variables": {
475"e": dict(TC_DATA, decision_id=""),
476"tasks_for": "action",
477},
478}
479for name, a in action.by_name.items():
480if name != "decision":
481actions["actions"].append(
482{
483"kind": "task",
484"name": a.name,
485"title": a.title,
486"description": a.description,
487"context": [],
488"task": a.task,
489}
490)
491
492with open("actions.json", "w") as out:
493out.write(json.dumps(actions, indent=True))
494
495
496if __name__ == "__main__":
497main()
498