1
0
forked from boos/BoosAPI

fix: harden mac release architecture verification

This commit is contained in:
Cita
2026-03-19 20:49:21 +08:00
parent 36e9a85664
commit 6c3a09da86
+417 -443
View File
@@ -1,443 +1,417 @@
name: Release name: Release
on: on:
push: push:
tags: ['v*'] tags: ['v*']
workflow_dispatch: workflow_dispatch:
permissions: permissions:
contents: write contents: write
packages: write packages: write
concurrency: concurrency:
group: release-${{ github.ref }} group: release-${{ github.ref }}
cancel-in-progress: false cancel-in-progress: false
jobs: jobs:
verify: verify:
name: Verify name: Verify
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 20 timeout-minutes: 20
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 22
cache: npm cache: npm
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci
- name: Run tests - name: Run tests
run: npm test run: npm test
- name: Build - name: Build
run: npm run build run: npm run build
- name: Upload built dist - name: Upload built dist
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
name: metapi-dist-${{ github.sha }} name: metapi-dist-${{ github.sha }}
path: dist path: dist
if-no-files-found: error if-no-files-found: error
build-packages: build-packages:
name: Build Desktop Package (${{ matrix.platform }}) name: Build Desktop Package (${{ matrix.platform }})
needs: verify needs: verify
runs-on: ${{ matrix.runner }} runs-on: ${{ matrix.runner }}
timeout-minutes: 60 timeout-minutes: 60
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- platform: linux - platform: linux
runner: ubuntu-latest runner: ubuntu-latest
artifactLabel: linux-x64 artifactLabel: linux-x64
- platform: windows - platform: windows
runner: windows-latest runner: windows-latest
artifactLabel: windows-x64 artifactLabel: windows-x64
- platform: mac-arm64 - platform: mac-arm64
runner: macos-15 runner: macos-15
artifactLabel: mac-arm64 artifactLabel: mac-arm64
packageCommand: npm run package:desktop -- --mac --arm64 packageCommand: npm run package:desktop -- --mac --arm64
expectedMacArch: arm64 expectedMacArch: arm64
- platform: mac-x64 - platform: mac-x64
runner: macos-15-intel runner: macos-15-intel
artifactLabel: mac-x64 artifactLabel: mac-x64
packageCommand: npm run package:desktop:mac:intel packageCommand: npm run package:desktop:mac:intel
expectedMacArch: x64 expectedMacArch: x64
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:
node-version: 22 node-version: 22
cache: npm cache: npm
- name: Restore Electron cache (Windows) - name: Restore Electron cache (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: actions/cache@v5 uses: actions/cache@v5
with: with:
path: | path: |
${{ runner.temp }}/.cache/electron ${{ runner.temp }}/.cache/electron
${{ runner.temp }}/.cache/electron-builder ${{ runner.temp }}/.cache/electron-builder
${{ env.LOCALAPPDATA }}\electron\Cache ${{ env.LOCALAPPDATA }}\electron\Cache
${{ env.LOCALAPPDATA }}\electron-builder\Cache ${{ env.LOCALAPPDATA }}\electron-builder\Cache
key: electron-cache-${{ runner.os }}-${{ hashFiles('package-lock.json', 'package.json', 'electron-builder.yml') }} key: electron-cache-${{ runner.os }}-${{ hashFiles('package-lock.json', 'package.json', 'electron-builder.yml') }}
restore-keys: | restore-keys: |
electron-cache-${{ runner.os }}- electron-cache-${{ runner.os }}-
- name: Restore Electron cache (Linux) - name: Restore Electron cache (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
uses: actions/cache@v5 uses: actions/cache@v5
with: with:
path: | path: |
${{ runner.temp }}/.cache/electron ${{ runner.temp }}/.cache/electron
${{ runner.temp }}/.cache/electron-builder ${{ runner.temp }}/.cache/electron-builder
${{ runner.temp }}/.cache/appimage-tools ${{ runner.temp }}/.cache/appimage-tools
~/.cache/electron ~/.cache/electron
~/.cache/electron-builder ~/.cache/electron-builder
key: electron-cache-${{ runner.os }}-${{ hashFiles('package-lock.json', 'package.json', 'electron-builder.yml') }} key: electron-cache-${{ runner.os }}-${{ hashFiles('package-lock.json', 'package.json', 'electron-builder.yml') }}
restore-keys: | restore-keys: |
electron-cache-${{ runner.os }}- electron-cache-${{ runner.os }}-
- name: Restore Electron cache (macOS) - name: Restore Electron cache (macOS)
if: runner.os == 'macOS' if: runner.os == 'macOS'
uses: actions/cache@v5 uses: actions/cache@v5
with: with:
path: | path: |
${{ runner.temp }}/.cache/electron ${{ runner.temp }}/.cache/electron
${{ runner.temp }}/.cache/electron-builder ${{ runner.temp }}/.cache/electron-builder
~/Library/Caches/electron ~/Library/Caches/electron
~/Library/Caches/electron-builder ~/Library/Caches/electron-builder
key: electron-cache-${{ runner.os }}-${{ hashFiles('package-lock.json', 'package.json', 'electron-builder.yml') }} key: electron-cache-${{ runner.os }}-${{ hashFiles('package-lock.json', 'package.json', 'electron-builder.yml') }}
restore-keys: | restore-keys: |
electron-cache-${{ runner.os }}- electron-cache-${{ runner.os }}-
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci
- name: Download built dist - name: Download built dist
uses: actions/download-artifact@v8 uses: actions/download-artifact@v8
with: with:
name: metapi-dist-${{ github.sha }} name: metapi-dist-${{ github.sha }}
path: dist path: dist
- name: Build desktop artifacts (Windows) - name: Build desktop artifacts (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
shell: pwsh shell: pwsh
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
npm_config_cache: ${{ runner.temp }}/.npm npm_config_cache: ${{ runner.temp }}/.npm
ELECTRON_CACHE: ${{ runner.temp }}/.cache/electron ELECTRON_CACHE: ${{ runner.temp }}/.cache/electron
ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/.cache/electron-builder ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/.cache/electron-builder
ELECTRON_BUILDER_BINARIES_MIRROR: https://github.com/electron-userland/electron-builder-binaries/releases/download/ ELECTRON_BUILDER_BINARIES_MIRROR: https://github.com/electron-userland/electron-builder-binaries/releases/download/
npm_config_electron_builder_binaries_mirror: https://github.com/electron-userland/electron-builder-binaries/releases/download/ npm_config_electron_builder_binaries_mirror: https://github.com/electron-userland/electron-builder-binaries/releases/download/
WIN_CSC_LINK_INPUT: ${{ secrets.CSC_LINK }} WIN_CSC_LINK_INPUT: ${{ secrets.CSC_LINK }}
WIN_CSC_KEY_PASSWORD_INPUT: ${{ secrets.CSC_KEY_PASSWORD }} WIN_CSC_KEY_PASSWORD_INPUT: ${{ secrets.CSC_KEY_PASSWORD }}
run: | run: |
if ($env:WIN_CSC_LINK_INPUT) { $env:WIN_CSC_LINK = $env:WIN_CSC_LINK_INPUT } if ($env:WIN_CSC_LINK_INPUT) { $env:WIN_CSC_LINK = $env:WIN_CSC_LINK_INPUT }
if ($env:WIN_CSC_KEY_PASSWORD_INPUT) { $env:WIN_CSC_KEY_PASSWORD = $env:WIN_CSC_KEY_PASSWORD_INPUT } if ($env:WIN_CSC_KEY_PASSWORD_INPUT) { $env:WIN_CSC_KEY_PASSWORD = $env:WIN_CSC_KEY_PASSWORD_INPUT }
for ($attempt = 1; $attempt -le 2; $attempt++) { for ($attempt = 1; $attempt -le 2; $attempt++) {
Write-Host "Desktop build attempt $attempt/2" Write-Host "Desktop build attempt $attempt/2"
npm run package:desktop npm run package:desktop
if ($LASTEXITCODE -eq 0) { if ($LASTEXITCODE -eq 0) {
break break
} }
if ($attempt -eq 2) { if ($attempt -eq 2) {
exit $LASTEXITCODE exit $LASTEXITCODE
} }
Start-Sleep -Seconds 90 Start-Sleep -Seconds 90
} }
- name: Smoke test packaged native modules (Windows) - name: Smoke test packaged native modules (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
shell: pwsh shell: pwsh
env: env:
ELECTRON_RUN_AS_NODE: 1 ELECTRON_RUN_AS_NODE: 1
run: | run: |
$appRoot = Join-Path $PWD 'release\win-unpacked\resources\app' $appRoot = Join-Path $PWD 'release\win-unpacked\resources\app'
$exePath = Join-Path $PWD 'release\win-unpacked\Metapi.exe' $exePath = Join-Path $PWD 'release\win-unpacked\Metapi.exe'
Push-Location $appRoot Push-Location $appRoot
try { try {
& $exePath -e "require('./node_modules/better-sqlite3'); console.log('better-sqlite3 ok')" & $exePath -e "require('./node_modules/better-sqlite3'); console.log('better-sqlite3 ok')"
if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE exit $LASTEXITCODE
} }
} finally { } finally {
Pop-Location Pop-Location
} }
- name: Build desktop artifacts (Linux) - name: Build desktop artifacts (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
shell: bash shell: bash
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
npm_config_cache: ${{ runner.temp }}/.npm npm_config_cache: ${{ runner.temp }}/.npm
ELECTRON_CACHE: ${{ runner.temp }}/.cache/electron ELECTRON_CACHE: ${{ runner.temp }}/.cache/electron
ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/.cache/electron-builder ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/.cache/electron-builder
ELECTRON_BUILDER_BINARIES_MIRROR: https://github.com/electron-userland/electron-builder-binaries/releases/download/ ELECTRON_BUILDER_BINARIES_MIRROR: https://github.com/electron-userland/electron-builder-binaries/releases/download/
npm_config_electron_builder_binaries_mirror: https://github.com/electron-userland/electron-builder-binaries/releases/download/ npm_config_electron_builder_binaries_mirror: https://github.com/electron-userland/electron-builder-binaries/releases/download/
run: | run: |
set -euo pipefail set -euo pipefail
for attempt in 1 2; do for attempt in 1 2; do
echo "Desktop build attempt ${attempt}/2" echo "Desktop build attempt ${attempt}/2"
if npm run package:desktop; then if npm run package:desktop; then
break break
fi fi
if [ "$attempt" -eq 2 ]; then if [ "$attempt" -eq 2 ]; then
exit 1 exit 1
fi fi
sleep 90 sleep 90
done done
- name: Build desktop artifacts (macOS) - name: Build desktop artifacts (macOS)
if: startsWith(matrix.platform, 'mac-') if: startsWith(matrix.platform, 'mac-')
shell: bash shell: bash
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
npm_config_cache: ${{ runner.temp }}/.npm npm_config_cache: ${{ runner.temp }}/.npm
ELECTRON_CACHE: ${{ runner.temp }}/.cache/electron ELECTRON_CACHE: ${{ runner.temp }}/.cache/electron
ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/.cache/electron-builder ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/.cache/electron-builder
ELECTRON_BUILDER_BINARIES_MIRROR: https://github.com/electron-userland/electron-builder-binaries/releases/download/ ELECTRON_BUILDER_BINARIES_MIRROR: https://github.com/electron-userland/electron-builder-binaries/releases/download/
npm_config_electron_builder_binaries_mirror: https://github.com/electron-userland/electron-builder-binaries/releases/download/ npm_config_electron_builder_binaries_mirror: https://github.com/electron-userland/electron-builder-binaries/releases/download/
APPLE_ID_INPUT: ${{ secrets.APPLE_ID }} APPLE_ID_INPUT: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD_INPUT: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} APPLE_APP_SPECIFIC_PASSWORD_INPUT: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID_INPUT: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID_INPUT: ${{ secrets.APPLE_TEAM_ID }}
PACKAGE_COMMAND: ${{ matrix.packageCommand }} PACKAGE_COMMAND: ${{ matrix.packageCommand }}
run: | run: |
set -euo pipefail set -euo pipefail
if [ -n "${APPLE_ID_INPUT:-}" ]; then export APPLE_ID="$APPLE_ID_INPUT"; fi if [ -n "${APPLE_ID_INPUT:-}" ]; then export APPLE_ID="$APPLE_ID_INPUT"; fi
if [ -n "${APPLE_APP_SPECIFIC_PASSWORD_INPUT:-}" ]; then export APPLE_APP_SPECIFIC_PASSWORD="$APPLE_APP_SPECIFIC_PASSWORD_INPUT"; fi if [ -n "${APPLE_APP_SPECIFIC_PASSWORD_INPUT:-}" ]; then export APPLE_APP_SPECIFIC_PASSWORD="$APPLE_APP_SPECIFIC_PASSWORD_INPUT"; fi
if [ -n "${APPLE_TEAM_ID_INPUT:-}" ]; then export APPLE_TEAM_ID="$APPLE_TEAM_ID_INPUT"; fi if [ -n "${APPLE_TEAM_ID_INPUT:-}" ]; then export APPLE_TEAM_ID="$APPLE_TEAM_ID_INPUT"; fi
for attempt in 1 2; do for attempt in 1 2; do
echo "Desktop build (${PACKAGE_COMMAND}) attempt ${attempt}/2" echo "Desktop build (${PACKAGE_COMMAND}) attempt ${attempt}/2"
if eval "${PACKAGE_COMMAND}"; then if eval "${PACKAGE_COMMAND}"; then
break break
fi fi
if [ "$attempt" -eq 2 ]; then if [ "$attempt" -eq 2 ]; then
exit 1 exit 1
fi fi
sleep 90 sleep 90
done done
- name: Verify packaged macOS architecture - name: Verify packaged macOS architecture
if: startsWith(matrix.platform, 'mac-') if: startsWith(matrix.platform, 'mac-')
shell: bash shell: bash
env: env:
expectedMacArch: ${{ matrix.expectedMacArch }} expectedMacArch: ${{ matrix.expectedMacArch }}
run: | run: |
set -euo pipefail node scripts/desktop/verifyMacArchitecture.mjs --release-dir release --expected-arch "$expectedMacArch"
mapfile -t app_binaries < <(find release -type f -path '*/Metapi.app/Contents/MacOS/Metapi')
if [ "${#app_binaries[@]}" -eq 0 ]; then - name: Upload desktop package
echo "No packaged macOS app binary found under release/" uses: actions/upload-artifact@v7
exit 1 with:
fi name: metapi-desktop-${{ github.run_id }}-${{ matrix.artifactLabel }}
path: |
for binary in "${app_binaries[@]}"; do release/*.exe
archs="$(lipo -archs "$binary" 2>/dev/null | xargs)" release/*.dmg
echo "$binary => ${archs:-unknown}" release/*.zip
if [ -z "${archs}" ]; then release/*.AppImage
echo "Unable to detect architecture via lipo" release/*.deb
exit 1 release/*.blockmap
fi release/latest*.yml
release/latest*.json
if [ "${expectedMacArch}" = "x64" ]; then release/builder-debug.yml
[ "${archs}" = "x86_64" ] || { if-no-files-found: error
echo "Expected x86_64-only binary but got: ${archs}"
exit 1 publish-release:
} name: Publish GitHub Release
else needs: build-packages
[ "${archs}" = "arm64" ] || { runs-on: ubuntu-latest
echo "Expected arm64-only binary but got: ${archs}" timeout-minutes: 15
exit 1 if: startsWith(github.ref, 'refs/tags/')
}
fi steps:
done - name: Download package artifacts
uses: actions/download-artifact@v8
- name: Upload desktop package with:
uses: actions/upload-artifact@v7 path: release-assets
with: merge-multiple: true
name: metapi-desktop-${{ github.run_id }}-${{ matrix.artifactLabel }}
path: | - name: Show release files
release/*.exe run: ls -la release-assets
release/*.dmg
release/*.zip - name: Upload Release Assets
release/*.AppImage uses: softprops/action-gh-release@v2
release/*.deb with:
release/*.blockmap tag_name: ${{ github.ref_name }}
release/latest*.yml generate_release_notes: true
release/latest*.json files: release-assets/*
release/builder-debug.yml
if-no-files-found: error publish-docker-arch:
name: Publish Docker Image (${{ matrix.arch }})
publish-release: needs: build-packages
name: Publish GitHub Release runs-on: ${{ matrix.runner }}
needs: build-packages timeout-minutes: 45
runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/')
timeout-minutes: 15 strategy:
if: startsWith(github.ref, 'refs/tags/') fail-fast: false
matrix:
steps: include:
- name: Download package artifacts - arch: amd64
uses: actions/download-artifact@v8 platform: linux/amd64
with: runner: ubuntu-latest
path: release-assets - arch: arm64
merge-multiple: true platform: linux/arm64
runner: ubuntu-24.04-arm
- name: Show release files
run: ls -la release-assets steps:
- name: Checkout
- name: Upload Release Assets uses: actions/checkout@v6
uses: softprops/action-gh-release@v2
with: - name: Ensure Docker Hub secrets are configured
tag_name: ${{ github.ref_name }} run: |
generate_release_notes: true if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
files: release-assets/* echo "Missing Docker Hub secrets: DOCKERHUB_USERNAME / DOCKERHUB_TOKEN"
exit 1
publish-docker-arch: fi
name: Publish Docker Image (${{ matrix.arch }})
needs: build-packages - name: Set up Docker Buildx
runs-on: ${{ matrix.runner }} uses: docker/setup-buildx-action@v4
timeout-minutes: 45
if: startsWith(github.ref, 'refs/tags/') - name: Login to GitHub Container Registry
strategy: uses: docker/login-action@v4
fail-fast: false with:
matrix: registry: ghcr.io
include: username: ${{ github.actor }}
- arch: amd64 password: ${{ secrets.GITHUB_TOKEN }}
platform: linux/amd64
runner: ubuntu-latest - name: Login to Docker Hub
- arch: arm64 uses: docker/login-action@v4
platform: linux/arm64 with:
runner: ubuntu-24.04-arm username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- name: Checkout - name: Docker metadata
uses: actions/checkout@v6 id: meta
uses: docker/metadata-action@v6
- name: Ensure Docker Hub secrets are configured with:
run: | images: |
if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then 1467078763/metapi
echo "Missing Docker Hub secrets: DOCKERHUB_USERNAME / DOCKERHUB_TOKEN" ghcr.io/${{ github.repository }}
exit 1 tags: |
fi type=ref,event=tag
type=raw,value=latest
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4 - name: Add architecture suffix to tags
id: arch-tags
- name: Login to GitHub Container Registry shell: bash
uses: docker/login-action@v4 run: |
with: set -euo pipefail
registry: ghcr.io rm -f tags.txt
username: ${{ github.actor }} while IFS= read -r tag; do
password: ${{ secrets.GITHUB_TOKEN }} [ -n "$tag" ] || continue
echo "${tag}-${{ matrix.arch }}" >> tags.txt
- name: Login to Docker Hub done <<< "${{ steps.meta.outputs.tags }}"
uses: docker/login-action@v4 {
with: echo 'tags<<EOF'
username: ${{ secrets.DOCKERHUB_USERNAME }} cat tags.txt
password: ${{ secrets.DOCKERHUB_TOKEN }} echo 'EOF'
} >> "$GITHUB_OUTPUT"
- name: Docker metadata
id: meta - name: Build and push ${{ matrix.platform }} image
uses: docker/metadata-action@v6 uses: docker/build-push-action@v7
with: with:
images: | context: .
1467078763/metapi file: docker/Dockerfile
ghcr.io/${{ github.repository }} platforms: ${{ matrix.platform }}
tags: | push: true
type=ref,event=tag tags: ${{ steps.arch-tags.outputs.tags }}
type=raw,value=latest labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=release-${{ matrix.arch }}
- name: Add architecture suffix to tags cache-to: type=gha,mode=max,scope=release-${{ matrix.arch }}
id: arch-tags
shell: bash publish-docker:
run: | name: Publish Docker Manifests
set -euo pipefail needs: publish-docker-arch
rm -f tags.txt runs-on: ubuntu-latest
while IFS= read -r tag; do timeout-minutes: 15
[ -n "$tag" ] || continue if: startsWith(github.ref, 'refs/tags/')
echo "${tag}-${{ matrix.arch }}" >> tags.txt
done <<< "${{ steps.meta.outputs.tags }}" steps:
{ - name: Checkout
echo 'tags<<EOF' uses: actions/checkout@v6
cat tags.txt
echo 'EOF' - name: Ensure Docker Hub secrets are configured
} >> "$GITHUB_OUTPUT" run: |
if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then
- name: Build and push ${{ matrix.platform }} image echo "Missing Docker Hub secrets: DOCKERHUB_USERNAME / DOCKERHUB_TOKEN"
uses: docker/build-push-action@v7 exit 1
with: fi
context: .
file: docker/Dockerfile - name: Set up Docker Buildx
platforms: ${{ matrix.platform }} uses: docker/setup-buildx-action@v4
push: true
tags: ${{ steps.arch-tags.outputs.tags }} - name: Login to GitHub Container Registry
labels: ${{ steps.meta.outputs.labels }} uses: docker/login-action@v4
cache-from: type=gha,scope=release-${{ matrix.arch }} with:
cache-to: type=gha,mode=max,scope=release-${{ matrix.arch }} registry: ghcr.io
username: ${{ github.actor }}
publish-docker: password: ${{ secrets.GITHUB_TOKEN }}
name: Publish Docker Manifests
needs: publish-docker-arch - name: Login to Docker Hub
runs-on: ubuntu-latest uses: docker/login-action@v4
timeout-minutes: 15 with:
if: startsWith(github.ref, 'refs/tags/') username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
steps:
- name: Checkout - name: Docker metadata
uses: actions/checkout@v6 id: meta
uses: docker/metadata-action@v6
- name: Ensure Docker Hub secrets are configured with:
run: | images: |
if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then 1467078763/metapi
echo "Missing Docker Hub secrets: DOCKERHUB_USERNAME / DOCKERHUB_TOKEN" ghcr.io/${{ github.repository }}
exit 1 tags: |
fi type=ref,event=tag
type=raw,value=latest
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4 - name: Create multi-arch manifests
shell: bash
- name: Login to GitHub Container Registry run: |
uses: docker/login-action@v4 set -euo pipefail
with: while IFS= read -r tag; do
registry: ghcr.io [ -n "$tag" ] || continue
username: ${{ github.actor }} docker buildx imagetools create \
password: ${{ secrets.GITHUB_TOKEN }} --tag "$tag" \
"${tag}-amd64" \
- name: Login to Docker Hub "${tag}-arm64"
uses: docker/login-action@v4 done <<< "${{ steps.meta.outputs.tags }}"
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker metadata
id: meta
uses: docker/metadata-action@v6
with:
images: |
1467078763/metapi
ghcr.io/${{ github.repository }}
tags: |
type=ref,event=tag
type=raw,value=latest
- name: Create multi-arch manifests
shell: bash
run: |
set -euo pipefail
while IFS= read -r tag; do
[ -n "$tag" ] || continue
docker buildx imagetools create \
--tag "$tag" \
"${tag}-amd64" \
"${tag}-arm64"
done <<< "${{ steps.meta.outputs.tags }}"