Skip to content

release: v14.10.4

release: v14.10.4 #947

Workflow file for this run

on:
push:
tags:
- 'v*'
workflow_dispatch:
name: Release
permissions: write-all
env:
IMAGE: innei/mx-server
REDISMS_DISABLE_POSTINSTALL: 1
MONGOMS_DISABLE_POSTINSTALL: 1
# Cloudflare R2 (S3-compatible) admin publish target — hardcoded per request.
# Credentials come from repo secrets AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY.
R2_BUCKET: admin-r2
R2_ENDPOINT: https://de7ecb0eaa0a328071255d557a6adb66.r2.cloudflarestorage.com
ADMIN_PUBLIC_BASE: https://admin-r2.innei.dev
jobs:
quality:
name: Lint & Typecheck
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node
with:
node-version: '22.x'
- name: Run Lint
run: pnpm run lint
- name: Run Typecheck
run: pnpm run typecheck
- name: Lint migrations (expand-contract guard)
run: pnpm -C apps/core run lint:migrations
build:
name: Core
runs-on: ubuntu-latest
needs: quality
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: mx
POSTGRES_PASSWORD: mx
POSTGRES_DB: mx_core
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U mx -d mx_core"
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis
ports:
- 6379:6379
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node
with:
node-version: '22.x'
- name: Build project
run: |
pnpm run bundle
- name: Test Bundle Server
env:
SNOWFLAKE_WORKER_ID: 1
PG_HOST: 127.0.0.1
PG_PORT: 5432
PG_USER: mx
PG_PASSWORD: mx
PG_DATABASE: mx_core
REDIS_HOST: 127.0.0.1
REDIS_PORT: 6379
JWT_SECRET: test-bundle-server-jwt-secret
MIGRATIONS_DIR: ${{ github.workspace }}/apps/core/src/database/migrations
run: |
bash scripts/workflow/test-server.sh
- name: Zip Assets
run: |
sh apps/core/zip-asset.sh
mv release.zip release-linux.zip
# Publish the bundled admin to Cloudflare R2 (S3-compatible) only on core
# patch releases (vX.Y.Z, Z>0). A major/minor bump ships admin that needs the
# new core API, so older cores must not OTA onto it — it ships bundled instead.
- name: Publish admin assets to R2
if: ${{ !endsWith(github.ref_name, '.0') }}
continue-on-error: true
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
# Recent aws-cli defaults to CRC64-NVME checksums that R2 rejects — force off.
AWS_REQUEST_CHECKSUM_CALCULATION: when_required
AWS_RESPONSE_CHECKSUM_VALIDATION: when_required
run: |
set -e
ADMIN_VERSION="$(node -p "require('./apps/admin/package.json').version")"
ADMIN_ZIP="admin-${ADMIN_VERSION}.zip"
# Package the locally-built admin (apps/admin/dist) with a top-level dist/ wrapper.
( cd apps/admin && zip -r "../../${ADMIN_ZIP}" dist )
SHA="$(sha256sum "${ADMIN_ZIP}" | awk '{print $1}')"
cat > latest.json <<EOF
{
"version": "${ADMIN_VERSION}",
"file": "${ADMIN_ZIP}",
"url": "${ADMIN_PUBLIC_BASE%/}/${ADMIN_ZIP}",
"sha256": "${SHA}",
"tag": "${{ github.ref_name }}"
}
EOF
aws s3 cp "${ADMIN_ZIP}" "s3://${R2_BUCKET}/${ADMIN_ZIP}" --endpoint-url "${R2_ENDPOINT}"
aws s3 cp latest.json "s3://${R2_BUCKET}/latest.json" --endpoint-url "${R2_ENDPOINT}"
- name: Verify release notes present
run: |
if [ ! -s apps/core/RELEASE_NOTES.md ]; then
echo "::error::apps/core/RELEASE_NOTES.md is missing or empty. The release-core skill must produce it before tagging."
exit 1
fi
- name: Release
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
body_path: apps/core/RELEASE_NOTES.md
prerelease: ${{ contains(fromJSON('["alpha", "beta"]'), github.ref_name) }}
files: release-linux.zip
docker:
name: Docker (${{ matrix.platform }})
runs-on: ${{ matrix.runner }}
needs: quality
strategy:
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Docker Setup QEMU
uses: docker/setup-qemu-action@v4
- name: Metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.IMAGE }}
- name: Set up Buildx
uses: docker/setup-buildx-action@v4
- name: Copy .env
run: |
cp .env.example .env
- name: Build and load
uses: docker/build-push-action@v7
with:
context: .
load: true
tags: ${{ env.IMAGE }}
labels: ${{ steps.meta.outputs.labels }}
- name: Test
run: |
bash ./scripts/workflow/test-docker.sh
sudo rm -rf ./data
- name: Login to DockerHub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v7
with:
platforms: ${{ matrix.platform }}
context: .
tags: ${{ env.IMAGE }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
merge:
name: Merge
runs-on: ubuntu-latest
needs: docker
steps:
- name: Download digests
uses: actions/download-artifact@v8
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
type=raw,value=latest
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.IMAGE }}:${{ steps.meta.outputs.version }}
dokploy:
name: Trigger Dokploy Redeploy
runs-on: ubuntu-latest
needs: merge
env:
DOKPLOY_WEBHOOK_URL: ${{ secrets.DOKPLOY_WEBHOOK_URL }}
steps:
- name: Trigger Dokploy webhook
if: ${{ env.DOKPLOY_WEBHOOK_URL != '' }}
run: |
curl -sf -X POST "${{ env.DOKPLOY_WEBHOOK_URL }}" \
-H "Content-Type: application/json" \
-d "{\"source\":\"github-release\",\"tag\":\"${{ github.ref_name }}\"}"