mirror of
https://github.com/citron-neo/PR.git
synced 2026-07-05 15:22:02 -07:00
337 lines
15 KiB
YAML
337 lines
15 KiB
YAML
name: Build Citron (Windows)
|
|
|
|
concurrency:
|
|
group: build-windows-${{ inputs.target_branch || github.event.pull_request.head.ref || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
target_branch:
|
|
description: "Upstream branch to build"
|
|
required: false
|
|
type: string
|
|
pr_number:
|
|
description: "PR number to comment on"
|
|
required: false
|
|
type: string
|
|
head_repo:
|
|
description: "Head repo to clone from (e.g. user/emulator)"
|
|
required: false
|
|
type: string
|
|
|
|
env:
|
|
UPSTREAM_REPO: "citron-neo/emulator" # <--- Change this to switch forks
|
|
|
|
jobs:
|
|
check-version:
|
|
name: Check if build needed
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
should_build: ${{ steps.compare.outputs.should_build }}
|
|
new_hash: ${{ steps.upstream.outputs.hash_short }}
|
|
new_hash_full: ${{ steps.upstream.outputs.hash_full }}
|
|
branch_name: ${{ steps.upstream.outputs.branch_name }}
|
|
head_repo: ${{ steps.upstream.outputs.head_repo }}
|
|
pr_number: ${{ steps.upstream.outputs.pr_number }}
|
|
steps:
|
|
- name: Checkout this repo
|
|
uses: actions/checkout@v4
|
|
- name: Get upstream commit hash
|
|
id: upstream
|
|
run: |
|
|
PR_NUMBER="${{ inputs.pr_number }}"
|
|
TARGET_BRANCH="${{ inputs.target_branch }}"
|
|
HEAD_REPO="${{ inputs.head_repo }}"
|
|
|
|
if [ -z "$HEAD_REPO" ]; then HEAD_REPO="${{ env.UPSTREAM_REPO }}"; fi
|
|
if [ -z "$TARGET_BRANCH" ]; then TARGET_BRANCH="main"; fi
|
|
|
|
REPO_URL="https://github.com/${HEAD_REPO}.git"
|
|
FULL_HASH=$(git ls-remote "$REPO_URL" "refs/heads/$TARGET_BRANCH" | cut -f1 | head -n1)
|
|
|
|
if [ -z "$FULL_HASH" ]; then
|
|
REPO_URL="https://github.com/${{ env.UPSTREAM_REPO }}.git"
|
|
TARGET_BRANCH="main"
|
|
FULL_HASH=$(git ls-remote "$REPO_URL" HEAD | cut -f1)
|
|
fi
|
|
|
|
SHORT_HASH=$(echo "$FULL_HASH" | cut -c1-7)
|
|
echo "branch_name=$TARGET_BRANCH" >> $GITHUB_OUTPUT
|
|
echo "hash_short=$SHORT_HASH" >> $GITHUB_OUTPUT
|
|
echo "hash_full=$FULL_HASH" >> $GITHUB_OUTPUT
|
|
echo "head_repo=$HEAD_REPO" >> $GITHUB_OUTPUT
|
|
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
|
|
|
|
- name: Check if already built (via PR comment)
|
|
id: built_check
|
|
env:
|
|
GH_TOKEN: ${{ secrets.EMULATOR_PR_TOKEN }}
|
|
run: |
|
|
HASH_SHORT="${{ steps.upstream.outputs.hash_short }}"
|
|
PR_NUMBER="${{ steps.upstream.outputs.pr_number }}"
|
|
UPSTREAM="${{ env.UPSTREAM_REPO }}"
|
|
PLATFORM="Windows"
|
|
|
|
if [ -z "$GH_TOKEN" ] || [ -z "$PR_NUMBER" ]; then
|
|
echo "No token or PR number, will build."
|
|
echo "already_built=false" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
|
|
MARKER="<!-- citron-pr-builds:${PR_NUMBER} -->"
|
|
COMMENT_BODY=$(gh api "repos/${UPSTREAM}/issues/${PR_NUMBER}/comments?per_page=100" \
|
|
--jq "[.[] | select(.body | contains(\"$MARKER\") or contains(\"Build Artifacts for PR #${PR_NUMBER}\"))] | last | .body // empty" \
|
|
2>/dev/null || echo "")
|
|
|
|
if [ -z "$COMMENT_BODY" ]; then
|
|
echo "No master comment found, will build."
|
|
echo "already_built=false" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
|
|
PLATFORM_ROW=$(echo "$COMMENT_BODY" | grep "\*\*${PLATFORM}\*\*" || true)
|
|
echo "Platform row: $PLATFORM_ROW"
|
|
|
|
if echo "$PLATFORM_ROW" | grep -qF "$HASH_SHORT"; then
|
|
if echo "$PLATFORM_ROW" | grep -qE "(\[MSVC\]|\[Clangtron\]|Failed)"; then
|
|
echo "Commit ${HASH_SHORT} already built or failed for ${PLATFORM}. Skipping."
|
|
echo "already_built=true" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
echo "No conclusive result for commit ${HASH_SHORT} on ${PLATFORM}. Will build."
|
|
echo "already_built=false" >> $GITHUB_OUTPUT
|
|
continue-on-error: true
|
|
|
|
- name: Compare versions
|
|
id: compare
|
|
run: |
|
|
if [ "${{ steps.built_check.outputs.already_built }}" = "true" ]; then
|
|
echo "should_build=false" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "should_build=true" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
comment-start:
|
|
name: Mark Progress
|
|
needs: [check-version]
|
|
if: needs.check-version.outputs.should_build == 'true' && needs.check-version.outputs.pr_number != ''
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Update Master Comment (In Progress)
|
|
env:
|
|
GH_TOKEN: ${{ secrets.EMULATOR_PR_TOKEN }}
|
|
PR_NUMBER: ${{ needs.check-version.outputs.pr_number }}
|
|
UPSTREAM: ${{ env.UPSTREAM_REPO }}
|
|
HASH_SHORT: ${{ needs.check-version.outputs.new_hash }}
|
|
HASH_FULL: ${{ needs.check-version.outputs.new_hash_full }}
|
|
HEAD_REPO: ${{ needs.check-version.outputs.head_repo }}
|
|
run: |
|
|
if [ -z "$GH_TOKEN" ]; then echo "EMULATOR_PR_TOKEN not set"; exit 0; fi
|
|
MARKER="<!-- citron-pr-builds:${PR_NUMBER} -->"
|
|
TITLE="**Build Artifacts for PR #${PR_NUMBER}**"
|
|
COMMENT_IDS=$(gh api "repos/${UPSTREAM}/issues/${PR_NUMBER}/comments?per_page=100" --jq ".[] | select(.body | contains(\"$MARKER\") or contains(\"Build Artifacts for PR #${PR_NUMBER}\")) | .id" 2>/dev/null || true)
|
|
COMMENT_ID=$(printf '%s\n' "$COMMENT_IDS" | sed '/^$/d' | tail -n 1)
|
|
|
|
if [ -z "$COMMENT_ID" ]; then
|
|
INITIAL_BODY="${MARKER}
|
|
${TITLE}
|
|
|
|
| Platform | Commit | Artifacts (Direct Download) | Logs |
|
|
| :--- | :--- | :--- | :--- |
|
|
| **Windows** | | Pending | |
|
|
| **Linux** | | Pending | |
|
|
| **Android** | | Pending | |
|
|
| **macOS** | | Pending | |
|
|
|
|
*Note: Direct downloads powered by nightly.link*"
|
|
jq -n --arg body "$INITIAL_BODY" '{body: $body}' | gh api -X POST "repos/${UPSTREAM}/issues/${PR_NUMBER}/comments" --input - >/dev/null || echo "Failed creation"
|
|
sleep 2
|
|
COMMENT_IDS=$(gh api "repos/${UPSTREAM}/issues/${PR_NUMBER}/comments?per_page=100" --jq ".[] | select(.body | contains(\"$MARKER\") or contains(\"Build Artifacts for PR #${PR_NUMBER}\")) | .id" 2>/dev/null || true)
|
|
COMMENT_ID=$(printf '%s\n' "$COMMENT_IDS" | sed '/^$/d' | tail -n 1)
|
|
fi
|
|
|
|
for DUPLICATE_ID in $COMMENT_IDS; do
|
|
if [ -n "$COMMENT_ID" ] && [ "$DUPLICATE_ID" != "$COMMENT_ID" ]; then
|
|
gh api -X DELETE "repos/${UPSTREAM}/issues/comments/${DUPLICATE_ID}" >/dev/null 2>&1 || true
|
|
fi
|
|
done
|
|
|
|
if [ -n "$COMMENT_ID" ]; then
|
|
COMMIT_LINK="[\`${HASH_SHORT}\`](https://github.com/${HEAD_REPO}/commit/${HASH_FULL})"
|
|
export NEW_ROW="| **Windows** | ${COMMIT_LINK} | Building... | |"
|
|
gh api "repos/${UPSTREAM}/issues/comments/${COMMENT_ID}" --jq ".body" > body.txt
|
|
grep -Fq "$MARKER" body.txt || { BODY=$(cat body.txt); printf '%s\n%s\n' "$MARKER" "$BODY" > body.txt; }
|
|
perl -pi -e 's|.*\*\*Windows\*\*.*|$ENV{NEW_ROW}|' body.txt
|
|
jq -n --arg body "$(cat body.txt)" '{body: $body}' | gh api -X PATCH "repos/${UPSTREAM}/issues/comments/${COMMENT_ID}" --input - || echo "Failed update"
|
|
fi
|
|
continue-on-error: true
|
|
|
|
build-msvc:
|
|
name: "Citron Build (Windows MSVC - x64)"
|
|
needs: [check-version]
|
|
if: needs.check-version.outputs.should_build == 'true'
|
|
runs-on: windows-latest
|
|
timeout-minutes: 180
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Install Vulkan SDK
|
|
shell: pwsh
|
|
run: |
|
|
$vulkan_sdk_url = "https://sdk.lunarg.com/sdk/download/1.3.283.0/windows/VulkanSDK-1.3.283.0-Installer.exe"
|
|
Invoke-WebRequest -Uri $vulkan_sdk_url -OutFile VulkanSDK-Installer.exe
|
|
Start-Process -FilePath ".\VulkanSDK-Installer.exe" -ArgumentList "--accept-licenses", "--default-answer", "--confirm-command", "install" -Wait
|
|
echo "C:\VulkanSDK\1.3.283.0\Bin" | Out-File -FilePath $env:GITHUB_PATH -Append
|
|
|
|
- name: Clone Citron Source
|
|
shell: bash
|
|
run: |
|
|
CLONE_REPO="${{ needs.check-version.outputs.head_repo }}"
|
|
if [[ "$CLONE_REPO" != *"/"* ]]; then CLONE_REPO="${{ env.UPSTREAM_REPO }}"; fi
|
|
git clone --recursive -b "${{ needs.check-version.outputs.branch_name }}" "https://github.com/${CLONE_REPO}.git" citron
|
|
|
|
- name: Cache vcpkg and Qt dependencies (MSVC)
|
|
uses: actions/cache@v4
|
|
id: cache-vcpkg
|
|
with:
|
|
path: |
|
|
./citron/build
|
|
~/.vcpkg/archives
|
|
${{ env.LOCALAPPDATA }}\vcpkg\archives
|
|
key: vcpkg-qt-cache-x64-${{ runner.os }}-${{ hashFiles('citron/vcpkg.json') }}-${{ hashFiles('citron/externals/vcpkg/.git/HEAD') }}
|
|
restore-keys: |
|
|
vcpkg-qt-cache-x64-${{ runner.os }}-${{ hashFiles('citron/vcpkg.json') }}-
|
|
|
|
- name: Configure CMake
|
|
working-directory: ./citron
|
|
shell: cmd
|
|
run: |
|
|
cmake -B build -S . -G "Visual Studio 17 2022" -A x64 ^
|
|
-DCITRON_USE_BUNDLED_VCPKG=ON -DCITRON_BUILD_TYPE=Release -DCITRON_ENABLE_LTO=ON ^
|
|
-DCITRON_USE_BUNDLED_QT=ON -DENABLE_QT6=ON -DCITRON_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON ^
|
|
-DENABLE_WEB_SERVICE=ON -DENABLE_OPENSSL=ON -DCITRON_USE_AUTO_UPDATER=ON -DCITRON_ENABLE_LIBARCHIVE=ON
|
|
|
|
- name: Build
|
|
working-directory: ./citron
|
|
shell: cmd
|
|
run: cmake --build build --config Release --parallel --target citron citron-cmd citron-room
|
|
|
|
- name: Prepare Package
|
|
shell: pwsh
|
|
run: |
|
|
mkdir temp_package
|
|
Get-ChildItem ".\citron\build\bin\Release\" -Recurse | Where-Object { $_.Extension -ne ".pdb" } | ForEach-Object {
|
|
$relativePath = $_.FullName.Replace((Resolve-Path ".\citron\build\bin\Release\").Path, "").TrimStart('\')
|
|
$destPath = Join-Path ".\temp_package" $relativePath
|
|
$destDir = Split-Path $destPath -Parent
|
|
if (!(Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
|
|
Copy-Item $_.FullName $destPath -Force
|
|
}
|
|
|
|
- name: Upload
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: Citron-Windows-MSVC
|
|
path: "temp_package/*"
|
|
|
|
build-clangtron:
|
|
name: "Citron Build (Windows Clangtron - x64)"
|
|
needs: [check-version]
|
|
if: needs.check-version.outputs.should_build == 'true'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 240
|
|
env:
|
|
CPM_SOURCE_CACHE: /home/runner/.cache/cpm
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Clone Citron Source
|
|
run: |
|
|
CLONE_REPO="${{ needs.check-version.outputs.head_repo }}"
|
|
if [[ "$CLONE_REPO" != *"/"* ]]; then CLONE_REPO="${{ env.UPSTREAM_REPO }}"; fi
|
|
git clone -b "${{ needs.check-version.outputs.branch_name }}" "https://github.com/${CLONE_REPO}.git" citron
|
|
|
|
- name: Cache CPM and Toolchain (Clangtron)
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: /home/runner/.cache/cpm
|
|
key: clangtron-cache-${{ runner.os }}-${{ hashFiles('citron/CMakeModules/dependencies.cmake', 'citron/build-clangtron-windows.sh') }}
|
|
restore-keys: |
|
|
clangtron-cache-${{ runner.os }}-
|
|
|
|
- name: Skip BOLT build (not needed for --pgo none)
|
|
run: |
|
|
echo '#!/bin/bash' | sudo tee /usr/local/bin/llvm-bolt-21
|
|
echo '#!/bin/bash' | sudo tee /usr/local/bin/merge-fdata-21
|
|
sudo chmod +x /usr/local/bin/llvm-bolt-21
|
|
sudo chmod +x /usr/local/bin/merge-fdata-21
|
|
|
|
- name: Setup Clangtron
|
|
working-directory: ./citron
|
|
run: |
|
|
chmod +x build-clangtron-windows.sh
|
|
./build-clangtron-windows.sh setup
|
|
echo "${HOME}/.cache/cpm/llvm-mingw/bin" >> "$GITHUB_PATH"
|
|
- name: Build
|
|
working-directory: ./citron
|
|
run: |
|
|
export CITRON_BUILD_TYPE=Release
|
|
./build-clangtron-windows.sh use --pgo none --lto full
|
|
- name: Upload
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: Citron-Windows-Clangtron
|
|
path: "./citron/build/use-nopgo/bin/*"
|
|
|
|
comment-end:
|
|
name: Comment End
|
|
needs: [check-version, build-msvc, build-clangtron]
|
|
if: always() && needs.check-version.outputs.should_build == 'true' && needs.check-version.outputs.pr_number != ''
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Update Master Comment (Results)
|
|
env:
|
|
GH_TOKEN: ${{ secrets.EMULATOR_PR_TOKEN }}
|
|
PR_NUMBER: ${{ needs.check-version.outputs.pr_number }}
|
|
RUN_ID: ${{ github.run_id }}
|
|
REPO: ${{ github.repository }}
|
|
UPSTREAM: ${{ env.UPSTREAM_REPO }}
|
|
HASH_SHORT: ${{ needs.check-version.outputs.new_hash }}
|
|
HASH_FULL: ${{ needs.check-version.outputs.new_hash_full }}
|
|
HEAD_REPO: ${{ needs.check-version.outputs.head_repo }}
|
|
MSVC_STATUS: ${{ needs.build-msvc.result }}
|
|
CLANG_STATUS: ${{ needs.build-clangtron.result }}
|
|
run: |
|
|
if [ -z "$GH_TOKEN" ]; then echo "EMULATOR_PR_TOKEN not set"; exit 1; fi
|
|
MARKER="<!-- citron-pr-builds:${PR_NUMBER} -->"
|
|
COMMENT_IDS=$(gh api "repos/${UPSTREAM}/issues/${PR_NUMBER}/comments?per_page=100" --jq ".[] | select(.body | contains(\"$MARKER\") or contains(\"Build Artifacts for PR #${PR_NUMBER}\")) | .id" 2>/dev/null || true)
|
|
COMMENT_ID=$(printf '%s\n' "$COMMENT_IDS" | sed '/^$/d' | tail -n 1)
|
|
|
|
for DUPLICATE_ID in $COMMENT_IDS; do
|
|
if [ -n "$COMMENT_ID" ] && [ "$DUPLICATE_ID" != "$COMMENT_ID" ]; then
|
|
gh api -X DELETE "repos/${UPSTREAM}/issues/comments/${DUPLICATE_ID}" >/dev/null 2>&1 || true
|
|
fi
|
|
done
|
|
|
|
if [ -n "$COMMENT_ID" ]; then
|
|
LOG_URL="https://github.com/${REPO}/actions/runs/${RUN_ID}"
|
|
COMMIT_LINK="[\`${HASH_SHORT}\`](https://github.com/${HEAD_REPO}/commit/${HASH_FULL})"
|
|
|
|
if [ "$MSVC_STATUS" = "success" ] && [ "$CLANG_STATUS" = "success" ]; then
|
|
MSVC_URL="https://nightly.link/${REPO}/actions/runs/${RUN_ID}/Citron-Windows-MSVC.zip"
|
|
CLANG_URL="https://nightly.link/${REPO}/actions/runs/${RUN_ID}/Citron-Windows-Clangtron.zip"
|
|
ARTIFACTS="[MSVC](${MSVC_URL}) [Clangtron](${CLANG_URL})"
|
|
else
|
|
ARTIFACTS="Failed"
|
|
fi
|
|
|
|
export NEW_ROW="| **Windows** | ${COMMIT_LINK} | ${ARTIFACTS} | [Run](${LOG_URL}) |"
|
|
gh api "repos/${UPSTREAM}/issues/comments/${COMMENT_ID}" --jq ".body" > body.txt
|
|
grep -Fq "$MARKER" body.txt || { BODY=$(cat body.txt); printf '%s\n%s\n' "$MARKER" "$BODY" > body.txt; }
|
|
perl -pi -e 's|.*\*\*Windows\*\*.*|$ENV{NEW_ROW}|' body.txt
|
|
jq -n --arg body "$(cat body.txt)" '{body: $body}' | gh api -X PATCH "repos/${UPSTREAM}/issues/comments/${COMMENT_ID}" --input - || echo "Failed final update"
|
|
else
|
|
echo "Master comment not found, skipping final update."
|
|
fi
|