← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad-buildd:lpcraft-common-output-directory into launchpad-buildd:master

 

Colin Watson has proposed merging ~cjwatson/launchpad-buildd:lpcraft-common-output-directory into launchpad-buildd:master.

Commit message:
Use a common output directory for all lpcraft jobs

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad-buildd/+git/launchpad-buildd/+merge/427734

In order to implement the `input` keyword in lpcraft, launchpad-buildd needs to use the same top-level output directory for each job so that lpcraft can find artifacts from previously-executed jobs.  This is problematic with the current arrangements, because launchpad-buildd has to pass different `--output-directory` options for each individual job so that it can accurately determine which output artifacts belong to which job.

However, with https://code.launchpad.net/~cjwatson/lpcraft/+git/lpcraft/+merge/427724, it becomes possible for launchpad-buildd to identify artifacts by job name and index within a single common output directory, so we can take advantage of that by looking for output files more precisely.

This also gathers the `properties` file (currently unused) in a slightly different way, preparing for Launchpad to extract those output properties and store them in the database in a way that can conveniently be used downstream.

This mustn't be merged until https://code.launchpad.net/~cjwatson/lpcraft/+git/lpcraft/+merge/427724 has been released to the `stable` channel.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad-buildd:lpcraft-common-output-directory into launchpad-buildd:master.
diff --git a/debian/changelog b/debian/changelog
index 810b64d..679201c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+launchpad-buildd (218) UNRELEASED; urgency=medium
+
+  * Use a common output directory for all lpcraft jobs.
+
+ -- Colin Watson <cjwatson@xxxxxxxxxx>  Tue, 02 Aug 2022 15:55:19 +0100
+
 launchpad-buildd (217) focal; urgency=medium
 
   [ Colin Watson ]
