snakemake-software-deployment-plugin-container 0.5.2__tar.gz → 0.6.1__tar.gz

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 (12) hide show
  1. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/.github/workflows/ci.yml +4 -0
  2. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/.gitignore +5 -0
  3. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/CHANGELOG.md +19 -0
  4. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/PKG-INFO +1 -1
  5. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/pyproject.toml +1 -1
  6. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/src/snakemake_software_deployment_plugin_container/__init__.py +42 -2
  7. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/tests/test_plugin.py +21 -27
  8. snakemake_software_deployment_plugin_container-0.5.2/pixi.lock +0 -1464
  9. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/.github/workflows/conventional-prs.yml +0 -0
  10. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/.github/workflows/release-please.yml +0 -0
  11. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/LICENSE +0 -0
  12. {snakemake_software_deployment_plugin_container-0.5.2 → snakemake_software_deployment_plugin_container-0.6.1}/README.md +0 -0
@@ -41,6 +41,10 @@ jobs:
41
41
  - name: Setup pixi
42
42
  uses: prefix-dev/setup-pixi@v0
43
43
 
44
+ - run: |
45
+ sudo sysctl -w kernel.apparmor_restrict_unprivileged_unconfined=0
46
+ sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
47
+
44
48
  - name: Run pytest
45
49
  run: pixi run test -v
46
50
 
@@ -5,3 +5,8 @@ __pycache__/
5
5
  *$py.class
6
6
  *.swp
7
7
  *.swo
8
+
9
+ /.coverage
10
+ /pixi.lock
11
+ /.zed
12
+ coverage.xml
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.1](https://github.com/snakemake/snakemake-software-deployment-plugin-container/compare/v0.6.0...v0.6.1) (2026-06-03)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * add parse and unparse func for runtime argument ([e4f9a8d](https://github.com/snakemake/snakemake-software-deployment-plugin-container/commit/e4f9a8df27ea4848b46127f7f88e8045202ed489))
9
+
10
+ ## [0.6.0](https://github.com/snakemake/snakemake-software-deployment-plugin-container/compare/v0.5.2...v0.6.0) (2026-03-10)
11
+
12
+
13
+ ### Features
14
+
15
+ * support for docker ([#14](https://github.com/snakemake/snakemake-software-deployment-plugin-container/issues/14)) ([1ccee66](https://github.com/snakemake/snakemake-software-deployment-plugin-container/commit/1ccee662f69a60b4a3afa300c4ec3c70da96bc6a))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * properly handle docker:// urls for non-apptainer runtimes ([2b8323d](https://github.com/snakemake/snakemake-software-deployment-plugin-container/commit/2b8323d0a1731d75393ca9d5c1c2b362358ab6ba))
21
+
3
22
  ## [0.5.2](https://github.com/snakemake/snakemake-software-deployment-plugin-container/compare/v0.5.1...v0.5.2) (2026-03-10)
4
23
 
5
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snakemake-software-deployment-plugin-container
3
- Version: 0.5.2
3
+ Version: 0.6.1
4
4
  Project-URL: repository, https://github.com/snakemake/snakemake-software-deployment-plugin-container
5
5
  Project-URL: documentation, https://snakemake.github.io/snakemake-plugin-catalog/plugins/software-deployment/container.html
6
6
  Author-email: Ben Carrillo <ben.uzh@pm.me>, Johannes Köster <johannes.koester@uni-due.de>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "snakemake-software-deployment-plugin-container"
3
- version = "0.5.2"
3
+ version = "0.6.1"
4
4
  authors = [
5
5
  { name = "Ben Carrillo", email = "ben.uzh@pm.me" },
6
6
  { name = "Johannes Köster", email = "johannes.koester@uni-due.de" }
@@ -5,6 +5,7 @@ __copyright__ = "Copyright 2025, ben carrillo"
5
5
  __email__ = "ben.uzh@pm.me"
6
6
  __license__ = "MIT"
7
7
 
8
+ import os
8
9
  import shlex
9
10
  from dataclasses import dataclass, field
10
11
  from os import getcwd
@@ -33,6 +34,7 @@ class Runtime(SettingsEnumBase):
33
34
  UDOCKER = 0
34
35
  PODMAN = 1
35
36
  APPTAINER = 2
37
+ DOCKER = 3
36
38
 
37
39
 
38
40
  @dataclass
@@ -44,6 +46,8 @@ class Settings(SoftwareDeploymentSettingsBase):
44
46
  "env_var": False,
45
47
  "required": False,
46
48
  "choices": Runtime.choices(),
49
+ "parse_func": Runtime.parse_choice,
50
+ "unparse_func": Runtime.item_to_choice,
47
51
  },
48
52
  )
49
53
  mountpoints: List[str] = field(
@@ -54,6 +58,16 @@ class Settings(SoftwareDeploymentSettingsBase):
54
58
  "Current working directory and the system tmpdir are always mounted.",
55
59
  },
56
60
  )
61
+ use_user_namespaces: bool = field(
62
+ default=False,
63
+ metadata={
64
+ "help": "Whether to use user namespaces (if supported by the runtime). "
65
+ "This can be useful to avoid permission issues, but is not always "
66
+ "supported. For non-docker runtimes, this is not needed. "
67
+ "For docker, the fallback option is to set the user and group id via "
68
+ "--user. This happens automatically if this flag here is not used.",
69
+ },
70
+ )
57
71
 
58
72
 
59
73
  common_settings = CommonSettings(provides="container")
@@ -92,6 +106,10 @@ class Env(EnvBase):
92
106
  self.runtime_manager = RuntimeManager(self)
93
107
  if self.settings.runtime == Runtime.APPTAINER:
94
108
  self.runtime_manager = RuntimeManagerApptainer(self)
109
+ elif self.settings.runtime == Runtime.DOCKER:
110
+ self.runtime_manager = RuntimeManagerDocker(self)
111
+ elif self.settings.runtime == Runtime.UDOCKER:
112
+ self.runtime_manager = RuntimeManagerUdocker(self)
95
113
 
96
114
  # The decorator ensures that the decorated method is only called once
97
115
  # in case multiple environments of the same kind are created.
@@ -156,7 +174,10 @@ class RuntimeManager:
156
174
  return "-v"
157
175
 
158
176
  def image_uri(self) -> str:
159
- return self.env.spec.image_uri
177
+ image_uri = self.env.spec.image_uri
178
+ if image_uri.startswith("docker://"):
179
+ return image_uri[9:]
180
+ return image_uri
160
181
 
161
182
  def subcommand(self) -> str:
162
183
  return "run"
@@ -186,7 +207,7 @@ class RuntimeManager:
186
207
  f" {self.workdir_option()} {getcwd()!r}" # Working directory inside container
187
208
  f" {mountpoints}"
188
209
  f" {self.image_uri()}" # Container image
189
- " /bin/sh" # Shell executable
210
+ " /bin/bash" # Shell executable
190
211
  f" -c {shlex.quote(cmd)}" # The command to execute
191
212
  )
