mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-12 20:15:26 +00:00
Merge pull request #2748 from arc53/ci/release-chain
ci: publish Docker images and the PyPI package from the release workflow
This commit is contained in:
4 files changed
+120
-22
No files matched your search
@@ -1,5 +1,12 @@
|
||||
name: Backend release
|
||||
|
||||
# A version bump on main tags the commit, creates the GitHub release, then
|
||||
# publishes the Docker images and the PyPI package by calling those workflows.
|
||||
# The release is created with GITHUB_TOKEN, and GitHub never starts workflows
|
||||
# from events that token produces, so the `release: published` triggers on the
|
||||
# publish workflows would not fire (0.18.0 got no images that way). Releases
|
||||
# created by hand still publish through those triggers.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
@@ -7,8 +14,7 @@ on:
|
||||
- 'docsgpt/version.py'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: backend-release
|
||||
@@ -18,6 +24,14 @@ jobs:
|
||||
release:
|
||||
if: github.repository == 'arc53/DocsGPT'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
version: ${{ steps.ver.outputs.version }}
|
||||
# The publish jobs run only for a release this run created. A release
|
||||
# that already existed was either made by hand (its release event
|
||||
# published it) or made by an earlier run: re-run that run's failed jobs.
|
||||
created: ${{ steps.check.outputs.release == 'missing' }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
@@ -34,20 +48,30 @@ jobs:
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "Resolved version: $VERSION"
|
||||
|
||||
- name: Check if tag already exists
|
||||
- name: Check what already exists
|
||||
id: check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ steps.ver.outputs.version }}
|
||||
run: |
|
||||
# The tag and the release are checked separately: a run that pushed
|
||||
# the tag and then failed to create the release must still create
|
||||
# it (and publish) when re-run.
|
||||
if git ls-remote --tags --exit-code origin "refs/tags/$VERSION" >/dev/null 2>&1; then
|
||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Tag $VERSION already exists on origin — skipping."
|
||||
echo "tag=exists" >> "$GITHUB_OUTPUT"
|
||||
echo "Tag $VERSION already exists on origin."
|
||||
else
|
||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=missing" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
if gh release view "$VERSION" >/dev/null 2>&1; then
|
||||
echo "release=exists" >> "$GITHUB_OUTPUT"
|
||||
echo "Release $VERSION already exists — nothing to publish."
|
||||
else
|
||||
echo "release=missing" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Create and push tag
|
||||
if: steps.check.outputs.exists == 'false'
|
||||
if: steps.check.outputs.tag == 'missing'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ steps.ver.outputs.version }}
|
||||
@@ -63,7 +87,7 @@ jobs:
|
||||
"$VERSION"
|
||||
|
||||
- name: Create GitHub release
|
||||
if: steps.check.outputs.exists == 'false'
|
||||
if: steps.check.outputs.release == 'missing'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ steps.ver.outputs.version }}
|
||||
@@ -71,3 +95,26 @@ jobs:
|
||||
gh release create "$VERSION" \
|
||||
--title "v$VERSION" \
|
||||
--generate-notes
|
||||
|
||||
docker:
|
||||
needs: release
|
||||
if: needs.release.outputs.created == 'true'
|
||||
uses: $/.github/workflows/ci.yml
|
||||
with:
|
||||
version: ${{ needs.release.outputs.version }}
|
||||
secrets:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
permissions:
|
||||
contents: write # release-assets attaches the compose file to the release
|
||||
packages: write
|
||||
|
||||
pypi:
|
||||
needs: release
|
||||
if: needs.release.outputs.created == 'true'
|
||||
uses: $/.github/workflows/pypi-publish.yml
|
||||
with:
|
||||
version: ${{ needs.release.outputs.version }}
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
@@ -1,8 +1,31 @@
|
||||
name: Build and push DocsGPT Docker image
|
||||
|
||||
# Runs for a release created by hand (the release event), or called by the
|
||||
# backend-release workflow with the version it just tagged: GitHub never starts
|
||||
# workflows from events GITHUB_TOKEN produces, so a bot-created release does not
|
||||
# fire the release trigger on its own.
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
description: Release tag to build and push (the images are tagged with it)
|
||||
type: string
|
||||
required: true
|
||||
secrets:
|
||||
DOCKER_USERNAME:
|
||||
required: true
|
||||
DOCKER_PASSWORD:
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
# The tag being published: passed in by the caller, or the release's own.
|
||||
RELEASE_TAG: ${{ inputs.version || github.event.release.tag_name }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -58,7 +81,7 @@ jobs:
|
||||
ghcr.io/${{ github.repository_owner }}/docsgpt
|
||||
labels: |
|
||||
org.opencontainers.image.title=DocsGPT${{ matrix.variant }}
|
||||
org.opencontainers.image.version=${{ github.event.release.tag_name }}
|
||||
org.opencontainers.image.version=${{ env.RELEASE_TAG }}
|
||||
|
||||
- name: Build and push platform-specific images
|
||||
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
||||
@@ -71,8 +94,8 @@ jobs:
|
||||
EXTRAS=${{ matrix.variant == '-docling' && 'docling' || '' }}
|
||||
INSTALL_TESSERACT=${{ matrix.variant == '-docling' && 'true' || 'false' }}
|
||||
tags: |
|
||||
${{ env.DOCKERHUB_NAMESPACE }}/docsgpt:${{ github.event.release.tag_name }}${{ matrix.variant }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}
|
||||
ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}${{ matrix.variant }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}
|
||||
${{ env.DOCKERHUB_NAMESPACE }}/docsgpt:${{ env.RELEASE_TAG }}${{ matrix.variant }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}
|
||||
ghcr.io/${{ github.repository_owner }}/docsgpt:${{ env.RELEASE_TAG }}${{ matrix.variant }}-${{ matrix.platform == 'linux/arm64' && 'arm64' || 'amd64' }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
provenance: false
|
||||
sbom: false
|
||||
@@ -118,7 +141,7 @@ jobs:
|
||||
|
||||
- name: Create and push multi-arch manifests
|
||||
env:
|
||||
TAG: ${{ github.event.release.tag_name }}${{ matrix.variant }}
|
||||
TAG: ${{ env.RELEASE_TAG }}${{ matrix.variant }}
|
||||
LATEST: latest${{ matrix.variant }}
|
||||
run: |
|
||||
set -e
|
||||
@@ -145,6 +168,6 @@ jobs:
|
||||
- name: Attach the standalone compose file to the release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
TAG: ${{ env.RELEASE_TAG }}
|
||||
run: |
|
||||
gh release upload "$TAG" deployment/docker-compose-standalone.yaml --clobber
|
||||
@@ -1,13 +1,25 @@
|
||||
name: Publish to PyPI
|
||||
|
||||
# Trusted publishing: PyPI trusts this workflow file in arc53/DocsGPT running
|
||||
# in the `pypi` environment, so no API token is stored. A published release
|
||||
# (which the backend-release workflow creates when docsgpt/version.py changes
|
||||
# on main) publishes to PyPI; a manual run publishes to TestPyPI by default.
|
||||
# in the `pypi` environment, so no API token is stored. The backend-release
|
||||
# workflow calls this one after it tags a version bump on main (a release it
|
||||
# creates with GITHUB_TOKEN never fires the release trigger); a release created
|
||||
# by hand publishes through that trigger; a manual run publishes to TestPyPI
|
||||
# by default.
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
description: Version the archives must carry (empty skips the check)
|
||||
type: string
|
||||
default: ''
|
||||
target:
|
||||
description: Package index to publish to (pypi or testpypi)
|
||||
type: string
|
||||
default: pypi
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target:
|
||||
@@ -19,6 +31,11 @@ on:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
# The version being released: passed in by the caller, or the release's tag;
|
||||
# empty for a manual run, which publishes whatever main builds.
|
||||
RELEASE_VERSION: ${{ inputs.version || github.event.release.tag_name }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository == 'arc53/DocsGPT'
|
||||
@@ -33,15 +50,22 @@ jobs:
|
||||
# A publishing workflow must not restore a cache another job could have poisoned.
|
||||
enable-cache: false
|
||||
|
||||
- name: The publish target is one this workflow knows
|
||||
# A caller's typo would otherwise skip both publish jobs and upload nothing.
|
||||
if: inputs.target != '' && inputs.target != 'pypi' && inputs.target != 'testpypi'
|
||||
env:
|
||||
TARGET: ${{ inputs.target }}
|
||||
run: |
|
||||
echo "::error::unknown target '$TARGET': expected pypi or testpypi"
|
||||
exit 1
|
||||
|
||||
- name: Build the sdist and wheel
|
||||
run: uv build
|
||||
|
||||
- name: The archives carry the released version
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
TAG: ${{ github.event.release.tag_name }}
|
||||
if: env.RELEASE_VERSION != ''
|
||||
run: |
|
||||
ls "dist/docsgpt-${TAG}.tar.gz" "dist/docsgpt-${TAG}-py3-none-any.whl"
|
||||
ls "dist/docsgpt-${RELEASE_VERSION}.tar.gz" "dist/docsgpt-${RELEASE_VERSION}-py3-none-any.whl"
|
||||
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
@@ -50,6 +74,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
|
||||
publish-pypi:
|
||||
# A release event, or a call or manual run aimed at PyPI (the call default).
|
||||
if: github.event_name == 'release' || inputs.target == 'pypi'
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
@@ -67,7 +92,7 @@ jobs:
|
||||
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
||||
|
||||
publish-testpypi:
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
|
||||
if: inputs.target == 'testpypi'
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
@@ -84,3 +109,6 @@ jobs:
|
||||
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
# TestPyPI is a rehearsal target: a version that is already there
|
||||
# must not fail the run.
|
||||
skip-existing: true
|
||||
@@ -22,4 +22,4 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
|
||||
uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3
|
||||
Reference in new issue
Block a user