mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
The GIF key now lives in server config, so no build job needs it. Leaving the secret wired into the build env is the exact mechanism that leaked the original key: any future re-add of import.meta.env.VITE_KLIPY_API_KEY would silently inline it into the bundle again with the secret already present. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
455 lines
16 KiB
YAML
455 lines
16 KiB
YAML
name: Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*"
|
|
|
|
jobs:
|
|
release-client-windows:
|
|
name: Build Tauri (Windows)
|
|
runs-on: windows-latest
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
cache-dependency-path: Client/tauri-client/package-lock.json
|
|
|
|
- name: Install Rust
|
|
uses: dtolnay/rust-toolchain@stable
|
|
|
|
- name: Rust cache
|
|
uses: swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: Client/tauri-client/src-tauri
|
|
|
|
- name: Install npm dependencies
|
|
working-directory: Client/tauri-client
|
|
run: npm ci
|
|
|
|
- name: Build Tauri app
|
|
working-directory: Client/tauri-client
|
|
env:
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
run: npm run tauri build
|
|
|
|
- name: Stage Windows release assets
|
|
shell: bash
|
|
run: |
|
|
mkdir -p release-staging
|
|
NSIS_DIR="Client/tauri-client/src-tauri/target/release/bundle/nsis"
|
|
INSTALLER=$(find "$NSIS_DIR" -name "*.exe" | head -1)
|
|
cp "$INSTALLER" release-staging/
|
|
NSIS_ZIP=$(find "$NSIS_DIR" -name "*_x64-setup.nsis.zip" ! -name "*.sig" | head -1)
|
|
if [ -n "$NSIS_ZIP" ] && [ -f "$NSIS_ZIP" ]; then cp "$NSIS_ZIP" release-staging/; fi
|
|
NSIS_SIG=$(find "$NSIS_DIR" -name "*_x64-setup.nsis.zip.sig" | head -1)
|
|
if [ -n "$NSIS_SIG" ] && [ -f "$NSIS_SIG" ]; then cp "$NSIS_SIG" release-staging/; fi
|
|
|
|
- name: Upload Windows release assets
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: windows-release-assets
|
|
path: release-staging/
|
|
|
|
release-client-linux:
|
|
name: Build Tauri (Linux)
|
|
runs-on: ubuntu-22.04
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
cache-dependency-path: Client/tauri-client/package-lock.json
|
|
|
|
- name: Install Linux system dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
libwebkit2gtk-4.1-dev \
|
|
libgtk-3-dev \
|
|
libayatana-appindicator3-dev \
|
|
libsecret-1-dev \
|
|
libasound2-dev \
|
|
libssl-dev \
|
|
patchelf \
|
|
librsvg2-dev \
|
|
xdg-utils
|
|
|
|
- name: Install Rust
|
|
uses: dtolnay/rust-toolchain@stable
|
|
|
|
- name: Rust cache
|
|
uses: swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: Client/tauri-client/src-tauri
|
|
|
|
- name: Install npm dependencies
|
|
working-directory: Client/tauri-client
|
|
run: npm ci
|
|
|
|
- name: Build Tauri app (AppImage + deb)
|
|
working-directory: Client/tauri-client
|
|
env:
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
run: npm run tauri build -- --bundles appimage,deb
|
|
|
|
- name: Stage Linux release assets
|
|
shell: bash
|
|
run: |
|
|
mkdir -p linux-staging
|
|
BUNDLE_DIR="Client/tauri-client/src-tauri/target/release/bundle"
|
|
# AppImage
|
|
APPIMAGE=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage" ! -name "*.sig" | head -1)
|
|
if [ -n "$APPIMAGE" ] && [ -f "$APPIMAGE" ]; then cp "$APPIMAGE" linux-staging/; fi
|
|
APPIMAGE_SIG=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage.sig" | head -1)
|
|
if [ -n "$APPIMAGE_SIG" ] && [ -f "$APPIMAGE_SIG" ]; then cp "$APPIMAGE_SIG" linux-staging/; fi
|
|
APPIMAGE_TAR=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage.tar.gz" ! -name "*.sig" | head -1)
|
|
if [ -n "$APPIMAGE_TAR" ] && [ -f "$APPIMAGE_TAR" ]; then cp "$APPIMAGE_TAR" linux-staging/; fi
|
|
APPIMAGE_TAR_SIG=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage.tar.gz.sig" | head -1)
|
|
if [ -n "$APPIMAGE_TAR_SIG" ] && [ -f "$APPIMAGE_TAR_SIG" ]; then cp "$APPIMAGE_TAR_SIG" linux-staging/; fi
|
|
# .deb
|
|
DEB=$(find "$BUNDLE_DIR/deb" -name "*.deb" | head -1)
|
|
if [ -n "$DEB" ] && [ -f "$DEB" ]; then cp "$DEB" linux-staging/; fi
|
|
|
|
- name: Upload Linux release assets
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: linux-release-assets
|
|
path: linux-staging/
|
|
|
|
release-server:
|
|
name: Build server (${{ matrix.os }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os: windows-latest
|
|
artifact: server-windows
|
|
- os: ubuntu-latest
|
|
artifact: server-linux
|
|
runs-on: ${{ matrix.os }}
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-go@v5
|
|
with:
|
|
go-version: "1.25"
|
|
|
|
- name: Extract version from tag
|
|
shell: bash
|
|
run: |
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
|
|
|
- name: Build server (Windows)
|
|
if: matrix.os == 'windows-latest'
|
|
shell: bash
|
|
run: cd Server && go build -o chatserver.exe -ldflags "-s -w -X main.version=$VERSION" .
|
|
|
|
- name: Build server (Linux)
|
|
if: matrix.os == 'ubuntu-latest'
|
|
working-directory: Server
|
|
env:
|
|
CGO_ENABLED: "0"
|
|
run: go build -o chatserver -ldflags "-s -w -X main.version=$VERSION" .
|
|
|
|
- name: Create tar.gz (Linux)
|
|
if: matrix.os == 'ubuntu-latest'
|
|
working-directory: Server
|
|
run: tar czf ../chatserver-linux-amd64.tar.gz chatserver
|
|
|
|
- name: Upload Windows binary
|
|
if: matrix.os == 'windows-latest'
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: ${{ matrix.artifact }}
|
|
path: Server/chatserver.exe
|
|
|
|
- name: Upload Linux archive
|
|
if: matrix.os == 'ubuntu-latest'
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: ${{ matrix.artifact }}
|
|
path: chatserver-linux-amd64.tar.gz
|
|
|
|
release-client-linux-arm64:
|
|
name: Build Tauri (Linux ARM64)
|
|
runs-on: ubuntu-22.04-arm
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
cache-dependency-path: Client/tauri-client/package-lock.json
|
|
|
|
- name: Install Linux system dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
libwebkit2gtk-4.1-dev \
|
|
libgtk-3-dev \
|
|
libayatana-appindicator3-dev \
|
|
libsecret-1-dev \
|
|
libasound2-dev \
|
|
libssl-dev \
|
|
patchelf \
|
|
librsvg2-dev \
|
|
xdg-utils
|
|
|
|
- name: Install Rust
|
|
uses: dtolnay/rust-toolchain@stable
|
|
|
|
- name: Rust cache
|
|
uses: swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: Client/tauri-client/src-tauri
|
|
|
|
- name: Install npm dependencies
|
|
working-directory: Client/tauri-client
|
|
run: npm ci
|
|
|
|
- name: Build Tauri app (AppImage + deb)
|
|
working-directory: Client/tauri-client
|
|
env:
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
run: npm run tauri build -- --bundles appimage,deb
|
|
|
|
- name: Stage Linux ARM64 release assets
|
|
shell: bash
|
|
run: |
|
|
mkdir -p linux-arm64-staging
|
|
BUNDLE_DIR="Client/tauri-client/src-tauri/target/release/bundle"
|
|
# AppImage (ensure arch is in filename)
|
|
for f in "$BUNDLE_DIR"/appimage/*.AppImage; do
|
|
[ -f "$f" ] || continue
|
|
[[ "$f" == *.sig ]] && continue
|
|
dest="linux-arm64-staging/$(basename "$f")"
|
|
# Append _aarch64 if bundler omits arch from filename
|
|
[[ "$(basename "$f")" == *aarch64* ]] || dest="${dest%.AppImage}_aarch64.AppImage"
|
|
cp "$f" "$dest"
|
|
done
|
|
for f in "$BUNDLE_DIR"/appimage/*.AppImage.tar.gz; do
|
|
[ -f "$f" ] && cp "$f" linux-arm64-staging/
|
|
done
|
|
for f in "$BUNDLE_DIR"/appimage/*.sig; do
|
|
[ -f "$f" ] && cp "$f" linux-arm64-staging/
|
|
done
|
|
# .deb
|
|
for f in "$BUNDLE_DIR"/deb/*.deb; do
|
|
[ -f "$f" ] && cp "$f" linux-arm64-staging/
|
|
done
|
|
|
|
- name: Upload Linux ARM64 release assets
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: linux-arm64-release-assets
|
|
path: linux-arm64-staging/
|
|
|
|
release-server-docker:
|
|
name: Build & Push Server Docker Image
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Extract version from tag
|
|
shell: bash
|
|
run: |
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
|
|
|
- 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: Extract Docker metadata
|
|
id: meta
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ghcr.io/${{ github.repository_owner }}/owncord-server
|
|
tags: |
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=raw,value=latest
|
|
|
|
- name: Build and push
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
context: Server/
|
|
push: true
|
|
build-args: VERSION=${{ env.VERSION }}
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
|
|
publish:
|
|
name: Publish GitHub Release
|
|
needs: [release-client-windows, release-client-linux, release-client-linux-arm64, release-server, release-server-docker]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
cache-dependency-path: Client/tauri-client/package-lock.json
|
|
|
|
- name: Download Windows client assets
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: windows-release-assets
|
|
path: windows
|
|
|
|
- name: Download Linux x86_64 client assets
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: linux-release-assets
|
|
path: linux
|
|
|
|
- name: Download Linux ARM64 client assets
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: linux-arm64-release-assets
|
|
path: linux
|
|
|
|
- name: Download Windows server binary
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: server-windows
|
|
path: windows
|
|
|
|
- name: Download Linux server archive
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: server-linux
|
|
path: linux
|
|
|
|
- name: Extract version from tag
|
|
shell: bash
|
|
run: |
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
|
|
|
- name: Create source snapshot (AGPL source availability)
|
|
shell: bash
|
|
run: |
|
|
git archive --format=tar.gz --prefix="OwnCord-${VERSION}/" \
|
|
-o "owncord-src-${{ github.ref_name }}.tar.gz" HEAD
|
|
|
|
# Checksum lines must use bare asset filenames: the v1.0.0 updater's
|
|
# ParseChecksumFile does an exact match on the last field, so a
|
|
# "windows/" prefix would strand every deployed server on 1.0.0.
|
|
- name: Generate SHA256 checksums
|
|
shell: bash
|
|
run: |
|
|
(cd windows && sha256sum *) > checksums.sha256
|
|
(cd linux && sha256sum *) >> checksums.sha256
|
|
sha256sum owncord-src-*.tar.gz >> checksums.sha256
|
|
|
|
- name: Generate server update manifest
|
|
shell: bash
|
|
run: |
|
|
SERVER_HASH=$(sha256sum windows/chatserver.exe | awk '{print $1}')
|
|
printf '{"version":"v%s","asset":"chatserver.exe","sha256":"%s"}' "$VERSION" "$SERVER_HASH" > windows/server-update-manifest.json
|
|
|
|
- name: Sign server update assets
|
|
working-directory: Client/tauri-client
|
|
shell: bash
|
|
env:
|
|
SERVER_UPDATE_SIGNING_PRIVATE_KEY: ${{ secrets.SERVER_UPDATE_SIGNING_PRIVATE_KEY }}
|
|
SERVER_UPDATE_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.SERVER_UPDATE_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
run: |
|
|
KEY_PATH=$(mktemp)
|
|
printf '%s' "$SERVER_UPDATE_SIGNING_PRIVATE_KEY" > "$KEY_PATH"
|
|
trap 'rm -f "$KEY_PATH"' EXIT
|
|
npm ci
|
|
npx tauri signer sign -f "$KEY_PATH" -p "$SERVER_UPDATE_SIGNING_PRIVATE_KEY_PASSWORD" ../../windows/chatserver.exe
|
|
npx tauri signer sign -f "$KEY_PATH" -p "$SERVER_UPDATE_SIGNING_PRIVATE_KEY_PASSWORD" ../../windows/server-update-manifest.json
|
|
|
|
# Fail closed before publishing: prove the freshly signed assets verify
|
|
# against the pinned public key that ships inside the server binary.
|
|
# Catches key/pubkey mismatch, signature format drift, and signer flag
|
|
# regressions — each of which has silently broken this pipeline before.
|
|
- name: Verify signed assets against pinned server update key
|
|
shell: bash
|
|
run: |
|
|
sudo apt-get update && sudo apt-get install -y minisign
|
|
base64 -d Server/updater/server_update_public_key.txt > "$RUNNER_TEMP/server_update.pub"
|
|
for f in windows/chatserver.exe windows/server-update-manifest.json; do
|
|
base64 -d "$f.sig" > "$RUNNER_TEMP/asset.minisig"
|
|
minisign -Vm "$f" -x "$RUNNER_TEMP/asset.minisig" -p "$RUNNER_TEMP/server_update.pub"
|
|
done
|
|
|
|
- name: Install root dependencies (changelogen)
|
|
run: npm ci
|
|
|
|
- name: Generate changelog
|
|
shell: bash
|
|
run: npx changelogen --output CHANGELOG.md
|
|
|
|
- name: Create GitHub Release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
mapfile -t assets < <(find windows linux -type f)
|
|
assets+=(checksums.sha256 owncord-src-*.tar.gz)
|
|
gh release create "${{ github.ref_name }}" \
|
|
--notes-file CHANGELOG.md \
|
|
"${assets[@]}"
|
|
|
|
# The public releases repo is what deployed servers and clients poll for
|
|
# updates, and it carries the AGPL source snapshot while the source repo
|
|
# is private. Publishing there must never be skipped silently once the
|
|
# source repo is private.
|
|
- name: Publish to public releases repo
|
|
shell: bash
|
|
env:
|
|
RELEASES_TOKEN: ${{ secrets.RELEASES_REPO_TOKEN }}
|
|
SOURCE_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
if [ -z "$RELEASES_TOKEN" ]; then
|
|
PRIVATE=$(GH_TOKEN="$SOURCE_REPO_TOKEN" gh api "repos/$GITHUB_REPOSITORY" --jq .private)
|
|
if [ "$PRIVATE" = "true" ]; then
|
|
echo "::error::Source repo is private and RELEASES_REPO_TOKEN is unset — binaries would ship with no public source or update feed (AGPL violation, broken updater)."
|
|
exit 1
|
|
fi
|
|
echo "::warning::RELEASES_REPO_TOKEN not set — skipping publish to J3vb/OwnCord-releases."
|
|
exit 0
|
|
fi
|
|
mapfile -t assets < <(find windows linux -type f)
|
|
assets+=(checksums.sha256 owncord-src-*.tar.gz)
|
|
GH_TOKEN="$RELEASES_TOKEN" gh release create "${{ github.ref_name }}" \
|
|
--repo J3vb/OwnCord-releases \
|
|
--notes-file CHANGELOG.md \
|
|
"${assets[@]}"
|