diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f82d6b735..25cef668d 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -90,7 +90,16 @@ jobs: - name: Run smoke tests run: | mkdir -p charm-smoke-test - cd charm-smoke-test + pushd charm-smoke-test charmcraft -v init --author testuser - sg lxd -c "charmcraft -v build" + sg lxd -c "charmcraft -v pack" test -f *.charm + sg lxd -c "charmcraft -v clean" + popd + + mkdir -p another-directory + pushd another-directory + sg lxd -c "charmcraft -v pack --project-dir ../charm-smoke-test" + test -f *.charm + sg lxd -c "charmcraft -v clean --project-dir ../charm-smoke-test" + popd diff --git a/charmcraft/commands/build.py b/charmcraft/commands/build.py index af0c8702f..3abf4eb2d 100644 --- a/charmcraft/commands/build.py +++ b/charmcraft/commands/build.py @@ -25,12 +25,12 @@ import zipfile from typing import List, Optional - from charmcraft.bases import check_if_base_matches_host from charmcraft.cmdbase import BaseCommand, CommandError from charmcraft.config import Base, BasesConfiguration, Config from charmcraft.deprecations import notify_deprecation from charmcraft.env import ( + get_managed_environment_home_path, get_managed_environment_project_path, is_charmcraft_running_in_managed_mode, ) @@ -254,6 +254,20 @@ def pack_charm_in_instance( charm_name = format_charm_file_name( self.metadata.name, self.config.bases[bases_index] ) + + # If building in project directory, use the project path as the working + # directory. The output charms will be placed in the correct directory + # without needing retrieval. If outputing to a directory other than the + # charm project directory, we need to output the charm outside the + # project directory and can retrieve it when complete. + cwd = pathlib.Path.cwd() + if cwd == self.charmdir: + instance_output_dir = get_managed_environment_project_path() + pull_charm = False + else: + instance_output_dir = get_managed_environment_home_path() + pull_charm = True + cmd = ["charmcraft", "pack", "--bases-index", str(bases_index)] if message_handler.mode == message_handler.VERBOSE: @@ -273,7 +287,7 @@ def pack_charm_in_instance( instance.execute_run( cmd, check=True, - cwd=get_managed_environment_project_path().as_posix(), + cwd=instance_output_dir.as_posix(), ) except subprocess.CalledProcessError as error: capture_logs_from_instance(instance) @@ -281,6 +295,17 @@ def pack_charm_in_instance( f"Failed to build charm for bases index '{bases_index}'." ) from error + if pull_charm: + try: + instance.pull_file( + source=instance_output_dir / charm_name, + destination=cwd / charm_name, + ) + except FileNotFoundError as error: + raise CommandError( + "Unexpected error retrieving charm from instance." + ) from error + return charm_name def _load_juju_ignore(self): diff --git a/charmcraft/env.py b/charmcraft/env.py index e2a75087b..fbd506d29 100644 --- a/charmcraft/env.py +++ b/charmcraft/env.py @@ -25,6 +25,11 @@ from charmcraft.cmdbase import CommandError +def get_managed_environment_home_path(): + """Path for home when running in managed environment.""" + return pathlib.Path("/root") + + def get_managed_environment_log_path(): """Path for charmcraft log when running in managed environment.""" return pathlib.Path("/tmp/charmcraft.log") @@ -32,7 +37,7 @@ def get_managed_environment_log_path(): def get_managed_environment_project_path(): """Path for project when running in managed environment.""" - return pathlib.Path("/root/project") + return get_managed_environment_home_path() / "project" def is_charmcraft_running_from_snap(): diff --git a/tests/commands/test_build.py b/tests/commands/test_build.py index 466304484..6768e195c 100644 --- a/tests/commands/test_build.py +++ b/tests/commands/test_build.py @@ -679,6 +679,133 @@ def test_build_multiple_with_charmcraft_yaml(basic_project, monkeypatch, caplog) assert "Building for 'bases[2]' as host matches 'build-on[0]'." in records +def test_build_project_is_cwd( + basic_project, + caplog, + mock_capture_logs_from_instance, + mock_ensure_provider_is_available, + monkeypatch, +): + """Test cases for base-index parameter.""" + mode = message_handler.NORMAL + monkeypatch.setattr(message_handler, "mode", mode) + host_base = get_host_as_base() + host_arch = host_base.architectures[0] + charmcraft_file = basic_project / "charmcraft.yaml" + charmcraft_file.write_text( + dedent( + f"""\ + type: charm + bases: + - name: ubuntu + channel: "18.04" + architectures: {host_base.architectures!r} + """ + ) + ) + config = load(basic_project) + builder = Builder( + { + "from": basic_project, + "entrypoint": basic_project / "src" / "charm.py", + "requirement": [], + }, + config, + ) + + monkeypatch.chdir(basic_project) + with patch("charmcraft.commands.build.launched_environment") as mock_launch: + zipnames = builder.run([0]) + + assert zipnames == [ + f"name-from-metadata_ubuntu-18.04-{host_arch}.charm", + ] + assert mock_launch.mock_calls == [ + call( + charm_name="name-from-metadata", + project_path=basic_project, + base=Base(name="ubuntu", channel="18.04", architectures=[host_arch]), + bases_index=0, + build_on_index=0, + ), + call().__enter__(), + call() + .__enter__() + .execute_run( + ["charmcraft", "pack", "--bases-index", "0"], + check=True, + cwd="/root/project", + ), + call().__exit__(None, None, None), + ] + + +def test_build_project_is_not_cwd( + basic_project, + caplog, + mock_capture_logs_from_instance, + mock_ensure_provider_is_available, + monkeypatch, +): + """Test cases for base-index parameter.""" + mode = message_handler.NORMAL + monkeypatch.setattr(message_handler, "mode", mode) + host_base = get_host_as_base() + host_arch = host_base.architectures[0] + charmcraft_file = basic_project / "charmcraft.yaml" + charmcraft_file.write_text( + dedent( + f"""\ + type: charm + bases: + - name: ubuntu + channel: "18.04" + architectures: {host_base.architectures!r} + """ + ) + ) + config = load(basic_project) + builder = Builder( + { + "from": basic_project, + "entrypoint": basic_project / "src" / "charm.py", + "requirement": [], + }, + config, + ) + + with patch("charmcraft.commands.build.launched_environment") as mock_launch: + zipnames = builder.run([0]) + + assert zipnames == [ + f"name-from-metadata_ubuntu-18.04-{host_arch}.charm", + ] + assert mock_launch.mock_calls == [ + call( + charm_name="name-from-metadata", + project_path=basic_project, + base=Base(name="ubuntu", channel="18.04", architectures=[host_arch]), + bases_index=0, + build_on_index=0, + ), + call().__enter__(), + call() + .__enter__() + .execute_run( + ["charmcraft", "pack", "--bases-index", "0"], + check=True, + cwd="/root", + ), + call() + .__enter__() + .pull_file( + source=pathlib.Path("/root") / zipnames[0], + destination=pathlib.Path.cwd() / zipnames[0], + ), + call().__exit__(None, None, None), + ] + + @pytest.mark.parametrize( "mode,cmd_flags", [ diff --git a/tests/test_env.py b/tests/test_env.py index 92e9e09a1..d9368db63 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -23,6 +23,12 @@ from charmcraft.cmdbase import CommandError +def test_get_managed_environment_home_path(): + dirpath = env.get_managed_environment_home_path() + + assert dirpath == pathlib.Path("/root") + + def test_get_managed_environment_log_path(): dirpath = env.get_managed_environment_log_path()