diff --git a/lpbuildd/ci.py b/lpbuildd/ci.py
index ac6c585..fcfd8d4 100644
--- a/lpbuildd/ci.py
+++ b/lpbuildd/ci.py
@@ -241,16 +241,20 @@ class CIBuildManager(BuildManagerProxyMixin, DebianBuildManager):
         This is called once for each CI job in the pipeline.
         """
         job_status = {}
-        output_path = os.path.join("/build", "output", self.current_job_id)
-        log_path = "%s.log" % output_path
-        if self.backend.path_exists(log_path):
-            log_name = "%s.log" % self.current_job_id
-            self.addWaitingFileFromBackend(log_path, log_name)
-            job_status["log"] = self._builder.waitingfiles[log_name]
-        if self.backend.path_exists(output_path):
+        job_name, job_index = self.current_job
+        job_output_path = os.path.join(
+            "/build", "output", job_name, str(job_index))
+        for item_name in ("log", "properties"):
+            item_path = os.path.join(job_output_path, item_name)
+            if self.backend.path_exists(item_path):
+                item_id = f"{self.current_job_id}.{item_name}"
+                self.addWaitingFileFromBackend(item_path, name=item_id)
+                job_status[item_name] = self._builder.waitingfiles[item_id]
+        files_path = os.path.join(job_output_path, "files")
+        if self.backend.path_exists(files_path):
             for entry in sorted(self.backend.find(
-                    output_path, include_directories=False)):
-                path = os.path.join(output_path, entry)
+                    files_path, include_directories=False)):
+                path = os.path.join(files_path, entry)
                 if self.backend.islink(path):
                     continue
                 entry_base = os.path.basename(entry)
diff --git a/lpbuildd/target/run_ci.py b/lpbuildd/target/run_ci.py
index 75deaf9..7a8fa3b 100644
--- a/lpbuildd/target/run_ci.py
+++ b/lpbuildd/target/run_ci.py
@@ -127,8 +127,11 @@ class RunCI(BuilderProxyOperationMixin, Operation):
         env = self.build_proxy_environment(proxy_url=self.args.proxy_url)
         job_id = f"{self.args.job_name}:{self.args.job_index}"
         logger.info("Running %s" % job_id)
-        output_path = os.path.join("/build", "output", job_id)
-        self.backend.run(["mkdir", "-p", output_path])
+        output_path = os.path.join("/build", "output")
+        # This matches the per-job output path used by lpcraft.
+        job_output_path = os.path.join(
+            output_path, self.args.job_name, str(self.args.job_index))
+        self.backend.run(["mkdir", "-p", job_output_path])
         lpcraft_args = [
             "lpcraft",
             "-v",
@@ -161,7 +164,7 @@ class RunCI(BuilderProxyOperationMixin, Operation):
 
         escaped_lpcraft_args = (
             " ".join(shell_escape(arg) for arg in lpcraft_args))
-        tee_args = ["tee", "%s.log" % output_path]
+        tee_args = ["tee", os.path.join(job_output_path, "log")]
         escaped_tee_args = " ".join(shell_escape(arg) for arg in tee_args)
         args = [
             "/bin/bash", "-o", "pipefail", "-c",
diff --git a/lpbuildd/target/tests/test_run_ci.py b/lpbuildd/target/tests/test_run_ci.py
index 2f18bd5..ed6e0a2 100644
--- a/lpbuildd/target/tests/test_run_ci.py
+++ b/lpbuildd/target/tests/test_run_ci.py
@@ -316,11 +316,11 @@ class TestRunCI(TestCase):
         run_ci = parse_args(args=args).operation
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
-            RanCommand(["mkdir", "-p", "/build/output/test:0"]),
+            RanCommand(["mkdir", "-p", "/build/output/test/0"]),
             RanBuildCommand([
                 "/bin/bash", "-o", "pipefail", "-c",
-                "lpcraft -v run-one --output-directory /build/output/test:0 test 0 2>&1 "  # noqa: E501
-                "| tee /build/output/test:0.log",
+                "lpcraft -v run-one --output-directory /build/output test 0 2>&1 "  # noqa: E501
+                "| tee /build/output/test/0/log",
                 ], cwd="/build/tree"),
             ]))
 
@@ -340,11 +340,11 @@ class TestRunCI(TestCase):
             "SNAPPY_STORE_NO_CDN": "1",
             }
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
-            RanCommand(["mkdir", "-p", "/build/output/test:0"]),
+            RanCommand(["mkdir", "-p", "/build/output/test/0"]),
             RanBuildCommand([
                 "/bin/bash", "-o", "pipefail", "-c",
-                "lpcraft -v run-one --output-directory /build/output/test:0 test 0 2>&1 "  # noqa: E501
-                "| tee /build/output/test:0.log",
+                "lpcraft -v run-one --output-directory /build/output test 0 2>&1 "  # noqa: E501
+                "| tee /build/output/test/0/log",
                 ], cwd="/build/tree", **env),
             ]))
 
@@ -359,15 +359,15 @@ class TestRunCI(TestCase):
         run_ci = parse_args(args=args).operation
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
-            RanCommand(["mkdir", "-p", "/build/output/test:0"]),
+            RanCommand(["mkdir", "-p", "/build/output/test/0"]),
             RanBuildCommand([
                 "/bin/bash", "-o", "pipefail", "-c",
-                "lpcraft -v run-one --output-directory /build/output/test:0 "
+                "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
                 "--set-env PIP_INDEX_URL=http://example "
                 "--set-env SOME_PATH=/etc/some_path "
                 "2>&1 "
-                "| tee /build/output/test:0.log",
+                "| tee /build/output/test/0/log",
                 ], cwd="/build/tree"),
             ]))
 
@@ -384,15 +384,15 @@ class TestRunCI(TestCase):
         run_ci = parse_args(args=args).operation
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
-            RanCommand(["mkdir", "-p", "/build/output/test:0"]),
+            RanCommand(["mkdir", "-p", "/build/output/test/0"]),
             RanBuildCommand([
                 "/bin/bash", "-o", "pipefail", "-c",
-                "lpcraft -v run-one --output-directory /build/output/test:0 "
+                "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
                 "--apt-replace-repositories 'deb http://archive.ubuntu.com/ubuntu/ focal main restricted' "  # noqa: E501
                 "--apt-replace-repositories 'deb http://archive.ubuntu.com/ubuntu/ focal universe' "  # noqa: E501
                 "2>&1 "
-                "| tee /build/output/test:0.log",
+                "| tee /build/output/test/0/log",
                 ], cwd="/build/tree"),
             ]))
 
@@ -407,15 +407,15 @@ class TestRunCI(TestCase):
         run_ci = parse_args(args=args).operation
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
-            RanCommand(["mkdir", "-p", "/build/output/test:0"]),
+            RanCommand(["mkdir", "-p", "/build/output/test/0"]),
             RanBuildCommand([
                 "/bin/bash", "-o", "pipefail", "-c",
-                "lpcraft -v run-one --output-directory /build/output/test:0 "
+                "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
                 "--plugin-setting "
                 "miniconda_conda_channel=https://user:pass@xxxxxxxxxxxxxxxxxxxxx/artifactory/soss-conda-stable-local/ "  # noqa: E501
                 "2>&1 "
-                "| tee /build/output/test:0.log",
+                "| tee /build/output/test/0/log",
                 ], cwd="/build/tree"),
             ]))
 
@@ -429,14 +429,14 @@ class TestRunCI(TestCase):
         run_ci = parse_args(args=args).operation
         run_ci.run_job()
         self.assertThat(run_ci.backend.run.calls, MatchesListwise([
-            RanCommand(["mkdir", "-p", "/build/output/test:0"]),
+            RanCommand(["mkdir", "-p", "/build/output/test/0"]),
             RanBuildCommand([
                 "/bin/bash", "-o", "pipefail", "-c",
-                "lpcraft -v run-one --output-directory /build/output/test:0 "
+                "lpcraft -v run-one --output-directory /build/output "
                 "test 0 "
                 "--secrets /build/.launchpad-secrets.yaml "
                 "2>&1 "
-                "| tee /build/output/test:0.log",
+                "| tee /build/output/test/0/log",
                 ], cwd="/build/tree"),
             ]))
 
@@ -451,7 +451,7 @@ class TestRunCI(TestCase):
         # Just check that it did something in each step, not every detail.
         self.assertThat(
             run_ci.backend.run.calls,
-            AnyMatch(RanCommand(["mkdir", "-p", "/build/output/test:0"])))
+            AnyMatch(RanCommand(["mkdir", "-p", "/build/output/test/0"])))
 
     def test_run_install_fails(self):
         class FailInstall(FakeMethod):
diff --git a/lpbuildd/tests/test_ci.py b/lpbuildd/tests/test_ci.py
index f67300d..8fa6ee7 100644
--- a/lpbuildd/tests/test_ci.py
+++ b/lpbuildd/tests/test_ci.py
@@ -148,17 +148,19 @@ class TestCIBuildManagerIteration(TestCase):
             ]
         yield self.expectRunJob("build", "0", options=expected_job_options)
         self.buildmanager.backend.add_file(
-            "/build/output/build:0.log", b"I am a CI build job log.")
+            "/build/output/build/0/log", b"I am a CI build job log.")
         self.buildmanager.backend.add_file(
-            "/build/output/build:0/ci.whl",
+            "/build/output/build/0/files/ci.whl",
             b"I am output from a CI build job.")
+        self.buildmanager.backend.add_file(
+            "/build/output/build/0/properties", b'{"key": "value"}')
 
         # Collect the output of the first job and start running the second.
         yield self.expectRunJob("test", "0", options=expected_job_options)
         self.buildmanager.backend.add_file(
-            "/build/output/test:0.log", b"I am a CI test job log.")
+            "/build/output/test/0/log", b"I am a CI test job log.")
         self.buildmanager.backend.add_file(
-            "/build/output/test:0/ci.tar.gz",
+            "/build/output/test/0/files/ci.tar.gz",
             b"I am output from a CI test job.")
 
         # Output from the first job is visible in the status response.
@@ -167,6 +169,8 @@ class TestCIBuildManagerIteration(TestCase):
             {
                 "build:0": {
                     "log": self.builder.waitingfiles["build:0.log"],
+                    "properties": (
+                        self.builder.waitingfiles["build:0.properties"]),
                     "output": {
                         "ci.whl": self.builder.waitingfiles["build:0/ci.whl"],
                         },
@@ -188,6 +192,7 @@ class TestCIBuildManagerIteration(TestCase):
         self.assertFalse(self.builder.wasCalled("buildFail"))
         self.assertThat(self.builder, HasWaitingFiles.byEquality({
             "build:0.log": b"I am a CI build job log.",
+            "build:0.properties": b'{"key": "value"}',
             "build:0/ci.whl": b"I am output from a CI build job.",
             "test:0.log": b"I am a CI test job log.",
             "test:0/ci.tar.gz": b"I am output from a CI test job.",
@@ -199,6 +204,8 @@ class TestCIBuildManagerIteration(TestCase):
             {
                 "build:0": {
                     "log": self.builder.waitingfiles["build:0.log"],
+                    "properties": (
+                        self.builder.waitingfiles["build:0.properties"]),
                     "output": {
                         "ci.whl": self.builder.waitingfiles["build:0/ci.whl"],
                         },
@@ -283,7 +290,7 @@ class TestCIBuildManagerIteration(TestCase):
             ]
         yield self.expectRunJob("lint", "0", options=expected_job_options)
         self.buildmanager.backend.add_file(
-            "/build/output/lint:0.log", b"I am a failing CI lint job log.")
+            "/build/output/lint/0/log", b"I am a failing CI lint job log.")
 
         # Collect the output of the first job and start running the second.
         # (Note that `retcode` is the return code of the *first* job, not the
@@ -292,7 +299,7 @@ class TestCIBuildManagerIteration(TestCase):
             "build", "0", options=expected_job_options,
             retcode=RETCODE_FAILURE_BUILD)
         self.buildmanager.backend.add_file(
-            "/build/output/build:0.log", b"I am a CI build job log.")
+            "/build/output/build/0/log", b"I am a CI build job log.")
 
         # Output from the first job is visible in the status response.
         extra_status = self.buildmanager.status()