diff --git a/.github/workflows/steam.yml b/.github/workflows/steam.yml new file mode 100644 index 00000000000000..af2fc3e3aebbcd --- /dev/null +++ b/.github/workflows/steam.yml @@ -0,0 +1,215 @@ +name: Steam Upload + +on: + release: + types: + - published + workflow_dispatch: + inputs: + tag: + description: 'Tag to fetch and upload (nightly if none)' + required: false + win_url_override: + description: 'Windows build to use (.zip only)' + required: false + mac_url_override: + description: 'Mac build to use (.dmg only)' + required: false + schedule: + - cron: 0 0 * * * + +env: + WORKFLOW_ID: 583765 + GIT_NIGHTLY_BRANCH: master + STEAM_NIGHTLY_BRANCH: nightly + STEAM_STABLE_BRANCH: staging + STEAM_BETA_BRANCH: beta_staging + +jobs: + upload: + name: Steam upload + runs-on: ubuntu-20.04 + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + path: source + + - name: Setup 7-Zip (PPA) + # The 7-Zip version available in the default ubuntu repos (p7zip) is wildly out-of-date and does not properly support DMG files. + run: | + sudo add-apt-repository -y ppa:spvkgn/sevenzip + sudo apt update -y + sudo apt install -y 7-zip + + - name: Get build information + id: build-info + run: | + EVENT='${{ github.event_name }}' + if [[ ${EVENT} == 'release' || ( ${EVENT} == 'workflow_dispatch' && -n '${{ github.event.inputs.tag }}') ]]; then + if [[ ${EVENT} == "release" ]]; then + DESC='${{ github.event.release.tag_name }}' + if [[ '${{ github.event.release.prerelease }}' == 'true' ]]; then + BRANCH='${{ env.STEAM_BETA_BRANCH }}' + else + BRANCH='${{ env.STEAM_STABLE_BRANCH }}' + fi + ASSETS_URL='${{ github.event.release.assets_url }}' + else + RELEASE="$(curl -s '${{ github.api_url }}/repos/obsproject/obs-studio/releases/tags/${{ github.event.inputs.tag }}')" + + DESC="$(jq -r '.tag_name' <<< ${RELEASE})" + if [[ "$(jq -r '.prerelease' <<< ${RELEASE})" == 'true' ]]; then + BRANCH='${{ env.STEAM_BETA_BRANCH }}' + else + BRANCH='${{ env.STEAM_STABLE_BRANCH }}' + fi + ASSETS_URL="$(jq -r '.assets_url' <<< ${RELEASE})" + fi + + ASSETS="$(curl -s "${ASSETS_URL}")" + WIN_ASSET_URL="$(jq -r '.[] | select(.name|test(".*x64.zip")) .browser_download_url' <<< ${ASSETS})" + MAC_ASSET_URL="$(jq -r '.[] | select(.name|test(".*.dmg")) .browser_download_url' <<< ${ASSETS})" + TYPE='release' + else + BRANCH='${{ env.STEAM_NIGHTLY_BRANCH }}' + BUILDS="$(curl -s '${{ github.api_url }}/repos/obsproject/obs-studio/actions/workflows/${{ env.WORKFLOW_ID }}/runs?per_page=1&event=push&status=success&branch=${{ env.GIT_NIGHTLY_BRANCH }}')" + ARTIFACTS_URL="$(jq -r '.workflow_runs[].artifacts_url' <<< ${BUILDS})" + DESC="g$(jq -r '.workflow_runs[].head_sha' <<< "${BUILDS}" | cut -c1-9)" + + ARTIFACTS="$(curl -s ${ARTIFACTS_URL})" + WIN_ASSET_URL="$(jq -r '.artifacts[] | select(.name|test(".*win-x64.*")) .archive_download_url' <<< ${ARTIFACTS})" + MAC_ASSET_URL="$(jq -r '.artifacts[] | select(.name|test(".*macos-x86_64.*")) .archive_download_url' <<< ${ARTIFACTS})" + TYPE='nightly' + fi + + # Apply overrides from workflow_dispatch + if [[ ${EVENT} == 'workflow_dispatch' ]]; then + if [[ -n '${{ github.event.inputs.win_url_override }}' ]]; then + WIN_ASSET_URL='${{ github.event.inputs.win_url_override }}' + fi + + if [[ -n '${{ github.event.inputs.mac_url_override }}' ]]; then + MAC_ASSET_URL='${{ github.event.inputs.mac_url_override }}' + fi + fi + + if [[ -z ${WIN_ASSET_URL} || -z ${MAC_ASSET_URL} ]]; then + echo "Missing at least one asset URL!" + exit 1 + fi + + # set env variables for subsequent steps + echo "::set-output name=type::${TYPE}" + echo "::set-output name=branch::${BRANCH}" + echo "::set-output name=desc::${DESC}" + echo "::set-output name=win_url::${WIN_ASSET_URL}" + echo "::set-output name=mac_intel_url::${MAC_ASSET_URL}" + + - name: Restore build cache + id: cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/steam/build + key: ${{ steps.build-info.outputs.branch }}-${{ steps.build-info.outputs.desc }} + # Using "restore-keys" will restore the most recent cache for the branch, even if the exact cache doesn't exist. + # This doesn't set cache-hit to true so it won't skip the upload for nightlies. + restore-keys: ${{ steps.build-info.outputs.branch }} + + - name: Determine if Steam upload should run + # If the nightly build has already been uploaded and thus a cache exists skip this and the following steps. + # Steam does not prevent us from uploading duplicate builds so this would just pollute the dashboard. + # This is a bit of a hack and can fail to work if our cache has been evicted or we somehow have no commits for 7 days, + # but it's better than nothing! + id: should-run + run: | + if [[ '${{ steps.build-info.outputs.type }}' == 'release' || '${{ steps.cache.outputs.cache-hit }}' != 'true' ]]; then + echo "::set-output name=result::true" + else + echo "::set-output name=result::false" + fi + + - name: Download and prepare builds + if: steps.should-run.outputs.result == 'true' + run: | + echo "::group::Download Windows build" + if [[ '${{ steps.build-info.outputs.win_url }}' == *'api.github.com'* ]]; then + curl -L -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' '${{ steps.build-info.outputs.win_url }}' -o windows.zip + else + curl -L '${{ steps.build-info.outputs.win_url }}' -o windows.zip + fi + echo "::endgroup::" + + echo "::group::Download Mac build" + if [[ '${{ steps.build-info.outputs.mac_intel_url }}' == *'api.github.com'* ]]; then + curl -L -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' '${{ steps.build-info.outputs.mac_intel_url }}' -o mac_x86.dmg.zip + else + curl -L '${{ steps.build-info.outputs.mac_intel_url }}' -o mac_x86.dmg + fi + echo "::endgroup::" + + mkdir -p steam && cd steam + + echo "::group::Extract and prepare Win64" + mkdir steam-windows + ( + cd steam-windows + unzip ../../windows.zip + # CI builds can be double-zipped + if compgen -G "*.zip" > /dev/null; then + unzip *.zip + rm *.zip + fi + # copy install scripts and create sentinel file + cp -r ../../source/CI/steam/scripts scripts + touch disable_updater + ) + echo "::endgroup::" + + echo "::group::Extract macOS (x86)" + mkdir steam-macos + # CI builds are zipped + if [[ -f ../mac_x86.dmg.zip ]]; then + unzip ../mac_x86.dmg.zip + # 7-Zip will have an exit code of 2 due to the "unsafe" 'Applications' symlink. + # GitHub treats this as a failure so ignore non-zero exit codes here. + 7zz x *.dmg -otmp || true + else + 7zz x ../mac_x86.dmg -otmp || true + fi + + mv tmp/*/OBS.app steam-macos + echo "::endgroup::" + + - name: Setup steamcmd + if: steps.should-run.outputs.result == 'true' + uses: CyberAndrii/setup-steamcmd@e19cd1516315ce46dbfffa47193f92fe42d1419e + + - name: Generate Steam auth code + if: steps.should-run.outputs.result == 'true' + id: steam-totp + uses: CyberAndrii/steam-totp@0fc9e59dc5bbf4368d23d5a33956f104248da31a + with: + shared_secret: ${{ secrets.STEAM_SHARED_SECRET }} + + - name: Upload to Steam + if: steps.should-run.outputs.result == 'true' + run: | + cd steam + echo "::group::Prepare Steam build script" + # The description in Steamworks for the build will be "github_-", e.g. "github_nightly-gaa73de952" + sed 's/@@DESC@@/${{ steps.build-info.outputs.branch }}-${{ steps.build-info.outputs.desc }}/;s/@@BRANCH@@/${{ steps.build-info.outputs.branch }}/' ../source/CI/steam/obs_build.vdf > build.vdf + echo "Generated file:" + cat build.vdf + echo "::endgroup::" + echo "::group::Upload to Steam" + steamcmd +login '${{ secrets.STEAM_USER }}' '${{ secrets.STEAM_PASSWORD }}' '${{ steps.steam-totp.outputs.code }}' +run_app_build "$(pwd)/build.vdf" +quit + echo "::endgroup::" + + - name: Upload Steam build logs + if: steps.should-run.outputs.result == 'true' + uses: actions/upload-artifact@v2 + with: + name: steam-build-logs + path: ${{ github.workspace }}/steam/build/*.log diff --git a/CI/steam/obs_build.vdf b/CI/steam/obs_build.vdf new file mode 100644 index 00000000000000..d6d7b069362092 --- /dev/null +++ b/CI/steam/obs_build.vdf @@ -0,0 +1,36 @@ +"AppBuild" +{ + "AppID" "1905180" + "Desc" "github_@@DESC@@" + + "ContentRoot" "./" + "BuildOutput" "build/" + + "SetLive" "@@BRANCH@@" + + "Depots" + { + "1905181" // Windows + { + "ContentRoot" "./steam-windows" + "InstallScript" "scripts/installscript.vdf" + "FileMapping" + { + "LocalPath" "*" + "DepotPath" "." + "recursive" "1" + } + } + + "1905182" // Mac + { + "ContentRoot" "./steam-macos" + "FileMapping" + { + "LocalPath" "*" + "DepotPath" "." + "recursive" "1" + } + } + } +} diff --git a/CI/steam/scripts/install.bat b/CI/steam/scripts/install.bat new file mode 100644 index 00000000000000..44fa06188a4ca6 --- /dev/null +++ b/CI/steam/scripts/install.bat @@ -0,0 +1,85 @@ +@echo off +@cd /d "%~dp0" + +goto checkAdmin + + +:checkAdmin + net session >nul 2>&1 + if %errorLevel% == 0 ( + echo. + ) else ( + echo Administrative rights are required. Please re-run this script as Administrator. + goto end + ) + +:writeRegistry + reg add "HKLM\SOFTWARE\OBS Studio" /f /t REG_SZ /d %1 /reg:32 + reg add "HKLM\SOFTWARE\OBS Studio" /f /t REG_SZ /d %1 /reg:64 + +:setupProgramData + :: Required for UWP applications + mkdir "%PROGRAMDATA%\obs-studio-hook" + icacls "%PROGRAMDATA%\obs-studio-hook" /grant "ALL APPLICATION PACKAGES":(OI)(CI)(GR,GE) + +:checkDLL + echo Checking for 32-bit Virtual Cam registration... + reg query "HKLM\SOFTWARE\Classes\CLSID\{A3FCE0F5-3493-419F-958A-ABA1250EC20B}" >nul 2>&1 /reg:32 + if %errorLevel% == 0 ( + echo 32-bit Virtual Cam found, skipping install... + echo. + ) else ( + echo 32-bit Virtual Cam not found, installing... + goto install32DLL + ) + +:CheckDLLContinue + echo Checking for 64-bit Virtual Cam registration... + reg query "HKLM\SOFTWARE\Classes\CLSID\{A3FCE0F5-3493-419F-958A-ABA1250EC20B}" >nul 2>&1 /reg:64 + if %errorLevel% == 0 ( + echo 64-bit Virtual Cam found, skipping install... + echo. + ) else ( + echo 64-bit Virtual Cam not found, installing... + goto install64DLL + ) + goto endSuccess + +:install32DLL + echo Installing 32-bit Virtual Cam... + regsvr32.exe /i /s %1\data\obs-plugins\win-dshow\obs-virtualcam-module32.dll + reg query "HKLM\SOFTWARE\Classes\CLSID\{A3FCE0F5-3493-419F-958A-ABA1250EC20B}" >nul 2>&1 /reg:32 + if %errorLevel% == 0 ( + echo 32-bit Virtual Cam successfully installed + echo. + ) else ( + echo 32-bit Virtual Cam installation failed + echo. + goto endFail + ) + goto checkDLLContinue + +:install64DLL + echo Installing 64-bit Virtual Cam... + regsvr32.exe /i /s %1\data\obs-plugins\win-dshow\obs-virtualcam-module64.dll + reg query "HKLM\SOFTWARE\Classes\CLSID\{A3FCE0F5-3493-419F-958A-ABA1250EC20B}" >nul 2>&1 /reg:64 + if %errorLevel% == 0 ( + echo 64-bit Virtual Cam successfully installed + echo. + goto endSuccess + ) else ( + echo 64-bit Virtual Cam installation failed + echo. + goto endFail + ) + +:endFail + echo Something failed, please report this on the OBS Discord or Forums! + goto end + +:endSuccess + echo Virtual Cam installed! + echo. + +:end + exit diff --git a/CI/steam/scripts/installscript.vdf b/CI/steam/scripts/installscript.vdf new file mode 100644 index 00000000000000..b9dff87310e8d7 --- /dev/null +++ b/CI/steam/scripts/installscript.vdf @@ -0,0 +1,20 @@ +"InstallScript" +{ + "Run Process" + { + "install" + { + "process 1" "scripts\\install.bat" + "command 1" "\"%INSTALLDIR%\"" + } + } + + "Run Process On Uninstall" + { + "uninstall" + { + "process 1" "scripts\\uninstall.bat" + "command 1" "\"%INSTALLDIR%\"" + } + } +} diff --git a/CI/steam/scripts/uninstall.bat b/CI/steam/scripts/uninstall.bat new file mode 100644 index 00000000000000..5191f5855a5549 --- /dev/null +++ b/CI/steam/scripts/uninstall.bat @@ -0,0 +1,33 @@ +@echo off +@cd /d "%~dp0" +goto checkAdmin + +:checkAdmin + net session >nul 2>&1 + if %errorLevel% == 0 ( + echo. + ) else ( + echo Administrative rights are required. Please re-run this script as Administrator. + goto end + ) + +:clearRegistry + reg delete "HKLM\SOFTWARE\OBS Studio" /f /reg:32 + reg delete "HKLM\SOFTWARE\OBS Studio" /f /reg:64 + :: Vulkan layer keys + reg delete "HKLM\SOFTWARE\Khronos\Vulkan\ImplicitLayers" /f /v "%PROGRAMDATA%\obs-studio-hook\obs-vulkan64.json" /reg:32 + reg delete "HKLM\SOFTWARE\Khronos\Vulkan\ImplicitLayers" /f /v "%PROGRAMDATA%\obs-studio-hook\obs-vulkan32.json" /reg:64 + +:deleteProgramDataFolder + RMDIR /S /Q "%PROGRAMDATA%\obs-studio-hook" + +:uninstallDLLs + regsvr32.exe /u /s %1\data\obs-plugins\win-dshow\obs-virtualcam-module32.dll + regsvr32.exe /u /s %1\data\obs-plugins\win-dshow\obs-virtualcam-module64.dll + +:endSuccess + echo Virtual Cam uninstalled! + echo. + +:end + exit