192
213
 
@@ -208,3 +229,22 @@ class RuntimeManagerApptainer(RuntimeManager):
208
229
  if re.match(r"[a-z\.]+://", super().image_uri()):
209
230
  return super().image_uri()
210
231
  return f"docker://{super().image_uri()}"
232
+
233
+
234
+ class RuntimeManagerDocker(RuntimeManager):
235
+ def options(self) -> str:
236
+ options = super().options()
237
+ if self.env.settings.use_user_namespaces:
238
+ options += " --userns keep-id"
239
+ else:
240
+ uid = os.getuid()
241
+ gid = os.getgid()
242
+ options += f" --user {uid}:{gid}"
243
+ return options
244
+
245
+
246
+ class RuntimeManagerUdocker(RuntimeManager):
247
+ def options(self) -> str:
248
+ options = super().options()
249
+ options += " --nobanner --env TINI_SUBREAPER=1"
250
+ return options
@@ -21,18 +21,13 @@ from snakemake_software_deployment_plugin_container import (
21
21
  Runtime,
22
22
  )
23
23
 
24
- # There can be multiple subclasses of SoftwareDeploymentProviderBase here.
25
- # This way, you can implement multiple test scenarios.
26
- # For each subclass, the test suite tests the environment activation and execution
27
- # within, and, if applicable, environment deployment and archiving.
28
-
29
24
 
30
25
  class TestBase(TestSoftwareDeploymentBase):
31
- test_container = "alpine:latest"
26
+ test_container = "quay.io/biocontainers/samtools:1.23.1--ha83d96e_0"
32
27
 
33
28
  def get_contained_executable(self) -> str:
34
29
  # just provide something that is available inside of the container
35
- return "sh"
30
+ return "samtools"
36
31
 
37
32
  def get_env_spec(self) -> EnvSpecBase:
38
33
  # If the software deployment provider does not support deployable environments,
@@ -50,27 +45,11 @@ class TestBase(TestSoftwareDeploymentBase):
50
45
  def get_test_cmd(self) -> str:
51
46
  # Return a test command that should be executed within the environment
52
47
  # with exit code 0 (i.e. without error).
53
- return "/bin/true"
48
+ return "samtools --version"
54
49
 
55
50
 
56
51
  class TestApptainerContainer(TestBase):
57
- # TODO reactivate apptainer tests, they currently fail in CI with
58
- # ERROR : Could not write info to setgroups: Permission denied
59
- # ERROR : Error while waiting event for user namespace mappings: no event received
60
- __test__ = False
61
-
62
- def get_settings(
63
- self,
64
- ) -> Optional[SoftwareDeploymentSettingsBase]:
65
- return Settings(runtime=Runtime.APPTAINER)
66
-
67
-
68
- class TestApptainerContainerWithScheme(TestBase):
69
- # TODO reactivate apptainer tests, they currently fail in CI with
70
- # ERROR : Could not write info to setgroups: Permission denied
71
- # ERROR : Error while waiting event for user namespace mappings: no event received
72
- __test__ = False
73
- test_container = "docker://alpine:latest"
52
+ __test__ = True
74
53
 
75
54
  def get_settings(
76
55
  self,
@@ -87,18 +66,33 @@ class TestUDockerContainer(TestBase):
87
66
  return Settings(runtime=Runtime.UDOCKER)
88
67
 
89
68
 
90
- # Helper function to check if podman is available
91
69
  def is_podman_available():
92
70
  return shutil.which("podman") is not None
93
71
 
94
72
 
73
+ def is_docker_available():
74
+ return shutil.which("docker") is not None
75
+
76
+
95
77
  @pytest.mark.skipif(
96
78
  not is_podman_available(), reason="podman not available on the system"
97
79
  )
98
80
  class TestPodmanContainer(TestBase):
99
81
  __test__ = True # activate automatic testing
100
82
 
101
- def get_software_deployment_provider_settings(
83
+ def get_settings(
102
84
  self,
103
85
  ) -> Optional[SoftwareDeploymentSettingsBase]:
104
86
  return Settings(runtime=Runtime.PODMAN)
87
+
88
+
89
+ @pytest.mark.skipif(
90
+ not is_docker_available(), reason="docker not available on the system"
91
+ )
92
+ class TestDockerContainer(TestBase):
93
+ __test__ = True # activate automatic testing
94
+
95
+ def get_settings(
96
+ self,
97
+ ) -> Optional[SoftwareDeploymentSettingsBase]:
98
+ return Settings(runtime=Runtime.DOCKER)