alibuild 1.17.19__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. alibuild-1.17.19.data/scripts/aliBuild +137 -0
  2. alibuild-1.17.19.data/scripts/aliDeps +7 -0
  3. alibuild-1.17.19.data/scripts/aliDoctor +7 -0
  4. alibuild-1.17.19.data/scripts/alienv +344 -0
  5. alibuild-1.17.19.data/scripts/pb +7 -0
  6. alibuild-1.17.19.dist-info/METADATA +78 -0
  7. alibuild-1.17.19.dist-info/RECORD +74 -0
  8. alibuild-1.17.19.dist-info/WHEEL +5 -0
  9. alibuild-1.17.19.dist-info/licenses/LICENSE.md +674 -0
  10. alibuild-1.17.19.dist-info/top_level.txt +5 -0
  11. alibuild_helpers/__init__.py +21 -0
  12. alibuild_helpers/_version.py +21 -0
  13. alibuild_helpers/analytics.py +120 -0
  14. alibuild_helpers/args.py +493 -0
  15. alibuild_helpers/build.py +1209 -0
  16. alibuild_helpers/build_template.sh +314 -0
  17. alibuild_helpers/clean.py +83 -0
  18. alibuild_helpers/cmd.py +154 -0
  19. alibuild_helpers/deps.py +116 -0
  20. alibuild_helpers/doctor.py +195 -0
  21. alibuild_helpers/git.py +104 -0
  22. alibuild_helpers/init.py +103 -0
  23. alibuild_helpers/log.py +132 -0
  24. alibuild_helpers/scm.py +31 -0
  25. alibuild_helpers/sl.py +62 -0
  26. alibuild_helpers/sync.py +693 -0
  27. alibuild_helpers/templating_plugin.py +18 -0
  28. alibuild_helpers/utilities.py +662 -0
  29. alibuild_helpers/workarea.py +179 -0
  30. debian/changelog +11 -0
  31. debian/compat +1 -0
  32. debian/control +14 -0
  33. debian/copyright +10 -0
  34. debian/files +1 -0
  35. debian/rules +7 -0
  36. docs/README.md +1 -0
  37. docs/SUPPORT +3 -0
  38. docs/docs/alice_logo.png +0 -0
  39. docs/docs/deps.png +0 -0
  40. docs/docs/index.md +75 -0
  41. docs/docs/quick.md +89 -0
  42. docs/docs/reference.md +430 -0
  43. docs/docs/stylesheets/extra.css +9 -0
  44. docs/docs/troubleshooting.md +346 -0
  45. docs/docs/user.md +413 -0
  46. docs/mkdocs.yml +37 -0
  47. templates/alibuild_to_please.jnj +63 -0
  48. tests/test_analytics.py +42 -0
  49. tests/test_args.py +119 -0
  50. tests/test_build.py +426 -0
  51. tests/test_clean.py +154 -0
  52. tests/test_cmd.py +73 -0
  53. tests/test_deps.py +79 -0
  54. tests/test_doctor.py +128 -0
  55. tests/test_git.py +48 -0
  56. tests/test_hashing.py +67 -0
  57. tests/test_init.py +103 -0
  58. tests/test_log.py +50 -0
  59. tests/test_packagelist.py +235 -0
  60. tests/test_parseRecipe.py +132 -0
  61. tests/test_sync.py +332 -0
  62. tests/test_utilities.py +383 -0
  63. tests/test_workarea.py +101 -0
  64. tests/testdist/broken1.sh +1 -0
  65. tests/testdist/broken2.sh +1 -0
  66. tests/testdist/broken3.sh +3 -0
  67. tests/testdist/broken4.sh +2 -0
  68. tests/testdist/broken5.sh +2 -0
  69. tests/testdist/broken6.sh +2 -0
  70. tests/testdist/broken7.sh +5 -0
  71. tests/testdist/clobber-initdotsh.sh +4 -0
  72. tests/testdist/defaults-o2.sh +10 -0
  73. tests/testdist/delete-etc.sh +4 -0
  74. tests/testdist/tracking-env.sh +6 -0
