launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #28407
[Merge] ~jugmac00/lpcraft:add-optional-argument-to-replace-sources-list into lpcraft:main
Jürgen Gmach has proposed merging ~jugmac00/lpcraft:add-optional-argument-to-replace-sources-list into lpcraft:main.
Commit message:
Add optional CLI argument to replace `/etc/apt/sources.list`
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~jugmac00/lpcraft/+git/lpcraft/+merge/420745
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~jugmac00/lpcraft:add-optional-argument-to-replace-sources-list into lpcraft:main.
diff --git a/NEWS.rst b/NEWS.rst
index e67e124..1bbb813 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -5,7 +5,10 @@ Version history
0.0.11 (unreleased)
===================
-- Nothing yet.
+- Add new optional and stackable argument ``--replace-sources-list`` which
+ overwrites ``/etc/apt/sources.list``.
+
+- Add minimal CLI interface documentation.
0.0.10 (2022-04-27)
====================
diff --git a/docs/cli-interface.rst b/docs/cli-interface.rst
new file mode 100644
index 0000000..37f1136
--- /dev/null
+++ b/docs/cli-interface.rst
@@ -0,0 +1,42 @@
+=======================
+lpcraft - CLI interface
+=======================
+
+Please note that this is only a small selection of the available commands and
+options.
+
+Please run ``lpcraft --help`` to see all commands.
+
+lpcraft run
+-----------
+
+This command runs all jobs listed via pipelines from a configuration file.
+
+**Example:**
+
+``lpcraft run``
+
+lpcraft run optional arguments
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- ``--replace-sources-list SOURCE_LINE``, e.g.
+ ``lpcraft run --replace-sources-list "deb http://archive.ubuntu.com/ubuntu/ focal main restricted"``.
+ Please note that the option is stackable, so you can repeat it several times.
+
+lpcraft run-one
+---------------
+
+This commands runs one specified job.
+
+**Example:**
+
+``lpcraft run-one test 0``
+
+where ``test`` is the job name and ``0`` is the index of the job/matrix.
+
+lpcraft run-one optional arguments
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- ``--replace-sources-list SOURCE_LINE``, e.g.
+ ``lpcraft run-one --replace-sources list "deb http://archive.ubuntu.com/ubuntu/ focal main restricted" test 0``.
+ Please note that the option is stackable, so you can repeat it several times.
diff --git a/docs/index.rst b/docs/index.rst
index b8cfbab..288b9bd 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -46,6 +46,7 @@ Example configuration
self
configuration
+ cli-interface
plugins
CONTRIBUTING
release-process
diff --git a/lpcraft/commands/run.py b/lpcraft/commands/run.py
index e5bc296..cc37319 100644
--- a/lpcraft/commands/run.py
+++ b/lpcraft/commands/run.py
@@ -194,9 +194,15 @@ def _copy_output_properties(
def _run_job(
- job_name: str, job: Job, provider: Provider, output: Optional[Path]
+ job_name: str,
+ job: Job,
+ provider: Provider,
+ output: Optional[Path],
+ replacement_repositories: Optional[List[str]] = None,
) -> None:
"""Run a single job."""
+ # XXX jugmac00 2022-04-27: we should create a configuration object to be
+ # passed in and not so many arguments
host_architecture = get_host_architecture()
if host_architecture not in job.architectures:
return
@@ -253,6 +259,37 @@ def _run_job(
)
packages = list(itertools.chain(*pm.hook.lpcraft_install_packages()))
if packages:
+ if replacement_repositories:
+ # replace sources.list
+ lines = "\n".join(replacement_repositories)
+ with emit.open_stream(
+ "Replacing /etc/apt/sources.list"
+ ) as stream:
+ instance.push_file_io(
+ destination=PurePath("/etc/apt/sources.list"),
+ content=io.BytesIO(lines.encode()),
+ file_mode="0644",
+ group="root",
+ user="root",
+ )
+ # update local repository information
+ apt_update = ["apt", "update"]
+ with emit.open_stream(f"Running {apt_update}") as stream:
+ proc = instance.execute_run(
+ apt_update,
+ cwd=remote_cwd,
+ env=environment,
+ stdout=stream,
+ stderr=stream,
+ )
+ if proc.returncode != 0:
+ raise CommandError(
+ f"Job {job_name!r} for "
+ f"{job.series}/{host_architecture} failed with "
+ f"exit status {proc.returncode} "
+ f"while running `{shlex.join(apt_update)}`.",
+ retcode=proc.returncode,
+ )
packages_cmd = ["apt", "install", "-y"] + packages
emit.progress("Installing system packages")
with emit.open_stream(f"Running {packages_cmd}") as stream:
@@ -344,6 +381,9 @@ def run(args: Namespace) -> int:
job,
provider,
getattr(args, "output_directory", None),
+ replacement_repositories=getattr(
+ args, "replace_sources_list", None
+ ),
)
except CommandError as e:
if len(stage) == 1:
@@ -391,7 +431,13 @@ def run_one(args: Namespace) -> int:
try:
_run_job(
- args.job, job, provider, getattr(args, "output_directory", None)
+ args.job,
+ job,
+ provider,
+ getattr(args, "output_directory", None),
+ replacement_repositories=getattr(
+ args, "replace_sources_list", None
+ ),
)
finally:
should_clean_environment = getattr(args, "clean", False)
diff --git a/lpcraft/commands/tests/test_run.py b/lpcraft/commands/tests/test_run.py
index b5ae1db..fca99f4 100644
--- a/lpcraft/commands/tests/test_run.py
+++ b/lpcraft/commands/tests/test_run.py
@@ -419,6 +419,110 @@ class TestRun(RunBaseTestCase):
@patch("lpcraft.commands.run.get_provider")
@patch("lpcraft.commands.run.get_host_architecture", return_value="amd64")
+ def test_replace_sources_list(
+ self, mock_get_host_architecture, mock_get_provider
+ ):
+ launcher = Mock(spec=launch)
+ provider = makeLXDProvider(lxd_launcher=launcher)
+ mock_get_provider.return_value = provider
+ execute_run = launcher.return_value.execute_run
+ execute_run.return_value = subprocess.CompletedProcess([], 0)
+ launcher.return_value.pull_file
+ config = dedent(
+ """
+ pipeline:
+ - test
+ jobs:
+ test:
+ series: focal
+ architectures: amd64
+ run: ls -la
+ packages: [git]
+ """
+ )
+ Path(".launchpad.yaml").write_text(config)
+
+ result = self.run_command("run", "--replace-sources-list", "repo info")
+
+ self.assertEqual(0, result.exit_code)
+ self.assertEqual(
+ [
+ call(
+ ["apt", "update"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ call(
+ ["apt", "install", "-y", "git"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ call(
+ ["bash", "--noprofile", "--norc", "-ec", "ls -la"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ ],
+ execute_run.call_args_list,
+ )
+
+ mock_info = launcher.return_value.push_file_io.call_args_list[0][1]
+ self.assertEqual(
+ Path("/etc/apt/sources.list"), mock_info["destination"]
+ )
+ self.assertEqual("repo info", mock_info["content"].read().decode())
+ self.assertEqual("0644", mock_info["file_mode"])
+ self.assertEqual("root", mock_info["group"])
+ self.assertEqual("root", mock_info["user"])
+
+ @patch("lpcraft.commands.run.get_provider")
+ @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64")
+ def test_updating_package_info_fails(
+ self, mock_get_host_architecture, mock_get_provider
+ ):
+ launcher = Mock(spec=launch)
+ provider = makeLXDProvider(lxd_launcher=launcher)
+ mock_get_provider.return_value = provider
+ execute_run = launcher.return_value.execute_run
+ execute_run.return_value = subprocess.CompletedProcess([], 100)
+ config = dedent(
+ """
+ pipeline:
+ - test
+ jobs:
+ test:
+ series: focal
+ architectures: amd64
+ run: ls -la
+ packages: [git]
+ """
+ )
+ Path(".launchpad.yaml").write_text(config)
+
+ result = self.run_command("run", "--replace-sources-list", "repo info")
+
+ self.assertEqual(100, result.exit_code)
+ self.assertEqual(
+ [
+ call(
+ ["apt", "update"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ )
+ ],
+ execute_run.call_args_list,
+ )
+
+ @patch("lpcraft.commands.run.get_provider")
+ @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64")
def test_default_to_run_command(
self, mock_get_host_architecture, mock_get_provider
):
@@ -2149,3 +2253,69 @@ class TestRunOne(RunBaseTestCase):
project_path=self.tmp_project_path,
instances=instance_names,
)
+
+ @patch("lpcraft.commands.run.get_provider")
+ @patch("lpcraft.commands.run.get_host_architecture", return_value="amd64")
+ def test_replace_sources_list(
+ self, mock_get_host_architecture, mock_get_provider
+ ):
+ launcher = Mock(spec=launch)
+ provider = makeLXDProvider(lxd_launcher=launcher)
+ mock_get_provider.return_value = provider
+ execute_run = launcher.return_value.execute_run
+ execute_run.return_value = subprocess.CompletedProcess([], 0)
+ launcher.return_value.pull_file
+ config = dedent(
+ """
+ pipeline:
+ - test
+ jobs:
+ test:
+ series: focal
+ architectures: amd64
+ run: ls -la
+ packages: [git]
+ """
+ )
+ Path(".launchpad.yaml").write_text(config)
+
+ result = self.run_command(
+ "run-one", "--replace-sources-list", "repo info", "test", "0"
+ )
+
+ self.assertEqual(0, result.exit_code)
+ self.assertEqual(
+ [
+ call(
+ ["apt", "update"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ call(
+ ["apt", "install", "-y", "git"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ call(
+ ["bash", "--noprofile", "--norc", "-ec", "ls -la"],
+ cwd=Path("/root/lpcraft/project"),
+ env={},
+ stdout=ANY,
+ stderr=ANY,
+ ),
+ ],
+ execute_run.call_args_list,
+ )
+
+ mock_info = launcher.return_value.push_file_io.call_args_list[0][1]
+ self.assertEqual(
+ Path("/etc/apt/sources.list"), mock_info["destination"]
+ )
+ self.assertEqual("repo info", mock_info["content"].read().decode())
+ self.assertEqual("0644", mock_info["file_mode"])
+ self.assertEqual("root", mock_info["group"])
+ self.assertEqual("root", mock_info["user"])
diff --git a/lpcraft/main.py b/lpcraft/main.py
index 42a68e5..4a90d01 100644
--- a/lpcraft/main.py
+++ b/lpcraft/main.py
@@ -97,6 +97,11 @@ def main(argv: Optional[List[str]] = None) -> int:
"for the pipeline after the running it."
),
)
+ parser_run.add_argument(
+ "--replace-sources-list",
+ action="append",
+ help="Overwrite /etc/apt/sources.list.",
+ )
parser_run.set_defaults(func=run)
parser_run_one = subparsers.add_parser(
@@ -129,6 +134,11 @@ def main(argv: Optional[List[str]] = None) -> int:
metavar="N",
help="Run only the Nth job with the given name (indexing from 0).",
)
+ parser_run_one.add_argument(
+ "--replace-sources-list",
+ action="append",
+ help="Overwrite /etc/apt/sources.list.",
+ )
parser_run_one.set_defaults(func=run_one)
parser_version = subparsers.add_parser(