dockerfile-analyzer 0.3.22__tar.gz → 0.3.23__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.
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/.github/workflows/CI.yml +67 -1
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/Cargo.lock +11 -11
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/Cargo.toml +1 -1
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/PKG-INFO +1 -1
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/README.md +3 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/requirements-dev.txt +1 -0
- dockerfile_analyzer-0.3.23/tests/fixtures/multistage.Dockerfile +38 -0
- dockerfile_analyzer-0.3.23/tests/test_analyze_dockerfile.py +129 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/.github/dependabot.yml +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/.gitignore +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/LICENSE +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/dockerfile_analyzer.pyi +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/example.py +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/py.typed +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/pyproject.toml +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/src/analyzer.rs +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/src/constants.rs +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/src/lib.rs +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/src/models.rs +0 -0
- {dockerfile_analyzer-0.3.22 → dockerfile_analyzer-0.3.23}/src/parse_utils.rs +0 -0
|
@@ -19,7 +19,69 @@ permissions:
|
|
|
19
19
|
contents: read
|
|
20
20
|
|
|
21
21
|
jobs:
|
|
22
|
+
lint:
|
|
23
|
+
name: lint
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
# Keep in sync with requires-python in pyproject.toml (PyO3 needs a
|
|
30
|
+
# matching libpython for clippy on --all-targets).
|
|
31
|
+
python-version: "3.14"
|
|
32
|
+
- name: Install Rust toolchain
|
|
33
|
+
uses: dtolnay/rust-toolchain@stable
|
|
34
|
+
with:
|
|
35
|
+
components: rustfmt, clippy
|
|
36
|
+
- name: Check formatting
|
|
37
|
+
run: cargo fmt --all -- --check
|
|
38
|
+
- name: Clippy
|
|
39
|
+
run: cargo clippy --all-targets -- -D warnings
|
|
40
|
+
|
|
41
|
+
test:
|
|
42
|
+
name: cargo test
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v4
|
|
46
|
+
- uses: actions/setup-python@v5
|
|
47
|
+
with:
|
|
48
|
+
# Keep in sync with requires-python in pyproject.toml (PyO3 needs a
|
|
49
|
+
# matching libpython for linking the test binary).
|
|
50
|
+
python-version: "3.14"
|
|
51
|
+
- name: Install Rust toolchain
|
|
52
|
+
uses: dtolnay/rust-toolchain@stable
|
|
53
|
+
- name: Run tests
|
|
54
|
+
run: cargo test
|
|
55
|
+
|
|
56
|
+
python-test:
|
|
57
|
+
name: python test
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
- uses: actions/setup-python@v5
|
|
62
|
+
with:
|
|
63
|
+
# Keep in sync with requires-python in pyproject.toml.
|
|
64
|
+
python-version: "3.14"
|
|
65
|
+
- name: Install Rust toolchain
|
|
66
|
+
uses: dtolnay/rust-toolchain@stable
|
|
67
|
+
- name: Create virtual environment
|
|
68
|
+
# maturin develop requires an active virtualenv (not system/site-packages).
|
|
69
|
+
run: python -m venv .venv
|
|
70
|
+
- name: Install dev dependencies
|
|
71
|
+
run: |
|
|
72
|
+
source .venv/bin/activate
|
|
73
|
+
pip install -r requirements-dev.txt
|
|
74
|
+
- name: Build and install extension
|
|
75
|
+
run: |
|
|
76
|
+
source .venv/bin/activate
|
|
77
|
+
maturin develop
|
|
78
|
+
- name: Run Python tests
|
|
79
|
+
run: |
|
|
80
|
+
source .venv/bin/activate
|
|
81
|
+
pytest -v
|
|
82
|
+
|
|
22
83
|
linux:
|
|
84
|
+
needs: [lint, test, python-test]
|
|
23
85
|
runs-on: ${{ matrix.platform.runner }}
|
|
24
86
|
strategy:
|
|
25
87
|
matrix:
|
|
@@ -55,6 +117,7 @@ jobs:
|
|
|
55
117
|
path: dist
|
|
56
118
|
|
|
57
119
|
musllinux:
|
|
120
|
+
needs: [lint, test, python-test]
|
|
58
121
|
runs-on: ${{ matrix.platform.runner }}
|
|
59
122
|
strategy:
|
|
60
123
|
matrix:
|
|
@@ -86,6 +149,7 @@ jobs:
|
|
|
86
149
|
path: dist
|
|
87
150
|
|
|
88
151
|
windows:
|
|
152
|
+
needs: [lint, test, python-test]
|
|
89
153
|
runs-on: ${{ matrix.platform.runner }}
|
|
90
154
|
strategy:
|
|
91
155
|
matrix:
|
|
@@ -113,6 +177,7 @@ jobs:
|
|
|
113
177
|
path: dist
|
|
114
178
|
|
|
115
179
|
macos:
|
|
180
|
+
needs: [lint, test, python-test]
|
|
116
181
|
runs-on: ${{ matrix.platform.runner }}
|
|
117
182
|
strategy:
|
|
118
183
|
matrix:
|
|
@@ -139,6 +204,7 @@ jobs:
|
|
|
139
204
|
path: dist
|
|
140
205
|
|
|
141
206
|
sdist:
|
|
207
|
+
needs: [lint, test, python-test]
|
|
142
208
|
runs-on: ubuntu-latest
|
|
143
209
|
steps:
|
|
144
210
|
- uses: actions/checkout@v4
|
|
@@ -157,7 +223,7 @@ jobs:
|
|
|
157
223
|
name: Release
|
|
158
224
|
runs-on: ubuntu-latest
|
|
159
225
|
if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
|
|
160
|
-
needs: [linux, musllinux, windows, macos, sdist]
|
|
226
|
+
needs: [lint, test, python-test, linux, musllinux, windows, macos, sdist]
|
|
161
227
|
permissions:
|
|
162
228
|
# Use to sign the release artifacts
|
|
163
229
|
id-token: write
|
|
@@ -14,7 +14,7 @@ dependencies = [
|
|
|
14
14
|
|
|
15
15
|
[[package]]
|
|
16
16
|
name = "dockerfile-analyzer"
|
|
17
|
-
version = "0.3.
|
|
17
|
+
version = "0.3.23"
|
|
18
18
|
dependencies = [
|
|
19
19
|
"docker-image",
|
|
20
20
|
"parse-dockerfile",
|
|
@@ -73,9 +73,9 @@ dependencies = [
|
|
|
73
73
|
|
|
74
74
|
[[package]]
|
|
75
75
|
name = "pyo3"
|
|
76
|
-
version = "0.29.
|
|
76
|
+
version = "0.29.3"
|
|
77
77
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
78
|
-
checksum = "
|
|
78
|
+
checksum = "700d18fa267b73b9b521fd7e13580e2f446916f176cacee1ab63fcc8191f1655"
|
|
79
79
|
dependencies = [
|
|
80
80
|
"libc",
|
|
81
81
|
"once_cell",
|
|
@@ -87,18 +87,18 @@ dependencies = [
|
|
|
87
87
|
|
|
88
88
|
[[package]]
|
|
89
89
|
name = "pyo3-build-config"
|
|
90
|
-
version = "0.29.
|
|
90
|
+
version = "0.29.3"
|
|
91
91
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
92
|
-
checksum = "
|
|
92
|
+
checksum = "7b3fc0c4d08f6bb10e71fe39dfb9e2f59c6eb6854e22ec8092f50c69a4499adb"
|
|
93
93
|
dependencies = [
|
|
94
94
|
"target-lexicon",
|
|
95
95
|
]
|
|
96
96
|
|
|
97
97
|
[[package]]
|
|
98
98
|
name = "pyo3-ffi"
|
|
99
|
-
version = "0.29.
|
|
99
|
+
version = "0.29.3"
|
|
100
100
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
101
|
-
checksum = "
|
|
101
|
+
checksum = "dfc0b8e19df29aad7086cf977bb0c2a2f143e30567eb113e9cf72b62ca698330"
|
|
102
102
|
dependencies = [
|
|
103
103
|
"libc",
|
|
104
104
|
"pyo3-build-config",
|
|
@@ -106,9 +106,9 @@ dependencies = [
|
|
|
106
106
|
|
|
107
107
|
[[package]]
|
|
108
108
|
name = "pyo3-macros"
|
|
109
|
-
version = "0.29.
|
|
109
|
+
version = "0.29.3"
|
|
110
110
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
111
|
-
checksum = "
|
|
111
|
+
checksum = "6100e8a4b5eba53afaa5ed078364851a0b2499c553a44c31026b929049b49dc6"
|
|
112
112
|
dependencies = [
|
|
113
113
|
"proc-macro2",
|
|
114
114
|
"pyo3-macros-backend",
|
|
@@ -118,9 +118,9 @@ dependencies = [
|
|
|
118
118
|
|
|
119
119
|
[[package]]
|
|
120
120
|
name = "pyo3-macros-backend"
|
|
121
|
-
version = "0.29.
|
|
121
|
+
version = "0.29.3"
|
|
122
122
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
123
|
-
checksum = "
|
|
123
|
+
checksum = "6143877a16e82b5a727b7127ff4cd86858a24a28d745d72f43e6f227c7b1bdb3"
|
|
124
124
|
dependencies = [
|
|
125
125
|
"heck",
|
|
126
126
|
"proc-macro2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dockerfile-analyzer
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.23
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
@@ -280,6 +280,9 @@ Analysis(num_stages=4, images=[Image(full="builder", components="ImageComponents
|
|
|
280
280
|
python3 -m venv env
|
|
281
281
|
source env/bin/activate
|
|
282
282
|
pip install -r requirements-dev.txt
|
|
283
|
+
cargo fmt --all -- --check
|
|
284
|
+
cargo clippy --all-targets -- -D warnings
|
|
283
285
|
cargo test
|
|
284
286
|
maturin develop
|
|
287
|
+
pytest
|
|
285
288
|
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
FROM docker.abc.com/base-images/python:3.13-debian@sha256:55f1d15ef4c37870e23c03e89ad238940b55c8ede9f13fac4b7d71c7955f1053 AS base
|
|
2
|
+
|
|
3
|
+
LABEL org.opencontainers.image.title="My App" \
|
|
4
|
+
org.opencontainers.image.version="1.0" \
|
|
5
|
+
org.opencontainers.image.authors="john@example.com"
|
|
6
|
+
|
|
7
|
+
ENV PYTHONPATH=/src \
|
|
8
|
+
PYTHONUNBUFFERED=1 \
|
|
9
|
+
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt \
|
|
10
|
+
PATH="/home/appuser/.local/bin:$PATH"
|
|
11
|
+
WORKDIR /src
|
|
12
|
+
USER root:root
|
|
13
|
+
|
|
14
|
+
RUN apt-get update && \
|
|
15
|
+
apt-get install --no-install-recommends -y postgresql-client curl git && \
|
|
16
|
+
apt-get autoremove -y && \
|
|
17
|
+
apt-get clean && \
|
|
18
|
+
rm -rf /var/lib/apt/lists/*
|
|
19
|
+
|
|
20
|
+
RUN pip install --no-cache-dir --upgrade pip
|
|
21
|
+
COPY --chown=1000:1000 requirements.txt ./
|
|
22
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
23
|
+
|
|
24
|
+
FROM base AS test
|
|
25
|
+
COPY --chown=1000:1000 test-requirements.txt ./
|
|
26
|
+
USER 1000:1000
|
|
27
|
+
RUN pip install --user --no-cache-dir -r test-requirements.txt
|
|
28
|
+
COPY ./app ./app
|
|
29
|
+
COPY ./test ./test
|
|
30
|
+
|
|
31
|
+
FROM base
|
|
32
|
+
COPY --chown=1000:1000 ./app ./app
|
|
33
|
+
USER 1000:1000
|
|
34
|
+
ARG GIT_COMMIT
|
|
35
|
+
ENV GIT_COMMIT=$GIT_COMMIT
|
|
36
|
+
EXPOSE 5000
|
|
37
|
+
|
|
38
|
+
CMD ["uvicorn", "--host", "0.0.0.0", "--port", "5000", "app.main:app"]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Python integration tests for the dockerfile_analyzer extension module."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
import dockerfile_analyzer as da
|
|
8
|
+
|
|
9
|
+
FIXTURES = Path(__file__).parent / "fixtures"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _read_fixture(name: str) -> str:
|
|
13
|
+
return (FIXTURES / name).read_text()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_analyze_multistage_dockerfile():
|
|
17
|
+
analysis = da.analyze_dockerfile(_read_fixture("multistage.Dockerfile"))
|
|
18
|
+
|
|
19
|
+
assert analysis.num_stages == 3
|
|
20
|
+
assert analysis.stage_names == ["base", "test"]
|
|
21
|
+
assert analysis.copy_from_stages == []
|
|
22
|
+
assert analysis.add_from_stages == []
|
|
23
|
+
assert analysis.exposed_ports == ["5000"]
|
|
24
|
+
|
|
25
|
+
assert analysis.multistage_analysis.is_multistage is True
|
|
26
|
+
assert analysis.multistage_analysis.stages_used_as_base_images == ["base"]
|
|
27
|
+
assert analysis.multistage_analysis.stages_copied_from == []
|
|
28
|
+
assert analysis.multistage_analysis.stages_added_from == []
|
|
29
|
+
assert analysis.multistage_analysis.unused_stages == ["test"]
|
|
30
|
+
|
|
31
|
+
assert analysis.instructions.total_count == 22
|
|
32
|
+
assert analysis.instructions.by_type == {
|
|
33
|
+
"ARG": 1,
|
|
34
|
+
"CMD": 1,
|
|
35
|
+
"COPY": 5,
|
|
36
|
+
"ENV": 2,
|
|
37
|
+
"EXPOSE": 1,
|
|
38
|
+
"FROM": 3,
|
|
39
|
+
"LABEL": 1,
|
|
40
|
+
"RUN": 4,
|
|
41
|
+
"USER": 3,
|
|
42
|
+
"WORKDIR": 1,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
assert analysis.args == {"GIT_COMMIT": None}
|
|
46
|
+
assert analysis.labels == {
|
|
47
|
+
"org.opencontainers.image.title": "My App",
|
|
48
|
+
"org.opencontainers.image.version": "1.0",
|
|
49
|
+
"org.opencontainers.image.authors": "john@example.com",
|
|
50
|
+
}
|
|
51
|
+
assert analysis.env_vars == {
|
|
52
|
+
"PYTHONPATH": "/src",
|
|
53
|
+
"PYTHONUNBUFFERED": "1",
|
|
54
|
+
"REQUESTS_CA_BUNDLE": "/etc/ssl/certs/ca-certificates.crt",
|
|
55
|
+
"PATH": "/home/appuser/.local/bin:$PATH",
|
|
56
|
+
"GIT_COMMIT": "$GIT_COMMIT",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
assert len(analysis.images) == 2
|
|
60
|
+
base_stage_image, external_image = analysis.images
|
|
61
|
+
|
|
62
|
+
assert base_stage_image.full == "base"
|
|
63
|
+
assert base_stage_image.components is not None
|
|
64
|
+
assert base_stage_image.components.registry is None
|
|
65
|
+
assert base_stage_image.components.name == "base"
|
|
66
|
+
assert base_stage_image.components.tag is None
|
|
67
|
+
assert base_stage_image.components.digest is None
|
|
68
|
+
|
|
69
|
+
assert (
|
|
70
|
+
external_image.full
|
|
71
|
+
== "docker.abc.com/base-images/python:3.13-debian@sha256:55f1d15ef4c37870e23c03e89ad238940b55c8ede9f13fac4b7d71c7955f1053"
|
|
72
|
+
)
|
|
73
|
+
assert external_image.components is not None
|
|
74
|
+
assert external_image.components.registry == "docker.abc.com"
|
|
75
|
+
assert external_image.components.name == "base-images/python"
|
|
76
|
+
assert external_image.components.tag == "3.13-debian"
|
|
77
|
+
assert (
|
|
78
|
+
external_image.components.digest
|
|
79
|
+
== "sha256:55f1d15ef4c37870e23c03e89ad238940b55c8ede9f13fac4b7d71c7955f1053"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_analyze_multistage_to_dict():
|
|
84
|
+
analysis = da.analyze_dockerfile(_read_fixture("multistage.Dockerfile"))
|
|
85
|
+
data = analysis.to_dict()
|
|
86
|
+
|
|
87
|
+
assert data["num_stages"] == 3
|
|
88
|
+
assert data["stage_names"] == ["base", "test"]
|
|
89
|
+
assert data["exposed_ports"] == ["5000"]
|
|
90
|
+
assert data["multistage_analysis"]["is_multistage"] is True
|
|
91
|
+
assert data["multistage_analysis"]["unused_stages"] == ["test"]
|
|
92
|
+
assert data["instructions"]["total_count"] == 22
|
|
93
|
+
assert data["args"]["GIT_COMMIT"] is None
|
|
94
|
+
assert data["env_vars"]["PYTHONPATH"] == "/src"
|
|
95
|
+
assert data["images"][1]["components"]["registry"] == "docker.abc.com"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_analyze_single_stage_dockerfile():
|
|
99
|
+
dockerfile = """\
|
|
100
|
+
FROM node:20-alpine
|
|
101
|
+
WORKDIR /app
|
|
102
|
+
COPY package*.json ./
|
|
103
|
+
RUN npm install
|
|
104
|
+
COPY . .
|
|
105
|
+
EXPOSE 3000
|
|
106
|
+
CMD ["npm", "start"]
|
|
107
|
+
"""
|
|
108
|
+
analysis = da.analyze_dockerfile(dockerfile)
|
|
109
|
+
|
|
110
|
+
assert analysis.num_stages == 1
|
|
111
|
+
assert analysis.stage_names == []
|
|
112
|
+
assert analysis.multistage_analysis.is_multistage is False
|
|
113
|
+
assert analysis.exposed_ports == ["3000"]
|
|
114
|
+
assert analysis.instructions.by_type["FROM"] == 1
|
|
115
|
+
assert analysis.instructions.by_type["RUN"] == 1
|
|
116
|
+
assert analysis.images[0].full == "node:20-alpine"
|
|
117
|
+
assert analysis.images[0].components is not None
|
|
118
|
+
assert analysis.images[0].components.name == "node"
|
|
119
|
+
assert analysis.images[0].components.tag == "20-alpine"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_analyze_invalid_dockerfile_raises():
|
|
123
|
+
with pytest.raises(ValueError, match="unknown instruction"):
|
|
124
|
+
da.analyze_dockerfile("invalid dockerfile content")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_analyze_empty_dockerfile_raises():
|
|
128
|
+
with pytest.raises(ValueError):
|
|
129
|
+
da.analyze_dockerfile("")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|