Skip to content

feat: add fork, worktree and handoff support, also enhance desktop UI… #53

feat: add fork, worktree and handoff support, also enhance desktop UI…

feat: add fork, worktree and handoff support, also enhance desktop UI… #53

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:
include:
- os: ubuntu-latest
os_name: linux
runtime_id: linux-x64
output_name: DotCraft-linux-x64
cargo_target: x86_64-unknown-linux-gnu
electron_builder_args: ''
- os: windows-latest
os_name: windows
runtime_id: win-x64
output_name: DotCraft-win-x64
cargo_target: x86_64-pc-windows-msvc
electron_builder_args: --win --x64
- os: windows-latest
os_name: windows
runtime_id: win-arm64
output_name: DotCraft-win-arm64
cargo_target: aarch64-pc-windows-msvc
electron_builder_args: --win --arm64
- os: macos-15-intel
os_name: macos
runtime_id: osx-x64
output_name: DotCraft-macos-x64
cargo_target: x86_64-apple-darwin
electron_builder_args: --mac --x64
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: Install Rust target
run: rustup target add ${{ matrix.cargo_target }}
- 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: Setup MSVC x64 tools
if: matrix.runtime_id == 'win-x64'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Setup MSVC ARM64 cross tools
if: matrix.runtime_id == 'win-arm64'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: amd64_arm64
- 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 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 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 ${{ matrix.electron_builder_args }} --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-${{ matrix.runtime_id }}
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
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 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 }}