Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chart and image versioning, and Chart.yaml's --reset interaction #52

Merged
merged 9 commits into from
Oct 17, 2019
34 changes: 18 additions & 16 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ script:
- chartpress --version
- chartpress --help
- pyflakes .
- |
# This is a workaround to an issue caused by the existence of a docker registry
# mirror in our CI environment. Without this fix that removes the mirror,
# chartpress fails to realize the existence of already built images and rebuilds
# them.
#
# ref: https://github.com/moby/moby/issues/39120
echo '{"mtu": 1460}' | sudo dd of=/etc/docker/daemon.json

# This is a workaround to an issue caused by the existence of a docker registry
# mirror in our CI environment. Without this fix that removes the mirror,
# chartpress fails to realize the existence of already built images and rebuilds
# them.
#
# ref: https://github.com/moby/moby/issues/39120
- |-
echo '{"mtu": 1460}' | sudo dd of=/etc/docker/daemon.json &&
sudo systemctl restart docker
- |
# run chartpress on zero-to-jupyterhub-k8s
git clone https://github.com/jupyterhub/zero-to-jupyterhub-k8s
cd zero-to-jupyterhub-k8s

# run chartpress on zero-to-jupyterhub-k8s
- |-
git clone https://github.com/jupyterhub/zero-to-jupyterhub-k8s &&
cd zero-to-jupyterhub-k8s &&
chartpress
git --no-pager diff
- |
chartpress --reset
git --no-pager diff
- git --no-pager diff

- chartpress --reset
- git --no-pager diff
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

Chart and image versioning, and Chart.yaml's --reset interaction [#52](https://github.com/jupyterhub/chartpress/pull/52) ([@consideRatio](https://github.com/consideRatio))

Add --version flag [#45](https://github.com/jupyterhub/chartpress/pull/45) ([@consideRatio](https://github.com/consideRatio))

## 0.3
Expand Down
85 changes: 52 additions & 33 deletions chartpress.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def last_modified_commit(*paths, **kwargs):
'--pretty=format:%h',
'--',
*paths
], **kwargs).decode('utf-8')
], **kwargs).decode('utf-8').strip()


def last_modified_date(*paths, **kwargs):
Expand All @@ -77,7 +77,7 @@ def last_modified_date(*paths, **kwargs):
'--date=iso',
'--',
*paths
], **kwargs).decode('utf-8')
], **kwargs).decode('utf-8').strip()


def path_touched(*paths, commit_range):
Expand Down Expand Up @@ -195,12 +195,12 @@ def image_needs_building(image):
return image_needs_pushing(image)


