Skip to content

bump version to v0.6.3 #505

bump version to v0.6.3

bump version to v0.6.3 #505

name: Build Multi-Platform
on:
push:
branches: [ master, main ]
tags:
- 'v*.*.*'
pull_request:
branches: [ master, main ]
types: [labeled]
workflow_dispatch:
jobs:
test:
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'build')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '22'
- name: Test Docker configuration rendering
run: node --test docker/dotcraft/render-config.test.mjs
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- name: Restore dependencies
run: dotnet restore dotcraft.sln
- name: Run tests
run: dotnet test dotcraft.sln --configuration Release --no-restore
# The Satellite solution is Windows-only, so it cannot ride along with the Linux test job.
satellite:
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'build')
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- name: Build Satellite
run: dotnet build src/DotCraft.Satellite/DotCraft.Satellite.sln --configuration Release
- name: Run Satellite tests
run: dotnet test tests/DotCraft.Satellite.Tests/DotCraft.Satellite.Tests.csproj --configuration Release --no-build
build:
needs: test
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'build')
strategy:
matrix:
include:
- os: ubuntu-latest
os_name: linux
runtime_id: linux-x64
output_name: DotCraft-linux-x64
electron_builder_args: ''
- os: windows-latest
os_name: windows
runtime_id: win-x64
output_name: DotCraft-win-x64
companion_platform: win32
companion_arch: x64
electron_builder_args: --win --x64
- os: windows-latest
os_name: windows
runtime_id: win-arm64
output_name: DotCraft-win-arm64
companion_platform: win32
companion_arch: arm64
electron_builder_args: --win --arm64
- os: macos-15-intel
os_name: macos
runtime_id: osx-x64
output_name: DotCraft-macos-x64
file_arch: x86_64
companion_platform: darwin
companion_arch: x64
electron_builder_args: --mac --x64
- os: macos-15
os_name: macos
runtime_id: osx-arm64
output_name: DotCraft-macos-arm64
file_arch: arm64
companion_platform: darwin
companion_arch: arm64
electron_builder_args: --mac --arm64
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- name: Restore dependencies
run: |
dotnet restore src/DotCraft.App/DotCraft.App.csproj
dotnet restore src/DotCraft.Oratorio/DotCraft.Oratorio.csproj -r ${{ matrix.runtime_id }}
- name: Build ${{ matrix.os_name }}
run: dotnet publish src/DotCraft.App/DotCraft.App.csproj -c Release -r ${{ matrix.runtime_id }} -p:PublishProfile=ReleaseProfile -o ./publish/${{ matrix.output_name }}
- name: Build Oratorio Server for Desktop
if: runner.os != 'Linux'
run: dotnet publish src/DotCraft.Oratorio/DotCraft.Oratorio.csproj -c Release -r ${{ matrix.runtime_id }} --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o ./publish/oratorio-${{ matrix.runtime_id }}
- name: Verify macOS binary architectures
if: runner.os == 'macOS'
shell: bash
run: |
expected_arch="${{ matrix.file_arch }}"
file_output="$(file publish/${{ matrix.output_name }}/dotcraft)"
echo "$file_output"
if ! grep -q "dotcraft:.*Mach-O.*${expected_arch}" <<< "$file_output"; then
echo "::error::dotcraft is not a ${expected_arch} Mach-O binary"
exit 1
fi
oratorio_file_output="$(file publish/oratorio-${{ matrix.runtime_id }}/oratorio)"
echo "$oratorio_file_output"
if ! grep -q "oratorio:.*Mach-O.*${expected_arch}" <<< "$oratorio_file_output"; then
echo "::error::oratorio is not a ${expected_arch} Mach-O binary"
exit 1
fi
- name: Create archive for Linux/macOS
if: runner.os != 'Windows'
run: |
cd publish/${{ matrix.output_name }}
tar -czf ../../${{ matrix.output_name }}.tar.gz *
cd ../..
- name: Create archive for Windows
if: runner.os == 'Windows'
run: |
Compress-Archive -Path publish/${{ matrix.output_name }}/* -DestinationPath ${{ matrix.output_name }}.zip
- name: Upload artifact - Linux/macOS
if: runner.os != 'Windows'
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.output_name }}
path: ${{ matrix.output_name }}.tar.gz
- name: Upload artifact - Windows
if: runner.os == 'Windows'
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.output_name }}
path: ${{ matrix.output_name }}.zip
# ── Desktop (Electron) build ──────────────────────────────────────────────
- name: Setup Node.js
if: runner.os != 'Linux'
uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: |
desktop/package-lock.json
sdk/typescript/package-lock.json
- name: Install desktop dependencies
if: runner.os != 'Linux'
run: npm ci
working-directory: desktop
- name: Install Windows ARM64 optional runtime packages
if: matrix.runtime_id == 'win-arm64'
run: node scripts/install-target-optional-deps.mjs win32 arm64
working-directory: desktop
- name: Install TypeScript SDK dependencies
if: runner.os != 'Linux'
run: npm ci
working-directory: sdk/typescript
- name: Build TypeScript SDK
if: runner.os != 'Linux'
run: npm run build:all
working-directory: sdk/typescript
- name: Stage Feishu CLI companion for desktop
if: runner.os != 'Linux'
run: >-
node sdk/typescript/packages/channel-feishu/scripts/stage-official-cli.mjs
--platform ${{ matrix.companion_platform }}
--arch ${{ matrix.companion_arch }}
--output desktop/resources/modules/channel-feishu/vendor
- name: Stage embedded binary for desktop - Windows
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path desktop/resources/bin | Out-Null
Copy-Item -Path publish/${{ matrix.output_name }}/dotcraft.exe -Destination desktop/resources/bin/dotcraft.exe -Force
Copy-Item -Path publish/oratorio-${{ matrix.runtime_id }}/oratorio.exe -Destination desktop/resources/bin/oratorio.exe -Force
- name: Stage embedded binary for desktop - macOS
if: runner.os == 'macOS'
shell: bash
run: |
mkdir -p desktop/resources/bin
cp publish/${{ matrix.output_name }}/dotcraft desktop/resources/bin/dotcraft
cp publish/oratorio-${{ matrix.runtime_id }}/oratorio desktop/resources/bin/oratorio
chmod +x desktop/resources/bin/oratorio
- name: Stage bundled modules for desktop - Windows
if: runner.os == 'Windows'
shell: pwsh
run: |
$modules = @('channel-feishu', 'channel-qq', 'channel-wecom', 'channel-weixin', 'channel-telegram')
foreach ($mod in $modules) {
$src = "sdk/typescript/packages/$mod"
$dst = "desktop/resources/modules/$mod"
New-Item -ItemType Directory -Force -Path $dst | Out-Null
Copy-Item -Path "$src/manifest.json" -Destination "$dst/manifest.json" -Force
Copy-Item -Path "$src/package.json" -Destination "$dst/package.json" -Force
Copy-Item -Path "$src/dist" -Destination "$dst/dist" -Recurse -Force
if (Test-Path "$src/assets") {
Copy-Item -Path "$src/assets" -Destination "$dst/assets" -Recurse -Force
}
}
- name: Stage bundled modules for desktop - macOS
if: runner.os == 'macOS'
shell: bash
run: |
for mod in channel-feishu channel-qq channel-wecom channel-weixin channel-telegram; do
src="sdk/typescript/packages/$mod"
dst="desktop/resources/modules/$mod"
mkdir -p "$dst"
cp "$src/manifest.json" "$dst/manifest.json"
cp "$src/package.json" "$dst/package.json"
cp -r "$src/dist" "$dst/dist"
if [ -d "$src/assets" ]; then
cp -r "$src/assets" "$dst/assets"
fi
done
# Match build.bat: avoid stale dist confusing electron-builder; safe on fresh checkouts too.
- name: Clean desktop dist
if: runner.os != 'Linux'
shell: bash
run: rm -rf desktop/dist
- name: Build desktop app
if: runner.os != 'Linux'
run: npm run dist:package -- ${{ matrix.electron_builder_args }}
working-directory: desktop
env:
# No code signing cert in CI; skip discovery so macOS/Windows builds do not fail.
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
- name: Prepare Windows artifacts
if: runner.os == 'Windows'
shell: bash
run: |
mkdir -p desktop/dist/installer
mv desktop/dist/*Setup*.exe desktop/dist/installer/ || true
- name: Upload app installer - Windows
if: runner.os == 'Windows'
uses: actions/upload-artifact@v6
with:
name: DotCraft-Installer-${{ matrix.runtime_id }}
path: desktop/dist/installer/*.exe
- name: Upload app artifacts - macOS
if: runner.os == 'macOS'
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.output_name }}-dmg
path: desktop/dist/*.dmg
# ── Satellite (WinUI tray client) ─────────────────────────────────────────
- name: Build Satellite
if: runner.os == 'Windows'
run: dotnet publish src/DotCraft.Satellite/DotCraft.Satellite.csproj -c Release -r ${{ matrix.runtime_id }} -o ./publish/satellite-${{ matrix.runtime_id }}
- name: Package Satellite installer
if: runner.os == 'Windows'
shell: pwsh
env:
WINDOWS_CERT_BASE64: ${{ secrets.WINDOWS_CERT_BASE64 }}
WINDOWS_CERT_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }}
run: |
dotnet tool restore
$version = (Select-Xml -Path src/DotCraft.App/DotCraft.App.csproj -XPath '//Version').Node.InnerText
$arguments = @(
'vpk', 'pack',
'--packId', 'DotCraft.Satellite',
'--packVersion', $version,
'--packDir', "./publish/satellite-${{ matrix.runtime_id }}",
'--mainExe', 'dotcraft-satellite.exe',
'--packTitle', 'DotCraft Satellite',
'--icon', 'src/DotCraft.Satellite/Assets/satellite.ico',
'--noPortable', '--delta', 'none',
'--outputDir', "./satellite-installer/${{ matrix.runtime_id }}"
)
if ($env:WINDOWS_CERT_BASE64) {
[IO.File]::WriteAllBytes("$env:RUNNER_TEMP/signing.pfx", [Convert]::FromBase64String($env:WINDOWS_CERT_BASE64))
$arguments += @('--signParams', "/fd sha256 /f `"$env:RUNNER_TEMP/signing.pfx`" /p `"$env:WINDOWS_CERT_PASSWORD`" /tr http://timestamp.digicert.com /td sha256")
}
else {
Write-Host 'No signing certificate is configured; the installer ships unsigned.'
}
dotnet @arguments
- name: Upload Satellite installer
if: runner.os == 'Windows'
uses: actions/upload-artifact@v6
with:
name: DotCraft-Satellite-${{ matrix.runtime_id }}
path: satellite-installer/${{ matrix.runtime_id }}/*Setup.exe
release:
needs: build
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Download all artifacts
uses: actions/download-artifact@v7
with:
path: artifacts
- name: Rename artifacts with version
run: |
VERSION=${GITHUB_REF#refs/tags/}
echo "Version: $VERSION"
# Create release directory
mkdir -p release
# Rename CLI artifacts
for file in artifacts/DotCraft-*/DotCraft-*.tar.gz; do
[ -f "$file" ] || continue
target=$(basename "$file")
target=${target#DotCraft-}
target=${target%.tar.gz}
new_name="DotCraft-${VERSION}-${target}.tar.gz"
cp "$file" "release/$new_name"
echo "Renamed: $(basename "$file") -> $new_name"
done
for file in artifacts/DotCraft-*/DotCraft-*.zip; do
[ -f "$file" ] || continue
target=$(basename "$file")
target=${target#DotCraft-}
target=${target%.zip}
new_name="DotCraft-${VERSION}-${target}.zip"
cp "$file" "release/$new_name"
echo "Renamed: $(basename "$file") -> $new_name"
done
# Rename app artifacts - Windows
for file in artifacts/DotCraft-Installer-win-*/*.exe; do
[ -f "$file" ] || continue
target=$(basename "$(dirname "$file")")
target=${target#DotCraft-Installer-}
new_name="DotCraft-${VERSION}-${target}-Setup.exe"
cp "$file" "release/$new_name"
echo "Renamed: $(basename "$file") -> $new_name"
done
# Rename Satellite installers
for file in artifacts/DotCraft-Satellite-win-*/*Setup.exe; do
[ -f "$file" ] || continue
target=$(basename "$(dirname "$file")")
target=${target#DotCraft-Satellite-}
new_name="DotCraft-Satellite-${VERSION}-${target}-Setup.exe"
cp "$file" "release/$new_name"
echo "Renamed: $(basename "$file") -> $new_name"
done
# Rename app artifacts - macOS
for file in artifacts/DotCraft-macos-*-dmg/*.dmg; do
[ -f "$file" ] || continue
target=$(basename "$(dirname "$file")")
target=${target#DotCraft-}
target=${target%-dmg}
new_name="DotCraft-${VERSION}-${target}.dmg"
cp "$file" "release/$new_name"
echo "Renamed: $(basename "$file") -> $new_name"
done
echo "Final release files:"
ls -lh release/
- name: Prepare Release Notes
id: release_notes
run: |
VERSION=${GITHUB_REF#refs/tags/}
REPO=${{ github.repository }}
# Use template file when available.
if [ -f ".github/release_template.md" ]; then
sed \
-e "s|{{ VERSION }}|$VERSION|g" \
-e "s|{{ REPO }}|$REPO|g" \
-e "s|{{ CHANGELOG }}|- See auto-generated notes below.|g" \
".github/release_template.md" > "release_notes.md"
else
echo "Automatic release $VERSION" > "release_notes.md"
fi
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: release/*
body_path: release_notes.md
append_body: true
draft: false
prerelease: false
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}