From 91d0b1ae48a3b50ed12033d51f1dd60db1d9f841 Mon Sep 17 00:00:00 2001 From: Joshua Chi <112539+JoshuaChi@users.noreply.github.com> Date: Sat, 8 Aug 2026 17:44:28 +0800 Subject: [PATCH 1/3] fix #12: github docker release script --- .github/workflows/docker-release.yml | 90 ++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index fde61c0..6c991c9 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -13,12 +13,63 @@ on: permissions: contents: read +concurrency: + group: docker-release-${{ github.event.release.tag_name || inputs.version }} + cancel-in-progress: false + env: IMAGE: deventlab/d-lmdb jobs: + check-version: + name: Check version and reject duplicates + runs-on: ubuntu-latest + outputs: + stripped_version: ${{ steps.resolve.outputs.stripped_version }} + + steps: + - name: Resolve version + id: resolve + shell: bash + env: + VERSION_INPUT: ${{ inputs.version }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: | + VERSION="${VERSION_INPUT:-$RELEASE_TAG}" + [ -n "$VERSION" ] || { echo "ERROR: no version resolved from input or release tag"; exit 1; } + echo "stripped_version=${VERSION#v}" >> "$GITHUB_OUTPUT" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_ACCESS_TOKEN }} + + - name: Reject if version tag already exists + shell: bash + env: + IMAGE: ${{ env.IMAGE }} + STRIPPED_VERSION: ${{ steps.resolve.outputs.stripped_version }} + run: | + ERR=$(docker buildx imagetools inspect "docker.io/${IMAGE}:${STRIPPED_VERSION}" 2>&1 >/dev/null) + RC=$? + if [ $RC -eq 0 ]; then + echo "ERROR: ${STRIPPED_VERSION} already exists — will not overwrite" + exit 1 + elif echo "$ERR" | grep -qi 'not found\|manifest unknown'; then + echo "OK: ${STRIPPED_VERSION} is available" + else + echo "ERROR: inspect failed for an unrelated reason:" + echo "$ERR" + exit 1 + fi + build-test-push: name: Build, test, push (${{ matrix.platform }}) + needs: check-version runs-on: ${{ matrix.runs-on }} strategy: fail-fast: false @@ -67,8 +118,11 @@ jobs: run: | TARGET="docker.io/${IMAGE}:${{ github.sha }}-${{ env.PLATFORM_PAIR }}" docker tag d-lmdb-server:compose "$TARGET" - docker push "$TARGET" - DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "$TARGET" | grep -o 'sha256:[a-f0-9]\{64\}') + # NOTE: assumes classic `docker push` output has exactly one `sha256:...` match + # (the final digest line). If Docker output format changes (e.g. containerd + # snapshotter) and this starts matching multiple lines, DIGEST becomes + # multi-line and downstream refs go invalid — check here first. + DIGEST=$(docker push "$TARGET" | tee /dev/stderr | grep -oE 'sha256:[a-f0-9]{64}') echo "digest=$DIGEST" >> "$GITHUB_OUTPUT" - name: Export digest @@ -76,6 +130,7 @@ jobs: run: | mkdir -p /tmp/digests DIGEST="${{ steps.push.outputs.digest }}" + [ -n "$DIGEST" ] || { echo "ERROR: empty digest from push step"; exit 1; } touch "/tmp/digests/${DIGEST#sha256:}" - name: Upload digest @@ -89,18 +144,11 @@ jobs: merge: name: Merge multi-platform manifest runs-on: ubuntu-latest - needs: build-test-push + needs: [check-version, build-test-push] + env: + STRIPPED_VERSION: ${{ needs.check-version.outputs.stripped_version }} steps: - - name: Resolve version - shell: bash - env: - VERSION_INPUT: ${{ inputs.version }} - RELEASE_TAG: ${{ github.event.release.tag_name }} - run: | - VERSION="${VERSION_INPUT:-$RELEASE_TAG}" - printf 'STRIPPED_VERSION=%s\n' "${VERSION#v}" >> "$GITHUB_ENV" - - name: Download digests uses: actions/download-artifact@v4 with: @@ -117,28 +165,20 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} - - name: Reject if version tag already exists - shell: bash - env: - IMAGE: ${{ env.IMAGE }} - run: | - if docker buildx imagetools inspect "docker.io/${IMAGE}:${STRIPPED_VERSION}" >/dev/null 2>&1; then - echo "ERROR: ${STRIPPED_VERSION} already exists — will not overwrite" - exit 1 - fi - echo "OK: ${STRIPPED_VERSION} is available" - - name: Create and push manifest list shell: bash working-directory: /tmp/digests env: IMAGE: ${{ env.IMAGE }} run: | - # shellcheck disable=SC2046 # intentional word-splitting + REFS=() + for f in *; do + REFS+=("docker.io/${IMAGE}@sha256:${f}") + done docker buildx imagetools create \ -t "docker.io/${IMAGE}:${STRIPPED_VERSION}" \ -t "docker.io/${IMAGE}:latest" \ - $(printf 'docker.io/%s@sha256:%s ' "${IMAGE}" *) + "${REFS[@]}" - name: Verify manifest shell: bash From de790b429424406f36fde7996dc0854ea6ac6735 Mon Sep 17 00:00:00 2001 From: Joshua Chi <112539+JoshuaChi@users.noreply.github.com> Date: Sat, 8 Aug 2026 17:54:58 +0800 Subject: [PATCH 2/3] fix(ci) #12: harden docker-release workflow against silent version/digest failures and concurrent-release races --- .github/workflows/docker-release.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 6c991c9..e61907c 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -14,7 +14,7 @@ permissions: contents: read concurrency: - group: docker-release-${{ github.event.release.tag_name || inputs.version }} + group: docker-release cancel-in-progress: false env: @@ -118,11 +118,13 @@ jobs: run: | TARGET="docker.io/${IMAGE}:${{ github.sha }}-${{ env.PLATFORM_PAIR }}" docker tag d-lmdb-server:compose "$TARGET" - # NOTE: assumes classic `docker push` output has exactly one `sha256:...` match - # (the final digest line). If Docker output format changes (e.g. containerd - # snapshotter) and this starts matching multiple lines, DIGEST becomes - # multi-line and downstream refs go invalid — check here first. - DIGEST=$(docker push "$TARGET" | tee /dev/stderr | grep -oE 'sha256:[a-f0-9]{64}') + PUSH_OUTPUT=$(docker push "$TARGET" | tee /dev/stderr) + mapfile -t DIGESTS < <(printf '%s\n' "$PUSH_OUTPUT" | grep -oE 'sha256:[a-f0-9]{64}') + if [ "${#DIGESTS[@]}" -ne 1 ]; then + echo "ERROR: expected exactly 1 digest from docker push output, got ${#DIGESTS[@]}" + exit 1 + fi + DIGEST="${DIGESTS[0]}" echo "digest=$DIGEST" >> "$GITHUB_OUTPUT" - name: Export digest From 62510b7bdba26f2a7d967d145e72ef4396f13def Mon Sep 17 00:00:00 2001 From: Joshua Chi <112539+JoshuaChi@users.noreply.github.com> Date: Sat, 8 Aug 2026 18:05:34 +0800 Subject: [PATCH 3/3] fix(ci) #12: trigger docker-ci on every PR to unblock required status check stuck on path filter --- .github/workflows/docker-ci.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml index 734aac0..07d2483 100644 --- a/.github/workflows/docker-ci.yml +++ b/.github/workflows/docker-ci.yml @@ -12,16 +12,7 @@ on: - "Cargo.lock" - "**/*.rs" - ".github/workflows/docker-ci.yml" - pull_request: - paths: - - "Dockerfile" - - "compose/**" - - "d-lmdb/**" - - "d-lmdb-server/**" - - "Cargo.toml" - - "Cargo.lock" - - "**/*.rs" - - ".github/workflows/docker-ci.yml" + pull_request: {} permissions: contents: read