1
0
mirror of https://github.com/pypiserver/pypiserver synced 2024-12-20 13:55:49 +01:00

chore: use the official docker-meta action instead of ci-helper (#625)

* chore: use the official `docker-meta` action

* chore: remove redundant pattern

* chore: configure push settings

* chore: add concurrency setting

* chore: remove unused `ci_helper` script

* chore: login only on `main` and `tag`s

* fix: remove accidental code

* chore: special `docker` concurrency group
This commit is contained in:
Mitja O 2024-11-24 22:10:24 +01:00 committed by GitHub
parent 849335256e
commit 1e7d6caae7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 34 additions and 148 deletions

@ -124,14 +124,13 @@ jobs:
## PYPI ## PYPI
build-wheel-and-push-to-pypi: create-pypi-wheels:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- "tests" - "tests"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up Python - uses: actions/setup-python@v4
uses: actions/setup-python@v4
with: with:
python-version: ${{ env.LAST_SUPPORTED_PYTHON }} python-version: ${{ env.LAST_SUPPORTED_PYTHON }}
@ -152,56 +151,32 @@ jobs:
## DOCKER (DOCKER HUB & CONTAINER REGISTRY) ## DOCKER (DOCKER HUB & CONTAINER REGISTRY)
# figure out which docker tags we need to push create-docker-images:
docker-determine-tags:
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-docker
cancel-in-progress: ${{ github.ref_type != 'tag' }}
env:
DOCKER_IMAGE_BASE: pypiserver/pypiserver
needs: needs:
- "tests" - "tests"
env:
STABLE_IMAGES: '["pypiserver/pypiserver", "ghcr.io/pypiserver/pypiserver"]'
FLEXIBLE_IMAGES: '["pypiserver/pypiserver"]'
outputs:
tags: "${{ steps.tags.outputs.tags }}"
has_tags: "${{ steps.has_tags.outputs.has_tags }}"
images: ${{ contains(steps.tags.outputs.tags, 'unstable') && env.FLEXIBLE_IMAGES || env.STABLE_IMAGES }}
steps: steps:
- uses: "actions/checkout@v3" - uses: "actions/checkout@v3"
- uses: "actions/setup-python@v4" - id: docker-metadata
uses: docker/metadata-action@v5
with: with:
python-version: ${{ env.LAST_SUPPORTED_PYTHON }} images: |
${{ env.DOCKER_IMAGE_BASE }}
${{ github.ref_type == 'tag' && format('ghcr.io/{0}', env.DOCKER_IMAGE_BASE) || '' }}
tags: |
type=ref,event=pr
type=edge,event=branch,branch=main
type=raw,value=unstable,event=branch,branch=main
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
# This script prints a JSON array of needed docker tags, depending on the - name: Cache Docker layers
# ref. That array is then used to construct the matrix of the
# deploy-docker job
- name: "Get expected docker tags"
id: "tags"
run: >-
echo "::set-output name=tags::$(bin/ci_helper.py ${{ github.ref }} docker_tags)"
# This is needed because GH actions will fail on an empty matrix, so
# we need to be sure the `if` condition is false on the next job if
# the matrix will be empty. The script prints 'true' if the array is
# not empty, or 'false' otherwise.
- name: "Determine whether any tags are needed"
id: "has_tags"
run: >-
echo "::set-output name=has_tags::$(bin/ci_helper.py ${{ github.ref }} has_tags)"
# Deploy any needed docker tags
deploy-docker:
runs-on: "ubuntu-latest"
needs:
- "docker-determine-tags"
if: "${{ fromJson(needs.docker-determine-tags.outputs.has_tags) }}"
strategy:
matrix:
tag: "${{ fromJson(needs.docker-determine-tags.outputs.tags) }}"
image: "${{ fromJson(needs.docker-determine-tags.outputs.images) }}"
steps:
- uses: "actions/checkout@v3"
- name: "Cache Docker layers"
uses: "actions/cache@v3" uses: "actions/cache@v3"
with: with:
path: "/tmp/.buildx-cache" path: "/tmp/.buildx-cache"
@ -209,48 +184,48 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-buildx- ${{ runner.os }}-buildx-
- name: "Login to Docker Hub" - name: Login to Docker Hub
if: github.ref_type == 'tag' || (github.ref_type == 'branch' && github.ref_name == 'main')
uses: "docker/login-action@v3" uses: "docker/login-action@v3"
with: with:
username: "${{ secrets.DOCKER_HUB_USER }}" username: "${{ secrets.DOCKER_HUB_USER }}"
password: "${{ secrets.DOCKER_HUB_TOKEN }}" password: "${{ secrets.DOCKER_HUB_TOKEN }}"
- name: "Login to GitHub Container Registry" - name: Login to GitHub Container Registry
if: github.ref_type == 'tag' || (github.ref_type == 'branch' && github.ref_name == 'main')
uses: "docker/login-action@v3" uses: "docker/login-action@v3"
with: with:
registry: "ghcr.io" registry: "ghcr.io"
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: "Set up QEMU" - uses: "docker/setup-qemu-action@v3"
uses: "docker/setup-qemu-action@v3" - id: buildx
- name: "Set up Docker Buildx"
id: "buildx"
uses: "docker/setup-buildx-action@v3" uses: "docker/setup-buildx-action@v3"
- name: "Build and push" - name: Build and push
id: "docker_build" id: docker_build
uses: "docker/build-push-action@v5" uses: "docker/build-push-action@v5"
with: with:
context: "./" context: "./"
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
file: "./Dockerfile" file: "./Dockerfile"
builder: "${{ steps.buildx.outputs.name }}" builder: "${{ steps.buildx.outputs.name }}"
push: true push: ${{ github.ref_type == 'tag' || (github.ref_type == 'branch' && github.ref_name == 'main') }}
tags: "${{ matrix.image }}:${{ matrix.tag }}" tags: ${{ steps.docker-metadata.outputs.tags }}
labels: ${{ steps.docker-metadata.outputs.labels }}
cache-from: "type=local,src=/tmp/.buildx-cache" cache-from: "type=local,src=/tmp/.buildx-cache"
cache-to: "type=local,dest=/tmp/.buildx-cache" cache-to: "type=local,dest=/tmp/.buildx-cache"
- name: "Image digest" - name: Image digest
run: "echo ${{ steps.docker_build.outputs.digest }}" run: "echo ${{ steps.docker_build.outputs.digest }}"
- name: "Docker Hub Description" - name: Docker Hub Description
uses: peter-evans/dockerhub-description@v3 uses: peter-evans/dockerhub-description@v3
with: with:
username: ${{ secrets.DOCKER_HUB_USER }} username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }} password: ${{ secrets.DOCKER_HUB_TOKEN }}
repository: pypiserver/pypiserver repository: ${{ env.DOCKER_IMAGE_BASE }}
## GITHUB RELEASE DRAFT ## GITHUB RELEASE DRAFT