tests/test_build.py ADDED
@@ -0,0 +1,426 @@
1
+ from argparse import Namespace
2
+ import os
3
+ import os.path
4
+ import re
5
+ import sys
6
+ import unittest
7
+ # Assuming you are using the mock library to ... mock things
8
+ from unittest.mock import call, patch, MagicMock, DEFAULT
9
+ from io import StringIO
10
+ from collections import OrderedDict
11
+
12
+ from alibuild_helpers.utilities import parseRecipe, resolve_tag
13
+ from alibuild_helpers.build import doBuild, storeHashes, generate_initdotsh
14
+
15
+
16
+ TEST_DEFAULT_RELEASE = """\
17
+ package: defaults-release
18
+ version: v1
19
+ ---
20
+ : this line should trigger a warning
21
+ """
22
+ TEST_DEFAULT_RELEASE_BUILD_HASH = "27ce49698e818e8efb56b6eff6dd785e503df341"
23
+
24
+ TEST_ZLIB_RECIPE = """\
25
+ package: zlib
26
+ version: v1.2.3
27
+ source: https://github.com/madler/zlib
28
+ tag: master
29
+ ---
30
+ ./configure
31
+ make
32
+ make install
33
+ """
34
+ TEST_ZLIB_GIT_REFS = "8822efa61f2a385e0bc83ca5819d608111b2168a\trefs/heads/master"
35
+ TEST_ZLIB_BUILD_HASH = "8cd1f56c450f05ffbba3276bad08eae30f814999"
36
+
37
+ TEST_ROOT_RECIPE = """\
38
+ package: ROOT
39
+ version: v6-08-30
40
+ source: https://github.com/root-mirror/root
41
+ tag: v6-08-00-patches
42
+ requires:
43
+ - zlib
44
+ env:
45
+ ROOT_TEST_1: "root test 1"
46
+ ROOT_TEST_2: "root test 2"
47
+ ROOT_TEST_3: "root test 3"
48
+ ROOT_TEST_4: "root test 4"
49
+ ROOT_TEST_5: "root test 5"
50
+ ROOT_TEST_6: "root test 6"
51
+ prepend_path:
52
+ PREPEND_ROOT_1: "prepend root 1"
53
+ PREPEND_ROOT_2: "prepend root 2"
54
+ PREPEND_ROOT_3: "prepend root 3"
55
+ PREPEND_ROOT_4: "prepend root 4"
56
+ PREPEND_ROOT_5: "prepend root 5"
57
+ PREPEND_ROOT_6: "prepend root 6"
58
+ append_path:
59
+ APPEND_ROOT_1: "append root 1"
60
+ APPEND_ROOT_2: "append root 2"
61
+ APPEND_ROOT_3: "append root 3"
62
+ APPEND_ROOT_4: "append root 4"
63
+ APPEND_ROOT_5: "append root 5"
64
+ APPEND_ROOT_6: "append root 6"
65
+ ---
66
+ ./configure
67
+ make
68
+ make install
69
+ """
70
+ TEST_ROOT_GIT_REFS = """\
71
+ 87b87c4322d2a3fad315c919cb2e2dd73f2154dc\trefs/heads/master
72
+ f7b336611753f1f4aaa94222b0d620748ae230c0\trefs/heads/v6-08-00-patches
73
+ f7b336611753f1f4aaa94222b0d620748ae230c0\trefs/tags/test-tag"""
74
+ TEST_ROOT_BUILD_HASH = ("8ec3f41b6b585ef86a02e9c595eed67f34d63f08")
75
+
76
+
77
+ TEST_EXTRA_RECIPE = """\
78
+ package: Extra
79
+ version: v1
80
+ tag: v1
81
+ source: file:///dev/null
82
+ requires:
83
+ - ROOT
84
+ ---
85
+ """
86
+ TEST_EXTRA_GIT_REFS = """\
87
+ f000\trefs/heads/master
88
+ ba22\trefs/tags/v1
89
+ ba22\trefs/tags/v2
90
+ baad\trefs/tags/v3"""
91
+ TEST_EXTRA_BUILD_HASH = ("5afae57bfc6a374e74c1c4427698ab5edebce0bc")
92
+
93
+
94
+ GIT_CLONE_REF_ZLIB_ARGS = ("clone", "--bare", "https://github.com/madler/zlib",
95
+ "/sw/MIRROR/zlib", "--filter=blob:none"), ".", False
96
+ GIT_CLONE_SRC_ZLIB_ARGS = ("clone", "-n", "https://github.com/madler/zlib",
97
+ "/sw/SOURCES/zlib/v1.2.3/8822efa61f",
98
+ "--dissociate", "--reference", "/sw/MIRROR/zlib", "--filter=blob:none"), ".", False
99
+ GIT_SET_URL_ZLIB_ARGS = ("remote", "set-url", "--push", "origin", "https://github.com/madler/zlib"), \
100
+ "/sw/SOURCES/zlib/v1.2.3/8822efa61f", False
101
+ GIT_CHECKOUT_ZLIB_ARGS = ("checkout", "-f", "master"), \
102
+ "/sw/SOURCES/zlib/v1.2.3/8822efa61f", False
103
+
104
+ GIT_FETCH_REF_ROOT_ARGS = ("fetch", "-f", "--filter=blob:none", "https://github.com/root-mirror/root", "+refs/tags/*:refs/tags/*",
105
+ "+refs/heads/*:refs/heads/*"), "/sw/MIRROR/root", False
106
+ GIT_CLONE_SRC_ROOT_ARGS = ("clone", "-n", "https://github.com/root-mirror/root",
107
+ "/sw/SOURCES/ROOT/v6-08-30/f7b3366117",
108
+ "--dissociate", "--reference", "/sw/MIRROR/root", "--filter=blob:none"), ".", False
109
+ GIT_SET_URL_ROOT_ARGS = ("remote", "set-url", "--push", "origin", "https://github.com/root-mirror/root"), \
110
+ "/sw/SOURCES/ROOT/v6-08-30/f7b3366117", False
111
+ GIT_CHECKOUT_ROOT_ARGS = ("checkout", "-f", "v6-08-00-patches"), \
112
+ "/sw/SOURCES/ROOT/v6-08-30/f7b3366117", False
113
+
114
+
115
+ def dummy_git(args, directory=".", check=True, prompt=True):
116
+ return {
117
+ (("symbolic-ref", "-q", "HEAD"), "/alidist", False): (0, "master"),
118
+ (("rev-parse", "HEAD"), "/alidist", True): "6cec7b7b3769826219dfa85e5daa6de6522229a0",
119
+ (("ls-remote", "--heads", "--tags", "/sw/MIRROR/root"), ".", False): (0, TEST_ROOT_GIT_REFS),
120
+ (("ls-remote", "--heads", "--tags", "/sw/MIRROR/zlib"), ".", False): (0, TEST_ZLIB_GIT_REFS),
121
+ GIT_CLONE_REF_ZLIB_ARGS: (0, ""),
122
+ GIT_CLONE_SRC_ZLIB_ARGS: (0, ""),
123
+ GIT_SET_URL_ZLIB_ARGS: (0, ""),
124
+ GIT_CHECKOUT_ZLIB_ARGS: (0, ""),
125
+ GIT_FETCH_REF_ROOT_ARGS: (0, ""),
126
+ GIT_CLONE_SRC_ROOT_ARGS: (0, ""),
127
+ GIT_SET_URL_ROOT_ARGS: (0, ""),
128
+ GIT_CHECKOUT_ROOT_ARGS: (0, ""),
129
+ }[(tuple(args), directory, check)]
130
+
131
+
132
+ TIMES_ASKED = {}
133
+
134
+
135
+ def dummy_open(x, mode="r", encoding=None, errors=None):
136
+ if x.endswith("/fetch-log.txt") and mode == "w":
137
+ return MagicMock(__enter__=lambda _: StringIO())
138
+ if x.endswith("/alibuild_helpers/build_template.sh"):
139
+ return DEFAULT # actually open the real build_template.sh
140
+ if mode == "r":
141
+ try:
142
+ threshold, result = {
143
+ "/sw/BUILD/%s/defaults-release/.build_succeeded" % TEST_DEFAULT_RELEASE_BUILD_HASH: (0, StringIO("0")),
144
+ "/sw/BUILD/%s/zlib/.build_succeeded" % TEST_ZLIB_BUILD_HASH: (0, StringIO("0")),
145
+ "/sw/BUILD/%s/ROOT/.build_succeeded" % TEST_ROOT_BUILD_HASH: (0, StringIO("0")),
146
+ "/sw/osx_x86-64/defaults-release/v1-1/.build-hash": (1, StringIO(TEST_DEFAULT_RELEASE_BUILD_HASH)),
147
+ "/sw/osx_x86-64/zlib/v1.2.3-local1/.build-hash": (1, StringIO(TEST_ZLIB_BUILD_HASH)),
148
+ "/sw/osx_x86-64/ROOT/v6-08-30-local1/.build-hash": (1, StringIO(TEST_ROOT_BUILD_HASH))
149
+ }[x]
150
+ except KeyError:
151
+ return DEFAULT
152
+ if threshold > TIMES_ASKED.get(x, 0):
153
+ result = None
154
+ TIMES_ASKED[x] = TIMES_ASKED.get(x, 0) + 1
155
+ if not result:
156
+ raise IOError
157
+ return result
158
+ return DEFAULT
159
+
160
+
161
+ def dummy_execute(x, **kwds):
162
+ s = " ".join(x) if isinstance(x, list) else x
163
+ if re.match(".*ln -sfn.*TARS", s):
164
+ return 0
165
+ return {
166
+ "/bin/bash -e -x /sw/SPECS/osx_x86-64/defaults-release/v1-1/build.sh 2>&1": 0,
167
+ '/bin/bash -e -x /sw/SPECS/osx_x86-64/zlib/v1.2.3-local1/build.sh 2>&1': 0,
168
+ '/bin/bash -e -x /sw/SPECS/osx_x86-64/ROOT/v6-08-30-local1/build.sh 2>&1': 0,
169
+ }[s]
170
+
171
+
172
+ def dummy_readlink(x):
173
+ return {
174
+ "/sw/TARS/osx_x86-64/defaults-release/defaults-release-v1-1.osx_x86-64.tar.gz":
175
+ "../../osx_x86-64/store/%s/%s/defaults-release-v1-1.osx_x86-64.tar.gz" %
176
+ (TEST_DEFAULT_RELEASE_BUILD_HASH[:2], TEST_DEFAULT_RELEASE_BUILD_HASH)
177
+ }[x]
178
+
179
+
180
+ def dummy_exists(x):
181
+ if x.endswith("alibuild_helpers/.git"):
182
+ return False
183
+ return {
184
+ "/alidist": True,
185
+ "/alidist/.git": True,
186
+ "/alidist/.sl": False,
187
+ "/sw": True,
188
+ "/sw/SPECS": False,
189
+ "/sw/MIRROR/root": True,
190
+ "/sw/MIRROR/zlib": False,
191
+ }.get(x, DEFAULT)
192
+
193
+
194
+ # A few errors we should handle, together with the expected result
195
+ @patch("alibuild_helpers.git.clone_speedup_options",
196
+ new=MagicMock(return_value=["--filter=blob:none"]))
197
+ @patch("alibuild_helpers.build.BASH", new="/bin/bash")
198
+ class BuildTestCase(unittest.TestCase):
199
+ @patch("alibuild_helpers.analytics", new=MagicMock())
200
+ @patch("requests.Session.get", new=MagicMock())
201
+ @patch("alibuild_helpers.sync.execute", new=dummy_execute)
202
+ @patch("alibuild_helpers.git.git")
203
+ @patch("alibuild_helpers.build.exists", new=MagicMock(side_effect=dummy_exists))
204
+ @patch("os.path.exists", new=MagicMock(side_effect=dummy_exists))
205
+ @patch("alibuild_helpers.build.dieOnError", new=MagicMock())
206
+ @patch("alibuild_helpers.utilities.dieOnError", new=MagicMock())
207
+ @patch("alibuild_helpers.utilities.warning")
208
+ @patch("alibuild_helpers.build.readDefaults",
209
+ new=MagicMock(return_value=(OrderedDict({"package": "defaults-release", "disable": []}), "")))
210
+ @patch("shutil.rmtree", new=MagicMock(return_value=None))
211
+ @patch("os.makedirs", new=MagicMock(return_value=None))
212
+ @patch("alibuild_helpers.build.makedirs", new=MagicMock(return_value=None))
213
+ @patch("alibuild_helpers.build.symlink", new=MagicMock(return_value=None))
214
+ @patch("alibuild_helpers.workarea.symlink", new=MagicMock(return_value=None))
215
+ @patch("alibuild_helpers.utilities.open", new=lambda x: {
216
+ "/alidist/root.sh": StringIO(TEST_ROOT_RECIPE),
217
+ "/alidist/zlib.sh": StringIO(TEST_ZLIB_RECIPE),
218
+ "/alidist/defaults-release.sh": StringIO(TEST_DEFAULT_RELEASE)
219
+ }[x])
220
+ @patch("alibuild_helpers.sync.open", new=MagicMock(side_effect=dummy_open))
221
+ @patch("alibuild_helpers.build.open", new=MagicMock(side_effect=dummy_open))
222
+ @patch("codecs.open", new=MagicMock(side_effect=dummy_open))
223
+ @patch("alibuild_helpers.build.shutil", new=MagicMock())
224
+ @patch("os.listdir")
225
+ @patch("alibuild_helpers.build.glob", new=lambda pattern: {
226
+ "*": ["zlib"],
227
+ "/sw/TARS/osx_x86-64/store/%s/%s/*gz" % (TEST_DEFAULT_RELEASE_BUILD_HASH[:2],
228
+ TEST_DEFAULT_RELEASE_BUILD_HASH): [],
229
+ "/sw/TARS/osx_x86-64/store/%s/%s/*gz" % (TEST_ZLIB_BUILD_HASH[:2], TEST_ZLIB_BUILD_HASH): [],
230
+ "/sw/TARS/osx_x86-64/store/%s/%s/*gz" % (TEST_ROOT_BUILD_HASH[:2], TEST_ROOT_BUILD_HASH): [],
231
+ "/sw/TARS/osx_x86-64/defaults-release/defaults-release-v1-1.osx_x86-64.tar.gz":
232
+ ["../../osx_x86-64/store/%s/%s/defaults-release-v1-1.osx_x86-64.tar.gz" %
233
+ (TEST_DEFAULT_RELEASE_BUILD_HASH[:2], TEST_DEFAULT_RELEASE_BUILD_HASH)],
234
+ }[pattern])
235
+ @patch("alibuild_helpers.build.readlink", new=dummy_readlink)
236
+ @patch("alibuild_helpers.build.banner", new=MagicMock(return_value=None))
237
+ @patch("alibuild_helpers.build.debug")
238
+ @patch("alibuild_helpers.workarea.is_writeable", new=MagicMock(return_value=True))
239
+ @patch("alibuild_helpers.build.basename", new=MagicMock(return_value="aliBuild"))
240
+ @patch("alibuild_helpers.build.install_wrapper_script", new=MagicMock())
241
+ def test_coverDoBuild(self, mock_debug, mock_listdir, mock_warning, mock_git_git) -> None:
242
+ mock_git_git.side_effect = dummy_git
243
+ mock_debug.side_effect = lambda *args: None
244
+ mock_warning.side_effect = lambda *args: None
245
+ mock_listdir.side_effect = lambda directory: {
246
+ "/sw/TARS/osx_x86-64/defaults-release": ["defaults-release-v1-1.osx_x86-64.tar.gz"],
247
+ "/sw/TARS/osx_x86-64/zlib": [],
248
+ "/sw/TARS/osx_x86-64/ROOT": [],
249
+ }.get(directory, DEFAULT)
250
+ os.environ["ALIBUILD_NO_ANALYTICS"] = "1"
251
+
252
+ mock_parser = MagicMock()
253
+ args = Namespace(
254
+ remoteStore="",
255
+ writeStore="",
256
+ referenceSources="/sw/MIRROR",
257
+ docker=False,
258
+ dockerImage=None,
259
+ docker_extra_args=["--network=host"],
260
+ architecture="osx_x86-64",
261
+ workDir="/sw",
262
+ pkgname=["root"],
263
+ configDir="/alidist",
264
+ disable=[],
265
+ force_rebuild=[],
266
+ defaults="release",
267
+ jobs=2,
268
+ annotate={},
269
+ preferSystem=[],
270
+ noSystem=None,
271
+ debug=True,
272
+ dryRun=False,
273
+ aggressiveCleanup=False,
274
+ environment={},
275
+ autoCleanup=False,
276
+ noDevel=[],
277
+ onlyDeps=False,
278
+ fetchRepos=False,
279
+ forceTracked=False,
280
+ plugin="legacy"
281
+ )
282
+
283
+ def mkcall(args):
284
+ cmd, directory, check = args
285
+ return call(list(cmd), directory=directory, check=check, prompt=False)
286
+
287
+ common_calls = [
288
+ call(("rev-parse", "HEAD"), args.configDir),
289
+ mkcall(GIT_CLONE_REF_ZLIB_ARGS),
290
+ call(["ls-remote", "--heads", "--tags", args.referenceSources + "/zlib"],
291
+ directory=".", check=False, prompt=False),
292
+ call(["ls-remote", "--heads", "--tags", args.referenceSources + "/root"],
293
+ directory=".", check=False, prompt=False),
294
+ ]
295
+
296
+ mock_git_git.reset_mock()
297
+ mock_debug.reset_mock()
298
+ mock_warning.reset_mock()
299
+ doBuild(args, mock_parser)
300
+ mock_warning.assert_called_with("%s.sh contains a recipe, which will be ignored", "defaults-release")
301
+ mock_debug.assert_called_with("Everything done")
302
+ # After this run, .build-hash files will be simulated to exist
303
+ # already, so sw/SOURCES repos must only be checked out on this run.
304
+ mock_git_git.assert_has_calls(common_calls + [
305
+ mkcall(GIT_CLONE_SRC_ZLIB_ARGS),
306
+ mkcall(GIT_SET_URL_ZLIB_ARGS),
307
+ mkcall(GIT_CHECKOUT_ZLIB_ARGS),
308
+ mkcall(GIT_CLONE_SRC_ROOT_ARGS),
309
+ mkcall(GIT_SET_URL_ROOT_ARGS),
310
+ mkcall(GIT_CHECKOUT_ROOT_ARGS),
311
+ ], any_order=True)
312
+ self.assertEqual(mock_git_git.call_count, len(common_calls) + 6)
313
+
314
+ # Force fetching repos
315
+ mock_git_git.reset_mock()
316
+ mock_debug.reset_mock()
317
+ mock_warning.reset_mock()
318
+ args.fetchRepos = True
319
+ doBuild(args, mock_parser)
320
+ mock_warning.assert_called_with("%s.sh contains a recipe, which will be ignored", "defaults-release")
321
+ mock_debug.assert_called_with("Everything done")
322
+ mock_listdir.assert_called_with("/sw/TARS/osx_x86-64/ROOT")
323
+ # We can't compare directly against the list of calls here as they
324
+ # might happen in any order.
325
+ mock_git_git.assert_has_calls(common_calls + [
326
+ mkcall(GIT_FETCH_REF_ROOT_ARGS),
327
+ ], any_order=True)
328
+ self.assertEqual(mock_git_git.call_count, len(common_calls) + 1)
329
+
330
+ def setup_spec(self, script):
331
+ """Parse the alidist recipe in SCRIPT and return its spec."""
332
+ err, spec, recipe = parseRecipe(lambda: script)
333
+ self.assertIsNone(err)
334
+ spec["recipe"] = "" if spec["package"].startswith("defaults-") else recipe.strip("\n")
335
+ spec.setdefault("tag", spec["version"])
336
+ spec["tag"] = resolve_tag(spec)
337
+ return spec
338
+
339
+ def test_hashing(self) -> None:
340
+ """Check that the hashes assigned to packages remain constant."""
341
+ default = self.setup_spec(TEST_DEFAULT_RELEASE)
342
+ zlib = self.setup_spec(TEST_ZLIB_RECIPE)
343
+ root = self.setup_spec(TEST_ROOT_RECIPE)
344
+ extra = self.setup_spec(TEST_EXTRA_RECIPE)
345
+ default["commit_hash"] = "0"
346
+ for spec, refs in ((zlib, TEST_ZLIB_GIT_REFS),
347
+ (root, TEST_ROOT_GIT_REFS),
348
+ (extra, TEST_EXTRA_GIT_REFS)):
349
+ spec.setdefault("requires", []).append(default["package"])
350
+ spec["scm_refs"] = {ref: hash for hash, _, ref in (
351
+ line.partition("\t") for line in refs.splitlines()
352
+ )}
353
+ try:
354
+ spec["commit_hash"] = spec["scm_refs"]["refs/tags/" + spec["tag"]]
355
+ except KeyError:
356
+ spec["commit_hash"] = spec["scm_refs"]["refs/heads/" + spec["tag"]]
357
+ specs = {pkg["package"]: pkg for pkg in (default, zlib, root, extra)}
358
+ for spec in specs.values():
359
+ spec["is_devel_pkg"] = False
360
+
361
+ storeHashes("defaults-release", specs, considerRelocation=False)
362
+ default["hash"] = default["remote_revision_hash"]
363
+ self.assertEqual(default["hash"], TEST_DEFAULT_RELEASE_BUILD_HASH)
364
+ self.assertEqual(default["remote_hashes"], [TEST_DEFAULT_RELEASE_BUILD_HASH])
365
+
366
+ storeHashes("zlib", specs, considerRelocation=False)
367
+ zlib["hash"] = zlib["local_revision_hash"]
368
+ self.assertEqual(zlib["hash"], TEST_ZLIB_BUILD_HASH)
369
+ self.assertEqual(zlib["local_hashes"], [TEST_ZLIB_BUILD_HASH])
370
+
371
+ storeHashes("ROOT", specs, considerRelocation=False)
372
+ root["hash"] = root["local_revision_hash"]
373
+ self.assertEqual(root["hash"], TEST_ROOT_BUILD_HASH)
374
+ # Equivalent "commit hashes": "f7b336611753f1f4aaa94222b0d620748ae230c0"
375
+ # (head of v6-08-00-patches and commit of test-tag), and "test-tag".
376
+ self.assertEqual(len(root["local_hashes"]), 2)
377
+ self.assertEqual(root["local_hashes"][0], TEST_ROOT_BUILD_HASH)
378
+
379
+ storeHashes("Extra", specs, considerRelocation=False)
380
+ extra["hash"] = extra["local_revision_hash"]
381
+ self.assertEqual(extra["hash"], TEST_EXTRA_BUILD_HASH)
382
+ # Equivalent "commit hashes": "v1", "v2", "ba22".
383
+ self.assertEqual(len(extra["local_hashes"]), 3)
384
+ self.assertEqual(len(extra["remote_hashes"]), 3)
385
+ self.assertEqual(extra["local_hashes"][0], TEST_EXTRA_BUILD_HASH)
386
+
387
+ def test_initdotsh(self) -> None:
388
+ """Sanity-check the generated init.sh for a few variables."""
389
+ specs = {
390
+ # Add some attributes that are normally set by doBuild(), but
391
+ # required by generate_initdotsh().
392
+ spec["package"]: dict(spec, revision="1", commit_hash="424242", hash="010101")
393
+ for spec in map(self.setup_spec, (
394
+ TEST_DEFAULT_RELEASE,
395
+ TEST_ZLIB_RECIPE,
396
+ TEST_ROOT_RECIPE,
397
+ TEST_EXTRA_RECIPE,
398
+ ))
399
+ }
400
+
401
+ setup_initdotsh = generate_initdotsh("ROOT", specs, "slc7_x86-64", post_build=False)
402
+ complete_initdotsh = generate_initdotsh("ROOT", specs, "slc7_x86-64", post_build=True)
403
+
404
+ # We only generate init.sh for ROOT, so Extra should not appear at all.
405
+ self.assertNotIn("Extra", setup_initdotsh)
406
+ self.assertNotIn("Extra", complete_initdotsh)
407
+
408
+ # Dependencies must be loaded both for this build and for subsequent ones.
409
+ self.assertIn('. "$WORK_DIR/$ALIBUILD_ARCH_PREFIX"/zlib/v1.2.3-1/etc/profile.d/init.sh', setup_initdotsh)
410
+ self.assertIn('. "$WORK_DIR/$ALIBUILD_ARCH_PREFIX"/zlib/v1.2.3-1/etc/profile.d/init.sh', complete_initdotsh)
411
+
412
+ # ROOT-specific variables must not be set during ROOT's build yet...
413
+ self.assertNotIn("export ROOT_VERSION=", setup_initdotsh)
414
+ self.assertNotIn("export ROOT_TEST_1=", setup_initdotsh)
415
+ self.assertNotIn("export APPEND_ROOT_1=", setup_initdotsh)
416
+ self.assertNotIn("export PREPEND_ROOT_1=", setup_initdotsh)
417
+
418
+ # ...but they must be set once ROOT's build has completed.
419
+ self.assertIn("export ROOT_VERSION=v6-08-30", complete_initdotsh)
420
+ self.assertIn('export ROOT_TEST_1="root test 1"', complete_initdotsh)
421
+ self.assertIn("export APPEND_ROOT_1=", complete_initdotsh)
422
+ self.assertIn("export PREPEND_ROOT_1=", complete_initdotsh)
423
+
424
+
425
+ if __name__ == '__main__':
426
+ unittest.main()
tests/test_clean.py ADDED
@@ -0,0 +1,154 @@
1
+ # Assuming you are using the mock library to ... mock things
2
+ from unittest.mock import patch, call
3
+
4
+ from alibuild_helpers.clean import decideClean, doClean
5
+
6
+ import unittest
7
+
8
+ REALPATH_WITH_OBSOLETE_FILES = {
9
+ "sw/BUILD/a-latest": "/sw/BUILD/f339115741c6ab9cf291d3210f44bee795c56e16",
10
+ "sw/BUILD/b-latest": "/sw/BUILD/fcdfc2e1c9f0433c60b3b000e0e2737d297a9b1c",
11
+ "sw/BUILD/f339115741c6ab9cf291d3210f44bee795c56e16": "/sw/BUILD/f339115741c6ab9cf291d3210f44bee795c56e16",
12
+ "sw/BUILD/fcdfc2e1c9f0433c60b3b000e0e2737d297a9b1c": "/sw/BUILD/fcdfc2e1c9f0433c60b3b000e0e2737d297a9b1c",
13
+ "sw/BUILD/somethingtodelete": "/sw/BUILD/somethingtodelete",
14
+ "sw/osx_x86-64/b/latest": "/sw/osx_x86-64/b/v2",
15
+ "sw/osx_x86-64/a/latest": "/sw/osx_x86-64/a/v1",
16
+ "sw/osx_x86-64/b/latest-root6": "/sw/osx_x86-64/b/v4",
17
+ "sw/osx_x86-64/b/latest-release": "/sw/osx_x86-64/b/v2",
18
+ "sw/osx_x86-64/a/latest-release": "/sw/osx_x86-64/a/v1",
19
+ "sw/osx_x86-64/b/v4": "/sw/osx_x86-64/b/v4",
20
+ "sw/osx_x86-64/b/v3": "/sw/osx_x86-64/b/v3",
21
+ "sw/osx_x86-64/b/v2": "/sw/osx_x86-64/b/v2",
22
+ "sw/osx_x86-64/b/v1": "/sw/osx_x86-64/b/v1",
23
+ "sw/osx_x86-64/a/v1": "/sw/osx_x86-64/a/v1"
24
+ }
25
+
26
+ GLOB_WITH_OBSOLETE_FILES = {
27
+ "sw/BUILD/*-latest*": ["sw/BUILD/a-latest", "sw/BUILD/b-latest"],
28
+ "sw/BUILD/*": ["sw/BUILD/a-latest",
29
+ "sw/BUILD/b-latest",
30
+ "sw/BUILD/f339115741c6ab9cf291d3210f44bee795c56e16",
31
+ "sw/BUILD/fcdfc2e1c9f0433c60b3b000e0e2737d297a9b1c",
32
+ "sw/BUILD/somethingtodelete"],
33
+ "sw/osx_x86-64/*/": ["sw/osx_x86-64/a/", "sw/osx_x86-64/b/"],
34
+ "sw/osx_x86-64/b/latest*": ["sw/osx_x86-64/b/latest",
35
+ "sw/osx_x86-64/b/latest-release",
36
+ "sw/osx_x86-64/b/latest-root6"],
37
+ "sw/osx_x86-64/a/latest*": ["sw/osx_x86-64/a/latest", "sw/osx_x86-64/a/latest-release"],
38
+ "sw/osx_x86-64/*/*": ["sw/osx_x86-64/a/latest", "sw/osx_x86-64/a/v1",
39
+ "sw/osx_x86-64/b/latest", "sw/osx_x86-64/b/v1",
40
+ "sw/osx_x86-64/b/v2", "sw/osx_x86-64/b/v3",
41
+ "sw/osx_x86-64/b/v4"],
42
+ "sw/slc7_x86-64/*/": [],
43
+ "sw/slc7_x86-64/*/*": []
44
+ }
45
+
46
+ READLINK_MOCKUP_DB = {
47
+ "sw/BUILD/a-latest": "f339115741c6ab9cf291d3210f44bee795c56e16",
48
+ "sw/BUILD/b-latest": "fcdfc2e1c9f0433c60b3b000e0e2737d297a9b1c"
49
+ }
50
+
51
+
52
+ class CleanTestCase(unittest.TestCase):
53
+ @patch('alibuild_helpers.clean.glob')
54
+ @patch('alibuild_helpers.clean.os')
55
+ @patch('alibuild_helpers.clean.path')
56
+ def test_decideClean(self, mock_path, mock_os, mock_glob):
57
+ mock_path.realpath.side_effect = lambda x : REALPATH_WITH_OBSOLETE_FILES[x]
58
+ mock_path.islink.side_effect = lambda x : "latest" in x
59
+ mock_glob.glob.side_effect = lambda x : GLOB_WITH_OBSOLETE_FILES[x]
60
+ mock_os.readlink.side_effect = lambda x : READLINK_MOCKUP_DB[x]
61
+ toDelete = decideClean(workDir="sw", architecture="osx_x86-64", aggressiveCleanup=False)
62
+ mock_os.readlink.assert_called_with("sw/BUILD/b-latest")
63
+ mock_path.islink.assert_called_with("sw/osx_x86-64/b/v4")
64
+ mock_path.exists.assert_called_with("sw/osx_x86-64/b/v3")
65
+ self.assertEqual(toDelete, ['sw/TMP', 'sw/INSTALLROOT', 'sw/BUILD/somethingtodelete',
66
+ 'sw/osx_x86-64/b/v1', 'sw/osx_x86-64/b/v3'])
67
+ toDelete = decideClean(workDir="sw", architecture="osx_x86-64", aggressiveCleanup=True)
68
+ self.assertEqual(toDelete, ['sw/TMP', 'sw/INSTALLROOT', 'sw/TARS/osx_x86-64/store',
69
+ 'sw/SOURCES', 'sw/BUILD/somethingtodelete',
70
+ 'sw/osx_x86-64/b/v1', 'sw/osx_x86-64/b/v3'])
71
+ toDelete = decideClean(workDir="sw", architecture="slc7_x86-64", aggressiveCleanup=True)
72
+ self.assertEqual(toDelete, ['sw/TMP', 'sw/INSTALLROOT', 'sw/TARS/slc7_x86-64/store',
73
+ 'sw/SOURCES', 'sw/BUILD/somethingtodelete'])
74
+
75
+ @patch('alibuild_helpers.clean.glob')
76
+ @patch('alibuild_helpers.clean.os')
77
+ @patch('alibuild_helpers.clean.path')
78
+ @patch('alibuild_helpers.clean.shutil')
79
+ @patch('alibuild_helpers.clean.log')
80
+ def test_doClean(self, mock_log, mock_shutil, mock_path, mock_os, mock_glob):
81
+ mock_path.realpath.side_effect = lambda x: REALPATH_WITH_OBSOLETE_FILES[x]
82
+ mock_path.islink.side_effect = lambda x: "latest" in x
83
+ mock_os.readlink.side_effect = lambda x: READLINK_MOCKUP_DB[x]
84
+
85
+ files_to_delete = [
86
+ "sw/TMP",
87
+ "sw/INSTALLROOT",
88
+ "sw/TARS/osx_x86-64/store",
89
+ "sw/SOURCES",
90
+ "sw/BUILD/somethingtodelete",
91
+ "sw/osx_x86-64/b/v1",
92
+ "sw/osx_x86-64/b/v3",
93
+ ]
94
+ remove_files_calls = list(map(call, files_to_delete))
95
+ files_delete_formatarg = "\n".join(files_to_delete)
96
+
97
+ mock_glob.glob.side_effect = lambda x: []
98
+ # To get rid of default entries like sw/TMP, sw/INSTALLROOT.
99
+ mock_path.exists.return_value = False
100
+
101
+ with self.assertRaises(SystemExit) as cm:
102
+ doClean(workDir="sw", architecture="osx_x86-64", aggressiveCleanup=True, dryRun=True)
103
+ self.assertEqual(cm.exception.code, 0)
104
+ mock_shutil.rmtree.assert_not_called()
105
+ mock_log.info.assert_called_with("Nothing to delete.")
106
+ mock_log.banner.assert_not_called()
107
+
108
+ mock_log.banner.reset_mock()
109
+ mock_log.info.reset_mock()
110
+
111
+ mock_glob.glob.side_effect = lambda x: GLOB_WITH_OBSOLETE_FILES[x]
112
+ mock_path.exists.return_value = True
113
+
114
+ with self.assertRaises(SystemExit) as cm:
115
+ doClean(workDir="sw", architecture="osx_x86-64", aggressiveCleanup=True, dryRun=True)
116
+ self.assertEqual(cm.exception.code, 0)
117
+ mock_shutil.rmtree.assert_not_called()
118
+ mock_log.banner.assert_called_with("This %s delete the following directories:\n%s",
119
+ "would", files_delete_formatarg)
120
+ mock_log.info.assert_called_with("--dry-run / -n specified. Doing nothing.")
121
+
122
+ mock_log.banner.reset_mock()
123
+ mock_log.info.reset_mock()
124
+
125
+ with self.assertRaises(SystemExit) as cm:
126
+ doClean(workDir="sw", architecture="osx_x86-64", aggressiveCleanup=True, dryRun=False)
127
+ self.assertEqual(cm.exception.code, 0)
128
+ self.assertEqual(mock_shutil.rmtree.mock_calls, remove_files_calls)
129
+ mock_log.banner.assert_called_with("This %s delete the following directories:\n%s",
130
+ "will", files_delete_formatarg)
131
+ mock_log.info.assert_not_called()
132
+
133
+ mock_log.banner.reset_mock()
134
+ mock_log.info.reset_mock()
135
+ mock_shutil.rmtree.reset_mock()
136
+
137
+ def failing_rmtree(directory):
138
+ raise OSError("sentinel exception")
139
+
140
+ mock_shutil.rmtree.side_effect = failing_rmtree
141
+
142
+ with self.assertRaises(SystemExit) as cm:
143
+ doClean(workDir="sw", architecture="osx_x86-64", aggressiveCleanup=True, dryRun=False)
144
+ self.assertEqual(cm.exception.code, 1)
145
+ # Make sure the function still attempts to delete all of the
146
+ # directories, even if some fail.
147
+ self.assertEqual(mock_shutil.rmtree.mock_calls, remove_files_calls)
148
+ mock_log.banner.assert_called_with("This %s delete the following directories:\n%s",
149
+ "will", files_delete_formatarg)
150
+ mock_log.info.assert_not_called()
151
+
152
+
153
+ if __name__ == '__main__':
154
+ unittest.main()
tests/test_cmd.py ADDED
@@ -0,0 +1,73 @@
1
+ # Assuming you are using the mock library to ... mock things
2
+ from unittest import mock
3
+
4
+ from alibuild_helpers.cmd import execute, DockerRunner
5
+
6
+ import unittest
7
+
8
+
9
+ @mock.patch("alibuild_helpers.cmd.BASH", new="/bin/bash")
10
+ class CmdTestCase(unittest.TestCase):
11
+ @mock.patch("alibuild_helpers.cmd.debug")
12
+ def test_execute(self, mock_debug):
13
+ err = execute("echo foo", mock_debug)
14
+ self.assertEqual(err, 0)
15
+ self.assertEqual(mock_debug.mock_calls, [mock.call("%s", "foo")])
16
+ mock_debug.reset_mock()
17
+ err = execute("echoo 2> /dev/null", mock_debug)
18
+ self.assertEqual(err, 127)
19
+ self.assertEqual(mock_debug.mock_calls, [])
20
+
21
+ @mock.patch("alibuild_helpers.cmd.getoutput")
22
+ @mock.patch("alibuild_helpers.cmd.getstatusoutput")
23
+ def test_DockerRunner(self, mock_getstatusoutput, mock_getoutput):
24
+ mock_getoutput.side_effect = lambda cmd: "container-id\n"
25
+ with DockerRunner("image", ["extra arg"]) as getstatusoutput_docker:
26
+ mock_getoutput.assert_called_with(["docker", "run", "--detach", "--rm", "--entrypoint=",
27
+ "extra arg", "image", "sleep", "inf"])
28
+ getstatusoutput_docker("echo foo")
29
+ mock_getstatusoutput.assert_called_with(["docker", "container", "exec", "container-id", "bash", "-c", "echo foo"], cwd=None)
30
+ mock_getstatusoutput.assert_called_with("docker container kill container-id")
31
+
32
+ mock_getoutput.reset_mock()
33
+ mock_getstatusoutput.reset_mock()
34
+ with DockerRunner("") as getstatusoutput_docker:
35
+ mock_getoutput.assert_not_called()
36
+ getstatusoutput_docker("echo foo")
37
+ mock_getstatusoutput.assert_called_with("/bin/bash -c 'echo foo'", cwd=None)
38
+ mock_getstatusoutput.reset_mock()
39
+ mock_getstatusoutput.assert_not_called()
40
+
41
+ @mock.patch("alibuild_helpers.cmd.getoutput")
42
+ @mock.patch("alibuild_helpers.cmd.getstatusoutput")
43
+ def test_DockerRunner_with_env_vars(self, mock_getstatusoutput, mock_getoutput):
44
+ # Test that environment variables are properly injected into docker exec commands.
45
+ mock_getoutput.side_effect = lambda cmd: "container-id\n"
46
+
47
+ # Test with environment variables
48
+ extra_env = {"TEST_VAR": "test_value", "ANOTHER_VAR": "another_value"}
49
+ with DockerRunner("image", extra_env=extra_env) as getstatusoutput_docker:
50
+ # Verify container creation includes environment variables
51
+ mock_getoutput.assert_called_with(["docker", "run", "--detach",
52
+ "-e", "TEST_VAR=test_value",
53
+ "-e", "ANOTHER_VAR=another_value",
54
+ "--rm", "--entrypoint=", "image", "sleep", "inf"])
55
+
56
+ # Test that exec command includes environment variables
57
+ getstatusoutput_docker("echo test")
58
+ mock_getstatusoutput.assert_called_with(["docker", "container", "exec",
59
+ "-e", "TEST_VAR=test_value",
60
+ "-e", "ANOTHER_VAR=another_value",
61
+ "container-id", "bash", "-c", "echo test"], cwd=None)
62
+
63
+ # Test host execution with environment variables
64
+ mock_getoutput.reset_mock()
65
+ mock_getstatusoutput.reset_mock()
66
+ with DockerRunner("", extra_env=extra_env) as getstatusoutput_docker:
67
+ mock_getoutput.assert_not_called()
68
+ getstatusoutput_docker("echo test")
69
+ mock_getstatusoutput.assert_called_with("env TEST_VAR=test_value ANOTHER_VAR=another_value /bin/bash -c 'echo test'", cwd=None)
70
+
71
+
72
+ if __name__ == '__main__':
73
+ unittest.main()