525 lines
19 KiB
YAML
525 lines
19 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*.*.*"
|
|
|
|
env:
|
|
RUST_BACKTRACE: short
|
|
HUSKY: 0
|
|
|
|
concurrency:
|
|
group: release-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
create-draft-release:
|
|
runs-on: ubuntu-22.04
|
|
permissions:
|
|
contents: write
|
|
outputs:
|
|
release_id: ${{ steps.release.outputs.release_id }}
|
|
release_url: ${{ steps.release.outputs.release_url }}
|
|
upload_url: ${{ steps.release.outputs.upload_url }}
|
|
prerelease: ${{ steps.meta.outputs.prerelease }}
|
|
release_body: ${{ steps.release.outputs.release_body }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- id: meta
|
|
name: Resolve release channel
|
|
shell: bash
|
|
run: |
|
|
if [[ "${GITHUB_REF_NAME}" == *"-rc"* ]]; then
|
|
echo "prerelease=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "prerelease=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Check tag commit belongs to default branch
|
|
shell: bash
|
|
run: |
|
|
DEFAULT_BRANCH="${{ github.event.repository.default_branch }}"
|
|
git fetch origin "${DEFAULT_BRANCH}"
|
|
|
|
TAG_COMMIT=$(git rev-list -n 1 "${GITHUB_REF_NAME}")
|
|
if git merge-base --is-ancestor "${TAG_COMMIT}" "origin/${DEFAULT_BRANCH}"; then
|
|
echo "Tag ${GITHUB_REF_NAME} is based on ${DEFAULT_BRANCH}"
|
|
else
|
|
echo "Tag ${GITHUB_REF_NAME} is not based on ${DEFAULT_BRANCH}"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
|
|
- name: Verify tag matches app version
|
|
shell: bash
|
|
run: |
|
|
APP_VERSION=$(node -p "require('./src-tauri/tauri.conf.json').version")
|
|
if [[ "v$APP_VERSION" != "${GITHUB_REF_NAME}" ]]; then
|
|
echo "Tag ${GITHUB_REF_NAME} does not match app version v$APP_VERSION"
|
|
exit 1
|
|
fi
|
|
|
|
- id: release
|
|
name: Create or reuse draft release
|
|
uses: actions/github-script@v7
|
|
env:
|
|
PRERELEASE: ${{ steps.meta.outputs.prerelease }}
|
|
with:
|
|
script: |
|
|
const tag = context.ref.replace("refs/tags/", "");
|
|
const { owner, repo } = context.repo;
|
|
const prerelease = process.env.PRERELEASE === "true";
|
|
const releaseName = `codeg ${tag}`;
|
|
const { data: tagRef } = await github.rest.git.getRef({
|
|
owner,
|
|
repo,
|
|
ref: `tags/${tag}`,
|
|
});
|
|
|
|
let commitSha = tagRef.object.sha;
|
|
if (tagRef.object.type === "tag") {
|
|
const { data: annotatedTag } = await github.rest.git.getTag({
|
|
owner,
|
|
repo,
|
|
tag_sha: commitSha,
|
|
});
|
|
if (annotatedTag.object.type !== "commit") {
|
|
core.setFailed(
|
|
`Tag ${tag} points to ${annotatedTag.object.type}, not a commit.`,
|
|
);
|
|
return;
|
|
}
|
|
commitSha = annotatedTag.object.sha;
|
|
}
|
|
|
|
const { data: commit } = await github.rest.repos.getCommit({
|
|
owner,
|
|
repo,
|
|
ref: commitSha,
|
|
});
|
|
|
|
const releaseBody =
|
|
commit.commit.message?.trim() || "_No commit message._";
|
|
let release;
|
|
|
|
try {
|
|
const existing = await github.rest.repos.getReleaseByTag({
|
|
owner,
|
|
repo,
|
|
tag,
|
|
});
|
|
|
|
if (!existing.data.draft) {
|
|
core.setFailed(
|
|
`Release for tag ${tag} already exists and is not a draft.`,
|
|
);
|
|
return;
|
|
}
|
|
|
|
release = existing.data;
|
|
if (
|
|
release.prerelease !== prerelease ||
|
|
release.name !== releaseName ||
|
|
(release.body ?? "").trim() !== releaseBody
|
|
) {
|
|
const updated = await github.rest.repos.updateRelease({
|
|
owner,
|
|
repo,
|
|
release_id: release.id,
|
|
name: releaseName,
|
|
prerelease,
|
|
body: releaseBody,
|
|
});
|
|
release = updated.data;
|
|
}
|
|
core.info(`Reusing existing draft release #${release.id}`);
|
|
} catch (error) {
|
|
if (error.status !== 404) {
|
|
throw error;
|
|
}
|
|
|
|
const created = await github.rest.repos.createRelease({
|
|
owner,
|
|
repo,
|
|
tag_name: tag,
|
|
name: releaseName,
|
|
body: releaseBody,
|
|
draft: true,
|
|
prerelease,
|
|
});
|
|
|
|
release = created.data;
|
|
core.info(`Created draft release #${release.id}`);
|
|
}
|
|
|
|
core.setOutput("release_id", String(release.id));
|
|
core.setOutput("release_url", release.html_url);
|
|
core.setOutput("upload_url", release.upload_url);
|
|
core.setOutput("release_body", releaseBody);
|
|
|
|
build-tauri:
|
|
needs: create-draft-release
|
|
name: Build ${{ matrix.name }}
|
|
permissions:
|
|
contents: write
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: "macOS x64"
|
|
runner: "macos-latest"
|
|
target: "x86_64-apple-darwin"
|
|
- name: "macOS arm64"
|
|
runner: "macos-latest"
|
|
target: "aarch64-apple-darwin"
|
|
- name: "Linux x64"
|
|
runner: "ubuntu-22.04"
|
|
target: "x86_64-unknown-linux-gnu"
|
|
- name: "Linux arm64"
|
|
runner: "ubuntu-22.04"
|
|
target: "aarch64-unknown-linux-gnu"
|
|
- name: "Windows x64"
|
|
runner: "windows-2022"
|
|
target: "x86_64-pc-windows-msvc"
|
|
- name: "Windows arm64"
|
|
runner: "windows-latest"
|
|
target: "aarch64-pc-windows-msvc"
|
|
runs-on: ${{ matrix.runner }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
version: 10
|
|
run_install: false
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: "pnpm"
|
|
|
|
- name: Install Rust stable
|
|
uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: ${{ matrix.target }}
|
|
|
|
- name: Rust cache
|
|
uses: swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: "./src-tauri -> target"
|
|
shared-key: ${{ matrix.target }}
|
|
|
|
- name: Install Linux x64 dependencies
|
|
if: matrix.target == 'x86_64-unknown-linux-gnu'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
libwebkit2gtk-4.1-dev \
|
|
libayatana-appindicator3-dev \
|
|
librsvg2-dev \
|
|
patchelf
|
|
|
|
- name: Install Linux arm64 cross dependencies
|
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
|
run: |
|
|
cat > /tmp/sources.list << 'EOF'
|
|
deb [arch=amd64] http://archive.ubuntu.com/ubuntu jammy main restricted universe multiverse
|
|
deb [arch=amd64] http://archive.ubuntu.com/ubuntu jammy-updates main restricted universe multiverse
|
|
deb [arch=amd64] http://archive.ubuntu.com/ubuntu jammy-backports main restricted universe multiverse
|
|
deb [arch=amd64] http://security.ubuntu.com/ubuntu jammy-security main restricted universe multiverse
|
|
|
|
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted universe multiverse
|
|
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted universe multiverse
|
|
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main restricted universe multiverse
|
|
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main restricted universe multiverse
|
|
EOF
|
|
sudo mv /etc/apt/sources.list /etc/apt/sources.list.default
|
|
sudo mv /tmp/sources.list /etc/apt/sources.list
|
|
|
|
sudo dpkg --add-architecture arm64
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
gcc-aarch64-linux-gnu \
|
|
g++-aarch64-linux-gnu \
|
|
libwebkit2gtk-4.1-dev:arm64 \
|
|
libayatana-appindicator3-dev:arm64 \
|
|
librsvg2-dev:arm64 \
|
|
libssl-dev:arm64 \
|
|
patchelf
|
|
|
|
- name: Configure Linux arm64 cross env
|
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
|
shell: bash
|
|
run: |
|
|
echo "PKG_CONFIG_ALLOW_CROSS=1" >> "$GITHUB_ENV"
|
|
echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig" >> "$GITHUB_ENV"
|
|
echo "PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig" >> "$GITHUB_ENV"
|
|
echo "PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu" >> "$GITHUB_ENV"
|
|
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
|
|
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_AR=aarch64-linux-gnu-gcc-ar" >> "$GITHUB_ENV"
|
|
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS=-Clinker=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
|
|
echo "CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
|
|
echo "CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++" >> "$GITHUB_ENV"
|
|
echo "AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc-ar" >> "$GITHUB_ENV"
|
|
|
|
- name: Verify Linux arm64 cross toolchain
|
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
|
shell: bash
|
|
run: |
|
|
which aarch64-linux-gnu-gcc
|
|
aarch64-linux-gnu-gcc -v
|
|
rustup target list --installed
|
|
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=${CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER}"
|
|
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_AR=${CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_AR}"
|
|
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS=${CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS}"
|
|
|
|
- name: Install frontend dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Build and upload to draft release (Linux arm64)
|
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
|
uses: tauri-apps/tauri-action@v0.6.1
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
PKG_CONFIG_ALLOW_CROSS: 1
|
|
PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig
|
|
PKG_CONFIG_LIBDIR: /usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig
|
|
PKG_CONFIG_SYSROOT_DIR: /usr/aarch64-linux-gnu
|
|
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
|
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_AR: aarch64-linux-gnu-gcc-ar
|
|
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS: -Clinker=aarch64-linux-gnu-gcc
|
|
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
|
|
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++
|
|
AR_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc-ar
|
|
with:
|
|
releaseId: ${{ needs.create-draft-release.outputs.release_id }}
|
|
tagName: ${{ github.ref_name }}
|
|
releaseBody: ${{ needs.create-draft-release.outputs.release_body }}
|
|
releaseDraft: true
|
|
prerelease: ${{ needs.create-draft-release.outputs.prerelease }}
|
|
tauriScript: pnpm tauri
|
|
args: --target ${{ matrix.target }} --bundles deb,rpm
|
|
includeUpdaterJson: false
|
|
retryAttempts: 2
|
|
|
|
- name: Build and upload to draft release (Non-Linux arm64)
|
|
if: matrix.target != 'aarch64-unknown-linux-gnu'
|
|
uses: tauri-apps/tauri-action@v0.6.1
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
with:
|
|
releaseId: ${{ needs.create-draft-release.outputs.release_id }}
|
|
tagName: ${{ github.ref_name }}
|
|
releaseBody: ${{ needs.create-draft-release.outputs.release_body }}
|
|
releaseDraft: true
|
|
prerelease: ${{ needs.create-draft-release.outputs.prerelease }}
|
|
tauriScript: pnpm tauri
|
|
args: --target ${{ matrix.target }}
|
|
includeUpdaterJson: true
|
|
retryAttempts: 2
|
|
|
|
build-server:
|
|
needs: create-draft-release
|
|
name: Server ${{ matrix.name }}
|
|
permissions:
|
|
contents: write
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: "Linux x64"
|
|
runner: "ubuntu-22.04"
|
|
target: "x86_64-unknown-linux-gnu"
|
|
artifact: "codeg-server-linux-x64"
|
|
- name: "Linux arm64"
|
|
runner: "ubuntu-22.04"
|
|
target: "aarch64-unknown-linux-gnu"
|
|
artifact: "codeg-server-linux-arm64"
|
|
- name: "macOS x64"
|
|
runner: "macos-latest"
|
|
target: "x86_64-apple-darwin"
|
|
artifact: "codeg-server-darwin-x64"
|
|
- name: "macOS arm64"
|
|
runner: "macos-latest"
|
|
target: "aarch64-apple-darwin"
|
|
artifact: "codeg-server-darwin-arm64"
|
|
- name: "Windows x64"
|
|
runner: "windows-2022"
|
|
target: "x86_64-pc-windows-msvc"
|
|
artifact: "codeg-server-windows-x64"
|
|
runs-on: ${{ matrix.runner }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install Rust stable
|
|
uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: ${{ matrix.target }}
|
|
|
|
- name: Rust cache
|
|
uses: swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: "./src-tauri -> target"
|
|
shared-key: server-${{ matrix.target }}
|
|
|
|
- name: Install Linux arm64 cross compiler
|
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y gcc-aarch64-linux-gnu
|
|
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
version: 10
|
|
run_install: false
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: "pnpm"
|
|
|
|
- name: Build frontend
|
|
run: |
|
|
pnpm install --frozen-lockfile
|
|
pnpm build
|
|
|
|
- name: Build server binary
|
|
working-directory: src-tauri
|
|
run: cargo build --release --bin codeg-server --no-default-features --target ${{ matrix.target }}
|
|
|
|
- name: Package (Unix)
|
|
if: runner.os != 'Windows'
|
|
run: |
|
|
mkdir -p dist/${{ matrix.artifact }}
|
|
cp src-tauri/target/${{ matrix.target }}/release/codeg-server dist/${{ matrix.artifact }}/
|
|
cp -r out dist/${{ matrix.artifact }}/web
|
|
cd dist && tar czf ${{ matrix.artifact }}.tar.gz ${{ matrix.artifact }}
|
|
|
|
- name: Package (Windows)
|
|
if: runner.os == 'Windows'
|
|
shell: pwsh
|
|
run: |
|
|
New-Item -ItemType Directory -Force -Path dist/${{ matrix.artifact }}
|
|
Copy-Item src-tauri/target/${{ matrix.target }}/release/codeg-server.exe dist/${{ matrix.artifact }}/
|
|
Copy-Item -Recurse out dist/${{ matrix.artifact }}/web
|
|
Compress-Archive -Path dist/${{ matrix.artifact }} -DestinationPath dist/${{ matrix.artifact }}.zip
|
|
|
|
- name: Upload to release (Unix)
|
|
if: runner.os != 'Windows'
|
|
uses: actions/upload-release-asset@v1
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
with:
|
|
upload_url: ${{ needs.create-draft-release.outputs.upload_url }}
|
|
asset_path: dist/${{ matrix.artifact }}.tar.gz
|
|
asset_name: ${{ matrix.artifact }}.tar.gz
|
|
asset_content_type: application/gzip
|
|
|
|
- name: Upload to release (Windows)
|
|
if: runner.os == 'Windows'
|
|
uses: actions/upload-release-asset@v1
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
with:
|
|
upload_url: ${{ needs.create-draft-release.outputs.upload_url }}
|
|
asset_path: dist/${{ matrix.artifact }}.zip
|
|
asset_name: ${{ matrix.artifact }}.zip
|
|
asset_content_type: application/zip
|
|
|
|
build-docker:
|
|
needs: create-draft-release
|
|
name: Build Docker image
|
|
runs-on: ubuntu-22.04
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Set up QEMU (for multi-arch builds)
|
|
uses: docker/setup-qemu-action@v3
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Extract version from tag
|
|
id: version
|
|
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Build and push Docker image
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
context: .
|
|
platforms: linux/amd64,linux/arm64
|
|
push: true
|
|
tags: |
|
|
ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }}
|
|
ghcr.io/${{ github.repository }}:latest
|
|
${{ secrets.DOCKERHUB_USERNAME }}/codeg:${{ steps.version.outputs.version }}
|
|
${{ secrets.DOCKERHUB_USERNAME }}/codeg:latest
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
|
|
publish-release:
|
|
needs:
|
|
- create-draft-release
|
|
- build-tauri
|
|
- build-server
|
|
if: ${{ needs.build-tauri.result == 'success' && needs.build-server.result == 'success' }}
|
|
runs-on: ubuntu-22.04
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Publish draft release
|
|
uses: actions/github-script@v7
|
|
env:
|
|
RELEASE_ID: ${{ needs.create-draft-release.outputs.release_id }}
|
|
with:
|
|
script: |
|
|
const { owner, repo } = context.repo;
|
|
const releaseId = Number(process.env.RELEASE_ID);
|
|
const prerelease =
|
|
"${{ needs.create-draft-release.outputs.prerelease }}" === "true";
|
|
|
|
await github.rest.repos.updateRelease({
|
|
owner,
|
|
repo,
|
|
release_id: releaseId,
|
|
draft: false,
|
|
prerelease,
|
|
});
|
|
|
|
core.info(`Published release #${releaseId}`);
|