Skip to content

Merge pull request #3 from DotHarness/feat/desktop-e2e-harness #4

Merge pull request #3 from DotHarness/feat/desktop-e2e-harness

Merge pull request #3 from DotHarness/feat/desktop-e2e-harness #4

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 .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
build:
needs: test
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'build')
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-15-intel]
include:
- os: ubuntu-latest
os_name: linux
runtime_id: linux-x64
output_name: DotCraft-linux-x64
cargo_target: x86_64-unknown-linux-gnu
- os: windows-latest
os_name: windows
runtime_id: win-x64
output_name: DotCraft-win-x64
cargo_target: x86_64-pc-windows-msvc
- os: macos-15-intel
os_name: macos
runtime_id: osx-x64
output_name: DotCraft-macos-x64
cargo_target: x86_64-apple-darwin
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
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: tui
- 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 TUI (dotcraft-tui)
run: |
cargo build --release --target ${{ matrix.cargo_target }}
working-directory: tui
- name: Copy TUI binary to publish - Windows
if: runner.os == 'Windows'
run: Copy-Item -Path tui\target\${{ matrix.cargo_target }}\release\dotcraft-tui.exe -Destination publish\${{ matrix.output_name }}\
- name: Copy TUI binary to publish - Linux/macOS
if: runner.os != 'Windows'
run: cp tui/target/${{ matrix.cargo_target }}/release/dotcraft-tui publish/${{ matrix.output_name }}/
- 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 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 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
- 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
- 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
}
- 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"
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 build
npx electron-builder --publish never
npm run verify:package
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-win-x64
path: desktop/dist/installer/*.exe
- name: Upload app artifacts - macOS
if: runner.os == 'macOS'
uses: actions/upload-artifact@v6
with:
name: DotCraft-macos-x64-dmg
path: desktop/dist/*.dmg
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
# Extract platform from filename (e.g., DotCraft-linux-x64.tar.gz -> linux)
platform=$(basename "$file" | sed 's/DotCraft-\(.*\)-x64.tar.gz/\1/')
new_name="DotCraft-${VERSION}-${platform}-x64.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
platform=$(basename "$file" | sed 's/DotCraft-\(.*\)-x64.zip/\1/')
new_name="DotCraft-${VERSION}-${platform}-x64.zip"
cp "$file" "release/$new_name"
echo "Renamed: $(basename $file) -> $new_name"
done
# Rename app artifacts - Windows
for file in artifacts/DotCraft-Installer-win-x64/*.exe; do
[ -f "$file" ] || continue
new_name="DotCraft-${VERSION}-win-x64-Setup.exe"
cp "$file" "release/$new_name"
echo "Renamed: $(basename $file) -> $new_name"
done
# Rename app artifacts - macOS
for file in artifacts/DotCraft-macos-x64-dmg/*.dmg; do
[ -f "$file" ] || continue
new_name="DotCraft-${VERSION}-macos-x64.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 }}