pyrig-containers 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Winipedia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyrig-containers
3
+ Version: 0.1.0
4
+ Summary: A pyrig plugin to integrate containers.
5
+ Keywords:
6
+ Author: Winipedia
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Programming Language :: Python :: 3.14
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Typing :: Typed
13
+ Requires-Dist: pyrig>=12.1.1
14
+ Maintainer: Winipedia
15
+ Requires-Python: >=3.13
16
+ Project-URL: Homepage, https://github.com/Winipedia/pyrig-containers
17
+ Project-URL: Documentation, https://Winipedia.github.io/pyrig-containers
18
+ Project-URL: Source, https://github.com/Winipedia/pyrig-containers
19
+ Project-URL: Issues, https://github.com/Winipedia/pyrig-containers/issues
20
+ Project-URL: Changelog, https://github.com/Winipedia/pyrig-containers/releases
21
+ Description-Content-Type: text/markdown
22
+
23
+ # pyrig-containers
24
+
25
+ <!-- security -->
26
+ [![DependencyAuditor](https://img.shields.io/badge/security-pip--audit-blue?logo=python)](https://github.com/pypa/pip-audit)
27
+ [![SecurityChecker](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)
28
+ <!-- ci/cd -->
29
+ [![CI](https://img.shields.io/github/actions/workflow/status/Winipedia/pyrig-containers/health_check.yml?label=CI&logo=github)](https://github.com/Winipedia/pyrig-containers/actions/workflows/health_check.yml)
30
+ [![CD](https://img.shields.io/github/actions/workflow/status/Winipedia/pyrig-containers/deploy.yml?label=CD&logo=github)](https://github.com/Winipedia/pyrig-containers/actions/workflows/deploy.yml)
31
+ <!-- code-quality -->
32
+ [![MarkdownLinter](https://img.shields.io/badge/markdown-rumdl-darkgreen)](https://github.com/rvben/rumdl)
33
+ [![PythonLinter](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
34
+ [![TypeChecker](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty)
35
+ [![VersionControlHookManager](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/j178/prek/master/docs/assets/badge-v0.json)](https://github.com/j178/prek)
36
+ <!-- testing -->
37
+ [![CoverageTester](https://codecov.io/gh/Winipedia/pyrig-containers/branch/main/graph/badge.svg)](https://codecov.io/gh/Winipedia/pyrig-containers)
38
+ [![ProjectTester](https://img.shields.io/badge/tested%20with-pytest-46a2f1.svg?logo=pytest)](https://pytest.org)
39
+ <!-- tooling -->
40
+ [![ContainerEngine](https://img.shields.io/badge/Container-Podman-A23CD6?logo=podman&logoColor=grey&colorA=0D1F3F&colorB=A23CD6)](https://podman.io)
41
+ [![PackageManager](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
42
+ [![Pyrigger](https://img.shields.io/badge/built%20with-pyrig-3776AB?logo=buildkite&logoColor=black)](https://github.com/Winipedia/pyrig)
43
+ [![RemoteVersionController](https://img.shields.io/github/stars/Winipedia/pyrig-containers?style=social)](https://github.com/Winipedia/pyrig-containers)
44
+ [![VersionController](https://img.shields.io/badge/Git-F05032?logo=git&logoColor=white)](https://git-scm.com)
45
+ <!-- documentation -->
46
+ [![DocsBuilder](https://img.shields.io/badge/MkDocs-Documentation-326CE5?logo=mkdocs&logoColor=white)](https://www.mkdocs.org)
47
+ [![Documentation](https://img.shields.io/badge/Docs-GitHub%20Pages-black?style=for-the-badge&logo=github&logoColor=white)](https://Winipedia.github.io/pyrig-containers)
48
+ <!-- project-info -->
49
+ [![ProgrammingLanguage](https://img.shields.io/badge/Language-Python-3776AB?logo=python&logoColor=white)](https://www.python.org)
50
+ [![License](https://img.shields.io/github/license/Winipedia/pyrig-containers)](https://github.com/Winipedia/pyrig-containers/blob/main/LICENSE)
51
+
52
+ ---
53
+
54
+ > A pyrig plugin to integrate containers.
55
+
56
+ ---
@@ -0,0 +1,34 @@
1
+ # pyrig-containers
2
+
3
+ <!-- security -->
4
+ [![DependencyAuditor](https://img.shields.io/badge/security-pip--audit-blue?logo=python)](https://github.com/pypa/pip-audit)
5
+ [![SecurityChecker](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)
6
+ <!-- ci/cd -->
7
+ [![CI](https://img.shields.io/github/actions/workflow/status/Winipedia/pyrig-containers/health_check.yml?label=CI&logo=github)](https://github.com/Winipedia/pyrig-containers/actions/workflows/health_check.yml)
8
+ [![CD](https://img.shields.io/github/actions/workflow/status/Winipedia/pyrig-containers/deploy.yml?label=CD&logo=github)](https://github.com/Winipedia/pyrig-containers/actions/workflows/deploy.yml)
9
+ <!-- code-quality -->
10
+ [![MarkdownLinter](https://img.shields.io/badge/markdown-rumdl-darkgreen)](https://github.com/rvben/rumdl)
11
+ [![PythonLinter](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
12
+ [![TypeChecker](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty)
13
+ [![VersionControlHookManager](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/j178/prek/master/docs/assets/badge-v0.json)](https://github.com/j178/prek)
14
+ <!-- testing -->
15
+ [![CoverageTester](https://codecov.io/gh/Winipedia/pyrig-containers/branch/main/graph/badge.svg)](https://codecov.io/gh/Winipedia/pyrig-containers)
16
+ [![ProjectTester](https://img.shields.io/badge/tested%20with-pytest-46a2f1.svg?logo=pytest)](https://pytest.org)
17
+ <!-- tooling -->
18
+ [![ContainerEngine](https://img.shields.io/badge/Container-Podman-A23CD6?logo=podman&logoColor=grey&colorA=0D1F3F&colorB=A23CD6)](https://podman.io)
19
+ [![PackageManager](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
20
+ [![Pyrigger](https://img.shields.io/badge/built%20with-pyrig-3776AB?logo=buildkite&logoColor=black)](https://github.com/Winipedia/pyrig)
21
+ [![RemoteVersionController](https://img.shields.io/github/stars/Winipedia/pyrig-containers?style=social)](https://github.com/Winipedia/pyrig-containers)
22
+ [![VersionController](https://img.shields.io/badge/Git-F05032?logo=git&logoColor=white)](https://git-scm.com)
23
+ <!-- documentation -->
24
+ [![DocsBuilder](https://img.shields.io/badge/MkDocs-Documentation-326CE5?logo=mkdocs&logoColor=white)](https://www.mkdocs.org)
25
+ [![Documentation](https://img.shields.io/badge/Docs-GitHub%20Pages-black?style=for-the-badge&logo=github&logoColor=white)](https://Winipedia.github.io/pyrig-containers)
26
+ <!-- project-info -->
27
+ [![ProgrammingLanguage](https://img.shields.io/badge/Language-Python-3776AB?logo=python&logoColor=white)](https://www.python.org)
28
+ [![License](https://img.shields.io/github/license/Winipedia/pyrig-containers)](https://github.com/Winipedia/pyrig-containers/blob/main/LICENSE)
29
+
30
+ ---
31
+
32
+ > A pyrig plugin to integrate containers.
33
+
34
+ ---
@@ -0,0 +1,97 @@
1
+ [project]
2
+ name = "pyrig-containers"
3
+ version = "0.1.0"
4
+ description = "A pyrig plugin to integrate containers."
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ dependencies = [
8
+ "pyrig>=12.1.1",
9
+ ]
10
+ license = "MIT"
11
+ license-files = [
12
+ "LICENSE",
13
+ ]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3.13",
16
+ "Programming Language :: Python :: 3.14",
17
+ "Operating System :: OS Independent",
18
+ "Typing :: Typed",
19
+ ]
20
+ keywords = []
21
+
22
+ [[project.authors]]
23
+ name = "Winipedia"
24
+
25
+ [[project.maintainers]]
26
+ name = "Winipedia"
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/Winipedia/pyrig-containers"
30
+ Documentation = "https://Winipedia.github.io/pyrig-containers"
31
+ Source = "https://github.com/Winipedia/pyrig-containers"
32
+ Issues = "https://github.com/Winipedia/pyrig-containers/issues"
33
+ Changelog = "https://github.com/Winipedia/pyrig-containers/releases"
34
+
35
+ [project.scripts]
36
+ pyrig-containers = "pyrig.rig.cli.main:main"
37
+
38
+ [dependency-groups]
39
+ dev = [
40
+ "bandit>=1.9.4",
41
+ "mkdocs>=1.6.1",
42
+ "mkdocs-material>=9.7.6",
43
+ "mkdocs-mermaid2-plugin>=1.2.3",
44
+ "mkdocstrings[python]>=1.0.4",
45
+ "pip-audit>=2.10.0",
46
+ "prek>=0.4.4",
47
+ "pygithub>=2.9.1",
48
+ "pyrig-codecov>=1.1.2",
49
+ "pyrig-dev>=1.0.0",
50
+ "pytest>=9.0.3",
51
+ "pytest-cov>=7.1.0",
52
+ "pytest-mock>=3.15.1",
53
+ "ruff>=0.15.16",
54
+ "rumdl>=0.2.9",
55
+ "ty>=0.0.44",
56
+ ]
57
+
58
+ [build-system]
59
+ requires = [
60
+ "uv_build",
61
+ ]
62
+ build-backend = "uv_build"
63
+
64
+ [tool.ruff.lint]
65
+ select = [
66
+ "ALL",
67
+ ]
68
+ ignore = [
69
+ "COM812",
70
+ "ANN401",
71
+ ]
72
+ fixable = [
73
+ "ALL",
74
+ ]
75
+
76
+ [tool.ruff.lint.per-file-ignores]
77
+ "**/tests/**/*.py" = [
78
+ "S101",
79
+ ]
80
+
81
+ [tool.ruff.lint.pydocstyle]
82
+ convention = "google"
83
+
84
+ [tool.ty.terminal]
85
+ error-on-warning = true
86
+
87
+ [tool.pytest.ini_options]
88
+ testpaths = [
89
+ "tests",
90
+ ]
91
+ addopts = "--cov=pyrig_containers --cov-branch --cov-report=term-missing --cov-fail-under=100 --cov-report=xml"
92
+
93
+ [tool.bandit.assert_used]
94
+ skips = [
95
+ "*/tests/*.py",
96
+ "*/test_*/*.py",
97
+ ]
@@ -0,0 +1 @@
1
+ """The top-level package for the project."""
File without changes
@@ -0,0 +1 @@
1
+ """Package initialization."""
@@ -0,0 +1 @@
1
+ """Package initialization."""
@@ -0,0 +1,99 @@
1
+ """Containerfile configuration management."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from pyrig.rig.configs.base.string_ import StringConfigFile
7
+ from pyrig.rig.configs.license import LicenseConfigFile
8
+ from pyrig.rig.configs.markdown.readme import ReadmeConfigFile
9
+ from pyrig.rig.configs.pyproject import PyprojectConfigFile
10
+ from pyrig.rig.tools.package_manager import PackageManager
11
+
12
+
13
+ class ContainerfileConfigFile(StringConfigFile):
14
+ """Generates a production-ready Containerfile for the project.
15
+
16
+ Produces a Containerfile with a Python slim base image, uv as the package
17
+ manager, a non-root runtime user (appuser, UID 1000), and layer ordering
18
+ optimized for cache reuse. Compatible with Docker, Podman, and buildah.
19
+ """
20
+
21
+ def stem(self) -> str:
22
+ """Return the filename stem 'Containerfile'."""
23
+ return "Containerfile"
24
+
25
+ def parent_path(self) -> Path:
26
+ """Return the project root directory."""
27
+ return Path()
28
+
29
+ def extension(self) -> str:
30
+ """Return an empty string (Containerfile has no file extension)."""
31
+ return ""
32
+
33
+ def extension_separator(self) -> str:
34
+ """Return an empty string, overriding the base class default separator '.'.
35
+
36
+ Prevents the base class from appending a dot to the filename, since
37
+ Containerfile uses neither an extension nor a separator.
38
+ """
39
+ return ""
40
+
41
+ def lines(self) -> list[str]:
42
+ """Return the Containerfile build instructions.
43
+
44
+ Returns:
45
+ List of instruction lines produced by `layers()`.
46
+ """
47
+ return self.layers()
48
+
49
+ def layers(self) -> list[str]:
50
+ """Generate the complete sequence of Containerfile build instructions.
51
+
52
+ Produces an optimized layer order so that infrequently changing files
53
+ (project metadata and lock file) are copied before the source tree is
54
+ added. This maximizes Docker/Podman build cache reuse when only source
55
+ code changes.
56
+
57
+ Returns:
58
+ List of Containerfile instruction strings followed by a trailing
59
+ empty string.
60
+
61
+ Note:
62
+ Reads ``requires-python`` from ``pyproject.toml`` and falls back to
63
+ the bundled ``LATEST_PYTHON_VERSION`` resource file when no upper
64
+ bound is specified.
65
+ """
66
+ latest_python_version = PyprojectConfigFile.I.latest_possible_python_version()
67
+ package_root = PackageManager.I.package_root().as_posix()
68
+ project_name = PackageManager.I.project_name()
69
+ workdir = Path(project_name).as_posix()
70
+ app_user_name = "appuser"
71
+ entrypoint = json.dumps(list(PackageManager.I.run_args(project_name)))
72
+ readme_path, license_path, pyproject_path, lock_file_path = (
73
+ ReadmeConfigFile.I.path().as_posix(),
74
+ LicenseConfigFile.I.path().as_posix(),
75
+ PyprojectConfigFile.I.path().as_posix(),
76
+ PackageManager.I.lock_file().as_posix(),
77
+ )
78
+ copy_files = f"{readme_path} {license_path} {pyproject_path} {lock_file_path}"
79
+ install_dependencies_no_dev = (
80
+ PackageManager.I.install_dependencies_no_dev_args()
81
+ )
82
+ image_url, image_source_path, image_destination_path = (
83
+ PackageManager.I.container_image()
84
+ )
85
+
86
+ return [
87
+ f"FROM python:{latest_python_version}-slim",
88
+ f"WORKDIR /{workdir}",
89
+ f"COPY --from={image_url} {image_source_path} {image_destination_path}",
90
+ f"COPY {copy_files} ./",
91
+ f"RUN useradd -m -u 1000 {app_user_name}",
92
+ f"RUN chown -R {app_user_name}:{app_user_name} .",
93
+ f"USER {app_user_name}",
94
+ f"COPY --chown={app_user_name}:{app_user_name} {package_root} {package_root}", # noqa: E501
95
+ f"RUN {install_dependencies_no_dev}",
96
+ f"RUN rm {copy_files}",
97
+ f"ENTRYPOINT {entrypoint}",
98
+ "",
99
+ ]
@@ -0,0 +1 @@
1
+ """Package initialization."""
@@ -0,0 +1,54 @@
1
+ """Container engine wrapper.
2
+
3
+ Wraps container engine commands and information.
4
+ """
5
+
6
+ from pyrig.rig.tools.base.tool import Group, Tool
7
+
8
+
9
+ class ContainerEngine(Tool):
10
+ """Container engine wrapper.
11
+
12
+ Constructs podman command arguments for building and saving container images.
13
+ Typical usage: call ``build_args`` to build the image, then ``save_args``
14
+ to export it as a tar archive.
15
+ """
16
+
17
+ def name(self) -> str:
18
+ """Get tool name.
19
+
20
+ Returns:
21
+ 'podman'
22
+ """
23
+ return "podman"
24
+
25
+ def group(self) -> str:
26
+ """Returns the group the tool belongs to."""
27
+ return Group.TOOLING
28
+
29
+ def image_url(self) -> str:
30
+ """Return the badge image URL for this tool.
31
+
32
+ Returns:
33
+ The URL of the badge image as a string.
34
+ """
35
+ return "https://img.shields.io/badge/Container-Podman-A23CD6?logo=podman&logoColor=grey&colorA=0D1F3F&colorB=A23CD6"
36
+
37
+ def link_url(self) -> str:
38
+ """Return the URL that the badge should link to for this tool.
39
+
40
+ Returns:
41
+ The URL of the project page as a string.
42
+ """
43
+ return "https://podman.io"
44
+
45
+ def dev_dependencies(self) -> tuple[str, ...]:
46
+ """Get tool dependencies.
47
+
48
+ Podman is a system package (not a Python dependency), so this
49
+ returns an empty tuple.
50
+
51
+ Returns:
52
+ Empty tuple — podman must be installed at the OS level.
53
+ """
54
+ return ()
@@ -0,0 +1,22 @@
1
+ """Package manager wrapper.
2
+
3
+ Wraps PackageManager commands and information.
4
+ """
5
+
6
+ from pyrig.rig.tools.package_manager import PackageManager as BasePackageManager
7
+
8
+
9
+ class PackageManager(BasePackageManager):
10
+ """You can override methods from the base class to customize behavior."""
11
+
12
+ def container_image(self) -> tuple[str, str, str]:
13
+ """Return the container image coordinates for copying uv.
14
+
15
+ Used when generating a ``Containerfile`` to add a
16
+ ``COPY --from=<image> <src> <dst>`` directive that installs uv
17
+ into the container image.
18
+
19
+ Returns:
20
+ Tuple of (image_name, path_in_source_image, path_in_target_image).
21
+ """
22
+ return "ghcr.io/astral-sh/uv:latest", "/uv", "/usr/local/bin/uv"