def build_images(prefix, images, tag=None, commit_range=None, push=False, chart_version=None, skip_build=False):
def build_images(prefix, images, tag=None, commit_range=None, push=False, chart_tag=None, skip_build=False):
"""Build a collection of docker images

Args:
prefix (str): the prefix to add to images
images (dict): dict of image-specs from chartpress.yml
prefix (str): the prefix to add to image names
images (dict): dict of image-specs from chartpress.yaml
tag (str):
Specific tag to use instead of the last modified commit.
If unspecified the tag for each image will be the hash of the last commit
Expand All @@ -211,8 +211,8 @@ def build_images(prefix, images, tag=None, commit_range=None, push=False, chart_
it will not be rebuilt.
push (bool):
Whether to push the resulting images (default: False).
chart_version (str):
The chart version, included as a prefix on image tags
chart_tag (str):
The latest chart tag, included as a prefix on image tags
if `tag` is not specified.
skip_build (bool):
Whether to skip the actual image build (only updates tags).
Expand All @@ -224,12 +224,21 @@ def build_images(prefix, images, tag=None, commit_range=None, push=False, chart_
# include chartpress.yaml itself as it can contain build args and
# similar that influence the image that would be built
paths = list(options.get('paths', [])) + [image_path, 'chartpress.yaml']
last_commit = last_modified_commit(*paths)
last_image_commit = last_modified_commit(*paths)
if tag is None:
if chart_version:
image_tag = "{}-{}".format(chart_version, last_commit)
else:
image_tag = last_commit
n_commits = check_output(
[
'git', 'rev-list', '--count',
# Note that the 0.0.1 chart_tag may not exist as it was a
# workaround to handle git histories with no tags in the
# current branch. Also, if the chart_tag is a later git
# reference than the last_image_commit, this command will
# return 0.
f'{chart_tag + ".." if chart_tag != "0.0.1" else ""}{last_image_commit}',
],
echo=False,
).decode('utf-8').strip()
image_tag = f"{chart_tag}_{n_commits}-{last_image_commit}"
image_name = prefix + name
image_spec = '{}:{}'.format(image_name, image_tag)

Expand All @@ -241,12 +250,11 @@ def build_images(prefix, images, tag=None, commit_range=None, push=False, chart_
if skip_build:
continue


if tag or image_needs_building(image_spec):
build_args = render_build_args(
options,
{
'LAST_COMMIT': last_commit,
'LAST_COMMIT': last_image_commit,
'TAG': image_tag,
},
)
Expand Down Expand Up @@ -300,21 +308,27 @@ def build_values(name, values_mods):
yaml.dump(values, f)


def build_chart(name, version=None, paths=None, reset=False):
"""Update chart with specified version or last-modified commit in path(s)"""
def build_chart(name, version=None, paths=None):
"""Update Chart.yaml with specified version or last-modified commit in path(s)"""
chart_file = os.path.join(name, 'Chart.yaml')
with open(chart_file) as f:
chart = yaml.load(f)

if version is None:
if paths is None:
paths = ['.']
commit = last_modified_commit(*paths)
last_chart_commit = last_modified_commit(*paths)

if reset:
version = chart['version'].split('-')[0]
else:
version = chart['version'].split('-')[0] + '-' + commit
if version is None:
try:
git_describe = check_output(['git', 'describe', '--tags', '--long', last_chart_commit]).decode('utf8').strip()
latest_tag_in_branch, n_commits, sha = git_describe.rsplit('-', maxsplit=2)
version = f"{latest_tag_in_branch}+{int(n_commits):03d}.{sha}"
except subprocess.CalledProcessError:
# no tags on branch: fallback to the SemVer 2 compliant version
# 0.0.1+<n_comits>.<last_chart_commit>
n_commits = check_output(
['git', 'rev-list', '--count', last_chart_commit],
echo=False,
).decode('utf-8').strip()
version = f"0.0.1+{int(n_commits):03d}.{last_chart_commit}"

chart['version'] = version

Expand Down Expand Up @@ -390,7 +404,7 @@ def main():
argparser.add_argument('--image-prefix', default=None,
help='Override image prefix with this value')
argparser.add_argument('--reset', action='store_true',
help='Reset image tags')
help="Skip image build and reset Chart.yaml's version field and values.yaml's image tags")
argparser.add_argument('--skip-build', action='store_true',
help='Skip image build, only render the charts')
argparser.add_argument('--version', action='store_true',
Expand All @@ -408,12 +422,15 @@ def main():
for chart in config['charts']:
chart_paths = ['.'] + list(chart.get('paths', []))

version = args.tag
if version:
# version of the chart shouldn't have leading 'v' prefix
# if tag is of the form 'v1.2.3'
version = version.lstrip('v')
chart_version = build_chart(chart['name'], paths=chart_paths, version=version, reset=args.reset)
chart_version = args.tag
if chart_version:
# The chart's version shouldn't have leading 'v' prefix if tag is of
# the form 'v1.2.3', as that would break Chart.yaml's SemVer 2
# requirement on the version field.
chart_version = chart_version.lstrip('v')
if args.reset:
chart_version = chart.get('resetTag', 'set-by-chartpress')
chart_version = build_chart(chart['name'], paths=chart_paths, version=chart_version)

if 'images' in chart:
image_prefix = args.image_prefix if args.image_prefix is not None else chart['imagePrefix']
Expand All @@ -423,8 +440,10 @@ def main():
tag=args.tag if not args.reset else chart.get('resetTag', 'set-by-chartpress'),
commit_range=args.commit_range,
push=args.push,
# exclude `-<hash>` from chart_version prefix for images
chart_version=chart_version.split('-', 1)[0],
# chart_tag will act as a image tag prefix, we can get it from
# the chart_version by stripping away the build part of the
# SemVer 2 compliant chart_version.
chart_tag=chart_version.split('+')[0],
skip_build=args.skip_build or args.reset,
)
build_values(chart['name'], value_mods)
Expand Down