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

Make rebuilding the app images smarter #694

Merged
merged 1 commit into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions .ci/scripts/check_up_to_date.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import argparse
import requests
from urllib.parse import urljoin
from packaging.version import parse
from packaging.requirements import Requirement

PACKAGES = [
"pulp-ansible",
"pulp-container",
"pulp-deb",
"pulp-gem",
"pulp-maven",
"pulp-python",
"pulp-rpm",
"pulp-ostree"
]

INDEX = "https://pypi.org"

def check_update(branch, current_versions):
"""
Go through each of the image's main Pulp components and see if there is a new version available.
"""
new_versions = {}
# Get the latest Z (or Y) pulpcore release for this branch
core_pypi_response = requests.get(urljoin(INDEX, "pypi/pulpcore/json"))
assert core_pypi_response.status_code == 200
core_version = parse(current_versions["pulpcore"])
for version, release in core_pypi_response.json()["releases"].items():
cur_version = parse(version)
if cur_version > core_version:
if branch != "latest":
if cur_version.major != core_version.major or cur_version.minor != core_version.minor:
continue
core_version = cur_version
new_versions["pulpcore"] = core_version

# Now check each plugin to see if they need updates
for plugin in PACKAGES:
if plugin not in current_versions:
continue
plugin_version = parse(current_versions[plugin])
plugin_pypi_response = requests.get(urljoin(INDEX, f"pypi/{plugin}/json"))
assert plugin_pypi_response.status_code == 200
plugin_versions = sorted((parse(v) for v in plugin_pypi_response.json()["releases"].keys()), reverse=True)
for version in plugin_versions:
if version <= plugin_version:
break
version_pypi_response = requests.get(urljoin(INDEX, f"pypi/{plugin}/{version}/json"))
assert version_pypi_response.status_code == 200
deps = version_pypi_response.json()["info"]["requires_dist"]
core_dep = next(filter(lambda dep: dep.startswith("pulpcore"), deps))
if core_version in Requirement(core_dep).specifier:
new_versions[plugin] = version
break

if new_versions:
print("Updates needed for:")
for plugin, version in new_versions.items():
print(f"{plugin}: {current_versions[plugin]} -> {version!s}")
exit(100)

print("No updates needed :)")

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("branch")
parser.add_argument("versions", type=argparse.FileType("r"))
opts = parser.parse_args()
versions = {}
for line in opts.versions:
plugin, _, version = line.rstrip("\n").partition("==")
versions[plugin] = version
check_update(opts.branch, versions)
3 changes: 3 additions & 0 deletions .github/actions/base_images/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ outputs:
base_cache_key:
value: ${{ steps.hash_key.outputs.base_cache_key }}
description: "The cache key the built images were uploaded to."
rebuilt_images:
value: ${{ env.BUILD_IMAGES }}
description: "The images that were rebuilt or empty"

runs:
using: "composite"
Expand Down
60 changes: 58 additions & 2 deletions .github/actions/build_image/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ inputs:
description: 'Use the latest pulp-ui when building the image'
default: 'false'
required: false
built_base_images:
description: 'A JSON list of the base-images that were freshly rebuilt prior'
required: true
outputs:
app_version:
value: ${{ steps.image_version_branch.outputs.app_version }}
description: 'The full version of the app in the built image'
app_branch:
value: ${{ steps.image_version_branch.outputs.app_branch }}
description: 'The pulpcore version branch that the built image matches'
rebuilt_images:
value: ${{ env.BUILD }}
description: 'true/false if the app image was rebuilt'

runs:
using: "composite"
Expand All @@ -39,7 +45,7 @@ runs:
shell: bash

- name: Restore podman images from cache
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
key: base-images=${{ inputs.image_cache_key }}
path: base-images.tar.gz
Expand All @@ -62,7 +68,42 @@ runs:
fi
shell: bash

- name: Set hash key
run: echo "VERSIONKEY=versions-${{ github.ref_name }}=${{ hashFiles(format('images/{0}/stable/**', inputs.image_name)) }}" >> $GITHUB_ENV
shell: bash

- name: Restore last builds versions from cache
id: cache
uses: actions/cache/restore@v4
with:
key: ${{ env.VERSIONKEY }}
path: versions.freeze

