diff --git a/README.md b/README.md index 073fd86..4d29d80 100644 --- a/README.md +++ b/README.md @@ -42,17 +42,21 @@ to build your chart(s) and image(s). Add `--push` to publish images to docker hub and `--publish-chart` to publish the chart and index to gh-pages. ``` -usage: chartpress [-h] [--push] [--publish-chart] +usage: chartpress [-h] [--push] [--force-push] [--publish-chart] [--extra-message EXTRA_MESSAGE] [--tag TAG | --long] - [--image-prefix IMAGE_PREFIX] [--reset] [--skip-build] - [--version] [--commit-range COMMIT_RANGE] + [--image-prefix IMAGE_PREFIX] [--reset] + [--skip-build | --force-build] [--version] + [--commit-range COMMIT_RANGE] Automate building and publishing helm charts and associated images. This is used as part of the JupyterHub and Binder projects. optional arguments: -h, --help show this help message and exit - --push Push built images to their docker image registry. + --push Push built images to their image registries, but not + if it would replace an existing image. + --force-push Push built images to their image registries, + regardless if it would replace an existing image. --publish-chart Package a Helm chart and publish it to a Helm chart repository contructed with a GitHub git repository and GitHub pages. @@ -70,6 +74,8 @@ optional arguments: can be configured in chartpress.yaml with the resetTag and resetVersion configurations. --skip-build Skip the image build step. + --force-build Enforce the image build step, regardless of if the + image already is available either locally or remotely. --version Print current chartpress version and exit. --commit-range COMMIT_RANGE Deprecated: this flag will be ignored. The new logic diff --git a/chartpress.py b/chartpress.py index 844e755..670a658 100755 --- a/chartpress.py +++ b/chartpress.py @@ -292,7 +292,7 @@ def _strip_identifiers_build_suffix(identifier): return re.sub(r'[-\.]\d{3,}\.\w{4,}\Z', "", identifier) -def build_images(prefix, images, tag=None, push=False, chart_version=None, skip_build=False, long=False): +def build_images(prefix, images, tag=None, push=False, force_push=False, chart_version=None, force_build=False, skip_build=False, long=False): """Build a collection of docker images Args: @@ -304,9 +304,14 @@ def build_images(prefix, images, tag=None, push=False, chart_version=None, skip_ to modify the image's files. push (bool): Whether to push the resulting images (default: False). + force_push (bool): + Whether to push the built images even if they already exist in the image + registry (default: False). chart_version (str): The latest chart version, trimmed from its build suffix, will be included as a prefix on image tags if `tag` is not specified. + force_build (bool): + To build even if the image is available locally or remotely already. skip_build (bool): Whether to skip the actual image build (only updates tags). long (bool): @@ -364,7 +369,7 @@ def build_images(prefix, images, tag=None, push=False, chart_version=None, skip_ if skip_build: continue - if tag or image_needs_building(image_spec): + if force_build or image_needs_building(image_spec): build_args = render_build_args( options, { @@ -376,8 +381,8 @@ def build_images(prefix, images, tag=None, push=False, chart_version=None, skip_ else: print(f"Skipping build for {image_spec}, it already exists") - if push: - if tag or image_needs_pushing(image_spec): + if push or force_push: + if force_push or image_needs_pushing(image_spec): check_call([ 'docker', 'push', image_spec ]) @@ -603,7 +608,12 @@ def main(args=None): argparser.add_argument( '--push', action='store_true', - help='Push built images to their docker image registry.', + help='Push built images to their image registries, but not if it would replace an existing image.', + ) + argparser.add_argument( + '--force-push', + action='store_true', + help='Push built images to their image registries, regardless if it would replace an existing image.', ) argparser.add_argument( '--publish-chart', @@ -636,11 +646,17 @@ def main(args=None): action='store_true', help="Skip image build step and reset Chart.yaml's version field and values.yaml's image tags. What it resets to can be configured in chartpress.yaml with the resetTag and resetVersion configurations.", ) - argparser.add_argument( + skip_or_force_build_group = argparser.add_mutually_exclusive_group() + skip_or_force_build_group.add_argument( '--skip-build', action='store_true', help='Skip the image build step.', ) + skip_or_force_build_group.add_argument( + '--force-build', + action='store_true', + help='Enforce the image build step, regardless of if the image already is available either locally or remotely.', + ) argparser.add_argument( '--version', action='store_true', @@ -695,7 +711,9 @@ def main(args=None): images=chart['images'], tag=args.tag if not args.reset else chart.get('resetTag', 'set-by-chartpress'), push=args.push, + force_push=args.force_push, chart_version=chart_version, + force_build=args.force_build, skip_build=args.skip_build or args.reset, long=args.long, ) diff --git a/tests/README.md b/tests/README.md index a655b2f..4659141 100644 --- a/tests/README.md +++ b/tests/README.md @@ -17,7 +17,7 @@ pytest -vx --flakes ``` ## Not yet tested -- `--push` +- `--push` or `--force-push` ## References - [pytest](https://docs.pytest.org) diff --git a/tests/test_repo_interactions.py b/tests/test_repo_interactions.py index bb293ea..66404c9 100644 --- a/tests/test_repo_interactions.py +++ b/tests/test_repo_interactions.py @@ -10,6 +10,8 @@ def test_git_repo_fixture(git_repo): assert os.path.isfile("chartpress.yaml") assert os.path.isfile("testchart/Chart.yaml") assert os.path.isfile("testchart/values.yaml") + assert os.path.isfile("testchart/templates/configmap.yaml") + assert os.path.isfile("image/Dockerfile") # assert there is another branch to contain published content as well git_repo.git.checkout("gh-pages") @@ -45,22 +47,24 @@ def test_chartpress_run(git_repo, capfd): out = _capture_output(["--reset"], capfd) assert f"Updating testchart/Chart.yaml: version: 0.0.1-test.reset.version" in out assert f"Updating testchart/values.yaml: image: testchart/testimage:test-reset-tag" in out - assert f"Updating testchart/values.yaml: list.0: testchart/testimage:test-reset-tag" in out - assert f"Updating testchart/values.yaml: list.1.image: testchart/testimage:test-reset-tag" in out + # verify that we don't need to rebuild the image out = _capture_output([], capfd) assert f"Skipping build" in out + # verify usage of --force-build + out = _capture_output(["--force-build"], capfd) + assert f"Successfully tagged" in out + + # verify usage --skip-build and --tag tag = "1.2.3-test.tag" out = _capture_output(["--skip-build", "--tag", tag], capfd) assert f"Successfully tagged" not in out assert f"Updating testchart/Chart.yaml: version: {tag}" in out assert f"Updating testchart/values.yaml: image: testchart/testimage:{tag}" in out - assert f"Updating testchart/values.yaml: list.0: testchart/testimage:{tag}" in out - assert f"Updating testchart/values.yaml: list.1.image: testchart/testimage:{tag}" in out # verify a real git tag is detected @@ -77,16 +81,12 @@ def test_chartpress_run(git_repo, capfd): out = _capture_output(["--skip-build", "--long"], capfd) assert f"Updating testchart/Chart.yaml: version: {tag}.000.{sha}" in out assert f"Updating testchart/values.yaml: image: testchart/testimage:{tag}.000.{sha}" in out - assert f"Updating testchart/values.yaml: list.0: testchart/testimage:{tag}.000.{sha}" in out - assert f"Updating testchart/values.yaml: list.1.image: testchart/testimage:{tag}.000.{sha}" in out # verify usage of --image-prefix out = _capture_output(["--skip-build", "--image-prefix", "test-prefix/"], capfd) assert f"Updating testchart/Chart.yaml: version: {tag}" in out assert f"Updating testchart/values.yaml: image: test-prefix/testimage:{tag}" in out - assert f"Updating testchart/values.yaml: list.0: test-prefix/testimage:{tag}" in out - assert f"Updating testchart/values.yaml: list.1.image: test-prefix/testimage:{tag}" in out # verify usage of --publish-chart and --extra-message @@ -98,11 +98,13 @@ def test_chartpress_run(git_repo, capfd): ], capfd, ) + # verify output of --publish-chart assert "Branch 'gh-pages' set up to track remote branch 'gh-pages' from 'origin'." in out assert "Successfully packaged chart and saved it to:" in out assert f"/testchart-{tag}.tgz" in out + # checkout gh-pages git_repo.git.stash() git_repo.git.checkout("gh-pages") @@ -118,6 +120,7 @@ def test_chartpress_run(git_repo, capfd): automatic_helm_chart_repo_commit = git_repo.commit("HEAD") assert "test added --extra-message" in automatic_helm_chart_repo_commit.message + # return to master git_repo.git.checkout("master") git_repo.git.stash("pop") @@ -134,11 +137,13 @@ def test_chartpress_run(git_repo, capfd): ], capfd, ) + # verify output of --publish-chart assert "Branch 'gh-pages' set up to track remote branch 'gh-pages' from 'origin'." in out assert "Successfully packaged chart and saved it to:" in out assert f"/testchart-{tag}.001.{sha}.tgz" in out + # checkout gh-pages git_repo.git.stash() git_repo.git.checkout("gh-pages") @@ -153,6 +158,7 @@ def test_chartpress_run(git_repo, capfd): assert f"version: {tag}" in index_yaml assert f"version: {tag}.001.{sha}" in index_yaml + # return to master git_repo.git.checkout("master") git_repo.git.stash("pop")