Files
ragflow/.github/workflows/release.yml
T

393 lines
14 KiB
YAML

name: release
on:
schedule:
- cron: '0 13 * * *' # This schedule runs every 13:00:00Z(21:00:00+08:00)
# https://github.com/orgs/community/discussions/26286?utm_source=chatgpt.com#discussioncomment-3251208
# "The create event does not support branch filter and tag filter."
# The "create tags" trigger is specifically focused on the creation of new tags, while the "push tags" trigger is activated when tags are pushed, including both new tag creations and updates to existing tags.
push:
tags:
- "v*.*.*" # normal release
- 'nightly' # mutable tag
permissions:
contents: write
actions: read
checks: read
statuses: read
# https://docs.github.com/en/actions/using-jobs/using-concurrency
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
prepare:
runs-on: ubuntu-24.04
outputs:
release_tag: ${{ steps.release.outputs.release_tag }}
prerelease: ${{ steps.release.outputs.prerelease }}
steps:
# https://github.com/actions/checkout/blob/v6/README.md
# Keep `token` so the move-mutable-tag step (schedule/nightly) can
# `git push -f` via the persisted GITHUB_TOKEN (persist-credentials
# defaults to true). Workflow-level `permissions: contents: write`
# grants the push.
- name: Check out code
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
fetch-tags: true
- name: Prepare release metadata
id: release
run: |
if [[ ${GITHUB_EVENT_NAME} != "schedule" ]]; then
RELEASE_TAG=${GITHUB_REF#refs/tags/}
if [[ ${RELEASE_TAG} == v* ]]; then
PRERELEASE=false
else
PRERELEASE=true
fi
echo "Workflow triggered by create tag: ${RELEASE_TAG}"
else
RELEASE_TAG=nightly
PRERELEASE=true
echo "Workflow triggered by schedule"
fi
echo "RELEASE_TAG=${RELEASE_TAG}" >> ${GITHUB_ENV}
echo "PRERELEASE=${PRERELEASE}" >> ${GITHUB_ENV}
echo "release_tag=${RELEASE_TAG}" >> ${GITHUB_OUTPUT}
echo "prerelease=${PRERELEASE}" >> ${GITHUB_OUTPUT}
- name: Move the existing mutable tag
# https://github.com/softprops/action-gh-release/issues/171
run: |
git fetch --tags
if [[ ${GITHUB_EVENT_NAME} == "schedule" ]]; then
# Determine if a given tag exists and matches a specific Git commit.
# actions/checkout@v6 fetch-tags doesn't work when triggered by schedule
if [ "$(git rev-parse -q --verify "refs/tags/${RELEASE_TAG}")" = "${GITHUB_SHA}" ]; then
echo "mutable tag ${RELEASE_TAG} exists and matches ${GITHUB_SHA}"
else
git tag -f ${RELEASE_TAG} ${GITHUB_SHA}
git push -f origin ${RELEASE_TAG}:refs/tags/${RELEASE_TAG}
echo "created/moved mutable tag ${RELEASE_TAG} to ${GITHUB_SHA}"
fi
fi
build_cli:
needs: prepare
strategy:
fail-fast: false
matrix:
include:
- goos: linux
goarch: amd64
runner: ubuntu-24.04
- goos: linux
goarch: arm64
runner: ubuntu-24.04-arm
- goos: darwin
goarch: amd64
runner: macos-15-intel
- goos: darwin
goarch: arm64
runner: macos-14
- goos: windows
goarch: amd64
runner: windows-latest
output_ext: .exe
- goos: windows
goarch: arm64
runner: windows-11-arm
output_ext: .exe
runs-on: ${{ matrix.runner }}
env:
CLI_NAME: ragflow-cli
CLI_MAIN: ./cmd/ragflow-cli.go
DIST_DIR: dist/cli
RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
steps:
# https://github.com/actions/checkout/blob/v6/README.md
- name: Check out code
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
fetch-tags: true
# https://github.com/actions/setup-go
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Build Go CLI release binaries on non-Windows
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
mkdir -p "${DIST_DIR}"
if [[ ! -e "${CLI_MAIN}" ]]; then
echo "::error::Go CLI entry does not exist: ${CLI_MAIN}"
echo "::error::Please update CLI_MAIN in .github/workflows/release.yml"
exit 1
fi
echo "Building Go CLI release binaries"
echo "CLI name: ${CLI_NAME}"
echo "CLI main: ${CLI_MAIN}"
echo "Release tag: ${RELEASE_TAG}"
echo "Commit: ${GITHUB_SHA}"
output="${DIST_DIR}/${CLI_NAME}-${RELEASE_TAG}-${{ matrix.goos }}-${{ matrix.goarch }}"
echo "Building ${{ matrix.goos }}/${{ matrix.goarch }} -> ${output}"
CGO_ENABLED=0 \
GOOS="${{ matrix.goos }}" \
GOARCH="${{ matrix.goarch }}" \
go build \
-trimpath \
-ldflags="-s -w -X main.version=${RELEASE_TAG} -X main.commit=${GITHUB_SHA}" \
-o "${output}" \
"${CLI_MAIN}"
chmod +x "${output}"
- name: Build Go CLI release binaries on Windows
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path $env:DIST_DIR | Out-Null
if (-not (Test-Path $env:CLI_MAIN)) {
Write-Error "Go CLI entry does not exist: $env:CLI_MAIN"
exit 1
}
$output = Join-Path $env:DIST_DIR "${env:CLI_NAME}-${env:RELEASE_TAG}-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.output_ext }}"
Write-Host "Building ${{ matrix.goos }}/${{ matrix.goarch }} -> $output"
$env:CGO_ENABLED = "0"
$env:GOOS = "${{ matrix.goos }}"
$env:GOARCH = "${{ matrix.goarch }}"
go build `
-trimpath `
-ldflags="-s -w -X main.version=$env:RELEASE_TAG -X main.commit=$env:GITHUB_SHA" `
-o "$output" `
"$env:CLI_MAIN"
- name: Upload CLI artifact
uses: actions/upload-artifact@v4
with:
name: cli-${{ matrix.goos }}-${{ matrix.goarch }}
path: dist/cli/*
if-no-files-found: error
publish_cli_assets:
needs:
- prepare
- build_cli
runs-on: ubuntu-24.04
steps:
# https://github.com/actions/checkout/blob/v6/README.md
- name: Check out code
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
- name: Download CLI artifacts
uses: actions/download-artifact@v5
with:
pattern: cli-*
path: dist/cli
merge-multiple: true
- name: Prepare CLI release assets
env:
RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
run: |
set -euo pipefail
RELEASE_DATETIME=$(date --rfc-3339=seconds)
echo Release ${RELEASE_TAG} created from ${GITHUB_SHA} at ${RELEASE_DATETIME} > release_body.md
cd dist/cli
sha256sum * > SHA256SUMS
cd -
echo "Generated CLI release assets:"
ls -lh dist/cli
- name: Upload Go CLI release assets
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
prerelease: ${{ needs.prepare.outputs.prerelease }}
tag_name: ${{ needs.prepare.outputs.release_tag }}
body_path: release_body.md
files: |
dist/cli/*
tools/scripts/install.sh
tools/scripts/install.ps1
# Publish the ragflow Python SDK to PyPI. Runs on a GitHub-hosted runner so
# it no longer depends on the self-hosted release runner. Only stable v* tags
# (not nightly/prerelease) publish, gated by the `if` on this job.
publish_sdk:
needs:
- prepare
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-24.04
steps:
# https://github.com/actions/checkout/blob/v6/README.md
- name: Check out code
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Build and push ragflow-sdk
run: |
cd sdk/python && uv build && uv publish --token ${{ secrets.PYPI_API_TOKEN }}
# Build the linux/amd64 image on a GitHub-hosted runner, mirroring
# `build-arm64`. Pushes the version tag (amd64-only) so amd64 pulls always
# work even if the ARM64 job fails, plus the arch-specific -amd64/:amd64 tags
# for direct pulls. The multi-arch manifest is created by the `build-arm64`
# job once both arches are pushed.
build-amd64:
needs:
- prepare
runs-on: ubuntu-24.04
env:
RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
steps:
- name: Check out code
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: infiniflow
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push amd64 image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
push: true
# Disable build attestations (provenance/SBOM) for the same reason as
# build-arm64: keep a clean manifest so the multi-arch index merged
# below contains only amd64 + arm64 image manifests.
provenance: false
sbom: false
# Use a Go proxy that is reliable from GitHub-hosted runners; the
# Dockerfile default (goproxy.cn) is flaky here (HTTP/2 GOAWAY).
build-args: |
GOPROXY=https://proxy.golang.org,direct
tags: |
infiniflow/ragflow:${{ needs.prepare.outputs.release_tag }}-amd64
infiniflow/ragflow:amd64
infiniflow/ragflow:${{ needs.prepare.outputs.release_tag }}
# Build the linux/arm64 image on a GitHub-hosted ARM64 runner and merge it
# with the amd64 image (pushed by the `build-amd64` job above) into a single
# multi-arch manifest list for infiniflow/ragflow:${RELEASE_TAG} and :latest,
# so `docker pull infiniflow/ragflow:${RELEASE_TAG}` resolves per-arch on both
# platforms. Runs after `build-amd64` so the amd64 (-amd64/:amd64) tags exist.
build-arm64:
needs:
- build-amd64
- prepare
runs-on: ubuntu-24.04-arm
env:
RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
PRERELEASE: ${{ needs.prepare.outputs.prerelease }}
steps:
- name: Check out code
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: infiniflow
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push arm64 image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/arm64
push: true
# Disable build attestations (provenance/SBOM). build-push-action v6
# otherwise attaches an application/vnd.docker.attestation.manifest.v1+json
# artifact to the image. That artifact is not supported by some registries
# (e.g. Alibaba Cloud personal OCR), and skopeo sync then fails with
# "unsupported image-specific operation on artifact with type
# application/vnd.docker.attestation.manifest.v1+json". Disabling it keeps
# the pushed image a clean manifest so the multi-arch index merged below
# contains only amd64 + arm64 image manifests.
provenance: false
sbom: false
# Use a Go proxy that is reliable from GitHub-hosted runners; the
# Dockerfile default (goproxy.cn) is flaky here (HTTP/2 GOAWAY).
build-args: |
GOPROXY=https://proxy.golang.org,direct
tags: |
infiniflow/ragflow:${{ needs.prepare.outputs.release_tag }}-arm64
infiniflow/ragflow:arm64
# Create/upgrade the public multi-arch tags now that both arch-specific
# images (-amd64/:amd64 from the `build-amd64` job, -arm64/:arm64 from this
# job) are pushed. :${RELEASE_TAG} was already pushed amd64-only by the
# build-amd64 job (so amd64 pulls always work); this upgrades it to multi-arch.
# :latest is created here for the first time this release (it is NOT pushed
# by the build-amd64 job), so the previous, possibly multi-arch, :latest is
# never overwritten with an amd64-only image before ARM64 is ready.
- name: Create multi-arch manifest
run: |
docker buildx imagetools create --tag infiniflow/ragflow:${RELEASE_TAG} \
infiniflow/ragflow:${RELEASE_TAG}-amd64 \
infiniflow/ragflow:${RELEASE_TAG}-arm64
# Only stable (non-prerelease) v* tags should move the mutable :latest
# tag. Nightly and prerelease builds keep their own version tag but must
# not overwrite the stable :latest.
if [ "${PRERELEASE}" != "true" ]; then
docker buildx imagetools create --tag infiniflow/ragflow:latest \
infiniflow/ragflow:amd64 \
infiniflow/ragflow:arm64
else
echo "RELEASE_TAG=${RELEASE_TAG} is a prerelease/nightly build; skipping :latest update."
fi