feat(release): 统一 2.x 镜像标签通道 (#512)
This commit is contained in:
1 parent
d6a66e9877
commit
3d14c18dcf
10 files changed
+832
-181
No files matched your search
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
VERSION_PATTERN = re.compile(
|
||||
r"^v?(0|[1-9][0-9]*)\."
|
||||
r"(0|[1-9][0-9]*)\."
|
||||
r"(0|[1-9][0-9]*)"
|
||||
r"(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$"
|
||||
)
|
||||
|
||||
|
||||
def parse_version(raw: str) -> tuple[int, int, int, list[tuple[bool, int | str]]]:
|
||||
match = VERSION_PATTERN.fullmatch(raw)
|
||||
if match is None:
|
||||
raise ValueError(f"invalid semantic version: {raw!r}")
|
||||
|
||||
prerelease: list[tuple[bool, int | str]] = []
|
||||
raw_prerelease = match.group(4)
|
||||
if raw_prerelease is not None:
|
||||
for identifier in raw_prerelease.split("."):
|
||||
if identifier.isascii() and identifier.isdigit():
|
||||
if len(identifier) > 1 and identifier.startswith("0"):
|
||||
raise ValueError(
|
||||
f"numeric prerelease identifier has a leading zero: {raw!r}"
|
||||
)
|
||||
prerelease.append((True, int(identifier)))
|
||||
else:
|
||||
prerelease.append((False, identifier))
|
||||
|
||||
return int(match.group(1)), int(match.group(2)), int(match.group(3)), prerelease
|
||||
|
||||
|
||||
def compare_identifiers(
|
||||
left: tuple[bool, int | str], right: tuple[bool, int | str]
|
||||
) -> int:
|
||||
left_numeric, left_value = left
|
||||
right_numeric, right_value = right
|
||||
if left_numeric != right_numeric:
|
||||
return -1 if left_numeric else 1
|
||||
return (left_value > right_value) - (left_value < right_value)
|
||||
|
||||
|
||||
def compare_versions(left_raw: str, right_raw: str) -> int:
|
||||
left_major, left_minor, left_patch, left_prerelease = parse_version(left_raw)
|
||||
right_major, right_minor, right_patch, right_prerelease = parse_version(right_raw)
|
||||
|
||||
left_core = (left_major, left_minor, left_patch)
|
||||
right_core = (right_major, right_minor, right_patch)
|
||||
if left_core != right_core:
|
||||
return (left_core > right_core) - (left_core < right_core)
|
||||
|
||||
if not left_prerelease and not right_prerelease:
|
||||
return 0
|
||||
if not left_prerelease:
|
||||
return 1
|
||||
if not right_prerelease:
|
||||
return -1
|
||||
|
||||
for left_identifier, right_identifier in zip(left_prerelease, right_prerelease):
|
||||
comparison = compare_identifiers(left_identifier, right_identifier)
|
||||
if comparison != 0:
|
||||
return comparison
|
||||
return (len(left_prerelease) > len(right_prerelease)) - (
|
||||
len(left_prerelease) < len(right_prerelease)
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) != 3:
|
||||
print("usage: release-compare-semver.py LEFT RIGHT", file=sys.stderr)
|
||||
return 2
|
||||
try:
|
||||
comparison = compare_versions(sys.argv[1], sys.argv[2])
|
||||
except ValueError as error:
|
||||
print(error, file=sys.stderr)
|
||||
return 2
|
||||
print(comparison)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# 读取多架构镜像内的软件版本标签。registry 返回值属于不可信输入,只有两个
|
||||
# 目标架构完全一致且满足严格 SemVer(允许 Git 的 v 前缀)时才输出。
|
||||
|
||||
image="${1:?image is required}"
|
||||
|
||||
if ! inspection="$(
|
||||
docker buildx imagetools inspect "${image}" --format '{{json .}}' 2>&1
|
||||
)"; then
|
||||
printf '%s\n' "${inspection}" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
semver_regex='^v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'
|
||||
if ! version="$(
|
||||
jq -er \
|
||||
--arg semver_regex "${semver_regex}" \
|
||||
'[
|
||||
.image["linux/amd64"].config.Labels[
|
||||
"org.opencontainers.image.version"
|
||||
],
|
||||
.image["linux/arm64"].config.Labels[
|
||||
"org.opencontainers.image.version"
|
||||
]
|
||||
]
|
||||
| if
|
||||
length == 2 and
|
||||
all(type == "string") and
|
||||
.[0] == .[1] and
|
||||
(.[0] | test($semver_regex))
|
||||
then .[0]
|
||||
else error("image version labels are missing, invalid, or inconsistent")
|
||||
end' \
|
||||
<<<"${inspection}" 2>/dev/null
|
||||
)"; then
|
||||
printf 'image version labels are missing, invalid, or inconsistent for %s\n' \
|
||||
"${image}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s' "${version}"
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
release_version="${RELEASE_VERSION:?RELEASE_VERSION is required}"
|
||||
image_exact="${IMAGE_EXACT:?IMAGE_EXACT is required}"
|
||||
image_beta="${IMAGE_BETA:-}"
|
||||
image_major="${IMAGE_MAJOR:?IMAGE_MAJOR is required}"
|
||||
promote_beta="${PROMOTE_BETA:?PROMOTE_BETA is required}"
|
||||
promote_major="${PROMOTE_MAJOR:?PROMOTE_MAJOR is required}"
|
||||
expected_revision="${EXPECTED_REVISION:?EXPECTED_REVISION is required}"
|
||||
expected_ghcr_latest="${EXPECTED_GHCR_LATEST:?EXPECTED_GHCR_LATEST is required}"
|
||||
expected_dockerhub_latest="${EXPECTED_DOCKERHUB_LATEST:?EXPECTED_DOCKERHUB_LATEST is required}"
|
||||
output_file="${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}"
|
||||
|
||||
semver_regex='^v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'
|
||||
digest_regex='^sha256:[0-9a-f]{64}$'
|
||||
alias_regex='^[0-9]+([.][0-9]+)?(-beta)?$'
|
||||
|
||||
[[ "${release_version}" =~ ${semver_regex} ]]
|
||||
[[ "${image_exact}" == "${release_version#v}" ]]
|
||||
[[ "${image_major}" =~ ^[0-9]+$ ]]
|
||||
[[ -z "${image_beta}" || "${image_beta}" =~ ${alias_regex} ]]
|
||||
[[ "${promote_beta}" == "true" || "${promote_beta}" == "false" ]]
|
||||
[[ "${promote_major}" == "true" || "${promote_major}" == "false" ]]
|
||||
[[ "${expected_revision}" =~ ^[0-9a-f]{40}$ ]]
|
||||
[[ "${expected_ghcr_latest}" == "absent" || "${expected_ghcr_latest}" =~ ${digest_regex} ]]
|
||||
[[ "${expected_dockerhub_latest}" == "absent" || "${expected_dockerhub_latest}" =~ ${digest_regex} ]]
|
||||
if [[ "${promote_beta}" == "true" ]]; then
|
||||
test -n "${image_beta}"
|
||||
fi
|
||||
|
||||
repositories=(ghcr.io/tbphp/gpt-load tbphp/gpt-load)
|
||||
exact_digest=
|
||||
for repository in "${repositories[@]}"; do
|
||||
exact="${repository}:${image_exact}"
|
||||
.github/scripts/release-verify-image-revision.sh \
|
||||
"${exact}" "${expected_revision}" >/dev/null
|
||||
digest="$(.github/scripts/release-image-digest.sh "${exact}")"
|
||||
[[ "${digest}" =~ ${digest_regex} ]]
|
||||
if [[ -z "${exact_digest}" ]]; then
|
||||
exact_digest="${digest}"
|
||||
else
|
||||
test "${digest}" = "${exact_digest}"
|
||||
fi
|
||||
done
|
||||
|
||||
channel_is_current=false
|
||||
promote_channel() {
|
||||
local alias="$1"
|
||||
local enabled="$2"
|
||||
local repository=
|
||||
local channel=
|
||||
local current_digest=
|
||||
local current_version=
|
||||
local comparison=
|
||||
local newer_exists=false
|
||||
local expected_digest=
|
||||
local source=
|
||||
local promoted_digest=
|
||||
local promoted_version=
|
||||
|
||||
channel_is_current=false
|
||||
if [[ "${enabled}" != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
for repository in "${repositories[@]}"; do
|
||||
channel="${repository}:${alias}"
|
||||
current_digest="$(.github/scripts/release-image-digest.sh "${channel}")"
|
||||
if [[ "${current_digest}" == "absent" ]]; then
|
||||
continue
|
||||
fi
|
||||
[[ "${current_digest}" =~ ${digest_regex} ]]
|
||||
current_version="$(.github/scripts/release-image-version.sh "${channel}")"
|
||||
comparison="$(
|
||||
python3 .github/scripts/release-compare-semver.py \
|
||||
"${current_version}" "${release_version}"
|
||||
)"
|
||||
[[ "${comparison}" == "-1" || "${comparison}" == "0" || "${comparison}" == "1" ]]
|
||||
if [[ "${comparison}" == "1" ]]; then
|
||||
newer_exists=true
|
||||
elif [[ "${comparison}" == "0" ]]; then
|
||||
expected_digest="${exact_digest}"
|
||||
if [[ "${current_digest}" != "${expected_digest}" ]]; then
|
||||
printf 'channel %s already has version %s with unexpected digest\n' \
|
||||
"${channel}" "${release_version}" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "${newer_exists}" == "true" ]]; then
|
||||
printf 'skip %s: a registry already points to a newer version than %s\n' \
|
||||
"${alias}" "${release_version}"
|
||||
return
|
||||
fi
|
||||
|
||||
for repository in "${repositories[@]}"; do
|
||||
channel="${repository}:${alias}"
|
||||
expected_digest="${exact_digest}"
|
||||
current_digest="$(.github/scripts/release-image-digest.sh "${channel}")"
|
||||
if [[ "${current_digest}" != "${expected_digest}" ]]; then
|
||||
source="${repository}@${expected_digest}"
|
||||
docker buildx imagetools create \
|
||||
--tag "${channel}" \
|
||||
"${source}"
|
||||
fi
|
||||
promoted_digest="$(.github/scripts/release-image-digest.sh "${channel}")"
|
||||
test "${promoted_digest}" = "${expected_digest}"
|
||||
promoted_version="$(.github/scripts/release-image-version.sh "${channel}")"
|
||||
test "${promoted_version}" = "${release_version}"
|
||||
done
|
||||
channel_is_current=true
|
||||
}
|
||||
|
||||
promote_channel "${image_beta}" "${promote_beta}"
|
||||
beta_current="${channel_is_current}"
|
||||
promote_channel "${image_major}" "${promote_major}"
|
||||
major_current="${channel_is_current}"
|
||||
|
||||
test "$(.github/scripts/release-image-digest.sh ghcr.io/tbphp/gpt-load:latest)" = \
|
||||
"${expected_ghcr_latest}"
|
||||
test "$(.github/scripts/release-image-digest.sh tbphp/gpt-load:latest)" = \
|
||||
"${expected_dockerhub_latest}"
|
||||
|
||||
{
|
||||
printf 'beta_current=%s\n' "${beta_current}"
|
||||
printf 'major_current=%s\n' "${major_current}"
|
||||
} >>"${output_file}"
|
||||
+119
-72
@@ -29,10 +29,11 @@ jobs:
|
||||
version: ${{ steps.tag.outputs.version }}
|
||||
prerelease: ${{ steps.tag.outputs.prerelease }}
|
||||
image_exact: ${{ steps.tag.outputs.image_exact }}
|
||||
image_minor: ${{ steps.tag.outputs.image_minor }}
|
||||
image_beta: ${{ steps.tag.outputs.image_beta }}
|
||||
image_major: ${{ steps.tag.outputs.image_major }}
|
||||
beta: ${{ steps.tag.outputs.beta }}
|
||||
release_kind: ${{ steps.tag.outputs.release_kind }}
|
||||
channel_beta: ${{ steps.tag.outputs.channel_beta }}
|
||||
promote_major: ${{ steps.tag.outputs.promote_major }}
|
||||
ci_verified: ${{ steps.ci.outputs.ci_verified }}
|
||||
steps:
|
||||
- name: Checkout tag commit
|
||||
@@ -56,15 +57,14 @@ jobs:
|
||||
version="${tag#v}"
|
||||
core="${version%%-*}"
|
||||
prerelease=false
|
||||
beta=false
|
||||
release_kind=stable
|
||||
channel_beta=false
|
||||
promote_major=true
|
||||
if [[ "${version}" == *-* ]]; then
|
||||
prerelease=true
|
||||
prerelease_value="${version#*-}"
|
||||
if [[ "${prerelease_value}" == "beta" ||
|
||||
"${prerelease_value}" == beta.* ]]; then
|
||||
beta=true
|
||||
fi
|
||||
release_kind=other
|
||||
promote_major=false
|
||||
IFS='.' read -r -a identifiers <<<"${prerelease_value}"
|
||||
for identifier in "${identifiers[@]}"; do
|
||||
if [[ "${identifier}" =~ ^[0-9]+$ ]] &&
|
||||
@@ -79,17 +79,29 @@ jobs:
|
||||
channel_beta_regex='^v2\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-beta\.(0|[1-9][0-9]*)$'
|
||||
if [[ "${tag}" =~ ${channel_beta_regex} ]]; then
|
||||
channel_beta=true
|
||||
release_kind=beta
|
||||
elif [[ "${tag}" =~ ^v2\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-rc\.(0|[1-9][0-9]*)$ ]]; then
|
||||
release_kind=rc
|
||||
fi
|
||||
|
||||
IFS='.' read -r major minor _ <<<"${core}"
|
||||
image_beta=
|
||||
if [[ "${channel_beta}" == "true" ]]; then
|
||||
image_beta="${major}.${minor}-beta"
|
||||
fi
|
||||
if [[ "${core}" == "2.0.0" ]] &&
|
||||
[[ "${release_kind}" == "beta" || "${release_kind}" == "rc" ]]; then
|
||||
promote_major=true
|
||||
fi
|
||||
{
|
||||
printf 'version=%s\n' "${tag}"
|
||||
printf 'prerelease=%s\n' "${prerelease}"
|
||||
printf 'image_exact=%s\n' "${tag}"
|
||||
printf 'image_minor=%s.%s\n' "${major}" "${minor}"
|
||||
printf 'image_exact=%s\n' "${version}"
|
||||
printf 'image_beta=%s\n' "${image_beta}"
|
||||
printf 'image_major=%s\n' "${major}"
|
||||
printf 'beta=%s\n' "${beta}"
|
||||
printf 'release_kind=%s\n' "${release_kind}"
|
||||
printf 'channel_beta=%s\n' "${channel_beta}"
|
||||
printf 'promote_major=%s\n' "${promote_major}"
|
||||
} >>"${GITHUB_OUTPUT}"
|
||||
# release-tag-validation:end
|
||||
|
||||
@@ -1038,33 +1050,6 @@ jobs:
|
||||
test -n "${ghcr_exact_digest}"
|
||||
test "${ghcr_exact_digest}" = "${dockerhub_exact_digest}"
|
||||
|
||||
- name: Update beta channel alias
|
||||
if: ${{ needs.publication-preflight.outputs.write_mode == 'publish' && needs.validate-tag.outputs.channel_beta == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for repository in ghcr.io/tbphp/gpt-load tbphp/gpt-load; do
|
||||
exact="${repository}:${{ needs.validate-tag.outputs.image_exact }}"
|
||||
docker buildx imagetools create \
|
||||
--tag "${repository}:v2beta" \
|
||||
"${exact}"
|
||||
done
|
||||
|
||||
- name: Update stable major and minor aliases
|
||||
if: ${{ needs.publication-preflight.outputs.write_mode == 'publish' && needs.validate-tag.outputs.prerelease == 'false' }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
for repository in ghcr.io/tbphp/gpt-load tbphp/gpt-load; do
|
||||
exact="${repository}:${{ needs.validate-tag.outputs.image_exact }}"
|
||||
docker buildx imagetools create \
|
||||
--tag "${repository}:${{ needs.validate-tag.outputs.image_minor }}" \
|
||||
"${exact}"
|
||||
docker buildx imagetools create \
|
||||
--tag "${repository}:${{ needs.validate-tag.outputs.image_major }}" \
|
||||
"${exact}"
|
||||
done
|
||||
|
||||
publish-github:
|
||||
needs:
|
||||
- validate-tag
|
||||
@@ -1297,26 +1282,6 @@ jobs:
|
||||
test "${latest_digest}" = "${expected_latest_digest}"
|
||||
test "${latest_digest}" != "${exact_digest}"
|
||||
|
||||
if [[ "${{ needs.validate-tag.outputs.channel_beta }}" == "true" ]]; then
|
||||
beta_digest="$(
|
||||
.github/scripts/release-image-digest.sh \
|
||||
"${repository}:v2beta"
|
||||
)"
|
||||
test "${beta_digest}" = "${exact_digest}"
|
||||
fi
|
||||
|
||||
if [[ "${{ needs.validate-tag.outputs.prerelease }}" == "false" ]]; then
|
||||
for alias in \
|
||||
"${{ needs.validate-tag.outputs.image_minor }}" \
|
||||
"${{ needs.validate-tag.outputs.image_major }}"; do
|
||||
alias_digest="$(
|
||||
docker buildx imagetools inspect \
|
||||
"${repository}:${alias}" \
|
||||
--format '{{.Manifest.Digest}}'
|
||||
)"
|
||||
test "${alias_digest}" = "${exact_digest}"
|
||||
done
|
||||
fi
|
||||
done
|
||||
test "${ghcr_exact_digest}" = "${dockerhub_exact_digest}"
|
||||
|
||||
@@ -1333,19 +1298,80 @@ jobs:
|
||||
docker image inspect -f '{{.Config.User}}' "${DOCKERHUB_EXACT_IMAGE}"
|
||||
)" = "10001:10001"
|
||||
|
||||
deploy-render:
|
||||
name: Deploy Render
|
||||
promote-image-channels:
|
||||
needs:
|
||||
- validate-tag
|
||||
- publication-preflight
|
||||
- post-publish-image-smoke
|
||||
- post-publish-verify
|
||||
if: >-
|
||||
${{
|
||||
!cancelled() &&
|
||||
needs.validate-tag.result == 'success' &&
|
||||
needs.publication-preflight.result == 'success' &&
|
||||
needs.post-publish-image-smoke.result == 'success' &&
|
||||
needs.post-publish-verify.result == 'success' &&
|
||||
needs.validate-tag.outputs.channel_beta == 'true'
|
||||
needs.post-publish-verify.result == 'success'
|
||||
}}
|
||||
concurrency:
|
||||
group: gpt-load-v2-image-channels
|
||||
cancel-in-progress: false
|
||||
queue: max
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
beta_current: ${{ steps.promote.outputs.beta_current }}
|
||||
major_current: ${{ steps.promote.outputs.major_current }}
|
||||
steps:
|
||||
- name: Checkout tag commit
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
|
||||
- name: Log in to GHCR for channel promotion
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Log in to Docker Hub for channel promotion
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Promote verified image channels
|
||||
id: promote
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_VERSION: ${{ needs.validate-tag.outputs.version }}
|
||||
IMAGE_EXACT: ${{ needs.validate-tag.outputs.image_exact }}
|
||||
IMAGE_BETA: ${{ needs.validate-tag.outputs.image_beta }}
|
||||
IMAGE_MAJOR: ${{ needs.validate-tag.outputs.image_major }}
|
||||
PROMOTE_BETA: ${{ needs.validate-tag.outputs.channel_beta }}
|
||||
PROMOTE_MAJOR: ${{ needs.validate-tag.outputs.promote_major }}
|
||||
EXPECTED_REVISION: ${{ github.sha }}
|
||||
EXPECTED_GHCR_LATEST: ${{ needs.publication-preflight.outputs.ghcr_latest_digest }}
|
||||
EXPECTED_DOCKERHUB_LATEST: ${{ needs.publication-preflight.outputs.dockerhub_latest_digest }}
|
||||
run: .github/scripts/release-promote-image-channels.sh
|
||||
|
||||
deploy-render:
|
||||
name: Deploy Render
|
||||
needs:
|
||||
- validate-tag
|
||||
- promote-image-channels
|
||||
if: >-
|
||||
${{
|
||||
!cancelled() &&
|
||||
needs.validate-tag.result == 'success' &&
|
||||
needs.promote-image-channels.result == 'success' &&
|
||||
needs.promote-image-channels.outputs.major_current == 'true'
|
||||
}}
|
||||
concurrency:
|
||||
group: gpt-load-render
|
||||
@@ -1360,7 +1386,7 @@ jobs:
|
||||
env:
|
||||
RENDER_SERVICE_ID: ${{ vars.RENDER_SERVICE_ID }}
|
||||
RENDER_SERVICE_URL: ${{ vars.RENDER_SERVICE_URL }}
|
||||
RELEASE_VERSION: ${{ needs.validate-tag.outputs.image_exact }}
|
||||
RELEASE_VERSION: ${{ needs.validate-tag.outputs.version }}
|
||||
IMAGE: ghcr.io/tbphp/gpt-load:${{ needs.validate-tag.outputs.image_exact }}
|
||||
RENDER_CLI_VERSION: "2.25.0"
|
||||
RENDER_CLI_SHA256: "3b3f1f839ef36b81f12d84ac7288f1c96f9f7519b39c53fe6f866612f704e7cd"
|
||||
@@ -1371,8 +1397,8 @@ jobs:
|
||||
set -euo pipefail
|
||||
[[ "${RENDER_SERVICE_ID}" =~ ^srv-[a-z0-9]+$ ]]
|
||||
[[ "${RENDER_SERVICE_URL}" =~ ^https://[^/?#]+/?$ ]]
|
||||
[[ "${RELEASE_VERSION}" =~ ^v2\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-beta\.(0|[1-9][0-9]*)$ ]]
|
||||
[[ "${IMAGE}" == "ghcr.io/tbphp/gpt-load:${RELEASE_VERSION}" ]]
|
||||
[[ "${RELEASE_VERSION}" =~ ^v2\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]
|
||||
[[ "${IMAGE}" == "ghcr.io/tbphp/gpt-load:${RELEASE_VERSION#v}" ]]
|
||||
|
||||
- name: Install pinned Render CLI
|
||||
shell: bash
|
||||
@@ -1466,11 +1492,13 @@ jobs:
|
||||
reconcile-publication:
|
||||
if: ${{ always() }}
|
||||
needs:
|
||||
- validate-tag
|
||||
- publication-preflight
|
||||
- publish-images
|
||||
- publish-github
|
||||
- post-publish-image-smoke
|
||||
- post-publish-verify
|
||||
- promote-image-channels
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
@@ -1512,6 +1540,10 @@ jobs:
|
||||
GITHUB_WRITER_RESULT: ${{ needs.publish-github.result }}
|
||||
IMAGE_SMOKE_RESULT: ${{ needs.post-publish-image-smoke.result }}
|
||||
VERIFY_POST_RESULT: ${{ needs.post-publish-verify.result }}
|
||||
PROMOTION_RESULT: ${{ needs.promote-image-channels.result }}
|
||||
IMAGE_EXACT: ${{ needs.validate-tag.outputs.image_exact }}
|
||||
IMAGE_BETA: ${{ needs.validate-tag.outputs.image_beta }}
|
||||
IMAGE_MAJOR: ${{ needs.validate-tag.outputs.image_major }}
|
||||
shell: bash
|
||||
run: |
|
||||
set +e
|
||||
@@ -1558,17 +1590,27 @@ jobs:
|
||||
|
||||
ghcr_inventory="$(
|
||||
read_image_inventory \
|
||||
"ghcr.io/tbphp/gpt-load:${GITHUB_REF_NAME}"
|
||||
"ghcr.io/tbphp/gpt-load:${IMAGE_EXACT}"
|
||||
)"
|
||||
dockerhub_inventory="$(
|
||||
read_image_inventory \
|
||||
"tbphp/gpt-load:${GITHUB_REF_NAME}"
|
||||
"tbphp/gpt-load:${IMAGE_EXACT}"
|
||||
)"
|
||||
ghcr_beta_inventory="$(
|
||||
read_image_inventory ghcr.io/tbphp/gpt-load:v2beta
|
||||
ghcr_beta_inventory=not-applicable
|
||||
dockerhub_beta_inventory=not-applicable
|
||||
if [[ -n "${IMAGE_BETA}" ]]; then
|
||||
ghcr_beta_inventory="$(
|
||||
read_image_inventory "ghcr.io/tbphp/gpt-load:${IMAGE_BETA}"
|
||||
)"
|
||||
dockerhub_beta_inventory="$(
|
||||
read_image_inventory "tbphp/gpt-load:${IMAGE_BETA}"
|
||||
)"
|
||||
fi
|
||||
ghcr_major_inventory="$(
|
||||
read_image_inventory "ghcr.io/tbphp/gpt-load:${IMAGE_MAJOR}"
|
||||
)"
|
||||
dockerhub_beta_inventory="$(
|
||||
read_image_inventory tbphp/gpt-load:v2beta
|
||||
dockerhub_major_inventory="$(
|
||||
read_image_inventory "tbphp/gpt-load:${IMAGE_MAJOR}"
|
||||
)"
|
||||
|
||||
{
|
||||
@@ -1578,14 +1620,19 @@ jobs:
|
||||
printf -- '- GitHub writer: `%s`\n' "${GITHUB_WRITER_RESULT}"
|
||||
printf -- '- published image smoke: `%s`\n' "${IMAGE_SMOKE_RESULT}"
|
||||
printf -- '- draft and image verification: `%s`\n' "${VERIFY_POST_RESULT}"
|
||||
printf -- '- image channel promotion: `%s`\n' "${PROMOTION_RESULT}"
|
||||
printf -- '- GitHub exact inventory: `%s`\n' "${github_inventory}"
|
||||
printf -- '- GHCR exact inventory: `%s`\n' "${ghcr_inventory}"
|
||||
printf -- '- Docker Hub exact inventory: `%s`\n' \
|
||||
"${dockerhub_inventory}"
|
||||
printf -- '- GHCR v2beta inventory: `%s`\n' \
|
||||
printf -- '- GHCR %s inventory: `%s`\n' "${IMAGE_BETA:-beta-not-applicable}" \
|
||||
"${ghcr_beta_inventory}"
|
||||
printf -- '- Docker Hub v2beta inventory: `%s`\n' \
|
||||
printf -- '- Docker Hub %s inventory: `%s`\n' "${IMAGE_BETA:-beta-not-applicable}" \
|
||||
"${dockerhub_beta_inventory}"
|
||||
printf -- '- GHCR %s inventory: `%s`\n' "${IMAGE_MAJOR}" \
|
||||
"${ghcr_major_inventory}"
|
||||
printf -- '- Docker Hub %s inventory: `%s`\n' "${IMAGE_MAJOR}" \
|
||||
"${dockerhub_major_inventory}"
|
||||
printf '\nmanual recovery: inspect the exact inventories and job logs; '
|
||||
printf 'do not assume cross-system atomic rollback.\n'
|
||||
} >>"${GITHUB_STEP_SUMMARY}"
|
||||
Reference in new issue
Block a user