Skip to content

Commit b377924

Browse files
authored
Add the --current-size and --exit-zero to optionally show current docker image size and avoid stop the CI even if exceed max_size and max-layers (#319)
1 parent 959e325 commit b377924

File tree

11 files changed

+224
-16
lines changed

11 files changed

+224
-16
lines changed

.github/workflows/test.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
strategy:
2121
fail-fast: false
2222
matrix:
23-
python-version: ['3.9', '3.10', '3.11', '3.12']
23+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
2424

2525
steps:
2626
- uses: actions/checkout@v4
@@ -52,10 +52,9 @@ jobs:
5252
poetry run flake8 .
5353
poetry run mypy .
5454
poetry run pytest
55-
poetry check
5655
poetry run pip check
5756
5857
- name: Upload coverage to Codecov
5958
uses: codecov/codecov-action@v5
6059
with:
61-
file: ./coverage.xml
60+
files: ./coverage.xml

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
We follow Semantic Version.
44

5+
## Version 2.1.0
6+
7+
### Features
8+
9+
- Adds `--current-size` flag to show the current size of the docker image.
10+
- Adds `--exit-zero` flag to force the exit code to be 0 even if there are errors.
11+
- Adds `__main__.py` entrypoint to be able to run it via `python -m docker_image_size_limit`
512

613
## Version 2.0.0
714

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ LABEL maintainer="[email protected]"
1818
LABEL vendor="wemake.services"
1919

2020
# Our own tool:
21-
ENV DISL_VERSION='2.0.0'
21+
ENV DISL_VERSION='2.1.0'
2222

2323
RUN apk add --no-cache bash docker
2424
RUN pip3 install "docker-image-size-limit==$DISL_VERSION"

README.md

+43
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,42 @@ $ disl your-image-name:label 300MiB --max-layers=5
4343
# ok!
4444
```
4545

46+
Add `--current-size` flag to show the current size your image:
47+
48+
```bash
49+
$ disl your-image-name:label 300MiB --current-size
50+
your-image-name:label size is 414.4 MiB
51+
your-image-name:label exceeds 300MiB limit by 114.4 MiB
52+
```
53+
54+
55+
Add `--exit-zero` flag to force the exit code to be 0 even if there are errors:
56+
57+
```bash
58+
$ disl your-image-name:label 300MiB --exit-zero
59+
your-image-name:label exceeds 300MiB limit by 114.4 MiB
60+
61+
$ echo $?
62+
0
63+
```
64+
65+
You can combine all flags together:
66+
67+
```bash
68+
$ disl your-image-name:label 300MiB --max-layers=5 --current-size --exit-zero
69+
your-image-name:label size is 414.4 MiB
70+
your-image-name:label exceeds 300MiB limit by 114.4 MiB
71+
your-image-name:label exceeds 5 maximum layers by 2
72+
```
73+
74+
Run `disl` as a module:
75+
76+
```bash
77+
$ python -m docker_image_size_limit your-image-name:label 300MiB
78+
your-image-name:label exceeds 300MiB limit by 114.4 MiB
79+
```
80+
81+
4682

4783
## Options
4884

@@ -84,6 +120,10 @@ You can also use this check as a [GitHub Action](https://github.com/marketplace/
84120
with:
85121
image: "$YOUR_IMAGE_NAME"
86122
size: "$YOUR_SIZE_LIMIT"
123+
# optional fields:
124+
max_layers: 5
125+
show_current_size: false
126+
exit_zero: false
87127
```
88128
89129
Here's [an example](https://github.com/wemake-services/docker-image-size-limit/actions?query=workflow%3Adisl).
@@ -105,6 +145,9 @@ Then you can use it like so:
105145
docker run -v /var/run/docker.sock:/var/run/docker.sock --rm \
106146
-e INPUT_IMAGE="$YOUR_IMAGE_NAME" \
107147
-e INPUT_SIZE="$YOUR_SIZE_LIMIT" \
148+
-e INPUT_MAX_LAYERS="$YOUR_MAX_LAYERS" \
149+
-e INPUT_SHOW_CURRENT_SIZE="true" \
150+
-e INPUT_EXIT_ZERO="true" \
108151
wemakeservices/docker-image-size-limit
109152
```
110153

action.yml

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# This is a definition file for a Github Action.
2-
# See: https://help.github.com/en/articles/creating-a-docker-container-action
2+
# See: https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-docker-container-action
33

44
# We also define metadata here:
5-
# See: https://help.github.com/en/articles/metadata-syntax-for-github-actions
5+
# See: https://docs.github.com/en/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions
66

77
name: 'docker-image-size-limit'
88
description: 'Runs docker-image-size-limit as a GitHub Action'
@@ -21,6 +21,14 @@ inputs:
2121
description: 'The maximum number of layers in the image'
2222
required: false
2323
default: -1
24+
show_current_size:
25+
description: 'Show the current size of the image'
26+
required: false
27+
default: 'false'
28+
exit_zero:
29+
description: 'Do not fail the action even if docker image size/layers exceed size and max_layers'
30+
required: false
31+
default: 'false'
2432
outputs:
2533
size:
2634
description: 'The output of docker-image-size-limit run'
@@ -32,3 +40,5 @@ runs:
3240
- ${{ inputs.image }}
3341
- ${{ inputs.size }}
3442
- ${{ inputs.layers }}
43+
- ${{ inputs.show_current_size }}
44+
- ${{ inputs.exit_zero }}

docker_image_size_limit/__init__.py

+29-6
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,21 @@
1515
)
1616

1717

18-
def main() -> NoReturn:
18+
def main(prog_name: str = 'disl') -> NoReturn: # noqa: WPS210
1919
"""Main CLI entrypoint."""
2020
client = docker.from_env()
21-
arguments = _parse_args()
22-
extra_size, extra_layers = _check_image(
21+
arguments = _parse_args(prog_name=prog_name)
22+
extra_size, extra_layers, image_current_size = _check_image(
2323
client,
2424
image=arguments.image,
2525
max_size=arguments.max_size,
2626
max_layers=arguments.max_layers,
2727
)
28+
if arguments.current_size:
29+
print('{0} current size is {1}'.format( # noqa: WPS421
30+
arguments.image,
31+
format_size(image_current_size, binary=True),
32+
))
2833

2934
exit_code = 0
3035
if extra_size > 0:
@@ -41,6 +46,8 @@ def main() -> NoReturn:
4146
extra_layers,
4247
))
4348
exit_code = 1
49+
if arguments.exit_zero:
50+
exit_code = 0
4451
sys.exit(exit_code)
4552

4653

@@ -49,14 +56,15 @@ def _check_image(
4956
image: str,
5057
max_size: str,
5158
max_layers: int,
52-
) -> Tuple[int, int]:
59+
) -> Tuple[int, int, int]:
5360
image_info = client.images.get(image)
61+
image_current_size: int = image_info.attrs['Size']
5462
size_overflow = check_image_size(image_info, limit=max_size)
5563
if max_layers > 0:
5664
layers_overflow = check_image_layers(image_info, limit=max_layers)
5765
else:
5866
layers_overflow = 0
59-
return size_overflow, layers_overflow
67+
return size_overflow, layers_overflow, image_current_size
6068

6169

6270
def check_image_size(
@@ -107,9 +115,10 @@ def check_image_layers(
107115
return len(layers) - limit
108116

109117

110-
def _parse_args() -> argparse.Namespace:
118+
def _parse_args(prog_name: str) -> argparse.Namespace:
111119
parser = argparse.ArgumentParser(
112120
description='Keep your docker images small',
121+
prog=prog_name,
113122
)
114123
parser.add_argument(
115124
'--version', action='version', version=_version,
@@ -126,4 +135,18 @@ def _parse_args() -> argparse.Namespace:
126135
help='Maximum number of image layers',
127136
default=-1,
128137
)
138+
parser.add_argument(
139+
'--current-size',
140+
action='store_true',
141+
help='Display the current size of the Docker image',
142+
default=False,
143+
dest='current_size',
144+
)
145+
parser.add_argument(
146+
'--exit-zero',
147+
action='store_true',
148+
help='Exit with 0 even if docker image size/layers exceed max_size and max-layers', # noqa: E501
149+
default=False,
150+
dest='exit_zero',
151+
)
129152
return parser.parse_args()

docker_image_size_limit/__main__.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import sys
2+
from os.path import basename
3+
4+
from docker_image_size_limit import main
5+
6+
if __name__ == '__main__': # pragma: no cover
7+
interpreter_binary_name = basename(sys.executable)
8+
main(
9+
prog_name='{0} -m docker_image_size_limit'.format(
10+
interpreter_binary_name,
11+
),
12+
)

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "docker-image-size-limit"
3-
version = "2.0.0"
3+
version = "2.1.0"
44
description = ""
55
license = "MIT"
66
authors = [

scripts/entrypoint.sh

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,42 @@
11
#!/bin/bash
22

3-
# Default values:
3+
# Passed args from GitHub Actions:
4+
: "${INPUT_IMAGE:=$1}" # Required
5+
: "${INPUT_SIZE:=$2}" # Required
6+
: "${INPUT_MAX_LAYERS:=$3}" # Optional
7+
: "${INPUT_SHOW_CURRENT_SIZE:=$4}" # Optional
8+
: "${INPUT_EXIT_ZERO:=$5}" # Optional
9+
10+
# Default values, needed because `Dockerfile` can be used directly:
11+
# These values must match ones in `action.yml`!
412
: "${INPUT_MAX_LAYERS:=-1}"
13+
: "${INPUT_SHOW_CURRENT_SIZE:=false}"
14+
: "${INPUT_EXIT_ZERO:=false}"
515

616
# Diagnostic output:
717
echo "Using image: $INPUT_IMAGE"
818
echo "Size limit: $INPUT_SIZE"
919
echo "Max layers: $INPUT_MAX_LAYERS"
20+
echo "Show Current Size: $INPUT_SHOW_CURRENT_SIZE"
21+
echo "Exit Zero: $INPUT_EXIT_ZERO"
1022
echo 'disl --version:'
1123
disl --version
1224
echo '================================='
1325
echo
1426

27+
SHOW_CURRENT_SIZE_FLAG=''
28+
if [ "$INPUT_SHOW_CURRENT_SIZE" = 'true' ]; then
29+
SHOW_CURRENT_SIZE_FLAG='--current-size'
30+
fi
31+
32+
EXIT_ZERO_FLAG=''
33+
if [ "$INPUT_EXIT_ZERO" = 'true' ]; then
34+
EXIT_ZERO_FLAG='--exit-zero'
35+
fi
36+
37+
1538
# Runs disl:
16-
output=$(disl "$INPUT_IMAGE" "$INPUT_SIZE" --max-layers="$INPUT_MAX_LAYERS")
39+
output=$(disl "$INPUT_IMAGE" "$INPUT_SIZE" --max-layers="$INPUT_MAX_LAYERS" "$SHOW_CURRENT_SIZE_FLAG" "$EXIT_ZERO_FLAG")
1740
status="$?"
1841

1942
# Sets the output variable for Github Action API:

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ ignore = D100, D104, D401, W504, RST303, RST304, DAR103, DAR203
3535

3636
per-file-ignores =
3737
# There are `assert`s in tests:
38-
tests/*.py: WPS442, S101, S404, S603, S607
38+
tests/*.py: WPS442, S101, S404, S603, S607, WPS226
3939

4040

4141
[isort]

tests/test_cli.py

+91
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import subprocess
2+
import sys
3+
from os.path import basename
24

35

46
def test_disl_exceeds_limit(image_name: str) -> None:
@@ -58,3 +60,92 @@ def test_disl_max_layers(image_name: str) -> None:
5860
assert process.returncode == 1
5961
assert 'maximum layers by' in output
6062
assert image_name in output
63+
64+
65+
def test_disl_current_size(image_name: str) -> None:
66+
"""Runs `disl` command with --current-size flag."""
67+
process = subprocess.Popen(
68+
[
69+
'disl',
70+
image_name,
71+
'1 kb',
72+
'--current-size',
73+
],
74+
stdout=subprocess.PIPE,
75+
stderr=subprocess.PIPE,
76+
universal_newlines=True,
77+
encoding='utf8',
78+
)
79+
output, _ = process.communicate()
80+
81+
assert process.returncode == 1
82+
assert 'current size is' in output
83+
assert image_name in output
84+
85+
86+
def test_disl_exit_zero(image_name: str) -> None:
87+
"""Runs `disl` command with --exit-zero flag."""
88+
process = subprocess.Popen(
89+
[
90+
'disl',
91+
image_name,
92+
'1 kb',
93+
'--current-size',
94+
'--exit-zero',
95+
],
96+
stdout=subprocess.PIPE,
97+
stderr=subprocess.PIPE,
98+
universal_newlines=True,
99+
encoding='utf8',
100+
)
101+
output, _ = process.communicate()
102+
103+
assert process.returncode == 0
104+
assert 'current size is' in output
105+
assert image_name in output
106+
107+
108+
def test_docker_image_size_limit_as_module(image_name: str) -> None:
109+
"""Runs `disl` command with --exit-zero flag."""
110+
interpreter_binary_name = basename(sys.executable)
111+
process = subprocess.Popen(
112+
[
113+
'{0}'.format(interpreter_binary_name),
114+
'-m',
115+
'docker_image_size_limit',
116+
image_name,
117+
'1 kb',
118+
'--current-size',
119+
'--exit-zero',
120+
],
121+
stdout=subprocess.PIPE,
122+
stderr=subprocess.PIPE,
123+
universal_newlines=True,
124+
encoding='utf8',
125+
)
126+
output, _ = process.communicate()
127+
128+
assert process.returncode == 0
129+
assert 'current size is' in output
130+
assert image_name in output
131+
132+
133+
def test_docker_image_size_limit_as_module_help_flag(image_name: str) -> None: # noqa: WPS118,E501
134+
"""Runs `disl` command via it python module."""
135+
interpreter_binary_name = basename(sys.executable)
136+
process = subprocess.Popen(
137+
[
138+
'{0}'.format(interpreter_binary_name),
139+
'-m',
140+
'docker_image_size_limit',
141+
'--help',
142+
],
143+
stdout=subprocess.PIPE,
144+
stderr=subprocess.PIPE,
145+
universal_newlines=True,
146+
encoding='utf8',
147+
)
148+
output, _ = process.communicate()
149+
150+
assert process.returncode == 0
151+
assert 'docker_image_size_limit' in output

0 commit comments

Comments
 (0)