github.yml 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. name: VCMI
  2. on:
  3. push:
  4. branches:
  5. - beta
  6. - master
  7. - develop
  8. pull_request:
  9. workflow_dispatch:
  10. concurrency:
  11. group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
  12. cancel-in-progress: true
  13. jobs:
  14. build:
  15. name: Build (${{ matrix.platform }})
  16. strategy:
  17. matrix:
  18. include:
  19. - platform: mac-intel
  20. os: macos-14
  21. pack: 1
  22. upload: 1
  23. pack_type: Release
  24. extension: dmg
  25. before_install: macos.sh
  26. preset: macos-conan-ninja-release
  27. conan_profile: macos-intel
  28. conan_prebuilts: dependencies-mac-intel
  29. conan_options: --profile=dependencies/conan_profiles/base/apple-system
  30. artifact_platform: intel
  31. - platform: mac-arm
  32. os: macos-14
  33. pack: 1
  34. upload: 1
  35. pack_type: Release
  36. extension: dmg
  37. before_install: macos.sh
  38. preset: macos-arm-conan-ninja-release
  39. conan_profile: macos-arm
  40. conan_prebuilts: dependencies-mac-arm
  41. conan_options: --profile=dependencies/conan_profiles/base/apple-system
  42. artifact_platform: arm
  43. - platform: ios
  44. os: macos-14
  45. pack: 1
  46. upload: 1
  47. pack_type: RelWithDebInfo
  48. extension: ipa
  49. before_install: macos.sh
  50. preset: ios-release-conan-ccache
  51. conan_profile: ios-arm64
  52. conan_prebuilts: dependencies-ios
  53. conan_options: --profile=dependencies/conan_profiles/base/apple-system
  54. - platform: msvc-x64
  55. arch: x64
  56. toolset: '14.29'
  57. os: windows-2025
  58. pack: 1
  59. upload: 0
  60. pack_type: RelWithDebInfo
  61. extension: zip
  62. preset: windows-msvc-ninja-release
  63. conan_profile: msvc-x64
  64. conan_prebuilts: dependencies-windows-x64
  65. conan_options: -s "&:build_type=RelWithDebInfo" -c tools.env.virtualenv:powershell=pwsh -o "&:target_pre_windows10=True"
  66. artifact_platform: x64
  67. - platform: msvc-x86
  68. arch: amd64_x86
  69. toolset: '14.29'
  70. os: windows-2025
  71. pack: 1
  72. upload: 0
  73. pack_type: RelWithDebInfo
  74. extension: zip
  75. preset: windows-msvc-ninja-release-x86
  76. conan_profile: msvc-x86
  77. conan_prebuilts: dependencies-windows-x86
  78. conan_options: -s "&:build_type=RelWithDebInfo" -c tools.env.virtualenv:powershell=pwsh -o "&:target_pre_windows10=True"
  79. artifact_platform: x86
  80. - platform: msvc-arm64
  81. arch: arm64
  82. os: windows-11-arm
  83. pack: 1
  84. upload: 0
  85. pack_type: RelWithDebInfo
  86. extension: zip
  87. preset: windows-msvc-ninja-release-arm64
  88. conan_profile: msvc-arm64
  89. conan_prebuilts: dependencies-windows-arm64
  90. conan_options: -s "&:build_type=RelWithDebInfo" -c tools.env.virtualenv:powershell=pwsh -o "&:lua_lib=lua"
  91. artifact_platform: arm64
  92. - platform: android-32
  93. os: ubuntu-latest
  94. upload: 1
  95. extension: apk
  96. preset: android-conan-ninja-release
  97. conan_profile: android-32-ndk
  98. conan_prebuilts: dependencies-android-armeabi-v7a
  99. conan_options: --profile=dependencies/conan_profiles/base/android-system
  100. artifact_platform: armeabi-v7a
  101. - platform: android-64
  102. os: ubuntu-latest
  103. upload: 1
  104. extension: apk
  105. preset: android-conan-ninja-release
  106. conan_profile: android-64-ndk
  107. conan_prebuilts: dependencies-android-arm64-v8a
  108. conan_options: --profile=dependencies/conan_profiles/base/android-system
  109. artifact_platform: arm64-v8a
  110. - platform: android-64-intel
  111. os: ubuntu-latest
  112. upload: 1
  113. extension: apk
  114. preset: android-conan-ninja-release
  115. conan_profile: android-x64-ndk
  116. conan_prebuilts: dependencies-android-x64
  117. conan_options: --profile=dependencies/conan_profiles/base/android-system
  118. artifact_platform: x64
  119. runs-on: ${{ matrix.os }}
  120. defaults:
  121. run:
  122. shell: bash
  123. steps:
  124. - name: Checkout repository
  125. uses: actions/checkout@v6
  126. with:
  127. submodules: recursive
  128. - name: Prepare APT staging dir
  129. if: contains(matrix.os, 'ubuntu')
  130. run: mkdir -p "$RUNNER_TEMP/apt-cache"
  131. - name: APT cache restore
  132. if: contains(matrix.os, 'ubuntu')
  133. id: aptcache
  134. uses: actions/cache/restore@v5
  135. with:
  136. path: ${{ runner.temp }}/apt-cache
  137. key: ${{ matrix.platform }}-apt-${{ matrix.os }}
  138. restore-keys: |
  139. ${{ matrix.platform }}-apt-${{ matrix.os }}
  140. - name: Prepare CI
  141. if: "${{ matrix.before_install != '' }}"
  142. run: source '${{ github.workspace }}/CI/before_install/${{ matrix.before_install }}'
  143. - name: Configure codesigning for macOS
  144. if: ${{ startsWith(matrix.platform, 'mac-') }}
  145. env:
  146. KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
  147. SIGNING_CERTIFICATE_BASE64: ${{ secrets.MACOS_SIGNING_CERTIFICATE_BASE64 }}
  148. SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_SIGNING_CERTIFICATE_PASSWORD }}
  149. run: |
  150. if [[ -z "$KEYCHAIN_PASSWORD" || -z "$SIGNING_CERTIFICATE_BASE64" || -z "$SIGNING_CERTIFICATE_PASSWORD" ]]; then
  151. echo "skip codesigning"
  152. exit 0
  153. fi
  154. keychainName=build.keychain
  155. security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychainName"
  156. security default-keychain -s "$keychainName"
  157. security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychainName"
  158. security set-keychain-settings -u -t 7200 "$keychainName"
  159. certPath="$RUNNER_TEMP/cert.p12"
  160. printf "$SIGNING_CERTIFICATE_BASE64" | base64 -D -o "$certPath"
  161. security import "$certPath" -f pkcs12 -P "$SIGNING_CERTIFICATE_PASSWORD" -T "$(which codesign)" -k "$keychainName"
  162. security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$keychainName" >/dev/null
  163. # fetch SHA-1 of the p12 cert. Sample output for awk processing:
  164. # SHA1 Fingerprint=00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33
  165. signingIdentitySHA=$(openssl pkcs12 -in "$certPath" -nodes -passin "pass:$SIGNING_CERTIFICATE_PASSWORD" 2>/dev/null \
  166. | openssl x509 -noout -fingerprint -sha1 \
  167. | awk -F = '{ gsub(/:/, "", $2); print $2 }')
  168. echo MACOS_CODE_SIGN_IDENTITY=$signingIdentitySHA >> $GITHUB_ENV
  169. # Save only on cache miss, GitHub caches are immutable per key
  170. - name: APT cache save
  171. if: contains(matrix.os, 'ubuntu') && steps.aptcache.outputs.cache-hit != 'true'
  172. uses: actions/cache/save@v5
  173. with:
  174. path: ${{ runner.temp }}/apt-cache
  175. key: ${{ steps.aptcache.outputs.cache-primary-key }}
  176. - name: Install Conan Dependencies
  177. if: "${{ matrix.conan_prebuilts != '' }}"
  178. run: |
  179. pipx install conan
  180. source '${{github.workspace}}/CI/install_conan_dependencies.sh' '${{matrix.conan_prebuilts}}'
  181. - name: Setup MSVC Developer Command Prompt
  182. if: ${{ startsWith(matrix.platform, 'msvc') }}
  183. uses: ilammy/msvc-dev-cmd@v1
  184. with:
  185. arch: ${{ matrix.arch }}
  186. toolset: ${{ matrix.toolset }}
  187. # ensure the cache for each PR is separate so they don't interfere with each other
  188. # fall back to cache of the vcmi/vcmi repo if no PR-specific cache is found
  189. - name: Setup compiler cache for PRs
  190. uses: hendrikmuhs/[email protected]
  191. if: ${{ github.event.number != '' }}
  192. with:
  193. variant: ${{ startsWith(matrix.platform, 'msvc') && 'sccache' || 'ccache' }}
  194. key: ${{ matrix.platform }}-PR-${{ github.event.number }}
  195. restore-keys: |
  196. ${{ matrix.platform }}-PR-${{ github.event.number }}
  197. ${{ matrix.platform }}-branch-${{ github.base_ref }}
  198. ${{ matrix.platform }}-
  199. create-symlink: ${{ !startsWith(matrix.platform, 'msvc') }}
  200. max-size: '5G'
  201. verbose: 2
  202. job-summary: "" # <-- disable built-in summary to avoid duplicate block
  203. - name: Setup compiler cache for branch builds
  204. uses: hendrikmuhs/[email protected]
  205. if: ${{ github.event.number == '' }}
  206. with:
  207. variant: ${{ startsWith(matrix.platform, 'msvc') && 'sccache' || 'ccache' }}
  208. key: ${{ matrix.platform }}-branch-${{ github.ref_name }}
  209. restore-keys: |
  210. ${{ matrix.platform }}-branch-${{ github.ref_name }}
  211. ${{ matrix.platform }}-
  212. create-symlink: ${{ !startsWith(matrix.platform, 'msvc') }}
  213. max-size: '5G'
  214. verbose: 2
  215. job-summary: "" # <-- disable built-in summary to avoid duplicate block
  216. - name: CCache tuning (Android)
  217. if: ${{ startsWith(matrix.platform, 'android') }}
  218. run: |
  219. echo "/usr/lib/ccache" >> $GITHUB_PATH
  220. ccache --set-config=compiler_check=content
  221. ccache --set-config=base_dir="${GITHUB_WORKSPACE}"
  222. ccache --set-config=hash_dir=true
  223. ccache --set-config=sloppiness=time_macros
  224. - name: Install Conan profile
  225. if: "${{ matrix.conan_profile != '' }}"
  226. run: |
  227. conan profile detect
  228. outFolder=conan-generated
  229. conan install . \
  230. --output-folder="$outFolder" \
  231. --build=never \
  232. --profile=dependencies/conan_profiles/${{ matrix.conan_profile }} \
  233. ${{ matrix.conan_options }}
  234. ${{ startsWith(matrix.platform, 'msvc') && 'echo CONANRUN_PWSH_SCRIPT="$outFolder/conanrun.ps1" >> $GITHUB_ENV' || '' }}
  235. # Can't be set in Gradle project
  236. - name: Configure enableUncompressedNativeLibs
  237. if: ${{ startsWith(matrix.platform, 'android') }}
  238. run: mkdir -p ~/.gradle && echo "android.bundle.enableUncompressedNativeLibs=true" > ~/.gradle/gradle.properties
  239. - name: Install Java
  240. uses: actions/setup-java@v5
  241. if: ${{ startsWith(matrix.platform, 'android') }}
  242. with:
  243. distribution: 'temurin'
  244. java-version: '17'
  245. # Frees 10+GB on linux runners by removing unused tools (.NET, Haskell compiler)
  246. - name: Free up disk space
  247. if: contains(matrix.os, 'ubuntu')
  248. run: |
  249. echo "Disk usage BEFORE cleanup:"
  250. df -h
  251. sudo rm -rf /opt/ghc /usr/local/.ghcup /usr/share/dotnet || :
  252. echo "Disk usage AFTER cleanup:"
  253. df -h
  254. # a hack to build ID for x64 build in order for Google Play to allow upload of both 32 and 64 bit builds
  255. - name: Bump Android x64 build ID
  256. if: ${{ matrix.platform == 'android-64' }}
  257. run: perl -i -pe 's/versionCode (\d+)/$x=$1+1; "versionCode $x"/e' android/vcmi-app/build.gradle
  258. - name: Bump Android x86_64 build ID
  259. if: ${{ matrix.platform == 'android-64-intel' }}
  260. run: perl -i -pe 's/versionCode (\d+)/$x=$1+2; "versionCode $x"/e' android/vcmi-app/build.gradle
  261. - name: Build Number
  262. run: |
  263. source '${{github.workspace}}/CI/get_package_name.sh'
  264. if [ '${{ matrix.artifact_platform }}' ]; then
  265. VCMI_PACKAGE_FILE_NAME+="-${{ matrix.artifact_platform }}"
  266. fi
  267. echo VCMI_PACKAGE_FILE_NAME="$VCMI_PACKAGE_FILE_NAME" >> $GITHUB_ENV
  268. echo VCMI_PACKAGE_BUILD="$VCMI_PACKAGE_BUILD" >> $GITHUB_ENV
  269. echo VCMI_PACKAGE_NAME_SUFFIX="$VCMI_PACKAGE_NAME_SUFFIX" >> $GITHUB_ENV
  270. echo VCMI_PACKAGE_GOLDMASTER="$VCMI_PACKAGE_GOLDMASTER" >> $GITHUB_ENV
  271. env:
  272. PULL_REQUEST: ${{ github.event.pull_request.number }}
  273. - name: Configure (non-MSVC)
  274. if: ${{ !startsWith(matrix.platform, 'msvc') }}
  275. run: |
  276. if [[ ("${{ matrix.preset }}" == "android-conan-ninja-release") && ("${{ github.ref }}" != 'refs/heads/master') ]]; then
  277. # key1=value1;key2=value2;...
  278. gradleProperties=$(python3 CI/android/gradle_daily_props.py)
  279. androidOptions=("-DANDROID_GRADLE_PROPERTIES=$gradleProperties")
  280. androidOptions+=("-DCMAKE_C_COMPILER_LAUNCHER=ccache" "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache")
  281. fi
  282. cmake -DENABLE_CCACHE:BOOL=ON "${androidOptions[@]}" --preset ${{ matrix.preset }}
  283. - name: Configure (MSVC)
  284. if: ${{ startsWith(matrix.platform, 'msvc') }}
  285. run: |
  286. & $env:CONANRUN_PWSH_SCRIPT
  287. cmake --preset ${{ matrix.preset }}
  288. shell: pwsh
  289. - name: Build
  290. run: |
  291. ${{ startsWith(matrix.platform, 'msvc') && '& $env:CONANRUN_PWSH_SCRIPT' }}
  292. cmake --build --preset ${{matrix.preset}}
  293. shell: pwsh
  294. env:
  295. ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
  296. ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
  297. - name: Kill XProtect to work around CPack issue on macOS
  298. if: ${{ startsWith(matrix.platform, 'mac') }}
  299. run: |
  300. # Cf. https://github.com/actions/runner-images/issues/7522#issuecomment-1556766641
  301. echo Killing...; sudo pkill -9 XProtect >/dev/null || true;
  302. echo "Waiting..."; counter=0; while pgrep XProtect && ((counter < 20)); do sleep 3; ((counter++)); done
  303. pgrep XProtect || true
  304. - name: Pack
  305. id: cpack
  306. if: ${{ matrix.pack == 1 }}
  307. run: |
  308. ${{ startsWith(matrix.platform, 'msvc') && '& $env:CONANRUN_PWSH_SCRIPT' }}
  309. cd "${{github.workspace}}/out/build/${{matrix.preset}}"
  310. cpack -C ${{matrix.pack_type}}
  311. shell: pwsh
  312. - name: Notarize macOS dmg
  313. if: ${{ startsWith(matrix.platform, 'mac-') }}
  314. env:
  315. NOTARIZATION_APPLE_ID: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
  316. NOTARIZATION_APPLE_ID_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID_PASSWORD }}
  317. NOTARIZATION_TEAM_ID: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
  318. run: |
  319. if [[ -z "$NOTARIZATION_APPLE_ID" || -z "$NOTARIZATION_APPLE_ID_PASSWORD" || -z "$NOTARIZATION_TEAM_ID" ]]; then
  320. echo "skip notarization"
  321. exit 0
  322. fi
  323. dmg='${{ github.workspace }}/out/build/${{ matrix.preset }}/${{ env.VCMI_PACKAGE_FILE_NAME }}.${{ matrix.extension }}'
  324. codesign --verbose=4 --force --sign "$MACOS_CODE_SIGN_IDENTITY" "$dmg"
  325. xcrun notarytool submit \
  326. --apple-id "$NOTARIZATION_APPLE_ID" \
  327. --password "$NOTARIZATION_APPLE_ID_PASSWORD" \
  328. --team-id "$NOTARIZATION_TEAM_ID" \
  329. --wait \
  330. "$dmg"
  331. xcrun stapler staple "$dmg"
  332. - name: Find Android package
  333. if: ${{ startsWith(matrix.platform, 'android') }}
  334. run: |
  335. OUTPUT_DIRECTORY="${{ github.workspace }}/out/build/${{ matrix.preset }}/android-build/vcmi-app/build/outputs"
  336. mv "$OUTPUT_DIRECTORY/apk/release/vcmi-release.apk" "${{github.workspace}}/out/build/${{matrix.preset}}/${{ env.VCMI_PACKAGE_FILE_NAME }}.apk"
  337. mv "$OUTPUT_DIRECTORY/bundle/release/vcmi-release.aab" "${{github.workspace}}/out/build/${{matrix.preset}}/${{ env.VCMI_PACKAGE_FILE_NAME }}.aab"
  338. - name: Upload Artifact
  339. id: upload_artifact
  340. uses: actions/upload-artifact@v6
  341. with:
  342. name: ${{ env.VCMI_PACKAGE_FILE_NAME }}-${{ matrix.platform }}
  343. compression-level: 9
  344. path: |
  345. ${{github.workspace}}/out/build/${{matrix.preset}}/${{ env.VCMI_PACKAGE_FILE_NAME }}.${{ matrix.extension }}
  346. - name: Upload AAB Artifact
  347. id: upload_aab
  348. if: ${{ startsWith(matrix.platform, 'android') && github.ref == 'refs/heads/master' }}
  349. uses: actions/upload-artifact@v6
  350. with:
  351. name: ${{ env.VCMI_PACKAGE_FILE_NAME }}-${{ matrix.platform }}-aab
  352. compression-level: 9
  353. path: |
  354. ${{github.workspace}}/out/build/${{matrix.preset}}/${{ env.VCMI_PACKAGE_FILE_NAME }}.aab
  355. - name: Upload MSVC debug symbols
  356. id: upload_symbols
  357. if: ${{ startsWith(matrix.platform, 'msvc') }}
  358. uses: actions/upload-artifact@v6
  359. with:
  360. name: ${{ env.VCMI_PACKAGE_FILE_NAME }}-${{ matrix.platform }}-symbols
  361. compression-level: 9
  362. path: |
  363. ${{github.workspace}}/**/*.pdb
  364. - name: Create iOS AppStore debug symbols
  365. if: ${{ matrix.platform == 'ios' }}
  366. run: |
  367. IOS_APPSTORE_SYMBOLS_DIR='${{ github.workspace }}/Symbols'
  368. mkdir "$IOS_APPSTORE_SYMBOLS_DIR"
  369. dsyms=$(find '${{ github.workspace }}/out/build/${{ matrix.preset }}' -name '*.dSYM')
  370. xcrun symbols -arch all -symbolsPackageDir "$IOS_APPSTORE_SYMBOLS_DIR" $dsyms
  371. echo IOS_APPSTORE_SYMBOLS_DIR="$IOS_APPSTORE_SYMBOLS_DIR" >> $GITHUB_ENV
  372. - name: Upload iOS AppStore debug symbols
  373. if: ${{ matrix.platform == 'ios' }}
  374. uses: actions/upload-artifact@v6
  375. with:
  376. name: ${{ env.VCMI_PACKAGE_FILE_NAME }}-${{ matrix.platform }}-symbols
  377. compression-level: 9
  378. path: |
  379. ${{ env.IOS_APPSTORE_SYMBOLS_DIR }}
  380. - name: Upload build to download.vcmi.eu
  381. if: ${{ matrix.upload == 1 && github.event.number == '' && env.DEPLOY_RSA != '' }}
  382. run: |
  383. if [ -z '${{ env.ANDROID_APK_PATH }}' ] ; then
  384. cd '${{github.workspace}}/out/build/${{matrix.preset}}'
  385. fi
  386. source '${{github.workspace}}/CI/upload_package.sh'
  387. env:
  388. DEPLOY_RSA: ${{ secrets.DEPLOY_RSA }}
  389. PACKAGE_EXTENSION: ${{ matrix.extension }}
  390. - name: Prepare partial JSON with build informations
  391. id: make_partial_json
  392. env:
  393. PLATFORM: ${{ matrix.platform }}
  394. ARTIFACT_URL: ${{ steps.upload_artifact.outputs.artifact-url }}
  395. DEBUG_SYMBOLS_URL: ${{ steps.upload_symbols.outputs.artifact-url }}
  396. AAB_URL: ${{ steps.upload_aab.outputs.artifact-url }}
  397. run: |
  398. python3 CI/emit_partial.py
  399. - name: Upload partial JSON with build informations
  400. uses: actions/upload-artifact@v6
  401. with:
  402. name: partial-json-${{ matrix.platform }}
  403. path: .summary/${{ matrix.platform }}.json
  404. upload-source-package:
  405. name: Upload Source Code Package
  406. if: always() && github.event.number == ''
  407. runs-on: ubuntu-latest
  408. defaults:
  409. run:
  410. shell: bash
  411. steps:
  412. - uses: actions/checkout@v6
  413. with:
  414. submodules: recursive
  415. - name: Build Number
  416. run: |
  417. source '${{github.workspace}}/CI/get_package_name.sh'
  418. echo VCMI_PACKAGE_FILE_NAME="$VCMI_PACKAGE_FILE_NAME" >> $GITHUB_ENV
  419. - name: Create source code archive (including submodules)
  420. run: |
  421. git archive HEAD -o "release.tar" --worktree-attributes -v
  422. git submodule update --init --recursive
  423. git submodule --quiet foreach 'cd "$toplevel"; tar -rvf "release.tar" "$sm_path"'
  424. gzip release.tar
  425. - name: Upload source code archive
  426. id: upload_source
  427. uses: actions/upload-artifact@v6
  428. with:
  429. name: ${{ env.VCMI_PACKAGE_FILE_NAME }}
  430. compression-level: 9
  431. path: |
  432. ./release.tar.gz
  433. - name: Prepare partial JSON with source informations
  434. shell: bash
  435. run: |
  436. mkdir -p .summary
  437. cat > .summary/source.json <<JSON
  438. {"source_url": "${{ steps.upload_source.outputs.artifact-url }}"}
  439. JSON
  440. - name: Upload partial JSON with source informations
  441. uses: actions/upload-artifact@v6
  442. with:
  443. name: partial-json-source
  444. path: .summary/source.json
  445. test:
  446. name: Test (${{ matrix.platform }})
  447. strategy:
  448. fail-fast: false
  449. matrix:
  450. include:
  451. - platform: gcc-latest-release
  452. os: ubuntu-24.04
  453. before_install: linux_qt6.sh
  454. compiler_cxx: g++-14
  455. compiler_cc: gcc-14
  456. preset: linux-gcc-test
  457. - platform: clang-latest-debug
  458. os: ubuntu-24.04
  459. before_install: linux_qt6.sh
  460. compiler_cxx: clang++-18
  461. compiler_cc: clang-18
  462. preset: linux-clang-debug
  463. - platform: gcc-oldest-debug
  464. os: ubuntu-22.04
  465. before_install: linux_qt5.sh
  466. compiler_cxx: g++-10
  467. compiler_cc: gcc-10
  468. preset: linux-gcc-debug
  469. - platform: clang-oldest-release
  470. os: ubuntu-22.04
  471. before_install: linux_qt5.sh
  472. compiler_cxx: clang++-13
  473. compiler_cc: clang-13
  474. preset: linux-clang-test
  475. runs-on: ${{ matrix.os }}
  476. defaults:
  477. run:
  478. shell: bash
  479. steps:
  480. - name: Checkout repository
  481. uses: actions/checkout@v6
  482. with:
  483. submodules: recursive
  484. - name: Prepare APT staging dir
  485. if: contains(matrix.os, 'ubuntu')
  486. run: mkdir -p "$RUNNER_TEMP/apt-cache"
  487. - name: APT cache restore
  488. if: contains(matrix.os, 'ubuntu')
  489. id: aptcache
  490. uses: actions/cache/restore@v5
  491. with:
  492. path: ${{ runner.temp }}/apt-cache
  493. key: ${{ matrix.platform }}-apt-${{ matrix.os }}
  494. restore-keys: |
  495. ${{ matrix.platform }}-apt-${{ matrix.os }}
  496. - name: Prepare CI
  497. run: source '${{ github.workspace }}/CI/before_install/${{ matrix.before_install }}'
  498. - name: APT cache save
  499. if: contains(matrix.os, 'ubuntu') && steps.aptcache.outputs.cache-hit != 'true'
  500. uses: actions/cache/save@v5
  501. with:
  502. path: ${{ runner.temp }}/apt-cache
  503. key: ${{ steps.aptcache.outputs.cache-primary-key }}
  504. - name: Setup compiler cache for PRs
  505. uses: hendrikmuhs/[email protected]
  506. if: ${{ github.event.number != '' }}
  507. with:
  508. key: ${{ matrix.platform }}-PR-${{ github.event.number }}
  509. restore-keys: |
  510. ${{ matrix.platform }}-PR-${{ github.event.number }}
  511. ${{ matrix.platform }}-branch-${{ github.base_ref }}
  512. ${{ matrix.platform }}-
  513. max-size: '5G'
  514. verbose: 2
  515. - name: Setup compiler cache for branch builds
  516. uses: hendrikmuhs/[email protected]
  517. if: ${{ github.event.number == '' }}
  518. with:
  519. key: ${{ matrix.platform }}-branch-${{ github.ref_name }}
  520. restore-keys: |
  521. ${{ matrix.platform }}-branch-${{ github.ref_name }}
  522. ${{ matrix.platform }}-
  523. max-size: '5G'
  524. verbose: 2
  525. - name: Prepare Heroes 3 data
  526. run: |
  527. data_url="https://github.com/vcmi-mods/vcmi-test-data/releases/download/v2.0/heroes3.7z"
  528. wget --progress=dot:giga "$data_url" -O heroes3.7z
  529. mkdir -p ~/.local/share/vcmi/
  530. 7za x -o"$HOME/.local/share/vcmi/" heroes3.7z
  531. - name: Configure
  532. run: |
  533. cmake -DENABLE_CCACHE:BOOL=ON -DCMAKE_C_COMPILER=${{ matrix.compiler_cc }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler_cxx }} --preset ${{ matrix.preset }}
  534. - name: Build
  535. run: |
  536. cmake --build --preset ${{matrix.preset}}
  537. - name: Test
  538. run: |
  539. ctest --preset ${{matrix.preset}}
  540. - name: Validate library startup
  541. run: |
  542. cd "${{github.workspace}}/out/build/${{matrix.preset}}/bin"
  543. ./vcmiserver --dummy-run 2> >(tee error.log)
  544. if [ ! -s error.log ]; then
  545. echo "Validation OK!"
  546. exit 0
  547. else
  548. echo "Validation failed!"
  549. exit 1
  550. fi
  551. windows-installer:
  552. needs: build
  553. if: ${{ needs.build.result == 'success' }}
  554. name: Create Windows Installer (${{ matrix.arch }})
  555. strategy:
  556. matrix:
  557. include:
  558. - platform: msvc-x64
  559. arch: x64
  560. os: windows-2025
  561. - platform: msvc-x86
  562. arch: x86
  563. os: windows-2025
  564. - platform: msvc-arm64
  565. arch: arm64
  566. os: windows-11-arm
  567. runs-on: ${{ matrix.os }}
  568. defaults:
  569. run:
  570. shell: bash
  571. steps:
  572. - name: Checkout repository
  573. uses: actions/checkout@v6
  574. with:
  575. submodules: recursive
  576. - name: Extract version info
  577. id: extract-version
  578. run: |
  579. filePath="${GITHUB_WORKSPACE}/cmake_modules/VersionDefinition.cmake"
  580. major=$(grep -m 1 "VCMI_VERSION_MAJOR" "$filePath" | tr -d -c 0-9)
  581. minor=$(grep -m 1 "VCMI_VERSION_MINOR" "$filePath" | tr -d -c 0-9)
  582. patch=$(grep -m 1 "VCMI_VERSION_PATCH" "$filePath" | tr -d -c 0-9)
  583. short_version="${major}.${minor}.${patch}"
  584. version_timestamp=$(date +"%Y%m%d%H%M%S")
  585. echo "short_version=${short_version}" >> "$GITHUB_OUTPUT"
  586. echo "version_timestamp=${version_timestamp}" >> "$GITHUB_OUTPUT"
  587. - name: Download UCRT
  588. run: source '${{github.workspace}}/CI/wininstaller/download_ucrt.sh' '${{matrix.platform}}'
  589. - name: Build Number
  590. run: |
  591. source '${{github.workspace}}/CI/get_package_name.sh'
  592. VCMI_PACKAGE_FILE_NAME+="-${{ matrix.arch }}"
  593. echo VCMI_PACKAGE_FILE_NAME="$VCMI_PACKAGE_FILE_NAME" >> $GITHUB_ENV
  594. echo VCMI_PACKAGE_BUILD="$VCMI_PACKAGE_BUILD" >> $GITHUB_ENV
  595. echo VCMI_PACKAGE_NAME_SUFFIX="$VCMI_PACKAGE_NAME_SUFFIX" >> $GITHUB_ENV
  596. echo VCMI_PACKAGE_GOLDMASTER="$VCMI_PACKAGE_GOLDMASTER" >> $GITHUB_ENV
  597. env:
  598. PULL_REQUEST: ${{ github.event.pull_request.number }}
  599. - name: Download Artifact
  600. uses: actions/download-artifact@v7
  601. with:
  602. name: ${{ env.VCMI_PACKAGE_FILE_NAME }}-${{ matrix.platform }}
  603. path: ${{github.workspace}}/artifact
  604. - name: Extract Artifact
  605. run: |
  606. mkdir artifact/extracted
  607. unzip "artifact/${{ env.VCMI_PACKAGE_FILE_NAME }}.zip" -d artifact/extracted
  608. - name: Ensure Inno Setup is installed
  609. run: |
  610. if [ ! -f "/c/Program Files (x86)/Inno Setup 6/ISCC.exe" ] && [ ! -f "/c/ProgramData/Chocolatey/bin/ISCC.exe" ]; then
  611. choco install innosetup --no-progress -y
  612. fi
  613. - name: Build VCMI Installer
  614. run: >
  615. CI\wininstaller\build_installer.cmd
  616. "${{ steps.extract-version.outputs.short_version }}"
  617. "${{ env.VCMI_PACKAGE_BUILD }}"
  618. "${{ matrix.arch }}"
  619. "VCMI ${{ env.VCMI_PACKAGE_NAME_SUFFIX }}"
  620. "${{ env.VCMI_PACKAGE_FILE_NAME }}"
  621. "${{ github.workspace }}\artifact\extracted"
  622. "${{ github.workspace }}\ucrt"
  623. shell: cmd
  624. - name: Upload VCMI Installer Artifacts
  625. id: upload_installer
  626. uses: actions/upload-artifact@v6
  627. with:
  628. name: ${{ env.VCMI_PACKAGE_FILE_NAME }}-${{ matrix.platform }}-installer
  629. compression-level: 9
  630. path: |
  631. ${{ github.workspace }}/CI/wininstaller/Output/*.exe
  632. - name: Upload Installer
  633. if: ${{ github.event.number == '' && env.DEPLOY_RSA != '' }}
  634. run: |
  635. cd '${{github.workspace}}/CI/wininstaller/Output'
  636. source '${{github.workspace}}/CI/upload_package.sh'
  637. env:
  638. DEPLOY_RSA: ${{ secrets.DEPLOY_RSA }}
  639. PACKAGE_EXTENSION: exe
  640. - name: Prepare partial JSON with installer informations
  641. shell: bash
  642. run: |
  643. mkdir -p .summary
  644. cat > .summary/installer-${{ matrix.platform }}.json <<JSON
  645. {
  646. "platform": "${{ matrix.platform }}",
  647. "installer_url": "${{ steps.upload_installer.outputs.artifact-url }}"
  648. }
  649. JSON
  650. - name: Upload partial JSON with installer informations
  651. uses: actions/upload-artifact@v6
  652. with:
  653. name: partial-json-${{ matrix.platform }}-installer
  654. path: .summary/installer-${{ matrix.platform }}.json
  655. validate-code:
  656. name: Validate Code
  657. if: always()
  658. runs-on: ubuntu-latest
  659. defaults:
  660. run:
  661. shell: bash
  662. steps:
  663. - uses: actions/checkout@v6
  664. - name: Ensure LF line endings
  665. run: |
  666. find . -path ./.git -prune -o -path ./AI/FuzzyLite -prune -o -path ./test/googletest \
  667. -o -path ./osx -prune -o -type f \
  668. -not -name '*.png' -and -not -name '*.ttf' -and -not -name '*.wav' -and -not -name '*.webm' -and -not -name '*.ico' -and -not -name '*.bat' -and -not -name '*.cmd' -and -not -name '*.iss' -and -not -name '*.isl' -print0 | \
  669. { ! xargs -0 grep -l -z -P '\r\n'; }
  670. - name: Validate JSON
  671. run: |
  672. sudo apt install python3-jstyleson
  673. python3 CI/validate_json.py
  674. - name: Validate Markdown
  675. uses: DavidAnson/markdownlint-cli2-action@v22
  676. with:
  677. config: 'CI/example.markdownlint-cli2.jsonc'
  678. globs: '**/*.md'
  679. final-summary:
  680. name: Build report
  681. if: always()
  682. needs: [build, windows-installer, validate-code, test, upload-source-package]
  683. runs-on: ubuntu-latest
  684. permissions:
  685. contents: read
  686. actions: write # Partial JSON artifacts can't be deleted in PRs
  687. steps:
  688. - name: Checkout (for script path)
  689. uses: actions/checkout@v6
  690. - name: Download all partial JSON artifacts
  691. continue-on-error: true
  692. uses: actions/download-artifact@v7
  693. with:
  694. pattern: partial-json-*
  695. merge-multiple: true
  696. path: partials
  697. - name: Run final summary
  698. env:
  699. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  700. run: |
  701. python3 CI/final_summary.py