- name: Check if rebuild is needed
run: |
# Rebuilds are needed for
# 1. CI is being ran in a PR or is a nightly run
# 2. Base images were rebuilt
# 3. New pulp versions was released
if [[ "${{ github.event_name }}" == "pull_request" || "${{ inputs.image_variant }}" == "nightly" || -n "${{ inputs.built_base_images }}" ]]; then
echo "BUILD=true" >> $GITHUB_ENV
else
if [[ "${{ steps.cache.outputs.cache-hit }}" == "false" ]]; then
echo "BUILD=true" >> $GITHUB_ENV
else
# Script returns non-zero (100) when new versions are available
if python .ci/scripts/check_up_to_date.py ${{ github.ref_name }} versions.freeze; then
echo "BUILD=false" >> $GITHUB_ENV
else
echo "BUILD=true" >> $GITHUB_ENV
fi
fi
fi
echo "Going to rebuild: ${BUILD}"
shell: bash

- name: Build images
if: env.BUILD == 'true'
run: |
podman version
buildah version
Expand All @@ -84,11 +125,26 @@ runs:
id: image_version_branch
run: |
base_image=$(echo ${{ inputs.image_name }} | cut -d '-' -f1)
app_version=$(podman run --pull=never pulp/${{ inputs.image_name }}:ci-amd64 bash -c "pip3 show pulpcore | sed -n -e 's/Version: //p'")
podman run --pull=never pulp/${{ inputs.image_name }}:ci-amd64 bash -c "pip3 freeze | grep pulp" >> versions.freeze
app_version=$(grep pulpcore versions.freeze | sed -n -e 's/pulpcore==//p')
app_branch=$(echo ${app_version} | grep -oP '\d+\.\d+')

echo "APP_VERSION: ${app_version}"
echo "APP_BRANCH: ${app_branch}"
echo "app_version=${app_version}" >> "$GITHUB_OUTPUT"
echo "app_branch=${app_branch}" >> "$GITHUB_OUTPUT"
shell: bash

- name: Clear cache for next upload
if: env.BUILD == 'true' && steps.cache.outputs.cache-hit == 'true' && github.event_name != 'pull_request'
run: |
echo "Deleting existing cache for ${{ env.VERSIONKEY }}"
gh cache delete ${{ env.VERSIONKEY }} -R ${{ github.repository }}
shell: bash

- name: Cache versions
if: env.BUILD == 'true' && github.event_name != 'pull_request'
uses: actions/cache/save@v4
with:
key: ${{ env.VERSIONKEY }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same cache that we key with base_images={{ env.VERSIONKEY }}?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the same cache container, but not the same entry. base_images={{ env.VERSIONKEY }} was a typo left over from copy-pasting. I fixed it, now the key should only be {{ env.VERSIONKEY }} for the versions.freeze file.

path: versions.freeze
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ jobs:
image_variant: "stable"
image_cache_key: ${{ needs.base-images.outputs.base_cache_key }}
latest_ui: ${{ github.base_ref == 'latest' }}
built_base_images: ${{ needs.base-images.outputs.rebuilt_images }}

- name: Test App Image
uses: "./.github/actions/test_image"
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,18 @@ jobs:
image_variant: ${{ matrix.image_variant }}
image_cache_key: ${{ needs.base-images.outputs.base_cache_key }}
latest_ui: ${{ github.ref_name == 'latest' }}
built_base_images: ${{ needs.base-images.outputs.rebuilt_images }}


- name: Test App Image
if: matrix.image_variant != 'nightly'
if: ${{ matrix.image_variant != 'nightly' && steps.build_image.outputs.rebuilt_images == 'true' }}
uses: "./.github/actions/test_image"
with:
image_name: ${{ matrix.image_name }}
app_branch: ${{ steps.build_image.outputs.app_branch }}

- name: Set tags
if: ${{ steps.build_image.outputs.rebuilt_images == 'true' }}
run: |
base_image=$(echo ${{ matrix.image_name }} | cut -d '-' -f1)
if [[ "${{ matrix.image_name }}" == "pulp" ]]; then
Expand Down Expand Up @@ -211,6 +214,7 @@ jobs:
echo "TAGS=${tags}" >> $GITHUB_ENV

- name: Publish App Image
if: ${{ steps.build_image.outputs.rebuilt_images == 'true' }}
uses: "./.github/actions/publish_images"
with:
image_names: ${{ env.IMAGES }}
Expand Down