docs: update docs #463
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| 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 | |
| } | |
| - 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 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 | |
| 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 }} |