diff --git a/invenio_cli/commands/services.py b/invenio_cli/commands/services.py index 182bf31..c31f362 100644 --- a/invenio_cli/commands/services.py +++ b/invenio_cli/commands/services.py @@ -135,6 +135,25 @@ def _default_location_path(self): return "{}/data".format(self.cli_config.get_instance_path()) return "{}://default".format(self.cli_config.get_file_storage().lower()) + def s3_create_default_bucket(self, bucket="default"): + """Steps to create the bucket used by default by Invenio's S3 storage. + + Unlike MinIO, RustFS does not auto-create buckets from directories + present on its data volume at startup, so this replicates that + behaviour. Skippable, as older invenio-s3 versions lack the command. + """ + pkg_man = self.cli_config.python_package_manager + steps = [ + CommandStep( + cmd=pkg_man.run_command("invenio", "s3", "create-bucket", bucket), + env={"PIPENV_VERBOSITY": "-1"}, + message="Creating default S3 bucket...", + skippable=True, + ) + ] + + return steps + def _setup(self, demo_data=False): """Services initialization steps.""" pkg_man = self.cli_config.python_package_manager @@ -149,6 +168,11 @@ def _setup(self, demo_data=False): env={"PIPENV_VERBOSITY": "-1"}, message="Creating database...", ), + *( + self.s3_create_default_bucket() + if self.cli_config.get_file_storage().lower() == "s3" + else [] + ), CommandStep( cmd=pkg_man.run_command( "invenio", diff --git a/tests/commands/test_services.py b/tests/commands/test_services.py new file mode 100644 index 0000000..445fa46 --- /dev/null +++ b/tests/commands/test_services.py @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2026 California Institute of Technology. +# SPDX-License-Identifier: MIT + +"""Services commands tests.""" + +from unittest.mock import Mock, patch + +from invenio_cli.commands.services import ServicesCommands + + +@patch("invenio_cli.commands.services.ils_version", return_value=None) +@patch("invenio_cli.commands.services.rdm_version", return_value=None) +def _setup_messages(cli_config, *mocks): + commands = ServicesCommands(cli_config, docker_helper=Mock()) + return [step.message for step in commands._setup()] + + +def test_setup_local_storage_skips_bucket_creation(mock_cli_config): + assert "Creating default S3 bucket..." not in _setup_messages(mock_cli_config) + + +def test_setup_s3_storage_creates_bucket_before_location(mock_cli_config): + mock_cli_config.get_file_storage = lambda: "S3" + messages = _setup_messages(mock_cli_config) + + bucket_index = messages.index("Creating default S3 bucket...") + assert messages[bucket_index + 1] == "Creating files location..." + + +def test_s3_create_default_bucket(mock_cli_config): + commands = ServicesCommands(mock_cli_config, docker_helper=Mock()) + (step,) = commands.s3_create_default_bucket() + + assert step.cmd == ["pipenv", "run", "invenio", "s3", "create-bucket", "default"] + assert step.skippable