@ -1,89 +0,0 @@
#!/usr/bin/env python3
"""Output expected docker tags to build in CI."""
import json
import typing as t
import re
from argparse import ArgumentParser, Namespace
RELEASE_RE = re.compile(r"v[0-9]+\.[0-9]+\.[0-9]+(\.post[0-9]+)?")
PRE_RELEASE_RE = re.compile(r"v[0-9]+\.[0-9]+\.[0-9]+(a|b|c|\.?dev)[0-9]+")
BASE_BRANCH = "main"
def parse_args() -> Namespace:
"""Parse cmdline args."""
parser = ArgumentParser()
parser.add_argument(
"ref",
help=(
"The github ref for which CI is running. This may be a full ref "
f"like refs/tags/v1.2.3 or refs/heads/{BASE_BRANCH}, or just a tag/branch "
"name like v1.2.3 or main."
),
)
parser.add_argument(
"action",
help=("The action to perform"),
choices=("docker_tags", "pypi_release", "has_tags"),
)
return parser.parse_args()
def strip_ref_to_name(ref: str) -> str:
"""Strip a full ref to a name."""
strips = ("refs/heads/", "refs/tags/")
for strip in strips:
if ref.startswith(strip):
return ref[len(strip) :]
return ref
def name_to_array(name: str) -> t.Tuple[str, ...]:
"""Convert a ref name to an array of tags to build."""
tags: t.Dict[str, t.Callable[[str], bool]] = {
# unstable for the regular, base, build
"unstable": lambda i: i == BASE_BRANCH,
# latest goes for full releases
"latest": lambda i: RELEASE_RE.fullmatch(i) is not None,
# the tag itself for any release or pre-release tag
name: lambda i: (
RELEASE_RE.fullmatch(i) is not None
or PRE_RELEASE_RE.fullmatch(i) is not None
),
}
return tuple(tag for tag, test in tags.items() if test(name))
def ref_to_json(ref: str) -> str:
"""Convert a ref to a JSON array and return it as a string."""
array = name_to_array(strip_ref_to_name(ref))
return json.dumps(array)
def should_deploy_to_pypi(ref: str) -> str:
"""Return a JSON bool indicating whether we should deploy to PyPI."""
name = strip_ref_to_name(ref)
return json.dumps(
RELEASE_RE.fullmatch(name) is not None
or PRE_RELEASE_RE.fullmatch(name) is not None
)
def main() -> None:
"""Parse args and print the JSON array."""
args = parse_args()
action_switch: t.Dict[str, t.Callable[[], None]] = {
"docker_tags": lambda: print(ref_to_json(args.ref)),
"has_tags": lambda: print(
json.dumps(len(name_to_array(strip_ref_to_name(args.ref))) > 0)
),
"pypi_release": lambda: print(should_deploy_to_pypi(args.ref)),
}
action_switch[args.action]()
if __name__ == "__main__":
main()