mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-08-24 10:14:13 -05:00
Merge pull request #8881 from PatTheMav/ci-update
CI: Update GitHub Actions workflows with repository actions and updated build scripts
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
name: Set Up and Build obs-studio
|
||||
description: Builds obs-studio for specified architecture and build config
|
||||
inputs:
|
||||
target:
|
||||
description: Build target for obs-studio
|
||||
required: true
|
||||
config:
|
||||
description: Build configuration
|
||||
required: false
|
||||
default: RelWithDebInfo
|
||||
codesign:
|
||||
description: Enable codesigning (macOS only)
|
||||
required: false
|
||||
default: 'false'
|
||||
codesignIdent:
|
||||
description: Developer ID for application codesigning (macOS only)
|
||||
required: false
|
||||
default: '-'
|
||||
codesignTeam:
|
||||
description: Team ID for application codesigning (macOS only)
|
||||
required: false
|
||||
default: ''
|
||||
workingDirectory:
|
||||
description: Working directory for packaging
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Run macOS Build
|
||||
if: runner.os == 'macOS'
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
env:
|
||||
CODESIGN_IDENT: ${{ inputs.codesignIdent }}
|
||||
CODESIGN_TEAM: ${{ inputs.codesignTeam }}
|
||||
run: |
|
||||
: Run macOS Build
|
||||
|
||||
local -a build_args=(
|
||||
--config ${{ inputs.config }}
|
||||
--target macos-${{ inputs.target }}
|
||||
)
|
||||
if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)
|
||||
|
||||
if [[ '${{ inputs.codesign }}' == true ]] build_args+=(--codesign)
|
||||
|
||||
git fetch origin --no-tags --no-recurse-submodules -q
|
||||
.github/scripts/build-macos ${build_args}
|
||||
|
||||
- name: Install Dependencies 🛍️
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
: Install Dependencies 🛍️
|
||||
echo ::group::Install Dependencies
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
|
||||
brew install --quiet zsh
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Run Ubuntu Build
|
||||
if: runner.os == 'Linux'
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
run: |
|
||||
: Run Ubuntu Build
|
||||
|
||||
local -a build_args=(
|
||||
--config ${{ inputs.config }}
|
||||
--target linux-${{ inputs.target }}
|
||||
--generator Ninja
|
||||
)
|
||||
if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)
|
||||
|
||||
git fetch origin --no-tags --no-recurse-submodules -q
|
||||
.github/scripts/build-linux ${build_args}
|
||||
|
||||
- name: Run Windows Build
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
run: |
|
||||
# Run Windows Build
|
||||
$BuildArgs = @{
|
||||
Target = '${{ inputs.target }}'
|
||||
Configuration = '${{ inputs.config }}'
|
||||
}
|
||||
|
||||
if ( $Env:RUNNER_DEBUG -ne $null ) {
|
||||
$BuildArgs += @{ Debug = $true }
|
||||
}
|
||||
|
||||
git fetch origin --no-tags --no-recurse-submodules -q
|
||||
.github/scripts/Build-Windows.ps1 @BuildArgs
|
||||
|
||||
- name: Create Summary 📊
|
||||
if: contains(fromJSON('["Linux", "macOS"]'), runner.os)
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
env:
|
||||
CCACHE_CONFIGPATH: ${{ inputs.workingDirectory }}/.ccache.conf
|
||||
run: |
|
||||
: Create Summary 📊
|
||||
|
||||
local -a ccache_data
|
||||
if (( ${+RUNNER_DEBUG} )) {
|
||||
setopt XTRACE
|
||||
ccache_data=("${(fA)$(ccache -s -vv)}")
|
||||
} else {
|
||||
ccache_data=("${(fA)$(ccache -s)}")
|
||||
}
|
||||
|
||||
print '### ${{ runner.os }} Ccache Stats (${{ inputs.target }})' >> $GITHUB_STEP_SUMMARY
|
||||
print '```' >> $GITHUB_STEP_SUMMARY
|
||||
for line (${ccache_data}) {
|
||||
print ${line} >> $GITHUB_STEP_SUMMARY
|
||||
}
|
||||
print '```' >> $GITHUB_STEP_SUMMARY
|
||||
@@ -0,0 +1,57 @@
|
||||
name: Check For Changed Files
|
||||
description: Checks for changed files compared to specific git reference and glob expression
|
||||
inputs:
|
||||
baseRef:
|
||||
description: Git reference to check against
|
||||
required: true
|
||||
ref:
|
||||
description: Git reference to check with
|
||||
required: false
|
||||
default: HEAD
|
||||
checkGlob:
|
||||
description: Glob expression to limit check to specific files
|
||||
required: false
|
||||
useFallback:
|
||||
description: Use fallback compare against prior commit
|
||||
required: false
|
||||
default: 'true'
|
||||
outputs:
|
||||
hasChangedFiles:
|
||||
value: ${{ steps.checks.outputs.hasChangedFiles }}
|
||||
description: True if specified files were changed in comparison to specified git reference
|
||||
changedFiles:
|
||||
value: ${{ toJSON(steps.checks.outputs.changedFiles) }}
|
||||
description: List of changed files
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check For Changed Files ✅
|
||||
shell: bash
|
||||
id: checks
|
||||
env:
|
||||
GIT_BASE_REF: ${{ inputs.baseRef }}
|
||||
GIT_REF: ${{ inputs.ref }}
|
||||
USE_FALLBACK: ${{ inputs.useFallback }}
|
||||
run: |
|
||||
: Check for Changed Files ✅
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
shopt -s extglob
|
||||
shopt -s dotglob
|
||||
|
||||
if ! git cat-file -e ${GIT_BASE_REF}; then
|
||||
echo "::warning::Provided base reference ${GIT_BASE_REF} is invalid"
|
||||
if [[ "${USE_FALLBACK}" == 'true' ]]; then
|
||||
GIT_BASE_REF='HEAD~1'
|
||||
fi
|
||||
fi
|
||||
|
||||
changes=($(git diff --name-only ${GIT_BASE_REF} ${GIT_REF} -- ${{ inputs.checkGlob }}))
|
||||
|
||||
if (( ${#changes[@]} )); then
|
||||
file_string="${changes[*]}"
|
||||
echo "hasChangedFiles=true" >> $GITHUB_OUTPUT
|
||||
echo "changedFiles=[${file_string// /,}]" >> GITHUB_OUTPUT
|
||||
else
|
||||
echo "hasChangedFiles=false" >> $GITHUB_OUTPUT
|
||||
echo "changedFiles=[]" >> GITHUB_OUTPUT
|
||||
fi
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Compatibility Data Validator
|
||||
description: Checks Windows compatibility data files
|
||||
inputs:
|
||||
repositorySecret:
|
||||
description: GitHub token for API access
|
||||
required: true
|
||||
workingDirectory:
|
||||
description: Working directory for checks
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo "services-validation action requires a macOS-based or Linux-based runner."
|
||||
exit 2
|
||||
|
||||
- name: Install and Configure Python 🐍
|
||||
shell: bash
|
||||
run: |
|
||||
: Install and Configure Python 🐍
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
echo ::group::Python Set Up
|
||||
if [[ "${RUNNER_OS}" == Linux ]]; then
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
|
||||
fi
|
||||
brew install --quiet python3
|
||||
python3 -m pip install jsonschema json_source_map
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Validate Compatibility Files JSON Schema 🕵️
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
run: |
|
||||
: Validate services file JSON schema 🕵️
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
shopt -s extglob
|
||||
|
||||
echo ::group::Schema Validation
|
||||
python3 -u \
|
||||
.github/scripts/utils.py/check-jsonschema.py \
|
||||
--loglevel INFO \
|
||||
plugins/win-capture/data/@(compatibility|package).json
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Annotate Schema Validation Errors 🏷️
|
||||
uses: yuzutech/annotations-action@v0.4.0
|
||||
if: failure()
|
||||
with:
|
||||
repo-token: ${{ inputs.repositorySecret }}
|
||||
title: Compatibility JSON Errors
|
||||
input: ${{ inputs.workingDirectory }}/validation_errors.json
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Flatpak Manifest Validator
|
||||
description: Checks order of Flatpak modules in manifest file
|
||||
inputs:
|
||||
manifestFile:
|
||||
description: Flatpak manifest file to check
|
||||
failCondition:
|
||||
description: Controls whether failed checks also fail the workflow run
|
||||
required: false
|
||||
default: never
|
||||
workingDirectory:
|
||||
description: Working directory for checks
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo "services-validation action requires a macOS-based or Linux-based runner."
|
||||
exit 2
|
||||
|
||||
- name: Validate Flatpak Manifest 🕵️
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
run: |
|
||||
: Validate Flatpak Manifest 🕵️
|
||||
|
||||
echo ::group::Run Validation
|
||||
if [[ '${{ inputs.failCondition }}' == 'never' ]]; then set +e; fi
|
||||
python3 -u \
|
||||
build-aux/format-manifest.py \
|
||||
build-aux/com.obsproject.Studio.json \
|
||||
--check \
|
||||
--loglevel INFO
|
||||
echo ::endgroup::
|
||||
@@ -0,0 +1,62 @@
|
||||
name: Generate Documentation
|
||||
description: Updates Sphinx-based documentation
|
||||
inputs:
|
||||
sourceDirectory:
|
||||
description: Path to repository checkout
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
disableLinkExtensions:
|
||||
description: Disable Sphinx link extensions
|
||||
required: false
|
||||
default: 'false'
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Update Version Number and Copyright ↗️
|
||||
id: setup
|
||||
shell: bash
|
||||
run: |
|
||||
: Update Version Number and Copyright ↗️
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
: "${major:=}"
|
||||
: "${minor:=}"
|
||||
: "${patch:=}"
|
||||
|
||||
read -r _ major _ minor _ patch _ <<< \
|
||||
"$(grep -E -e "#define LIBOBS_API_(MAJOR|MINOR|PATCH)_VER *" libobs/obs-config.h \
|
||||
| sed 's/#define //g' \
|
||||
| tr -s ' ' \
|
||||
| tr '\n' ' ')"
|
||||
|
||||
sed -i -E \
|
||||
-e "s/version = '([0-9]+\.[0-9]+\.[0-9]+)'/version = '${major}.${minor}.${patch}'/g" \
|
||||
-e "s/release = '([0-9]+\.[0-9]+\.[0-9]+)'/release = '${major}.${minor}.${patch}'/g" \
|
||||
-e "s/copyright = '(2017-[0-9]+, Lain Bailey)'/copyright = '2017-$(date +"%Y"), Lain Bailey'/g" \
|
||||
${{ inputs.sourceDirectory }}/docs/sphinx/conf.py
|
||||
|
||||
if [[ '${{ inputs.disableLinkExtensions }}' == 'true' ]]; then
|
||||
sed -i -e "s/html_link_suffix = None/html_link_suffix = ''/g" \
|
||||
${{ inputs.sourceDirectory }}/docs/sphinx/conf.py
|
||||
echo "artifactName=OBS Studio Docs (No Extensions)" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "artifactName=OBS Studio Docs" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
echo "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install Sphinx 📜
|
||||
uses: totaldebug/sphinx-publish-action@1.2.0
|
||||
with:
|
||||
sphinx_src: ${{ inputs.sourceDirectory }}/docs/sphinx
|
||||
build_only: true
|
||||
target_branch: master
|
||||
target_path: '../home/_build'
|
||||
pre_build_commands: 'pip install -Iv sphinx==5.1.1'
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ steps.setup.outputs.artifactName }} ${{ steps.setup.outputs.commitHash }}
|
||||
path: |
|
||||
${{ runner.temp }}/_github_home/_build
|
||||
!${{ runner.temp }}/_github_home/_build/.doctrees
|
||||
@@ -0,0 +1,114 @@
|
||||
name: Package obs-studio
|
||||
description: Packages obs-studio for specified architecture and build config
|
||||
inputs:
|
||||
target:
|
||||
description: Build target for dependencies
|
||||
required: true
|
||||
config:
|
||||
description: Build configuration
|
||||
required: false
|
||||
default: Release
|
||||
codesign:
|
||||
description: Enable codesigning (macOS only)
|
||||
required: false
|
||||
default: 'false'
|
||||
notarize:
|
||||
description: Enable notarization (macOS only)
|
||||
required: false
|
||||
default: 'false'
|
||||
codesignIdent:
|
||||
description: Developer ID for application codesigning (macOS only)
|
||||
required: false
|
||||
default: '-'
|
||||
codesignUser:
|
||||
description: Apple ID username for notarization (macOS only)
|
||||
required: false
|
||||
default: ''
|
||||
codesignPass:
|
||||
description: Apple ID password for notarization (macOS only)
|
||||
required: false
|
||||
default: ''
|
||||
package:
|
||||
description: Create platform-specific packages instead of archives
|
||||
required: false
|
||||
default: 'false'
|
||||
workingDirectory:
|
||||
description: Working directory for packaging
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Run macOS packaging
|
||||
if: runner.os == 'macOS'
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
env:
|
||||
CODESIGN_IDENT: ${{ inputs.codesignIdent }}
|
||||
CODESIGN_IDENT_USER: ${{ inputs.codesignUser }}
|
||||
CODESIGN_IDENT_PASS: ${{ inputs.codesignPass }}
|
||||
run: |
|
||||
: Run macOS Packaging
|
||||
|
||||
local -a package_args=(
|
||||
--target macos-${{ inputs.target }}
|
||||
--config ${{ inputs.config }}
|
||||
)
|
||||
if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)
|
||||
|
||||
if [[ '${{ inputs.codesign }}' == true ]] package_args+=(--codesign)
|
||||
if [[ '${{ inputs.notarize }}' == true ]] package_args+=(--notarize)
|
||||
if [[ '${{ inputs.package }}' == true ]] package_args+=(--package)
|
||||
|
||||
.github/scripts/package-macos ${package_args}
|
||||
|
||||
- name: Install Dependencies 🛍️
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
: Install Dependencies 🛍️
|
||||
echo ::group::Install Dependencies
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
|
||||
brew install --quiet zsh
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Run Ubuntu packaging
|
||||
if: runner.os == 'Linux'
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
run: |
|
||||
: Run Ubuntu Packaging
|
||||
|
||||
local -a package_args=(
|
||||
--target linux-${{ inputs.target }}
|
||||
--config ${{ inputs.config }}
|
||||
)
|
||||
if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)
|
||||
|
||||
if [[ '${{ inputs.package }}' == true ]] package_args+=(--package)
|
||||
|
||||
${{ inputs.workingDirectory }}/.github/scripts/package-linux ${package_args}
|
||||
|
||||
- name: Run Windows packaging
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
run: |
|
||||
# Run Windows Packaging
|
||||
$PackageArgs = @{
|
||||
Target = '${{ inputs.target }}'
|
||||
Configuration = '${{ inputs.config }}'
|
||||
}
|
||||
|
||||
if ( $Env:RUNNER_DEBUG -ne $null ) {
|
||||
$PackageArgs += @{ Debug = $true }
|
||||
}
|
||||
|
||||
if ( ( Test-Path env:CI ) -and ( Test-Path env:RUNNER_DEBUG ) ) {
|
||||
$BuildArgs += @{
|
||||
Debug = $true
|
||||
}
|
||||
}
|
||||
|
||||
.github/scripts/Package-windows.ps1 @PackageArgs
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Validate UI XML
|
||||
description: Validates Qt UI XML files
|
||||
inputs:
|
||||
failCondition:
|
||||
description: Controls whether failed checks also fail the workflow run
|
||||
required: false
|
||||
default: never
|
||||
workingDirectory:
|
||||
description: Working directory for checks
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo "::notice::qt-xml-validator action requires an Linux-based or macOS-based runner."
|
||||
exit 2
|
||||
|
||||
- name: Install xmllint 🕵️
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
: Install xmllint 🕵️
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
echo ::group::Installing libxml2-utils
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install --no-install-recommends -y libxml2-utils
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Register Annotations 📝
|
||||
uses: korelstar/xmllint-problem-matcher@v1
|
||||
|
||||
- name: Validate XML 💯
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_EVENT_FORCED: ${{ github.event.forced }}
|
||||
GITHUB_REF_BEFORE: ${{ github.event.before }}
|
||||
run: |
|
||||
: Validate XML 💯
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
shopt -s extglob
|
||||
|
||||
changes=($(git diff --name-only HEAD~1 HEAD -- UI/forms))
|
||||
case "${GITHUB_EVENT_NAME}" in
|
||||
pull_request) changes=($(git diff --name-only origin/"${GITHUB_BASE_REF}" HEAD -- UI/forms)) ;;
|
||||
push)
|
||||
if [[ "${GITHUB_EVENT_FORCED}" == false ]]; then
|
||||
changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD -- UI/forms))
|
||||
fi
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
if (( ${#changes[@]} )); then
|
||||
if [[ '${{ inputs.failCondition }}' == never ]]; then set +e; fi
|
||||
xmllint \
|
||||
--schema ${{ github.workspace }}/UI/forms/XML-Schema-Qt5.15.xsd \
|
||||
--noout "${changes[@]}"
|
||||
fi
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Run clang-format
|
||||
description: Runs clang-format and checks for any changes introduced by it
|
||||
inputs:
|
||||
failCondition:
|
||||
description: Controls whether failed checks also fail the workflow run
|
||||
required: false
|
||||
default: never
|
||||
workingDirectory:
|
||||
description: Working directory for checks
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo "::notice::run-clang-format action requires a macOS-based or Linux-based runner."
|
||||
exit 2
|
||||
|
||||
- name: Install Dependencies 🛍️
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
: Install Dependencies 🛍️
|
||||
echo ::group::Install Dependencies
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
|
||||
echo "/home/linuxbrew/.linuxbrew/opt/clang-format@13/bin" >> $GITHUB_PATH
|
||||
brew install --quiet zsh
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Run clang-format 🐉
|
||||
id: result
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
env:
|
||||
GITHUB_EVENT_FORCED: ${{ github.event.forced }}
|
||||
GITHUB_REF_BEFORE: ${{ github.event.before }}
|
||||
run: |
|
||||
: Run clang-format 🐉
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local -a changes=($(git diff --name-only HEAD~1 HEAD))
|
||||
case ${GITHUB_EVENT_NAME} {
|
||||
pull_request) changes=($(git diff --name-only origin/${GITHUB_BASE_REF} HEAD)) ;;
|
||||
push) if [[ ${GITHUB_EVENT_FORCED} != true ]] changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD)) ;;
|
||||
*) ;;
|
||||
}
|
||||
|
||||
if (( ${changes[(I)(*.c|*.h|*.cpp|*.hpp|*.m|*.mm)]} )) {
|
||||
print ::group::Install clang-format-13
|
||||
brew install --quiet obsproject/tools/clang-format@13
|
||||
print ::endgroup::
|
||||
|
||||
print ::group::Run clang-format-13
|
||||
./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check
|
||||
print ::endgroup::
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Run cmake-format
|
||||
description: Runs cmake-format and checks for any changes introduced by it
|
||||
inputs:
|
||||
failCondition:
|
||||
description: Controls whether failed checks also fail the workflow run
|
||||
required: false
|
||||
default: never
|
||||
workingDirectory:
|
||||
description: Working directory for checks
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo "::notice::run-cmake-format action requires a macOS-based or Linux-based runner."
|
||||
exit 2
|
||||
|
||||
- name: Install Dependencies 🛍️
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
: Install Dependencies 🛍️
|
||||
echo ::group::Install Dependencies
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
|
||||
brew install --quiet zsh
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Run cmake-format 🎛️
|
||||
id: result
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
working-directory: ${{ github.workspace }}
|
||||
env:
|
||||
GITHUB_EVENT_FORCED: ${{ github.event.forced }}
|
||||
GITHUB_REF_BEFORE: ${{ github.event.before }}
|
||||
run: |
|
||||
: Run cmake-format 🎛️
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local -a changes=($(git diff --name-only HEAD~1 HEAD))
|
||||
case ${GITHUB_EVENT_NAME} {
|
||||
pull_request) changes=($(git diff --name-only origin/${GITHUB_BASE_REF} HEAD)) ;;
|
||||
push) if [[ ${GITHUB_EVENT_FORCED} != true ]] changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD)) ;;
|
||||
*) ;;
|
||||
}
|
||||
|
||||
if (( ${changes[(I)*.cmake|*CMakeLists.txt]} )) {
|
||||
print ::group::Install cmakelang
|
||||
pip3 install cmakelang
|
||||
print ::endgroup::
|
||||
|
||||
print ::group::Run cmake-format
|
||||
./build-aux/run-cmake-format --fail-${{ inputs.failCondition }} --check
|
||||
print ::endgroup::
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Run swift-format
|
||||
description: Runs swift-format and checks for any changes introduced by it
|
||||
inputs:
|
||||
failCondition:
|
||||
description: Controls whether failed checks also fail the workflow run
|
||||
required: false
|
||||
default: never
|
||||
workingDirectory:
|
||||
description: Working directory for checks
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo "::notice::run-swift-format action requires a macOS-based or Linux-based runner."
|
||||
exit 2
|
||||
|
||||
- name: Install Dependencies 🛍️
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
: Install Dependencies 🛍️
|
||||
echo ::group::Install Dependencies
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
|
||||
brew install --quiet zsh
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Run swift-format 🔥
|
||||
id: result
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
working-directory: ${{ github.workspace }}
|
||||
env:
|
||||
GITHUB_EVENT_FORCED: ${{ github.event.forced }}
|
||||
GITHUB_REF_BEFORE: ${{ github.event.before }}
|
||||
run: |
|
||||
: Run swift-format 🔥
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local -a changes=($(git diff --name-only HEAD~1 HEAD))
|
||||
case ${GITHUB_EVENT_NAME} {
|
||||
pull_request) changes=($(git diff --name-only origin/${GITHUB_BASE_REF} HEAD)) ;;
|
||||
push) if [[ ${GITHUB_EVENT_FORCED} != true ]] changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD)) ;;
|
||||
*) ;;
|
||||
}
|
||||
|
||||
if (( ${changes[(I)*.swift]} )) {
|
||||
print ::group::Install swift-format
|
||||
brew install --quiet swift-format
|
||||
print ::endgroup::
|
||||
|
||||
print ::group::Run swift-format
|
||||
./build-aux/run-swift-format --fail-${{ inputs.failCondition }} --check
|
||||
print ::endgroup::
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
name: Services Validation
|
||||
description: Checks services configuration file and checks for defunct services
|
||||
inputs:
|
||||
repositorySecret:
|
||||
description: GitHub token for API access
|
||||
required: true
|
||||
runSchemaChecks:
|
||||
description: Enable schema checking
|
||||
required: false
|
||||
default: 'true'
|
||||
runServiceChecks:
|
||||
description: Enable defunct service checking
|
||||
required: false
|
||||
default: 'false'
|
||||
createPullRequest:
|
||||
description: Enable pull request creation after service checks
|
||||
required: false
|
||||
default: 'false'
|
||||
workingDirectory:
|
||||
description: Working directory for checks
|
||||
required: false
|
||||
default: ${{ github.workspace }}
|
||||
outputs:
|
||||
hasDefunctServices:
|
||||
description: True if defunct services were found in configuration
|
||||
value: ${{ steps.check.outputs.make_pr }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo "::notice::services-validation action requires a macOS-based or Linux-based runner."
|
||||
exit 2
|
||||
|
||||
- name: Install and Configure Python 🐍
|
||||
shell: bash
|
||||
run: |
|
||||
: Install and Configure Python 🐍
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
echo ::group::Python Set Up
|
||||
if [[ "${RUNNER_OS}" == Linux ]]; then
|
||||
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
|
||||
fi
|
||||
brew install --quiet python3
|
||||
python3 -m pip install jsonschema json_source_map requests
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Validate Services File JSON Schema 🕵️
|
||||
if: fromJSON(inputs.runSchemaChecks)
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
run: |
|
||||
: Validate Services File JSON Schema 🕵️
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
shopt -s extglob
|
||||
|
||||
echo ::group::Run Validation
|
||||
python3 -u \
|
||||
.github/scripts/utils.py/check-jsonschema.py \
|
||||
plugins/rtmp-services/data/@(services|package).json \
|
||||
--loglevel INFO
|
||||
echo ::endgroup::
|
||||
|
||||
- name: Annotate schema validation errors 🏷️
|
||||
if: fromJSON(inputs.runSchemaChecks) && failure()
|
||||
uses: yuzutech/annotations-action@v0.4.0
|
||||
with:
|
||||
repo-token: ${{ inputs.repositorySecret }}
|
||||
title: Service JSON Errors
|
||||
input: ${{ inputs.workingDirectory }}/validation_errors.json
|
||||
|
||||
- name: Restore Timestamp Cache ⏳
|
||||
if: fromJSON(inputs.runServiceChecks)
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/other
|
||||
key: service-check-${{ github.run_id }}
|
||||
restore-keys: service-check-
|
||||
|
||||
- name: Check for defunct services 📉
|
||||
id: services-check
|
||||
if: fromJSON(inputs.runServiceChecks)
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.workingDirectory }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.repositorySecret }}
|
||||
WORKFLOW_RUN_ID: ${{ github.run_id }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
: Check for defunct services 📉
|
||||
python3 -u .github/scripts/utils.py/check-services.py
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: fromJSON(inputs.runServiceChecks)
|
||||
with:
|
||||
name: timestamps
|
||||
path: ${{ inputs.workingDirectory }}/other/*
|
||||
|
||||
- name: Create pull request 🔧
|
||||
uses: peter-evans/create-pull-request@f094b77505fb89581e68a1163fbd2fffece39da1
|
||||
if: fromJSON(inputs.createPullRequest) && fromJSON(inputs.runServiceChecks) && fromJSON(steps.services-check.outputs.make_pr)
|
||||
with:
|
||||
author: 'Service Checker <commits@obsproject.com>'
|
||||
commit-message: 'rtmp-services: Remove defunct servers/services'
|
||||
title: 'rtmp-services: Remove defunct servers/services'
|
||||
branch: 'automated/clean-services'
|
||||
body: ${{ fromJSON(steps.services-check.outputs.pr_message) }}
|
||||
delete-branch: true
|
||||
@@ -0,0 +1,146 @@
|
||||
name: Set up macOS Code Signing
|
||||
description: Sets up code signing certificates, provisioning profiles, and notarization information
|
||||
inputs:
|
||||
codesignIdentity:
|
||||
description: Code signing identity
|
||||
required: true
|
||||
codesignCertificate:
|
||||
description: PKCS12 certificate in base64 format
|
||||
required: true
|
||||
certificatePassword:
|
||||
description: Password required to install PKCS12 certificate
|
||||
required: true
|
||||
keychainPassword:
|
||||
description: Password to use for temporary keychain
|
||||
required: false
|
||||
notarizationUser:
|
||||
description: Apple ID to use for notarization
|
||||
required: false
|
||||
notarizationPassword:
|
||||
description: Application password for notarization
|
||||
provisioningProfile:
|
||||
description: Provisioning profile in base64 format
|
||||
required: false
|
||||
outputs:
|
||||
haveCodesignIdent:
|
||||
description: True if necessary code signing credentials were found
|
||||
value: ${{ steps.codesign.outputs.haveCodesignIdent }}
|
||||
haveProvisioningProfile:
|
||||
description: True if necessary provisioning profile credentials were found
|
||||
value: ${{ steps.provisioning.outputs.haveProvisioningProfile }}
|
||||
haveNotarizationUser:
|
||||
description: True if necessary notarization credentials were found
|
||||
value: ${{ steps.notarization.outputs.haveNotarizationUser }}
|
||||
codesignIdent:
|
||||
description: Code signing identity
|
||||
value: ${{ steps.codesign.outputs.codesignIdent }}
|
||||
codesignTeam:
|
||||
description: Code signing team
|
||||
value: ${{ steps.codesign.outputs.codesignTeam }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os != 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo "setup-macos-codesigning action requires a macOS-based runner."
|
||||
exit 2
|
||||
|
||||
- name: macOS Code Signing ✍️
|
||||
id: codesign
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
env:
|
||||
MACOS_SIGNING_IDENTITY: ${{ inputs.codesignIdentity }}
|
||||
MACOS_SIGNING_CERT: ${{ inputs.codesignCertificate }}
|
||||
MAOCS_SIGNING_CERT_PASSWORD: ${{ inputs.certificatePassword }}
|
||||
MACOS_KEYCHAIN_PASSWORD: ${{ inputs.keychainPassword }}
|
||||
run: |
|
||||
: macOS Code Signing ✍️
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
if [[ ${MACOS_SIGNING_IDENTITY} && ${MACOS_SIGNING_CERT} ]] {
|
||||
print 'haveCodesignIdent=true' >> $GITHUB_OUTPUT
|
||||
|
||||
local -r certificate_path="${RUNNER_TEMP}/build_certificate.p12"
|
||||
local -r keychain_path="${RUNNER_TEMP}/app-signing.keychain-db"
|
||||
|
||||
print -n "${MACOS_SIGNING_CERT}" | base64 --decode --output=${certificate_path}
|
||||
|
||||
: "${MACOS_KEYCHAIN_PASSWORD:="$(print ${RANDOM} | sha1sum | head -c 32)"}"
|
||||
|
||||
print '::group::Keychain setup'
|
||||
security create-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" ${keychain_path}
|
||||
security set-keychain-settings -lut 21600 ${keychain_path}
|
||||
security unlock-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" ${keychain_path}
|
||||
|
||||
security import "${certificate_path}" -P "${MAOCS_SIGNING_CERT_PASSWORD}" -A \
|
||||
-t cert -f pkcs12 -k ${keychain_path} \
|
||||
-T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/xcrun
|
||||
|
||||
security set-key-partition-list -S 'apple-tool:,apple:' -k "${MACOS_KEYCHAIN_PASSWORD}" \
|
||||
${keychain_path} &> /dev/null
|
||||
|
||||
security list-keychain -d user -s ${keychain_path} 'login-keychain'
|
||||
print '::endgroup::'
|
||||
|
||||
local -r team_id="${${MACOS_SIGNING_IDENTITY##* }//(\(|\))/}"
|
||||
|
||||
print "codesignIdent=${MACOS_SIGNING_IDENTITY}" >> $GITHUB_OUTPUT
|
||||
print "MACOS_KEYCHAIN_PASSWORD=${MACOS_KEYCHAIN_PASSWORD}" >> $GITHUB_ENV
|
||||
print "codesignTeam=${team_id}" >> $GITHUB_OUTPUT
|
||||
} else {
|
||||
print 'haveCodesignIdent=false' >> $GITHUB_OUTPUT
|
||||
}
|
||||
|
||||
- name: Provisioning Profile 👤
|
||||
id: provisioning
|
||||
if: fromJSON(steps.codesign.outputs.haveCodesignIdent)
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
env:
|
||||
MACOS_SIGNING_PROVISIONING_PROFILE: ${{ inputs.provisioningProfile }}
|
||||
run: |
|
||||
: Provisioning Profile 👤
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
if [[ "${MACOS_SIGNING_PROVISIONING_PROFILE}" ]] {
|
||||
print 'haveProvisioningProfile=true' >> $GITHUB_OUTPUT
|
||||
|
||||
local -r profile_path="${RUNNER_TEMP}/build_profile.provisionprofile"
|
||||
print -n "${MACOS_SIGNING_PROVISIONING_PROFILE}" \
|
||||
| base64 --decode --output="${profile_path}"
|
||||
|
||||
print '::group::Provisioning Profile Setup'
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
security cms -D -i ${profile_path} -o ${RUNNER_TEMP}/build_profile.plist
|
||||
local -r uuid="$(plutil -extract UUID raw ${RUNNER_TEMP}/build_profile.plist)"
|
||||
local -r team_id="$(plutil -extract TeamIdentifier.0 raw -expect string ${RUNNER_TEMP}/build_profile.plist)"
|
||||
|
||||
if [[ ${team_id} != '${{ steps.codesign.codesignTeam }}' ]] {
|
||||
print '::notice::Code Signing team in provisioning profile does not match certificate.'
|
||||
}
|
||||
|
||||
cp ${profile_path} ~/Library/MobileDevice/Provisioning\ Profiles/${uuid}.provisionprofile
|
||||
print "provisioningProfileUUID=${uuid}" >> $GITHUB_OUTPUT
|
||||
print '::endgroup::'
|
||||
} else {
|
||||
print 'haveProvisioningProfile=false' >> $GITHUB_OUTPUT
|
||||
}
|
||||
|
||||
- name: Notarization 🧑💼
|
||||
id: notarization
|
||||
if: fromJSON(steps.codesign.outputs.haveCodesignIdent)
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
env:
|
||||
MACOS_NOTARIZATION_USERNAME: ${{ inputs.notarizationUser }}
|
||||
MACOS_NOTARIZATION_PASSWORD: ${{ inputs.notarizationPassword }}
|
||||
run: |
|
||||
: Notarization 🧑💼
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
if [[ ${MACOS_NOTARIZATION_USERNAME} && ${MACOS_NOTARIZATION_PASSWORD} ]] {
|
||||
print 'haveNotarizationUser=true' >> $GITHUB_OUTPUT
|
||||
} else {
|
||||
print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
name: Generate Sparkle Appcast
|
||||
description: Creates Sparkle Appcast for a new release and generates delta patch files
|
||||
inputs:
|
||||
sparklePrivateKey:
|
||||
description: Private key used for Sparkle signing
|
||||
required: true
|
||||
baseImage:
|
||||
description: Disk image to base the Sparkle Appcast on
|
||||
required: true
|
||||
channel:
|
||||
description: Sparkle Appcast channel to use
|
||||
required: false
|
||||
default: stable
|
||||
count:
|
||||
description: Number of old versions to generate deltas for
|
||||
required: false
|
||||
default: '1'
|
||||
urlPrefix:
|
||||
description: URL prefix to use for Sparkle downloads
|
||||
required: true
|
||||
customTitle:
|
||||
description: Custom title to use for Appcast
|
||||
required: false
|
||||
customLink:
|
||||
description: Custom link to use for Appcast
|
||||
required: false
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os != 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo '::notice::sparkle-appcast action requires a macOS-based runner.'
|
||||
exit 2
|
||||
|
||||
- name: Install Dependencies
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Install Dependencies
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
print ::group::Install Dependencies
|
||||
brew install --quiet coreutils pandoc
|
||||
print ::endgroup::
|
||||
|
||||
- name: Set Up Sparkle ✨
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Set Up Sparkle ✨
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local version
|
||||
local base_url
|
||||
local hash
|
||||
IFS=';' read -r version base_url hash <<< \
|
||||
"$(jq -r '.tools.sparkle | {version, baseUrl, hash} | join(";")' buildspec.json)"
|
||||
|
||||
mkdir -p Sparkle && pushd Sparkle
|
||||
curl -s -L -O "${base_url}/${version}/Sparkle-${version}.tar.xz"
|
||||
|
||||
local checksum="$(sha256sum Sparkle-${version}.tar.xz | cut -d " " -f 1)"
|
||||
|
||||
if [[ ${hash} != ${checksum} ]] {
|
||||
print "::error::Sparkle-${version}.tar.xz checksum mismatch: ${checksum} (expected: ${hash})"
|
||||
exit 2
|
||||
}
|
||||
|
||||
tar -xJf "Sparkle-${version}.tar.xz"
|
||||
popd
|
||||
|
||||
mkdir builds
|
||||
mkdir -p output/appcasts/stable
|
||||
mkdir -p output/sparkle_deltas
|
||||
|
||||
- name: Download Builds 📥
|
||||
id: builds
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Download Builds 📥
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
pushd builds
|
||||
local image_location=(${{ inputs.baseImage }})
|
||||
hdiutil attach -readonly -noverify -noautoopen -plist ${image_location} > result.plist
|
||||
|
||||
local -i num_entities=$(( $(plutil -extract system-entities raw -- result.plist) - 1 ))
|
||||
local keys
|
||||
local mount_point
|
||||
for i ({0..${num_entities}}) {
|
||||
keys=($(plutil -extract system-entities.${i} raw -- result.plist))
|
||||
if [[ ${keys} == *mount-point* ]] {
|
||||
mount_point=$(plutil -extract system-entities.${i}.mount-point raw -- result.plist)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
local feed_url
|
||||
local info_plist=(${mount_point}/*.app/Contents/Info.plist)
|
||||
|
||||
if [[ -f ${info_plist} ]] {
|
||||
feed_url=$(plutil -extract SUFeedURL raw -- ${info_plist})
|
||||
} else {
|
||||
print '::error:: No Info.plist file found in specified disk image.'
|
||||
hdiutil detach ${mount_point}
|
||||
exit 2
|
||||
}
|
||||
|
||||
print "feedUrl=${feed_url}" >> $GITHUB_OUTPUT
|
||||
hdiutil detach ${mount_point}
|
||||
|
||||
curl -s -L -O ${feed_url}
|
||||
local -a artifacts=($(\
|
||||
xmllint \
|
||||
-xpath "//rss/channel/item[*[local-name()='channel'][text()='${{ inputs.channel }}']]/enclosure/@url" \
|
||||
${feed_url:t} \
|
||||
| sed -n 's/.*url="\(.*\)"/\1/p')
|
||||
)
|
||||
|
||||
local url
|
||||
local file_name
|
||||
for i ({1..${{ inputs.count }}}) {
|
||||
url="${artifacts[${i}]}"
|
||||
file_name="${artifacts[${i}]:t}"
|
||||
curl -s -L -O ${url}
|
||||
}
|
||||
|
||||
mv ${{ inputs.baseImage }} ${PWD}
|
||||
rm -rf - result.plist
|
||||
popd
|
||||
|
||||
- name: Prepare Release Notes 📝
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Prepare Release Notes 📝
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
git tag -l --format='%(contents)' ${GITHUB_REF_NAME} \
|
||||
| tr '\n' '\\n' \
|
||||
| sed 's/-----BEGIN SSH SIGNATURE-----.*-----END SSH SIGNATURE-----//g' \
|
||||
| tr '\\n' '\n' > notes.rst
|
||||
|
||||
sed -i '' '2i\'$'\n''###################################################' notes.rst
|
||||
pandoc -f rst -t html notes.rst -o output/appcasts/notes_${{ inputs.channel }}.html
|
||||
|
||||
- name: Generate Appcast 🎙️
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Generate Appcast 🎙️
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
print -n '${{ inputs.sparklePrivateKey }}' >> eddsa_private.key
|
||||
local feed_url='${{ steps.builds.outputs.feedUrl }}'
|
||||
|
||||
Sparkle/bin/generate_appcast \
|
||||
--verbose \
|
||||
--ed-key-file eddsa_private.key \
|
||||
--download-url-prefix '${{ inputs.urlPrefix }}/' \
|
||||
--full-release-notes-url "${feed_url//updates_*/notes_${{ inputs.channel }}.html}" \
|
||||
--maximum-versions 0 \
|
||||
--maximum-deltas ${{ inputs.count }} \
|
||||
--channel '${{ inputs.channel }}' \
|
||||
builds
|
||||
|
||||
local -a deltas=(builds/*.delta(N))
|
||||
|
||||
if (( #deltas )) {
|
||||
mv ${deltas} output/sparkle_deltas
|
||||
}
|
||||
|
||||
mv builds/*.xml output/appcasts
|
||||
|
||||
- name: Adjust Appcast 🎙️
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Adjust Appcast 🎙️
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local feed_url='${{ steps.builds.outputs.feedUrl }}'
|
||||
local arch=${${${(s:_:)feed_url:t}[2]}//x86/x86_64}
|
||||
local -a appcasts=(output/appcasts/*_v2.xml)
|
||||
local adjusted
|
||||
for appcast (${appcasts}) {
|
||||
adjusted="${appcast//.xml/-adjusted.xml}"
|
||||
xsltproc \
|
||||
--stringparam pDeltaUrl "${{ inputs.urlPrefix }}/sparkle_deltas/${arch}/" \
|
||||
--stringparam pSparkleUrl '${{ inputs.urlPrefix }}/' \
|
||||
--stringparam pCustomTitle '${{ inputs.customTitle }}' \
|
||||
--stringparam pCustomLink '${{ inputs.customLink }}' \
|
||||
-o ${adjusted} ${GITHUB_ACTION_PATH}/appcast_adjust.xslt ${appcast}
|
||||
|
||||
xmllint --format ${adjusted} >! ${appcast}
|
||||
rm ${adjusted}
|
||||
}
|
||||
|
||||
- name: Create Legacy Appcast 📟
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Create Legacy Appcast 📟
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local -a appcasts=(output/appcasts/*_v2.xml)
|
||||
local legacy
|
||||
|
||||
for appcast (${appcasts}) {
|
||||
legacy="${appcast//.xml/-legacy.xml}"
|
||||
xsltproc \
|
||||
-o ${legacy} ${GITHUB_ACTION_PATH}/appcast_legacy.xslt ${appcast}
|
||||
|
||||
xmllint --format ${legacy} >! output/appcasts/stable/${${appcast:t}//-v2.xml/.xml}
|
||||
rm ${legacy}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsl:stylesheet version="1.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
|
||||
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="no"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<xsl:param name="pCustomTitle" select="/rss/channel/title" />
|
||||
<xsl:param name="pCustomLink" select="/rss/channel/link" />
|
||||
<xsl:param name="pSparkleUrl" select="''" />
|
||||
<xsl:param name="pDeltaUrl" select="''" />
|
||||
|
||||
<xsl:template match="@* | node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@* | node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
<xsl:template match="/rss/channel/title" />
|
||||
<xsl:template match="/rss/channel/link" />
|
||||
<xsl:template match="/rss/channel">
|
||||
<xsl:copy>
|
||||
<xsl:element name="title"><xsl:value-of select="$pCustomTitle" /></xsl:element>
|
||||
<xsl:element name="link"><xsl:value-of select="$pCustomLink" /></xsl:element>
|
||||
<xsl:apply-templates select="@* | node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
<xsl:template match="/rss/channel/item/sparkle:deltas/enclosure/@url">
|
||||
<xsl:attribute name="url">
|
||||
<xsl:choose>
|
||||
<xsl:when test="starts-with(., $pDeltaUrl)">
|
||||
<xsl:value-of select="." />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="$pDeltaUrl" />
|
||||
<xsl:value-of select="substring-after(., $pSparkleUrl)" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:attribute>
|
||||
</xsl:template>
|
||||
<xsl:template match="/rss/channel/item/sparkle:fullReleaseNotesLink">
|
||||
<xsl:element name="sparkle:releaseNotesLink"><xsl:apply-templates select="@* | node()" /></xsl:element>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsl:stylesheet version="1.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
|
||||
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="no"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<xsl:template match="@* | node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@* | node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
<xsl:template match="/rss/channel/item[sparkle:channel[text()!='stable']]" />
|
||||
<xsl:template match="/rss/channel/item/sparkle:channel" />
|
||||
<xsl:template match="/rss/channel/item/sparkle:deltas" />
|
||||
</xsl:stylesheet>
|
||||
@@ -0,0 +1,286 @@
|
||||
name: Steam Upload
|
||||
description: Creates and uploads stable and nightly builds of obs-studio and beta builds (if available)
|
||||
inputs:
|
||||
steamSecret:
|
||||
description: Steam auth code
|
||||
required: true
|
||||
steamUser:
|
||||
description: Steam user name
|
||||
required: true
|
||||
steamPassword:
|
||||
description: Steam user password
|
||||
required: true
|
||||
workflowSecret:
|
||||
description: GitHub API token to use for API calls
|
||||
required: true
|
||||
tagName:
|
||||
description: Tag name to use for packaging
|
||||
required: false
|
||||
default: ''
|
||||
stableBranch:
|
||||
description: Name of the stable branch to use
|
||||
required: false
|
||||
default: staging
|
||||
betaBranch:
|
||||
description: Name of the beta branch to use
|
||||
required: false
|
||||
default: beta_staging
|
||||
nightlyBranch:
|
||||
description: Name of the nightly branch to use
|
||||
required: false
|
||||
default: nightly
|
||||
playtestBranch:
|
||||
description: Name of the playtest branch to use
|
||||
required: false
|
||||
default: staging
|
||||
customAssetWindows:
|
||||
description: Custom asset for Windows
|
||||
required: false
|
||||
default: ''
|
||||
customAssetMacOSApple:
|
||||
description: Custom asset for macOS Apple Silicon
|
||||
required: false
|
||||
default: ''
|
||||
customAssetMacOSIntel:
|
||||
description: Custom asset for macOS Intel
|
||||
required: false
|
||||
default: ''
|
||||
preview:
|
||||
description: Enable preview mode (no uploads done)
|
||||
required: false
|
||||
default: ''
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check Runner Operating System 🏃♂️
|
||||
if: runner.os != 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
: Check Runner Operating System 🏃♂️
|
||||
echo '::error::steam-upload action requires a macOS-based runner.'
|
||||
exit 2
|
||||
|
||||
- name: Check GitHub Event 🔬
|
||||
if: contains(fromJSON('["release", "workflow_dispatch", "schedule"]'), github.event_name) != true
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Check GitHub Event 🔬
|
||||
print "::error:steam-upload action can only be used with 'release', 'workflow-dispatch', or 'schedule' events."
|
||||
exit 2
|
||||
|
||||
- name: Download Assets 📥
|
||||
id: asset-info
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
env:
|
||||
GH_TOKEN: ${{ inputs.workflowSecret }}
|
||||
windows_custom_asset: ${{ steps.asset-info.outputs.windowsAssetUrl }}
|
||||
macos_apple_custom_asset: ${{ steps.asset-info.outputs.macos_appleAssetUrl }}
|
||||
macos_intel_custom_asset: ${{ steps.asset-info.outputs.macos_intelAssetUrl }}
|
||||
run: |
|
||||
: Download Assets 📥
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local root_dir="${PWD}"
|
||||
local description
|
||||
local is_prerelease
|
||||
|
||||
case ${GITHUB_EVENT_NAME} {
|
||||
release)
|
||||
gh release download \
|
||||
--pattern '*macOS*.dmg' \
|
||||
--pattern '*Windows*' \
|
||||
--pattern '*.zip' \
|
||||
--clobber
|
||||
|
||||
IFS=';' read -r description is_prerelease <<< \
|
||||
"$(gh release view --json tagName,isPrerelease --jq 'join(";")')"
|
||||
;;
|
||||
workflow_dispatch)
|
||||
if [[ '${{ inputs.tagName }}' =~ [0-9]+\.[0-9]+\.[0-9]+(-(rc|beta)[0-9]+)*$ ]] {
|
||||
gh release download ${{ inputs.tagName }} \
|
||||
--pattern '*macOS*.dmg' \
|
||||
--pattern '*Windows*' \
|
||||
--pattern '*.zip' \
|
||||
--clobber
|
||||
|
||||
description='${{ inputs.tagName }}'
|
||||
read -r is_prerelease <<< \
|
||||
"$(gh release view ${{ inputs.tagName }} --json isPrerelease --jq '.isPrerelease')"
|
||||
asset_names=(gh release view ${{ inputs.tagName }} --json assets \
|
||||
--jq '.assets[] | select(.name|test(".*(macos|Full-x64|windows).*")) | .name')
|
||||
|
||||
local -A custom_assets=(
|
||||
windows "Windows x64;${windows_custom_asset}"
|
||||
macos_apple "macOS Apple;${macos_apple_custom_asset}"
|
||||
macos_intel "macOS Intel;${macos_intel_custom_asset}"
|
||||
)
|
||||
|
||||
local display_name
|
||||
local url
|
||||
mkdir -p custom_assets && pushd custom_assets
|
||||
for platform (windows macos_apple macos_intel) {
|
||||
IFS=';' read -r display_name url <<< "${custom_assets[${platform}]}"
|
||||
if [[ ${url} ]] {
|
||||
print "::group::Download of ${display_name} custom asset"
|
||||
curl --location --silent --remote-name ${url}
|
||||
|
||||
if [[ ! -f ${root_dir}/${url:t} ]] {
|
||||
print "::warning::Custom asset for ${display_name} does not replace an existing release asset"
|
||||
} else {
|
||||
rm -rf -- ${root_dir}/${url:t}
|
||||
}
|
||||
mv ${url:t} ${root_dir}
|
||||
print '::endgroup::'
|
||||
}
|
||||
}
|
||||
popd
|
||||
} else {
|
||||
print "::error::Invalid tag name for non-release workflow run: '${{ inputs.tagName }}'."
|
||||
exit 2
|
||||
}
|
||||
;;
|
||||
schedule)
|
||||
gh run download ${GITHUB_RUN_ID} \
|
||||
--pattern '*macos*' \
|
||||
--pattern '*windows*'
|
||||
|
||||
local short_hash="${GITHUB_SHA:0:9}"
|
||||
mv obs-studio-windows-x64-${short_hash}/obs-studio-*-windows-x64.zip \
|
||||
${root_dir}
|
||||
mv obs-studio-macos-arm64-${short_hash}/obs-studio-*-macos-apple.dmg \
|
||||
${root_dir}
|
||||
mv obs-studio-macos-intel-${short_hash}/obs-studio-*-macos-intel.dmg \
|
||||
${root_dir}
|
||||
|
||||
description="g${GITHUB_SHA}"
|
||||
is_prerelease='false'
|
||||
;;
|
||||
}
|
||||
|
||||
print "description=${description}" >> $GITHUB_OUTPUT
|
||||
print "is_prerelease=${is_prerelease}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Prepare Builds for Steam 🍜
|
||||
shell: zsh --no-rcs --errexit --pipefail --extendedglob {0}
|
||||
run: |
|
||||
: Prepare Builds for Steam 🍜
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local root_dir="${PWD}"
|
||||
mkdir -p steam && pushd steam
|
||||
|
||||
print '::group::Prepare Windows x64 assets'
|
||||
mkdir -p steam-windows && pushd steam-windows
|
||||
unzip ${root_dir}/(#i)obs-studio-*.zip
|
||||
rm ${root_dir}/(#i)obs-studio-*.zip
|
||||
|
||||
cp -r ${root_dir}/build-aux/steam/scripts_windows scripts
|
||||
touch disable_updater
|
||||
popd
|
||||
print '::endgroup::'
|
||||
|
||||
print '::group::Prepare macOS Apple assets'
|
||||
mkdir -p steam-macos/arm64/OBS.app
|
||||
hdiutil attach -noverify -readonly -noautoopen -mountpoint /Volumes/obs-studio-arm64 ${root_dir}/(#i)obs-studio-*-macos-apple.dmg
|
||||
ditto /Volumes/obs-studio-arm64/OBS.app steam-macos/arm64/OBS.app
|
||||
hdiutil unmount /Volumes/obs-studio-arm64
|
||||
rm ${root_dir}/(#i)obs-studio-*-macos-apple.dmg
|
||||
print '::endgroup::'
|
||||
|
||||
print '::group::Prepare macOS Intel assets'
|
||||
mkdir -p steam-macos/x86_64/OBS.app
|
||||
hdiutil attach -noverify -readonly -noautoopen -mountpoint /Volumes/obs-studio-x86_64 ${root_dir}/(#i)obs-studio-*-macos-intel.dmg
|
||||
ditto /Volumes/obs-studio-x86_64/OBS.app steam-macos/x86_64/OBS.app
|
||||
hdiutil unmount /Volumes/obs-studio-x86_64
|
||||
rm ${root_dir}/(#i)obs-studio-*-macos-intel.dmg
|
||||
print '::endgroup::'
|
||||
|
||||
cp ${root_dir}/build-aux/steam/scripts_macos/launch.sh steam-macos/launch.sh
|
||||
|
||||
popd
|
||||
|
||||
- name: Set Up steamcmd 🚂
|
||||
uses: CyberAndrii/setup-steamcmd@b786e0da44db3d817e66fa3910a9560cb28c9323
|
||||
|
||||
- name: Generate Steam auth code 🔐
|
||||
id: steam-totp
|
||||
uses: CyberAndrii/steam-totp@c7f636bc64e77f1b901e0420b7890813141508ee
|
||||
if: ${{ ! fromJSON(inputs.preview) }}
|
||||
with:
|
||||
shared_secret: ${{ inputs.steamSecret }}
|
||||
|
||||
- name: Upload to Steam 📤
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Upload to Steam 📤
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local root_dir="${PWD}"
|
||||
local build_file='build.vdf'
|
||||
local branch_name
|
||||
|
||||
pushd steam
|
||||
print '::group::Prepare Steam Build Script'
|
||||
|
||||
case ${GITHUB_EVENT_NAME} {
|
||||
schedule) branch_name='${{ inputs.nightlyBranch }}' ;;
|
||||
release|workflow_dispatch)
|
||||
if [[ '${{ steps.asset-info.outputs.is_prerelease }}' == 'true' ]] {
|
||||
branch_name='${{ inputs.betaBranch }}'
|
||||
} else {
|
||||
branch_name='${{ inputs.stableBranch }}'
|
||||
}
|
||||
;;
|
||||
}
|
||||
|
||||
sed "s/@@DESC@@/${branch_name}-${{ steps.asset-info.outputs.description }}/;s/@@BRANCH@@/${branch_name}/" \
|
||||
${root_dir}/build-aux/steam/obs_build.vdf > ${build_file}
|
||||
|
||||
print "Generated ${build_file}:\n$(<${build_file})"
|
||||
print '::endgroup::'
|
||||
|
||||
print '::group::Upload to Steam'
|
||||
local preview='${{ inputs.preview }}'
|
||||
|
||||
steamcmd \
|
||||
+login '${{ inputs.steamUser }}' '${{ inputs.steamPassword }}' '${{ steps.steam-totp.outputs.code }}' \
|
||||
+run_app_build ${preview:+-preview} ${build_file} \
|
||||
+quit
|
||||
print '::endgroup'
|
||||
popd
|
||||
|
||||
- name: Upload to Steam (Playtest) 📤
|
||||
if: fromJSON(steps.asset-info.outputs.is_prerelease)
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
run: |
|
||||
: Upload to Steam (Playtest) 📤
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local build_file='build_playtest.vdf'
|
||||
local branch_name='${{ inputs.playtestBranch }}'
|
||||
|
||||
pushd steam
|
||||
print '::group::Prepare Steam Build Script'
|
||||
|
||||
set "s/@@DESC@@/${branch_name}-${{ steps.asset-info.outputs.description }}/;s/@@BRANCH@@/${branch_name}" \
|
||||
${root_dir}/build-aux/steam/obs_playtest_build.vdf > ${build_file}
|
||||
|
||||
print "Generated ${build_file}:\n$(<${build_file})"
|
||||
print '::endgroup::'
|
||||
|
||||
print '::group::Upload to Steam'
|
||||
local preview
|
||||
if [[ '${{ inputs.preview }}' == 'true' ]] preview='-preview'
|
||||
|
||||
steamcmd \
|
||||
+login '${{ inputs.steamUser }}' '${{ inputs.steamPassword }}' '${{ steps.steam-totp.outputs.code }}' \
|
||||
+run_app_build ${preview} ${build_file} \
|
||||
+quit
|
||||
print '::endgroup'
|
||||
popd
|
||||
|
||||
- name: Upload Steam build logs
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: steam-build-logs
|
||||
path: ${{ github.workspace }}/steam/build/*.log
|
||||
@@ -0,0 +1,7 @@
|
||||
package 'ccache'
|
||||
package 'cmake'
|
||||
package 'curl'
|
||||
package 'git'
|
||||
package 'jq'
|
||||
package 'ninja-build', bin: 'ninja'
|
||||
package 'pkg-config'
|
||||
@@ -1,4 +1,5 @@
|
||||
brew "cmake"
|
||||
brew "ccache"
|
||||
brew "coreutils"
|
||||
brew "cmake"
|
||||
brew "git"
|
||||
brew "jq"
|
||||
brew "xcbeautify"
|
||||
Executable
+324
@@ -0,0 +1,324 @@
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
builtin emulate -L zsh
|
||||
setopt EXTENDED_GLOB
|
||||
setopt PUSHD_SILENT
|
||||
setopt ERR_EXIT
|
||||
setopt ERR_RETURN
|
||||
setopt NO_UNSET
|
||||
setopt PIPE_FAIL
|
||||
setopt NO_AUTO_PUSHD
|
||||
setopt NO_PUSHD_IGNORE_DUPS
|
||||
setopt FUNCTION_ARGZERO
|
||||
|
||||
## Enable for script debugging
|
||||
#setopt WARN_CREATE_GLOBAL
|
||||
#setopt WARN_NESTED_VAR
|
||||
#setopt XTRACE
|
||||
|
||||
autoload -Uz is-at-least && if ! is-at-least 5.2; then
|
||||
print -u2 -PR "%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade Zsh to fix this issue."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TRAPEXIT() {
|
||||
local return_value=$?
|
||||
|
||||
if (( ${+CI} )) unset NSUnbufferedIO
|
||||
|
||||
return ${return_value}
|
||||
}
|
||||
|
||||
TRAPZERR() {
|
||||
if (( ${_loglevel:-3} > 2 )) {
|
||||
print -u2 -PR "${CI:+::error::}%F{1} ✖︎ script execution error%f"
|
||||
print -PR -e "
|
||||
Callstack:
|
||||
${(j:\n :)funcfiletrace}
|
||||
"
|
||||
}
|
||||
|
||||
exit 2
|
||||
}
|
||||
|
||||
build() {
|
||||
if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}
|
||||
local host_os=${${(s:-:)ZSH_ARGZERO:t:r}[2]}
|
||||
local project_root=${SCRIPT_HOME:A:h:h}
|
||||
local buildspec_file=${project_root}/buildspec.json
|
||||
|
||||
fpath=(${SCRIPT_HOME}/utils.zsh ${fpath})
|
||||
autoload -Uz log_group log_info log_status log_error log_output set_loglevel check_${host_os} setup_ccache
|
||||
|
||||
if [[ ! -r ${buildspec_file} ]] {
|
||||
log_error \
|
||||
'No buildspec.json found. Please create a build specification for your project.' \
|
||||
'A buildspec.json.template file is provided in the repository to get you started.'
|
||||
return 2
|
||||
}
|
||||
|
||||
typeset -g -a skips=()
|
||||
local -i verbosity=1
|
||||
local -r _version='1.0.0'
|
||||
local -r -a _valid_targets=(
|
||||
macos-x86_64
|
||||
macos-arm64
|
||||
linux-x86_64
|
||||
)
|
||||
local target
|
||||
local config='RelWithDebInfo'
|
||||
local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel)
|
||||
local -i codesign=0
|
||||
|
||||
if [[ ${host_os} == linux ]] {
|
||||
local -r -a _valid_generators=(Ninja 'Unix Makefiles')
|
||||
local generator='Ninja'
|
||||
local -r _usage_host="
|
||||
%F{yellow} Additional options for Linux builds%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B--generator%b Specify build system to generate
|
||||
Available generators:
|
||||
- Ninja
|
||||
- Unix Makefiles"
|
||||
} elif [[ ${host_os} == macos ]] {
|
||||
local -r _usage_host="
|
||||
%F{yellow} Additional options for macOS builds%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-s | --codesign%b Enable codesigning (macOS only)"
|
||||
}
|
||||
|
||||
local -i _print_config=0
|
||||
local -r _usage="
|
||||
Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
|
||||
%BOptions%b:
|
||||
|
||||
%F{yellow} Build configuration options%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-t | --target%b Specify target - default: %B%F{green}${host_os}-${CPUTYPE}%f%b
|
||||
%B-c | --config%b Build configuration
|
||||
%B--print-config%b Print composed CMake configuration parameters
|
||||
%B--skip-[all|build|deps]%b Skip all|building OBS|checking for dependencies
|
||||
|
||||
%F{yellow} Output options%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-q | --quiet%b Quiet (error output only)
|
||||
%B-v | --verbose%b Verbose (more detailed output)
|
||||
%B--debug%b Debug (very detailed and added output)
|
||||
|
||||
%F{yellow} General options%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-h | --help%b Print this usage help
|
||||
%B-V | --version%b Print script version information
|
||||
${_usage_host:-}"
|
||||
|
||||
local -a args
|
||||
while (( # )) {
|
||||
case ${1} {
|
||||
-t|--target|--generator|-c|--config)
|
||||
if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {
|
||||
log_error "Missing value for option %B${1}%b"
|
||||
log_output ${_usage}
|
||||
exit 2
|
||||
}
|
||||
;;
|
||||
}
|
||||
case ${1} {
|
||||
--)
|
||||
shift
|
||||
args+=($@)
|
||||
break
|
||||
;;
|
||||
-t|--target)
|
||||
if (( ! ${_valid_targets[(Ie)${2}]} )) {
|
||||
log_error "Invalid value %B${2}%b for option %B${1}%b"
|
||||
log_output ${_usage}
|
||||
exit 2
|
||||
}
|
||||
target=${2}
|
||||
shift 2
|
||||
;;
|
||||
-c|--config)
|
||||
if (( ! ${_valid_configs[(Ie)${2}]} )) {
|
||||
log_error "Invalid value %B${2}%b for option %B${1}%b"
|
||||
log_output ${_usage}
|
||||
exit 2
|
||||
}
|
||||
config=${2}
|
||||
shift 2
|
||||
;;
|
||||
-s|--codesign) codesign=1; shift ;;
|
||||
-q|--quiet) (( verbosity -= 1 )) || true; shift ;;
|
||||
-v|--verbose) (( verbosity += 1 )); shift ;;
|
||||
-h|--help) log_output ${_usage}; exit 0 ;;
|
||||
-V|--version) print -Pr "${_version}"; exit 0 ;;
|
||||
--debug) verbosity=3; shift ;;
|
||||
--generator)
|
||||
if [[ ${host_os} == linux ]] {
|
||||
if (( ! ${_valid_generators[(Ie)${2}]} )) {
|
||||
log_error "Invalid value %B${2}%b for option %B${1}%b"
|
||||
log_output ${_usage}
|
||||
exit 2
|
||||
}
|
||||
generator=${2}
|
||||
}
|
||||
shift 2
|
||||
;;
|
||||
--print-config) _print_config=1; skips+=(unpack deps); shift ;;
|
||||
--skip-*)
|
||||
local _skip="${${(s:-:)1}[-1]}"
|
||||
local _check=(all deps build)
|
||||
(( ${_check[(Ie)${_skip}]} )) || log_warning "Invalid skip mode %B${_skip}%b supplied"
|
||||
skips+=(${_skip})
|
||||
shift
|
||||
;;
|
||||
*) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
|
||||
}
|
||||
}
|
||||
|
||||
: "${target:="${host_os}-${CPUTYPE}"}"
|
||||
|
||||
set -- ${(@)args}
|
||||
set_loglevel ${verbosity}
|
||||
|
||||
if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) {
|
||||
check_${host_os}
|
||||
}
|
||||
setup_ccache
|
||||
|
||||
if [[ ${host_os} == linux ]] {
|
||||
autoload -Uz setup_linux && setup_linux
|
||||
}
|
||||
|
||||
local product_name
|
||||
read -r product_name <<< \
|
||||
"$(jq -r '.name' ${buildspec_file})"
|
||||
|
||||
pushd ${project_root}
|
||||
if (( ! (${skips[(Ie)all]} + ${skips[(Ie)build]}) )) {
|
||||
log_group "Configuring ${product_name}..."
|
||||
|
||||
local -a cmake_args=()
|
||||
local -a cmake_build_args=(--build)
|
||||
local -a cmake_install_args=(--install)
|
||||
|
||||
case ${_loglevel} {
|
||||
0) cmake_args+=(-Wno_deprecated -Wno-dev --log-level=ERROR) ;;
|
||||
1) ;;
|
||||
2) cmake_build_args+=(--verbose) ;;
|
||||
*) cmake_args+=(--debug-output) ;;
|
||||
}
|
||||
|
||||
case ${target} {
|
||||
macos-*)
|
||||
cmake_args+=(
|
||||
--preset "macos${CI:+-ci}"
|
||||
-DCMAKE_OSX_ARCHITECTURES:STRING=${target##*-}
|
||||
)
|
||||
|
||||
if (( ${+CI} )) typeset -gx NSUnbufferedIO=YES
|
||||
|
||||
if (( codesign )) {
|
||||
autoload -Uz read_codesign_team && read_codesign_team
|
||||
|
||||
if [[ -z ${CODESIGN_TEAM} ]] {
|
||||
autoload -Uz read_codesign && read_codesign
|
||||
}
|
||||
}
|
||||
|
||||
cmake_args+=(
|
||||
-DOBS_CODESIGN_TEAM:STRING=${CODESIGN_TEAM:-}
|
||||
-DOBS_CODESIGN_IDENTITY:STRING=${CODESIGN_IDENT:--}
|
||||
)
|
||||
;;
|
||||
linux-*)
|
||||
cmake_args+=(
|
||||
-S ${PWD} -B "build_${target##*-}"
|
||||
-G "${generator}"
|
||||
-DCMAKE_BUILD_TYPE:STRING=${config}
|
||||
-DCEF_ROOT_DIR:PATH="${project_root}/.deps/cef_binary_${CEF_VERSION}_${target//-/_}"
|
||||
)
|
||||
|
||||
local cmake_version
|
||||
read -r _ _ cmake_version <<< "$(cmake --version)"
|
||||
|
||||
cmake_build_args+=("build_${target##*-}" --config ${config})
|
||||
|
||||
if [[ ${generator} == 'Unix Makefiles' ]] {
|
||||
cmake_build_args+=(--parallel $(( $(nproc) + 1 )))
|
||||
} else {
|
||||
cmake_build_args+=(--parallel)
|
||||
}
|
||||
|
||||
cmake_args+=(
|
||||
-DENABLE_AJA:BOOL=OFF
|
||||
-DENABLE_WEBRTC:BOOL=OFF
|
||||
)
|
||||
if (( ! UBUNTU_2210_OR_LATER )) cmake_args+=(-DENABLE_NEW_MPEGTS_OUTPUT:BOOL=OFF)
|
||||
|
||||
cmake_install_args+=(build_${target##*-} --prefix ${project_root}/build_${target##*-}/install/${config})
|
||||
;;
|
||||
}
|
||||
|
||||
if (( _print_config )) { log_output "CMake configuration: ${cmake_args}"; exit 0 }
|
||||
|
||||
log_debug "Attempting to configure with CMake arguments: ${cmake_args}"
|
||||
cmake -S ${project_root} ${cmake_args}
|
||||
|
||||
log_group "Building ${product_name}..."
|
||||
if [[ ${host_os} == macos ]] {
|
||||
local -a build_args=(
|
||||
ONLY_ACTIVE_ARCH=NO
|
||||
-project obs-studio.xcodeproj
|
||||
-target obs-studio
|
||||
-destination "generic/platform=macOS,name=Any Mac"
|
||||
-configuration ${config}
|
||||
-parallelizeTargets
|
||||
-hideShellScriptEnvironment
|
||||
build
|
||||
)
|
||||
|
||||
local -a archive_args=(
|
||||
ONLY_ACTIVE_ARCH=NO
|
||||
-project obs-studio.xcodeproj
|
||||
-scheme obs-studio
|
||||
-destination "generic/platform=macOS,name=Any Mac"
|
||||
-archivePath obs-studio.xcarchive
|
||||
-parallelizeTargets
|
||||
-hideShellScriptEnvironment
|
||||
archive
|
||||
)
|
||||
|
||||
local -a export_args=(
|
||||
-exportArchive
|
||||
-archivePath obs-studio.xcarchive
|
||||
-exportOptionsPlist exportOptions.plist
|
||||
-exportPath ${project_root}/build_macos
|
||||
)
|
||||
|
||||
autoload -Uz run_xcodebuild
|
||||
pushd build_macos
|
||||
if (( ${+CI} )) && [[ ${GITHUB_EVENT_NAME} == push && ${GITHUB_REF_NAME} =~ [0-9]+.[0-9]+.[0-9]+(-(rc|beta).+)? ]] {
|
||||
run_xcodebuild ${archive_args}
|
||||
run_xcodebuild ${export_args}
|
||||
} else {
|
||||
run_xcodebuild ${build_args}
|
||||
|
||||
rm -rf OBS.app
|
||||
mkdir OBS.app
|
||||
ditto UI/${config}/OBS.app OBS.app
|
||||
}
|
||||
popd
|
||||
} else {
|
||||
cmake ${cmake_build_args}
|
||||
|
||||
log_group "Installing ${product_name}..."
|
||||
if (( _loglevel > 1 )) cmake_install_args+=(--verbose)
|
||||
cmake ${cmake_install_args}
|
||||
popd
|
||||
}
|
||||
log_group
|
||||
}
|
||||
}
|
||||
|
||||
build ${@}
|
||||
Executable
+296
@@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
builtin emulate -L zsh
|
||||
setopt EXTENDED_GLOB
|
||||
setopt PUSHD_SILENT
|
||||
setopt ERR_EXIT
|
||||
setopt ERR_RETURN
|
||||
setopt NO_UNSET
|
||||
setopt PIPE_FAIL
|
||||
setopt NO_AUTO_PUSHD
|
||||
setopt NO_PUSHD_IGNORE_DUPS
|
||||
setopt FUNCTION_ARGZERO
|
||||
|
||||
## Enable for script debugging
|
||||
#setopt WARN_CREATE_GLOBAL
|
||||
#setopt WARN_NESTED_VAR
|
||||
#setopt XTRACE
|
||||
|
||||
autoload -Uz is-at-least && if ! is-at-least 5.2; then
|
||||
print -u2 -PR "%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade Zsh to fix this issue."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TRAPEXIT() {
|
||||
local return_value=$?
|
||||
|
||||
if (( ${+CI} )) {
|
||||
unset NSUnbufferedIO
|
||||
}
|
||||
|
||||
return ${return_value}
|
||||
}
|
||||
|
||||
TRAPZERR() {
|
||||
if (( ${_loglevel:-3} > 2 )) {
|
||||
print -u2 -PR "${CI:+::error::}%F{1} ✖︎ script execution error%f"
|
||||
print -PR -e "
|
||||
Callstack:
|
||||
${(j:\n :)funcfiletrace}
|
||||
"
|
||||
}
|
||||
|
||||
exit 2
|
||||
}
|
||||
|
||||
package() {
|
||||
if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}
|
||||
local host_os=${${(s:-:)ZSH_ARGZERO:t:r}[2]}
|
||||
local project_root=${SCRIPT_HOME:A:h:h}
|
||||
local buildspec_file=${project_root}/buildspec.json
|
||||
|
||||
fpath=(${SCRIPT_HOME}/utils.zsh ${fpath})
|
||||
autoload -Uz set_loglevel log_info log_error log_output check_${host_os}
|
||||
|
||||
local -i verbosity=1
|
||||
local -r _version='1.0.0'
|
||||
local -r -a _valid_targets=(
|
||||
macos-x86_64
|
||||
macos-arm64
|
||||
linux-x86_64
|
||||
)
|
||||
local target
|
||||
local config='RelWithDebInfo'
|
||||
local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel)
|
||||
local -i codesign=0
|
||||
local -i notarize=0
|
||||
local -i package=0
|
||||
local -i skip_deps=0
|
||||
|
||||
if [[ ${host_os} == macos ]] {
|
||||
local -r _usage_host="
|
||||
%F{yellow} Additional options for macOS builds%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-s | --codesign%b Enable codesigning (macOS only)
|
||||
%B-n | --notarize%b Enable notarization (macOS only)"
|
||||
}
|
||||
|
||||
local -r _usage="
|
||||
Usage: %B${functrace[1]%:*}%b <option> [<options>]
|
||||
|
||||
%BOptions%b:
|
||||
|
||||
%F{yellow} Package configuration options%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-t | --target%b Specify target - default: %B%F{green}${host_os}-${CPUTYPE}%f%b
|
||||
%B-c | --config%b Build configuration
|
||||
%B-p | --package%b Create package installer (macOS only)
|
||||
%B--skip-deps%b Skip checking for dependencies
|
||||
|
||||
%F{yellow} Output options%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-q | --quiet%b Quiet (error output only)
|
||||
%B-v | --verbose%b Verbose (more detailed output)
|
||||
%B--debug%b Debug (very detailed and added output)
|
||||
|
||||
%F{yellow} General options%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-h | --help%b Print this usage help
|
||||
%B-V | --version%b Print script version information
|
||||
${_usage_host:-}"
|
||||
|
||||
local -a args
|
||||
while (( # )) {
|
||||
case ${1} {
|
||||
-t|--target|-c|--config)
|
||||
if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {
|
||||
log_error "Missing value for option %B${1}%b"
|
||||
log_output ${_usage}
|
||||
exit 2
|
||||
}
|
||||
;;
|
||||
}
|
||||
case ${1} {
|
||||
--)
|
||||
shift
|
||||
args+=($@)
|
||||
break
|
||||
;;
|
||||
-t|--target)
|
||||
if (( ! ${_valid_targets[(Ie)${2}]} )) {
|
||||
log_error "Invalid value %B${2}%b for option %B${1}%b"
|
||||
log_output ${_usage}
|
||||
exit 2
|
||||
}
|
||||
target=${2}
|
||||
shift 2
|
||||
;;
|
||||
-c|--config)
|
||||
if (( ! ${_valid_configs[(Ie)${2}]} )) {
|
||||
log_error "Invalid value %B${2}%b for option %B${1}%b"
|
||||
log_output ${_usage}
|
||||
exit 2
|
||||
}
|
||||
config=${2}
|
||||
shift 2
|
||||
;;
|
||||
-s|--codesign) CODESIGN=1; shift ;;
|
||||
-n|--notarize) NOTARIZE=1; shift ;;
|
||||
-p|--package) typeset -g package=1; shift ;;
|
||||
--skip-deps) typeset -g skip_deps=1; shift ;;
|
||||
-q|--quiet) (( verbosity -= 1 )) || true; shift ;;
|
||||
-v|--verbose) (( verbosity += 1 )); shift ;;
|
||||
-h|--help) log_output ${_usage}; exit 0 ;;
|
||||
-V|--version) print -Pr "${_version}"; exit 0 ;;
|
||||
--debug) verbosity=3; shift ;;
|
||||
*) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
|
||||
}
|
||||
}
|
||||
|
||||
: "${target:="${host_os}-${CPUTYPE}"}"
|
||||
|
||||
set -- ${(@)args}
|
||||
set_loglevel ${verbosity}
|
||||
|
||||
if (( ! skip_deps )) {
|
||||
check_${host_os}
|
||||
}
|
||||
|
||||
local product_name
|
||||
read -r product_name <<< \
|
||||
"$(jq -r '.name' ${buildspec_file})"
|
||||
|
||||
local commit_version='0.0.0'
|
||||
local commit_distance='0'
|
||||
local commit_hash
|
||||
|
||||
if [[ -d ${project_root}/.git ]] {
|
||||
local git_description="$(git describe --tags --long)"
|
||||
commit_version="${${git_description%-*}%-*}"
|
||||
commit_hash="${git_description##*-g}"
|
||||
commit_distance="${${git_description%-*}##*-}"
|
||||
}
|
||||
|
||||
|
||||
local output_name
|
||||
if (( commit_distance > 0 )) {
|
||||
output_name="obs-studio-${commit_version}-${commit_hash}"
|
||||
} else {
|
||||
output_name="obs-studio-${commit_version}"
|
||||
}
|
||||
|
||||
if [[ ${host_os} == macos ]] {
|
||||
autoload -Uz read_codesign read_codesign_pass log_warning log_group
|
||||
|
||||
if [[ ! -d build_macos/OBS.app ]] {
|
||||
log_error 'No application bundle found. Run the build script to create a valid application bundle.'
|
||||
return 0
|
||||
}
|
||||
|
||||
local -A arch_names=(x86_64 Intel arm64 Apple)
|
||||
output_name="${output_name}-macos-${(L)arch_names[${target##*-}]}"
|
||||
|
||||
local volume_name
|
||||
if (( commit_distance > 0 )) {
|
||||
volume_name="OBS Studio ${commit_version}-${commit_hash} (${arch_names[${target##*-}]})"
|
||||
} else {
|
||||
volume_name="OBS Studio ${commit_version} (${arch_names[${target##*-}]})"
|
||||
}
|
||||
|
||||
local _tarflags='cJf'
|
||||
if (( _loglevel > 1 || ${+CI} )) _tarflags="v${_tarflags}"
|
||||
|
||||
if (( package )) {
|
||||
pushd build_macos
|
||||
|
||||
mkdir -p obs-studio/.background
|
||||
cp ${project_root}/cmake/macos/resources/background.tiff obs-studio/.background/
|
||||
cp ${project_root}/cmake/macos/resources/AppIcon.icns obs-studio/.VolumeIcon.icns
|
||||
ln -s /Applications obs-studio/Applications
|
||||
|
||||
mkdir -p obs-studio/OBS.app
|
||||
ditto OBS.app obs-studio/OBS.app
|
||||
|
||||
local -i _status=0
|
||||
|
||||
autoload -Uz create_diskimage
|
||||
create_diskimage obs-studio ${volume_name} ${output_name} || _status=1
|
||||
|
||||
rm -r obs-studio
|
||||
if (( _status )) {
|
||||
log_error "Disk image creation failed."
|
||||
return 2
|
||||
}
|
||||
|
||||
if (( codesign )) { autoload -Uz read_codesign && read_codesign }
|
||||
|
||||
codesign --sign "${CODESIGN_IDENT:--}" ${output_name}.dmg
|
||||
|
||||
if (( codesign && notarize )) {
|
||||
autoload -Uz read_codesign_pass && read_codesign_pass
|
||||
|
||||
xcrun notarytool submit "${output_name}".dmg --keychain-profile "OBS-Codesign-Password" --wait
|
||||
|
||||
local -i _status=0
|
||||
|
||||
xcrun stapler staple ${output_name}.dmg || _status=1
|
||||
|
||||
if (( _status )) {
|
||||
log_error "Notarization failed. Use 'xcrun notarytool log <submission ID>' to check errors."
|
||||
return 2
|
||||
}
|
||||
}
|
||||
popd
|
||||
} else {
|
||||
log_group "Archiving obs-studio..."
|
||||
pushd build_macos
|
||||
XZ_OPT=-T0 tar "-${_tarflags}" ${output_name}.tar.xz OBS.app
|
||||
popd
|
||||
}
|
||||
|
||||
if [[ ${config} == Release ]] {
|
||||
log_group "Archiving debug symbols..."
|
||||
mkdir -p build_macos/dSYMs
|
||||
pushd build_macos/dSYMs
|
||||
rm -rf -- *.dSYM(N)
|
||||
cp -pR ${PWD:h}/**/*.dSYM .
|
||||
XZ_OPT=-T0 tar "-${_tarflags}" ${output_name}-dSYMs.tar.xz -- *
|
||||
mv ${output_name}-dSYMs.tar.xz ${PWD:h}
|
||||
popd
|
||||
}
|
||||
|
||||
log_group
|
||||
|
||||
} elif [[ ${host_os} == linux ]] {
|
||||
local -a cmake_args=()
|
||||
if (( _loglevel > 1 )) cmake_args+=(--verbose)
|
||||
|
||||
if (( package )) {
|
||||
log_group "Packaging obs-studio..."
|
||||
pushd ${project_root}
|
||||
cmake --build build_${target##*-} --config ${config} -t package ${cmake_args}
|
||||
output_name="${output_name}-${target##*-}-linux-gnu"
|
||||
|
||||
pushd ${project_root}/build_${target##*-}
|
||||
local -a files=(obs-studio-*-Linux*.(ddeb|deb))
|
||||
for file (${files}) {
|
||||
mv ${file} ${file//obs-studio-*-Linux/${output_name}}
|
||||
}
|
||||
popd
|
||||
popd
|
||||
} else {
|
||||
log_group "Archiving obs-studio..."
|
||||
output_name="${output_name}-${target##*-}-linux-gnu"
|
||||
|
||||
local _tarflags='cJf'
|
||||
if (( _loglevel > 1 || ${+CI} )) _tarflags="v${_tarflags}"
|
||||
|
||||
pushd ${project_root}/build_${target##*-}/install/${config}
|
||||
XZ_OPT=-T0 tar "-${_tarflags}" ${project_root}/build_${target##*-}/${output_name}.tar.xz (bin|lib|share)
|
||||
popd
|
||||
}
|
||||
log_group
|
||||
}
|
||||
}
|
||||
|
||||
package ${@}
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
./.build.zsh
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
./.build.zsh
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
./.package.zsh
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
./.package.zsh
|
||||
@@ -0,0 +1,131 @@
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
from json_source_map import calculate
|
||||
from json_source_map.errors import InvalidInputError
|
||||
from jsonschema import Draft7Validator
|
||||
|
||||
|
||||
def discover_schema_file(filename: str) -> tuple[str | None, Any]:
|
||||
logger = logging.getLogger()
|
||||
|
||||
with open(filename) as json_file:
|
||||
json_data = json.load(json_file)
|
||||
|
||||
schema_filename = json_data.get("$schema", None)
|
||||
|
||||
if not schema_filename:
|
||||
logger.info(f"ℹ️ ${filename} has no schema definition")
|
||||
return (None, None)
|
||||
|
||||
schema_file = os.path.join(os.path.dirname(filename), schema_filename)
|
||||
|
||||
with open(schema_file) as schema_file:
|
||||
schema_data = json.load(schema_file)
|
||||
|
||||
return (str(schema_file), schema_data)
|
||||
|
||||
|
||||
def validate_json_files(
|
||||
schema_data: dict[Any, Any], json_file_name: str
|
||||
) -> list[dict[str, str]]:
|
||||
logger = logging.getLogger()
|
||||
|
||||
with open(json_file_name) as json_file:
|
||||
text_data = json_file.read()
|
||||
|
||||
json_data = json.loads(text_data)
|
||||
source_map = calculate(text_data)
|
||||
|
||||
validator = Draft7Validator(schema_data)
|
||||
|
||||
violations = []
|
||||
for violation in sorted(validator.iter_errors(json_data), key=str):
|
||||
logger.info(
|
||||
f"⚠️ Schema violation in file '{json_file_name}':\n{violation}\n----\n"
|
||||
)
|
||||
|
||||
if len(violation.absolute_path):
|
||||
error_path = "/".join(
|
||||
str(path_element) for path_element in violation.absolute_path
|
||||
)
|
||||
error_entry = source_map["/{}".format(error_path)]
|
||||
|
||||
violation_data = {
|
||||
"file": json_file_name,
|
||||
"title": "Validation Error",
|
||||
"message": violation.message,
|
||||
"annotation_level": "failure",
|
||||
"start_line": error_entry.value_start.line + 1,
|
||||
"end_line": error_entry.value_end.line + 1,
|
||||
}
|
||||
|
||||
violations.append(violation_data)
|
||||
|
||||
return violations
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Validate JSON files by schema definition"
|
||||
)
|
||||
parser.add_argument(
|
||||
"json_files", metavar="FILE", type=str, nargs="+", help="JSON file to validate"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--loglevel", type=str, help="Set log level", default="WARNING", required=False
|
||||
)
|
||||
|
||||
arguments = parser.parse_args()
|
||||
|
||||
logging.basicConfig(level=arguments.loglevel, format="%(levelname)s - %(message)s")
|
||||
logger = logging.getLogger()
|
||||
|
||||
schema_mappings = {}
|
||||
|
||||
for json_file in arguments.json_files:
|
||||
try:
|
||||
(schema_file, schema_data) = discover_schema_file(json_file)
|
||||
except OSError as e:
|
||||
logger.error(f"❌ Failed to discover schema for file '{json_file}': {e}")
|
||||
return 2
|
||||
|
||||
if schema_file and schema_file not in schema_mappings.keys():
|
||||
schema_mappings.update(
|
||||
{schema_file: {"schema_data": schema_data, "files": set()}}
|
||||
)
|
||||
|
||||
schema_mappings[schema_file]["files"].add(json_file)
|
||||
|
||||
validation_errors = []
|
||||
for schema_entry in schema_mappings.values():
|
||||
for json_file in schema_entry["files"]:
|
||||
try:
|
||||
new_errors = validate_json_files(schema_entry["schema_data"], json_file)
|
||||
except (InvalidInputError, OSError) as e:
|
||||
logger.error(
|
||||
f"❌ Failed to create JSON source map for file '{json_file}': {e}"
|
||||
)
|
||||
return 2
|
||||
|
||||
[validation_errors.append(error) for error in new_errors]
|
||||
|
||||
if validation_errors:
|
||||
try:
|
||||
with open("validation_errors.json", "w") as results_file:
|
||||
json.dump(validation_errors, results_file)
|
||||
except OSError as e:
|
||||
logger.error(f"❌ Failed to write validation results file: {e}")
|
||||
return 2
|
||||
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -14,14 +14,14 @@ from collections import defaultdict
|
||||
|
||||
MINIMUM_PURGE_AGE = 9.75 * 24 * 60 * 60 # slightly less than 10 days
|
||||
TIMEOUT = 10
|
||||
SKIPPED_SERVICES = {'YouNow', 'SHOWROOM', 'Dacast'}
|
||||
SERVICES_FILE = 'plugins/rtmp-services/data/services.json'
|
||||
PACKAGE_FILE = 'plugins/rtmp-services/data/package.json'
|
||||
CACHE_FILE = 'other/timestamps.json'
|
||||
GITHUB_OUTPUT_FILE = os.environ.get('GITHUB_OUTPUT', None)
|
||||
SKIPPED_SERVICES = {"YouNow", "SHOWROOM", "Dacast"}
|
||||
SERVICES_FILE = "plugins/rtmp-services/data/services.json"
|
||||
PACKAGE_FILE = "plugins/rtmp-services/data/package.json"
|
||||
CACHE_FILE = "other/timestamps.json"
|
||||
GITHUB_OUTPUT_FILE = os.environ.get("GITHUB_OUTPUT", None)
|
||||
|
||||
DO_NOT_PING = {'jp9000'}
|
||||
PR_MESSAGE = '''This is an automatically created pull request to remove unresponsive servers and services.
|
||||
DO_NOT_PING = {"jp9000"}
|
||||
PR_MESSAGE = """This is an automatically created pull request to remove unresponsive servers and services.
|
||||
|
||||
| Service | Action Taken | Author(s) |
|
||||
| ------- | ------------ | --------- |
|
||||
@@ -29,10 +29,10 @@ PR_MESSAGE = '''This is an automatically created pull request to remove unrespon
|
||||
|
||||
If you are not responsible for an affected service and want to be excluded from future pings please let us know.
|
||||
|
||||
Created by workflow run: https://github.com/{repository}/actions/runs/{run_id}'''
|
||||
Created by workflow run: https://github.com/{repository}/actions/runs/{run_id}"""
|
||||
|
||||
# GQL is great isn't it
|
||||
GQL_QUERY = '''{
|
||||
GQL_QUERY = """{
|
||||
repositoryOwner(login: "obsproject") {
|
||||
repository(name: "obs-studio") {
|
||||
object(expression: "master") {
|
||||
@@ -54,7 +54,7 @@ GQL_QUERY = '''{
|
||||
}
|
||||
}
|
||||
}
|
||||
}'''
|
||||
}"""
|
||||
|
||||
context = ssl.create_default_context()
|
||||
|
||||
@@ -64,7 +64,7 @@ def check_ftl_server(hostname) -> bool:
|
||||
try:
|
||||
socket.getaddrinfo(hostname, 8084, proto=socket.IPPROTO_UDP)
|
||||
except socket.gaierror as e:
|
||||
print(f'⚠️ Could not resolve hostname for server: {hostname} (Exception: {e})')
|
||||
print(f"⚠️ Could not resolve hostname for server: {hostname} (Exception: {e})")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -75,9 +75,9 @@ def check_hls_server(uri) -> bool:
|
||||
try:
|
||||
r = requests.post(uri, timeout=TIMEOUT)
|
||||
if r.status_code >= 500 or r.status_code == 404:
|
||||
raise Exception(f'Server responded with {r.status_code}')
|
||||
raise Exception(f"Server responded with {r.status_code}")
|
||||
except Exception as e:
|
||||
print(f'⚠️ Could not connect to HLS server: {uri} (Exception: {e})')
|
||||
print(f"⚠️ Could not connect to HLS server: {uri} (Exception: {e})")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -86,21 +86,21 @@ def check_hls_server(uri) -> bool:
|
||||
def check_rtmp_server(uri) -> bool:
|
||||
"""Try connecting and sending a RTMP handshake (with SSL if necessary)"""
|
||||
parsed = urlparse(uri)
|
||||
hostname, port = parsed.netloc.partition(':')[::2]
|
||||
hostname, port = parsed.netloc.partition(":")[::2]
|
||||
|
||||
if port:
|
||||
port = int(port)
|
||||
elif parsed.scheme == 'rtmps':
|
||||
elif parsed.scheme == "rtmps":
|
||||
port = 443
|
||||
else:
|
||||
port = 1935
|
||||
|
||||
try:
|
||||
recv = b''
|
||||
recv = b""
|
||||
with socket.create_connection((hostname, port), timeout=TIMEOUT) as sock:
|
||||
# RTMP handshake is \x03 + 4 bytes time (can be 0) + 4 zero bytes + 1528 bytes random
|
||||
handshake = b'\x03\x00\x00\x00\x00\x00\x00\x00\x00' + randbytes(1528)
|
||||
if parsed.scheme == 'rtmps':
|
||||
handshake = b"\x03\x00\x00\x00\x00\x00\x00\x00\x00" + randbytes(1528)
|
||||
if parsed.scheme == "rtmps":
|
||||
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
|
||||
ssock.sendall(handshake)
|
||||
while True:
|
||||
@@ -117,9 +117,9 @@ def check_rtmp_server(uri) -> bool:
|
||||
break
|
||||
|
||||
if len(recv) < 1536 or recv[0] != 3:
|
||||
raise ValueError('Invalid RTMP handshake received from server')
|
||||
raise ValueError("Invalid RTMP handshake received from server")
|
||||
except Exception as e:
|
||||
print(f'⚠️ Connection to server failed: {uri} (Exception: {e})')
|
||||
print(f"⚠️ Connection to server failed: {uri} (Exception: {e})")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -127,34 +127,40 @@ def check_rtmp_server(uri) -> bool:
|
||||
|
||||
def get_last_artifact():
|
||||
s = requests.session()
|
||||
s.headers['Authorization'] = f'Bearer {os.environ["GITHUB_TOKEN"]}'
|
||||
s.headers["Authorization"] = f'Bearer {os.environ["GITHUB_TOKEN"]}'
|
||||
|
||||
run_id = os.environ['WORKFLOW_RUN_ID']
|
||||
repo = os.environ['REPOSITORY']
|
||||
run_id = os.environ["WORKFLOW_RUN_ID"]
|
||||
repo = os.environ["REPOSITORY"]
|
||||
|
||||
# fetch run first, get workflow id from there to get workflow runs
|
||||
r = s.get(f'https://api.github.com/repos/{repo}/actions/runs/{run_id}')
|
||||
r = s.get(f"https://api.github.com/repos/{repo}/actions/runs/{run_id}")
|
||||
r.raise_for_status()
|
||||
workflow_id = r.json()['workflow_id']
|
||||
workflow_id = r.json()["workflow_id"]
|
||||
|
||||
r = s.get(
|
||||
f'https://api.github.com/repos/{repo}/actions/workflows/{workflow_id}/runs',
|
||||
params=dict(per_page=1, status='completed', branch='master', conclusion='success', event='schedule'),
|
||||
f"https://api.github.com/repos/{repo}/actions/workflows/{workflow_id}/runs",
|
||||
params=dict(
|
||||
per_page=1,
|
||||
status="completed",
|
||||
branch="master",
|
||||
conclusion="success",
|
||||
event="schedule",
|
||||
),
|
||||
)
|
||||
r.raise_for_status()
|
||||
runs = r.json()
|
||||
if not runs['workflow_runs']:
|
||||
raise ValueError('No completed workflow runs found')
|
||||
if not runs["workflow_runs"]:
|
||||
raise ValueError("No completed workflow runs found")
|
||||
|
||||
r = s.get(runs['workflow_runs'][0]['artifacts_url'])
|
||||
r = s.get(runs["workflow_runs"][0]["artifacts_url"])
|
||||
r.raise_for_status()
|
||||
|
||||
for artifact in r.json()['artifacts']:
|
||||
if artifact['name'] == 'timestamps':
|
||||
artifact_url = artifact['archive_download_url']
|
||||
for artifact in r.json()["artifacts"]:
|
||||
if artifact["name"] == "timestamps":
|
||||
artifact_url = artifact["archive_download_url"]
|
||||
break
|
||||
else:
|
||||
raise ValueError('No previous artifact found.')
|
||||
raise ValueError("No previous artifact found.")
|
||||
|
||||
r = s.get(artifact_url)
|
||||
r.raise_for_status()
|
||||
@@ -163,7 +169,7 @@ def get_last_artifact():
|
||||
|
||||
with zipfile.ZipFile(zip_data) as zip_ref:
|
||||
for info in zip_ref.infolist():
|
||||
if info.filename == 'timestamps.json':
|
||||
if info.filename == "timestamps.json":
|
||||
return json.loads(zip_ref.read(info.filename))
|
||||
|
||||
|
||||
@@ -173,18 +179,22 @@ def find_people_to_blame(raw_services: str, servers: list[tuple[str, str]]) -> d
|
||||
|
||||
# Fetch Blame data from github
|
||||
s = requests.session()
|
||||
s.headers['Authorization'] = f'Bearer {os.environ["GITHUB_TOKEN"]}'
|
||||
s.headers["Authorization"] = f'Bearer {os.environ["GITHUB_TOKEN"]}'
|
||||
|
||||
r = s.post('https://api.github.com/graphql', json=dict(query=GQL_QUERY, variables=dict()))
|
||||
r = s.post(
|
||||
"https://api.github.com/graphql", json=dict(query=GQL_QUERY, variables=dict())
|
||||
)
|
||||
r.raise_for_status()
|
||||
j = r.json()
|
||||
|
||||
# The file is only ~2600 lines so this isn't too crazy and makes the lookup very easy
|
||||
line_author = dict()
|
||||
for blame in j['data']['repositoryOwner']['repository']['object']['blame']['ranges']:
|
||||
for i in range(blame['startingLine'] - 1, blame['endingLine']):
|
||||
if user := blame['commit']['author']['user']:
|
||||
line_author[i] = user['login']
|
||||
for blame in j["data"]["repositoryOwner"]["repository"]["object"]["blame"][
|
||||
"ranges"
|
||||
]:
|
||||
for i in range(blame["startingLine"] - 1, blame["endingLine"]):
|
||||
if user := blame["commit"]["author"]["user"]:
|
||||
line_author[i] = user["login"]
|
||||
|
||||
service_authors = defaultdict(set)
|
||||
for i, line in enumerate(raw_services.splitlines()):
|
||||
@@ -203,40 +213,42 @@ def set_output(name, value):
|
||||
return
|
||||
|
||||
try:
|
||||
with open(GITHUB_OUTPUT_FILE, 'a', encoding='utf-8', newline='\n') as f:
|
||||
f.write(f'{name}={value}\n')
|
||||
with open(GITHUB_OUTPUT_FILE, "a", encoding="utf-8", newline="\n") as f:
|
||||
f.write(f"{name}={value}\n")
|
||||
except Exception as e:
|
||||
print(f'Writing to github output files failed: {e!r}')
|
||||
print(f"Writing to github output files failed: {e!r}")
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
with open(SERVICES_FILE, encoding='utf-8') as services_file:
|
||||
with open(SERVICES_FILE, encoding="utf-8") as services_file:
|
||||
raw_services = services_file.read()
|
||||
services = json.loads(raw_services)
|
||||
with open(PACKAGE_FILE, encoding='utf-8') as package_file:
|
||||
with open(PACKAGE_FILE, encoding="utf-8") as package_file:
|
||||
package = json.load(package_file)
|
||||
except OSError as e:
|
||||
print(f'❌ Could not open services/package file: {e}')
|
||||
print(f"❌ Could not open services/package file: {e}")
|
||||
return 1
|
||||
|
||||
# attempt to load last check result cache
|
||||
try:
|
||||
with open(CACHE_FILE, encoding='utf-8') as check_file:
|
||||
with open(CACHE_FILE, encoding="utf-8") as check_file:
|
||||
fail_timestamps = json.load(check_file)
|
||||
except OSError as e:
|
||||
# cache might be evicted or not exist yet, so this is non-fatal
|
||||
print(f'⚠️ Could not read cache file, trying to get last artifact (Exception: {e})')
|
||||
print(
|
||||
f"⚠️ Could not read cache file, trying to get last artifact (Exception: {e})"
|
||||
)
|
||||
|
||||
try:
|
||||
fail_timestamps = get_last_artifact()
|
||||
except Exception as e:
|
||||
print(f'⚠️ Could not fetch cache file, starting fresh. (Exception: {e})')
|
||||
print(f"⚠️ Could not fetch cache file, starting fresh. (Exception: {e})")
|
||||
fail_timestamps = dict()
|
||||
else:
|
||||
print('Fetched cache file from last run artifact.')
|
||||
print("Fetched cache file from last run artifact.")
|
||||
else:
|
||||
print('Successfully loaded cache file:', CACHE_FILE)
|
||||
print("Successfully loaded cache file:", CACHE_FILE)
|
||||
|
||||
start_time = int(time.time())
|
||||
affected_services = dict()
|
||||
@@ -244,120 +256,126 @@ def main():
|
||||
|
||||
# create temporary new list
|
||||
new_services = services.copy()
|
||||
new_services['services'] = []
|
||||
new_services["services"] = []
|
||||
|
||||
for service in services['services']:
|
||||
for service in services["services"]:
|
||||
# skip services that do custom stuff that we can't easily check
|
||||
if service['name'] in SKIPPED_SERVICES:
|
||||
new_services['services'].append(service)
|
||||
if service["name"] in SKIPPED_SERVICES:
|
||||
new_services["services"].append(service)
|
||||
continue
|
||||
|
||||
service_type = service.get('recommended', {}).get('output', 'rtmp_output')
|
||||
if service_type not in {'rtmp_output', 'ffmpeg_hls_muxer', 'ftl_output'}:
|
||||
print('Unknown service type:', service_type)
|
||||
new_services['services'].append(service)
|
||||
service_type = service.get("recommended", {}).get("output", "rtmp_output")
|
||||
if service_type not in {"rtmp_output", "ffmpeg_hls_muxer", "ftl_output"}:
|
||||
print("Unknown service type:", service_type)
|
||||
new_services["services"].append(service)
|
||||
continue
|
||||
|
||||
# create a copy to mess with
|
||||
new_service = service.copy()
|
||||
new_service['servers'] = []
|
||||
new_service["servers"] = []
|
||||
|
||||
# run checks for all the servers, and store results in timestamp cache
|
||||
for server in service['servers']:
|
||||
if service_type == 'ftl_output':
|
||||
is_ok = check_ftl_server(server['url'])
|
||||
elif service_type == 'ffmpeg_hls_muxer':
|
||||
is_ok = check_hls_server(server['url'])
|
||||
for server in service["servers"]:
|
||||
if service_type == "ftl_output":
|
||||
is_ok = check_ftl_server(server["url"])
|
||||
elif service_type == "ffmpeg_hls_muxer":
|
||||
is_ok = check_hls_server(server["url"])
|
||||
else: # rtmp
|
||||
is_ok = check_rtmp_server(server['url'])
|
||||
is_ok = check_rtmp_server(server["url"])
|
||||
|
||||
if not is_ok:
|
||||
if ts := fail_timestamps.get(server['url'], None):
|
||||
if ts := fail_timestamps.get(server["url"], None):
|
||||
if (delta := start_time - ts) >= MINIMUM_PURGE_AGE:
|
||||
print(
|
||||
f'🗑️ Purging server "{server["url"]}", it has been '
|
||||
f'unresponsive for {round(delta/60/60/24)} days.'
|
||||
f"unresponsive for {round(delta/60/60/24)} days."
|
||||
)
|
||||
removed_servers.append((server['url'], service['name']))
|
||||
removed_servers.append((server["url"], service["name"]))
|
||||
# continuing here means not adding it to the new list, thus dropping it
|
||||
continue
|
||||
else:
|
||||
fail_timestamps[server['url']] = start_time
|
||||
elif is_ok and server['url'] in fail_timestamps:
|
||||
fail_timestamps[server["url"]] = start_time
|
||||
elif is_ok and server["url"] in fail_timestamps:
|
||||
# remove timestamp of failed check if server is back
|
||||
delta = start_time - fail_timestamps[server['url']]
|
||||
print(f'💡 Server "{server["url"]}" is back after {round(delta/60/60/24)} days!')
|
||||
del fail_timestamps[server['url']]
|
||||
delta = start_time - fail_timestamps[server["url"]]
|
||||
print(
|
||||
f'💡 Server "{server["url"]}" is back after {round(delta/60/60/24)} days!'
|
||||
)
|
||||
del fail_timestamps[server["url"]]
|
||||
|
||||
new_service['servers'].append(server)
|
||||
new_service["servers"].append(server)
|
||||
|
||||
if (diff := len(service['servers']) - len(new_service['servers'])) > 0:
|
||||
if (diff := len(service["servers"]) - len(new_service["servers"])) > 0:
|
||||
print(f'ℹ️ Removed {diff} server(s) from {service["name"]}')
|
||||
affected_services[service['name']] = f'{diff} servers removed'
|
||||
affected_services[service["name"]] = f"{diff} servers removed"
|
||||
|
||||
# remove services with no valid servers
|
||||
if not new_service['servers']:
|
||||
if not new_service["servers"]:
|
||||
print(f'💀 Service "{service["name"]}" has no valid servers left, removing!')
|
||||
affected_services[service['name']] = f'Service removed'
|
||||
affected_services[service["name"]] = f"Service removed"
|
||||
continue
|
||||
|
||||
new_services['services'].append(new_service)
|
||||
new_services["services"].append(new_service)
|
||||
|
||||
# write cache file
|
||||
try:
|
||||
os.makedirs('other', exist_ok=True)
|
||||
with open(CACHE_FILE, 'w', encoding='utf-8') as cache_file:
|
||||
os.makedirs("other", exist_ok=True)
|
||||
with open(CACHE_FILE, "w", encoding="utf-8") as cache_file:
|
||||
json.dump(fail_timestamps, cache_file)
|
||||
except OSError as e:
|
||||
print(f'❌ Could not write cache file: {e}')
|
||||
print(f"❌ Could not write cache file: {e}")
|
||||
return 1
|
||||
else:
|
||||
print('Successfully wrote cache file:', CACHE_FILE)
|
||||
print("Successfully wrote cache file:", CACHE_FILE)
|
||||
|
||||
if removed_servers:
|
||||
# increment package version and save that as well
|
||||
package['version'] += 1
|
||||
package['files'][0]['version'] += 1
|
||||
package["version"] += 1
|
||||
package["files"][0]["version"] += 1
|
||||
|
||||
try:
|
||||
with open(SERVICES_FILE, 'w', encoding='utf-8') as services_file:
|
||||
with open(SERVICES_FILE, "w", encoding="utf-8") as services_file:
|
||||
json.dump(new_services, services_file, indent=4, ensure_ascii=False)
|
||||
services_file.write('\n')
|
||||
services_file.write("\n")
|
||||
|
||||
with open(PACKAGE_FILE, 'w', encoding='utf-8') as package_file:
|
||||
with open(PACKAGE_FILE, "w", encoding="utf-8") as package_file:
|
||||
json.dump(package, package_file, indent=4)
|
||||
package_file.write('\n')
|
||||
package_file.write("\n")
|
||||
except OSError as e:
|
||||
print(f'❌ Could not write services/package file: {e}')
|
||||
print(f"❌ Could not write services/package file: {e}")
|
||||
return 1
|
||||
else:
|
||||
print(f'Successfully wrote services/package files:\n- {SERVICES_FILE}\n- {PACKAGE_FILE}')
|
||||
print(
|
||||
f"Successfully wrote services/package files:\n- {SERVICES_FILE}\n- {PACKAGE_FILE}"
|
||||
)
|
||||
|
||||
# try to find authors to ping, this is optional and is allowed to fail
|
||||
try:
|
||||
service_authors = find_people_to_blame(raw_services, removed_servers)
|
||||
except Exception as e:
|
||||
print(f'⚠ Could not fetch blame for some reason: {e}')
|
||||
print(f"⚠ Could not fetch blame for some reason: {e}")
|
||||
service_authors = dict()
|
||||
|
||||
# set GitHub outputs
|
||||
set_output('make_pr', 'true')
|
||||
set_output("make_pr", "true")
|
||||
msg = PR_MESSAGE.format(
|
||||
repository=os.environ['REPOSITORY'],
|
||||
run_id=os.environ['WORKFLOW_RUN_ID'],
|
||||
table='\n'.join(
|
||||
'| {name} | {action} | {authors} |'.format(
|
||||
name=name.replace('|', '\\|'),
|
||||
repository=os.environ["REPOSITORY"],
|
||||
run_id=os.environ["WORKFLOW_RUN_ID"],
|
||||
table="\n".join(
|
||||
"| {name} | {action} | {authors} |".format(
|
||||
name=name.replace("|", "\\|"),
|
||||
action=action,
|
||||
authors=', '.join(f'@{author}' for author in sorted(service_authors.get(name, []))),
|
||||
authors=", ".join(
|
||||
f"@{author}" for author in sorted(service_authors.get(name, []))
|
||||
),
|
||||
)
|
||||
for name, action in sorted(affected_services.items())
|
||||
),
|
||||
)
|
||||
set_output('pr_message', json.dumps(msg))
|
||||
set_output("pr_message", json.dumps(msg))
|
||||
else:
|
||||
set_output('make_pr', 'false')
|
||||
set_output("make_pr", "false")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,61 @@
|
||||
autoload -Uz log_info log_status log_error log_debug log_warning log_group
|
||||
|
||||
log_group 'Check Linux build requirements'
|
||||
log_debug 'Checking Linux distribution name and version...'
|
||||
|
||||
# Check for Ubuntu version 22.10 or later, which have srt and librist available via apt-get
|
||||
typeset -g -i UBUNTU_2210_OR_LATER=0
|
||||
if [[ -f /etc/os_release ]] {
|
||||
local dist_name
|
||||
local dist_version
|
||||
read -r dist_name dist_version <<< "$(source /etc/os_release; print "${NAME} ${VERSION_ID}")"
|
||||
|
||||
autoload -Uz is-at-least
|
||||
if [[ ${dist_name} == Ubuntu ]] && is-at-least 22.10 ${dist_version}; then
|
||||
typeset -g -i UBUNTU_2210_OR_LATER=1
|
||||
fi
|
||||
}
|
||||
|
||||
log_debug 'Checking for apt-get...'
|
||||
if (( ! ${+commands[apt-get]} )) {
|
||||
log_error 'No apt-get command found. Please install apt'
|
||||
return 2
|
||||
} else {
|
||||
log_debug "Apt-get located at ${commands[apt-get]}"
|
||||
}
|
||||
|
||||
local -a dependencies=("${(fA)$(<${SCRIPT_HOME}/.Aptfile)}")
|
||||
local -a install_list
|
||||
local binary
|
||||
|
||||
sudo apt-get update -qq
|
||||
|
||||
for dependency (${dependencies}) {
|
||||
local -a tokens=(${=dependency//(,|:|\')/})
|
||||
|
||||
if [[ ! ${tokens[1]} == package ]] continue
|
||||
|
||||
if [[ ${#tokens} -gt 2 && ${tokens[3]} == bin ]] {
|
||||
binary=${tokens[4]}
|
||||
} else {
|
||||
binary=${tokens[2]}
|
||||
}
|
||||
|
||||
if (( ! ${+commands[${binary}]} )) install_list+=(${tokens[2]})
|
||||
}
|
||||
|
||||
log_debug "List of dependencies to install: ${install_list}"
|
||||
if (( #install_list )) {
|
||||
if (( ! ${+CI} )) log_warning 'Dependency installation via apt may require elevated privileges'
|
||||
|
||||
local -a apt_args=(
|
||||
${CI:+-y}
|
||||
--no-install-recommends
|
||||
)
|
||||
if (( _loglevel == 0 )) apt_args+=(--quiet)
|
||||
|
||||
sudo apt-get ${apt_args} install ${install_list}
|
||||
}
|
||||
|
||||
rehash
|
||||
log_group
|
||||
@@ -0,0 +1,22 @@
|
||||
autoload -Uz is-at-least log_group log_info log_error log_status
|
||||
|
||||
local macos_version=$(sw_vers -productVersion)
|
||||
|
||||
log_group 'Install macOS build requirements'
|
||||
log_info 'Checking macOS version...'
|
||||
if ! is-at-least 11.0 ${macos_version}; then
|
||||
log_error "Minimum required macOS version is 11.0, but running on macOS ${macos_version}"
|
||||
return 2
|
||||
else
|
||||
log_status "macOS ${macos_version} is recent"
|
||||
fi
|
||||
|
||||
log_info 'Checking for Homebrew...'
|
||||
if (( ! ${+commands[brew]} )) {
|
||||
log_error 'No Homebrew command found. Please install Homebrew (https://brew.sh)'
|
||||
return 2
|
||||
}
|
||||
|
||||
brew bundle --file ${SCRIPT_HOME}/.Brewfile
|
||||
rehash
|
||||
log_group
|
||||
@@ -0,0 +1,62 @@
|
||||
autoload -Uz log_debug log_error log_info log_status log_group log_output
|
||||
|
||||
local -r _usage="Usage: %B${0}%b <source> <volume name> <output_name>
|
||||
|
||||
Create macOS disk image <volume name> <output_name> with contents of <source>"
|
||||
|
||||
if (( ! # )) {
|
||||
log_error 'Called without arguments.'
|
||||
log_output ${_usage}
|
||||
return 2
|
||||
}
|
||||
|
||||
local source=${1}
|
||||
local volume_name=${2}
|
||||
local output_name=${3}
|
||||
|
||||
log_group "Create macOS disk image"
|
||||
|
||||
local _hdiutil_flags
|
||||
if (( _loglevel < 1 )) _hdiutil_flags='-quiet'
|
||||
|
||||
trap "hdiutil detach ${_hdiutil_flags} /Volumes/${output_name}; rm temp.dmg; log_group return 2" ERR
|
||||
|
||||
hdiutil create ${_hdiutil_flags} \
|
||||
-volname "${volume_name}" \
|
||||
-srcfolder ${source} \
|
||||
-ov \
|
||||
-fs APFS \
|
||||
-format UDRW \
|
||||
temp.dmg
|
||||
hdiutil attach ${_hdiutil_flags} \
|
||||
-noverify \
|
||||
-readwrite \
|
||||
-mountpoint /Volumes/${output_name} \
|
||||
temp.dmg
|
||||
|
||||
log_info "Waiting 2 seconds to ensure mounted volume is available..."
|
||||
sleep 2
|
||||
log_status "Done"
|
||||
log_info "Setting up disk volume..."
|
||||
log_status "Volume icon"
|
||||
SetFile -c icnC /Volumes/${output_name}/.VolumeIcon.icns
|
||||
log_status "Icon positions"
|
||||
osascript package.applescript ${output_name}
|
||||
log_status "File permissions"
|
||||
chmod -Rf go-w /Volumes/${output_name}
|
||||
SetFile -a C /Volumes/${output_name}
|
||||
rm -rf -- /Volumes/${output_name}/.fseventsd(N)
|
||||
log_info "Converting disk image..."
|
||||
hdiutil detach ${_hdiutil_flags} /Volumes/${output_name}
|
||||
hdiutil convert ${_hdiutil_flags} \
|
||||
-format ULMO \
|
||||
-ov \
|
||||
-o ${output_name}.dmg temp.dmg
|
||||
|
||||
rm temp.dmg
|
||||
|
||||
trap '' ERR
|
||||
|
||||
log_group
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,3 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 2 )) print -PR -e "${CI:+::debug::}%F{220}DEBUG: ${@}%f"
|
||||
@@ -0,0 +1,3 @@
|
||||
local icon=' ✖︎ '
|
||||
|
||||
print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}"
|
||||
@@ -0,0 +1,16 @@
|
||||
autoload -Uz log_info
|
||||
|
||||
if (( ! ${+_log_group} )) typeset -g _log_group=0
|
||||
|
||||
if (( ${+CI} )) {
|
||||
if (( _log_group )) {
|
||||
print "::endgroup::"
|
||||
typeset -g _log_group=0
|
||||
}
|
||||
if (( # )) {
|
||||
print "::group::${@}"
|
||||
typeset -g _log_group=1
|
||||
}
|
||||
} else {
|
||||
if (( # )) log_info ${@}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=' =>'
|
||||
|
||||
print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=''
|
||||
|
||||
print -PR " ${(r:5:)icon} ${@}"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=' >'
|
||||
|
||||
print -PR "%F{2} ${(r:5:)icon}%f ${@}"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=' =>'
|
||||
|
||||
print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
[[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1}
|
||||
@@ -0,0 +1,9 @@
|
||||
autoload -Uz log_info
|
||||
|
||||
if (( ! ${+CODESIGN_IDENT} )) {
|
||||
typeset -g CODESIGN_IDENT
|
||||
log_info 'Setting up Apple Developer ID for application codesigning...'
|
||||
read CODESIGN_IDENT'?Apple Developer Application ID: '
|
||||
}
|
||||
|
||||
typeset -g CODESIGN_TEAM=$(print "${CODESIGN_IDENT}" | /usr/bin/sed -En 's/.+\((.+)\)/\1/p')
|
||||
@@ -0,0 +1,24 @@
|
||||
autoload -Uz read_codesign read_codesign_user log_info log_warning
|
||||
|
||||
if (( ! ${+CODESIGN_IDENT} )) {
|
||||
read_codesign
|
||||
}
|
||||
|
||||
if (( ! ${+CODESIGN_IDENT_USER} )) {
|
||||
read_codesign_user
|
||||
}
|
||||
|
||||
log_info 'Setting up password for notarization keychain...'
|
||||
if (( ! ${+CODESIGN_IDENT_PASS} )) {
|
||||
read -s CODESIGN_IDENT_PASS'?Apple Developer ID password: '
|
||||
}
|
||||
|
||||
print ''
|
||||
log_info 'Setting up notarization keychain...'
|
||||
log_warning "
|
||||
+ Your Apple ID and an app-specific password is necessary for notarization from CLI
|
||||
+ This password will be stored in your macOS keychain under the identifier
|
||||
'OBS-Codesign-Password' with access Apple's 'altool' only.
|
||||
|
||||
"
|
||||
xcrun notarytool store-credentials 'OBS-Codesign-Password' --apple-id "${CODESIGN_IDENT_USER}" --team-id "${CODESIGN_TEAM}" --password "${CODESIGN_IDENT_PASS}"
|
||||
@@ -0,0 +1,7 @@
|
||||
autoload -Uz log_info
|
||||
|
||||
if (( ! ${+CODESIGN_TEAM} )) {
|
||||
typeset -g CODESIGN_TEAM
|
||||
log_info 'Setting up Apple Developer Team ID for codesigning...'
|
||||
read CODESIGN_TEAM'?Apple Developer Team ID (leave empty to use Apple Developer ID instead): '
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
autoload -Uz log_info
|
||||
|
||||
if (( ! ${+CODESIGN_IDENT_USER} )) {
|
||||
typeset -g CODESIGN_IDENT_USER
|
||||
log_info 'Setting up Apple ID for notarization...'
|
||||
read CODESIGN_IDENT_USER'?Apple ID: '
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
if (( _loglevel > 1 )) {
|
||||
xcodebuild ${@}
|
||||
} else {
|
||||
local -a xcbeautify_opts=()
|
||||
if (( _loglevel == 0 )) xcbeautify_opts+=(--quiet)
|
||||
xcodebuild ${@} 2>&1 | xcbeautify ${xcbeautify_opts}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
autoload -Uz log_debug log_error log_output
|
||||
|
||||
local -r _usage="Usage: %B${0}%b <loglevel>
|
||||
|
||||
Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)"
|
||||
|
||||
if (( ! # )); then
|
||||
log_error 'Called without arguments.'
|
||||
log_output ${_usage}
|
||||
return 2
|
||||
elif (( ${1} >= 4 )); then
|
||||
log_error 'Called with loglevel > 3.'
|
||||
log_output ${_usage}
|
||||
fi
|
||||
|
||||
typeset -g -i -r _loglevel=${1}
|
||||
log_debug "Log level set to '${1}'"
|
||||
@@ -0,0 +1,42 @@
|
||||
autoload -Uz log_debug log_warning log_error
|
||||
|
||||
if (( ! ${+project_root} )) {
|
||||
log_error "'project_root' not set. Please set before running ${0}."
|
||||
return 2
|
||||
}
|
||||
|
||||
if (( ${+commands[ccache]} )) {
|
||||
log_debug "Found ccache at ${commands[ccache]}"
|
||||
|
||||
typeset -gx CCACHE_CONFIGPATH="${project_root}/.ccache.conf"
|
||||
|
||||
ccache --set-config=run_second_cpp=true
|
||||
ccache --set-config=direct_mode=true
|
||||
ccache --set-config=inode_cache=true
|
||||
ccache --set-config=compiler_check=content
|
||||
ccache --set-config=file_clone=true
|
||||
|
||||
local -a sloppiness=(
|
||||
include_file_mtime
|
||||
include_file_ctime
|
||||
file_stat_matches
|
||||
system_headers
|
||||
)
|
||||
|
||||
if [[ ${host_os} == macos ]] {
|
||||
sloppiness+=(
|
||||
modules
|
||||
clang_index_store
|
||||
)
|
||||
|
||||
ccache --set-config=sloppiness=${(j:,:)sloppiness}
|
||||
}
|
||||
|
||||
if (( ${+CI} )) {
|
||||
ccache --set-config=cache_dir="${GITHUB_WORKSPACE:-${HOME}}/.ccache"
|
||||
ccache --set-config=max_size="${CCACHE_SIZE:-1G}"
|
||||
ccache -z > /dev/null
|
||||
}
|
||||
} else {
|
||||
log_warning "No ccache found on the system"
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
autoload -Uz log_group log_error log_status log_info log_debug
|
||||
|
||||
if (( ! ${+commands[curl]} )) {
|
||||
log_error 'curl not found. Please install curl.'
|
||||
return 2
|
||||
}
|
||||
|
||||
if (( ! ${+commands[jq]} )) {
|
||||
log_error 'jq not found. Please install jq.'
|
||||
return 2
|
||||
}
|
||||
|
||||
if (( ! ${+project_root} )) {
|
||||
log_error "'project_root' not set. Please set before running ${0}."
|
||||
return 2
|
||||
}
|
||||
|
||||
if (( ! ${+target} )) {
|
||||
log_error "'target' not set. Please set before running ${0}."
|
||||
return 2
|
||||
}
|
||||
|
||||
local -a curl_opts=()
|
||||
if (( ! ${+CI} )) {
|
||||
curl_opts+=(--progress-bar --continue-at -)
|
||||
} else {
|
||||
curl_opts+=(--show-error --silent)
|
||||
}
|
||||
curl_opts+=(--location -O ${@})
|
||||
|
||||
pushd ${project_root}
|
||||
|
||||
typeset -g QT_VERSION
|
||||
read -r QT_VERSION <<< \
|
||||
"$(jq -r --arg target "${target}" \
|
||||
'.platformConfig[$target] | { qtVersion } | join(" ")' \
|
||||
${buildspec_file})"
|
||||
|
||||
if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) {
|
||||
log_group 'Installing obs-studio build dependencies...'
|
||||
|
||||
mkdir -p ${project_root}/.deps
|
||||
local deps_version
|
||||
local deps_baseurl
|
||||
local deps_label
|
||||
local deps_hash
|
||||
|
||||
IFS=';' read -r deps_version deps_baseurl deps_label deps_hash <<< \
|
||||
"$(jq -r --arg target "${target}" \
|
||||
'.dependencies["cef"] | {version, baseUrl, "label", "hash": .hashes[$target]} | join(";")' \
|
||||
${buildspec_file})"
|
||||
|
||||
if (( ! deps_version )) {
|
||||
log_error 'No valid cef spec found in buildspec.json.'
|
||||
return 2
|
||||
}
|
||||
log_group 'Setting up pre-built Chromium Embedded Framework...'
|
||||
|
||||
pushd ${project_root}/.deps
|
||||
local _filename="cef_binary_${deps_version}_${target//-/_}.tar.xz"
|
||||
local _url=${deps_baseurl}/${_filename}
|
||||
local _target="cef_binary_${deps_version}_${target//-/_}"
|
||||
typeset -g CEF_VERSION=${deps_version}
|
||||
|
||||
log_status 'Checking for available wrapper library...'
|
||||
local -i _skip=0
|
||||
if [[ -f ${_target}/build/libcef_dll_wrapper/libcef_dll_wrapper.a ]] {
|
||||
_skip=1
|
||||
}
|
||||
|
||||
if ! (( _skip )) {
|
||||
if [[ ! -f ${_filename} ]] {
|
||||
log_debug "Running curl ${curl_opts} ${_url}"
|
||||
curl ${curl_opts} ${_url} && \
|
||||
log_status "Downloaded ${deps_label} for ${target}."
|
||||
} else {
|
||||
log_status "Found downloaded ${deps_label}"
|
||||
}
|
||||
|
||||
read -r artifact_checksum _ <<< "$(sha256sum ${_filename})"
|
||||
if [[ ${deps_hash} != ${artifact_checksum} ]] {
|
||||
log_error "Checksum of downloaded ${deps_label} does not match specification.
|
||||
Expected : ${deps_hash}
|
||||
Actual : ${artifact_checksum}"
|
||||
return 2
|
||||
}
|
||||
log_status "Checksum of downloaded ${deps_label} matches."
|
||||
mkdir -p ${_target} && pushd ${_target}
|
||||
|
||||
XZ_OPT=-T0 tar --strip-components 1 -xJf ../${_filename} && log_status "${deps_label} extracted."
|
||||
|
||||
if [[ ! -f build/libcef_dll_wrapper/libcef_dll_wrapper.a ]] {
|
||||
log_group "Configuring CEF wrapper library..."
|
||||
|
||||
local -a cmake_args=(
|
||||
-DPROJECT_ARCH:STRING=${target##*-}
|
||||
-DCEF_COMPILER_FLAGS:STRING="-Wno-deprecated-copy"
|
||||
-DCMAKE_BUILD_TYPE:STRING=${config}
|
||||
-DCMAKE_CXX_FLAGS:STRING="-std=c++11 -Wno-deprecated-declarations -Wno-unknonw-warning-option"
|
||||
-DCMAKE_EXE_LINKER_FLAGS:STRING="-std=c++11"
|
||||
)
|
||||
if (( _loglevel == 0 )) cmake_args+=(-Wno-deprecated -Wno-dev --log-level=ERROR)
|
||||
if (( ${+commands[ccache]} )) {
|
||||
cmake_args+=(
|
||||
-DCMAKE_C_COMPILER_LAUNCHER:STRING=ccache
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER:STRING=ccache
|
||||
)
|
||||
}
|
||||
|
||||
cmake -S . -B build -G Ninja ${cmake_args}
|
||||
|
||||
log_group "Building CEF Wrapper library..."
|
||||
cmake --build build
|
||||
}
|
||||
|
||||
mkdir -p build/libcef_dll
|
||||
popd
|
||||
} else {
|
||||
log_info 'Found existing Chromium Embedded Framework and loader library...'
|
||||
}
|
||||
popd
|
||||
|
||||
local -a apt_args=(
|
||||
${CI:+-y}
|
||||
--no-install-recommends
|
||||
)
|
||||
if (( _loglevel == 0 )) apt_args+=(--quiet)
|
||||
|
||||
local suffix
|
||||
if [[ ${CPUTYPE} != ${target##*-} ]] {
|
||||
local -A arch_mappings=(
|
||||
aarch64 arm64
|
||||
x86_64 amd64
|
||||
)
|
||||
|
||||
suffix=":${arch_mappings[${target##*-}]}"
|
||||
sudo apt-get install ${apt_args} gcc-${${target##*-}//_/-}-linux-gnu g++-${${target##*-}//_/-}-linux-gnu
|
||||
}
|
||||
|
||||
sudo apt-get install ${apt_args} \
|
||||
build-essential \
|
||||
libcurl4-openssl-dev \
|
||||
libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev \
|
||||
libswresample-dev libswscale-dev \
|
||||
libjansson-dev \
|
||||
libx11-xcb-dev \
|
||||
libgles2-mesa-dev libgles2-mesa \
|
||||
libwayland-dev \
|
||||
libpipewire-0.3-dev \
|
||||
libpulse-dev \
|
||||
libx264-dev \
|
||||
libmbedtls-dev \
|
||||
libgl1-mesa-dev \
|
||||
libjansson-dev \
|
||||
libluajit-5.1-dev python3-dev \
|
||||
libx11-dev libxcb-randr0-dev libxcb-shm0-dev libxcb-xinerama0-dev \
|
||||
libxcb-composite0-dev libxinerama-dev libxcb1-dev libx11-xcb-dev libxcb-xfixes0-dev \
|
||||
swig libcmocka-dev libxss-dev libglvnd-dev \
|
||||
libxkbcommon-dev \
|
||||
libasound2-dev libfdk-aac-dev libfontconfig-dev libfreetype6-dev libjack-jackd2-dev \
|
||||
libpulse-dev libsndio-dev libspeexdsp-dev libudev-dev libv4l-dev libva-dev libvlc-dev \
|
||||
libpci-dev libdrm-dev \
|
||||
nlohmann-json3-dev libwebsocketpp-dev libasio-dev libvpl-dev libqrcodegencpp-dev
|
||||
|
||||
if (( UBUNTU_2210_OR_LATER )) sudo apt-get install ${apt_args} librist-dev libsrt-openssl-dev
|
||||
|
||||
local -a _qt_packages=()
|
||||
|
||||
if (( QT_VERSION == 6 )) {
|
||||
_qt_packages+=(
|
||||
qt6-base-dev
|
||||
libqt6svg6-dev
|
||||
qt6-base-private-dev
|
||||
)
|
||||
} else {
|
||||
log_error "Unsupported Qt version '${QT_VERSION}' specified."
|
||||
return 2
|
||||
}
|
||||
|
||||
sudo apt-get install ${apt_args} ${_qt_packages}
|
||||
} else {
|
||||
local cef_version
|
||||
read -r cef_version <<< \
|
||||
"$(jq -r '.dependencies | [.cef.version] | join(" ")' ${buildspec_file})"
|
||||
|
||||
typeset -g CEF_VERSION=${cef_version}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
name: Build Project
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
check-event:
|
||||
name: Check GitHub Event Data 📡
|
||||
runs-on: ubuntu-22.04
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
outputs:
|
||||
package: ${{ steps.setup.outputs.package }}
|
||||
codesign: ${{ steps.setup.outputs.codesign }}
|
||||
notarize: ${{ steps.setup.outputs.notarize }}
|
||||
config: ${{ steps.setup.outputs.config }}
|
||||
commitHash: ${{ steps.setup.outputs.commitHash }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check Event Data ☑️
|
||||
id: setup
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
: Check Event Data ☑️
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
case "${GITHUB_EVENT_NAME}" in
|
||||
pull_request)
|
||||
config_data=('codesign:false' 'notarize:false' 'package:false' 'config:RelWithDebInfo')
|
||||
if gh pr view --json labels \
|
||||
| jq -e -r '.labels[] | select(.name == "Seeking Testers")' > /dev/null; then
|
||||
config_data[0]='codesign:true'
|
||||
config_data[2]='package:true'
|
||||
fi
|
||||
;;
|
||||
push)
|
||||
config_data=('codesign:true' 'notarize:false' 'package:true' 'config:RelWithDebInfo')
|
||||
if [[ ${GITHUB_REF_NAME} =~ [0-9]+.[0-9]+.[0-9]+(-(rc|beta).+)? ]]; then
|
||||
config_data[1]='notarize:true'
|
||||
config_data[3]='config:Release'
|
||||
fi
|
||||
;;
|
||||
workflow_dispatch)
|
||||
config_data=('codesign:true' 'notarize:false' 'package:false' 'config:RelWithDebInfo')
|
||||
;;
|
||||
schedule)
|
||||
config_data=('codesign:true' 'notarize:false' 'package:true' 'config:RelWithDebInfo')
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
for config in "${config_data[@]}"; do
|
||||
IFS=':' read -r key value <<< "${config}"
|
||||
echo "${key}=${value}" >> $GITHUB_OUTPUT
|
||||
done
|
||||
echo "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||
|
||||
macos-build:
|
||||
name: Build for macOS 🍏
|
||||
runs-on: macos-13
|
||||
needs: check-event
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [arm64, x86_64]
|
||||
defaults:
|
||||
run:
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set Up Environment 🔧
|
||||
id: setup
|
||||
run: |
|
||||
: Set Up Environment 🔧
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
print '::group::Enable Xcode 14.3.1 and AppleScript'
|
||||
sudo xcode-select --switch /Applications/Xcode_14.3.1.app/Contents/Developer
|
||||
sudo sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db \
|
||||
"INSERT OR REPLACE INTO access VALUES('kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552);"
|
||||
sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db \
|
||||
"INSERT OR REPLACE INTO access VALUES('kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552);"
|
||||
print '::endgroup::'
|
||||
|
||||
print '::group::Clean Homebrew Environment'
|
||||
local -a to_remove=()
|
||||
|
||||
for formula (curl) {
|
||||
if [[ -d ${HOMEBREW_PREFIX}/opt/${formula} ]] to_remove+=(${formula})
|
||||
}
|
||||
|
||||
if (( #to_remove )) brew uninstall --ignore-dependencies ${to_remove}
|
||||
print '::endgroup::'
|
||||
|
||||
local -A arch_names=(x86_64 intel arm64 apple)
|
||||
print "cpuName=${arch_names[${{ matrix.target }}]}" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/cache/restore@v3
|
||||
id: ccache-cache
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ runner.os }}-ccache-${{ matrix.target }}-${{ needs.check-event.outputs.config }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-ccache-${{ matrix.target }}-
|
||||
|
||||
- name: Set Up Code Signing 🔑
|
||||
uses: ./.github/actions/setup-macos-codesigning
|
||||
if: fromJSON(needs.check-event.outputs.codesign)
|
||||
id: codesign
|
||||
with:
|
||||
codesignIdentity: ${{ secrets.MACOS_SIGNING_IDENTITY }}
|
||||
codesignCertificate: ${{ secrets.MACOS_SIGNING_CERT }}
|
||||
certificatePassword: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}
|
||||
keychainPassword: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
provisioningProfile: ${{ secrets.MACOS_SIGNING_PROVISIONING_PROFILE }}
|
||||
notarizationUser: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
|
||||
notarizationPassword: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
|
||||
|
||||
- name: Build OBS Studio 🧱
|
||||
uses: ./.github/actions/build-obs
|
||||
env:
|
||||
TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}
|
||||
TWITCH_HASH: ${{ secrets.TWITCH_HASH }}
|
||||
RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}
|
||||
RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}
|
||||
YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}
|
||||
YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}
|
||||
YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}
|
||||
YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
config: ${{ needs.check-event.outputs.config }}
|
||||
codesign: ${{ fromJSON(needs.check-event.outputs.codesign) }}
|
||||
codesignIdent: ${{ steps.codesign.outputs.codesignIdent }}
|
||||
codesignTeam: ${{ steps.codesign.outputs.codesignTeam }}
|
||||
|
||||
- name: Package OBS Studio 📀
|
||||
uses: ./.github/actions/package-obs
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
config: ${{ needs.check-event.outputs.config }}
|
||||
package: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||
codesign: ${{ fromJSON(needs.check-event.outputs.codesign) && fromJSON(steps.codesign.outputs.haveCodesignIdent) }}
|
||||
codesignIdent: ${{ steps.codesign.outputs.codesignIdent }}
|
||||
notarize: ${{ fromJSON(needs.check-event.outputs.notarize) && fromJSON(steps.codesign.outputs.haveNotarizationUser) }}
|
||||
codesignUser: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
|
||||
codesignPass: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
|
||||
|
||||
- name: Upload Artifacts 📡
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: obs-studio-macos-${{ matrix.target }}-${{ needs.check-event.outputs.commitHash }}
|
||||
path: ${{ github.workspace }}/build_macos/obs-studio-*-macos-${{ steps.setup.outputs.cpuName }}.*
|
||||
|
||||
- name: Upload Debug Symbol Artifacts 🪲
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ needs.check-event.outputs.config == 'Release' }}
|
||||
with:
|
||||
name: obs-studio-macos-${{ matrix.target }}-${{ needs.check-event.outputs.commitHash }}-dSYMs
|
||||
path: ${{ github.workspace }}/build_macos/obs-studio-*-macos-${{ steps.setup.outputs.cpuName }}-dSYMs.tar.xz
|
||||
|
||||
- uses: actions/cache/save@v3
|
||||
if: github.event_name != 'pull_request' && steps.ccache-cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ runner.os }}-ccache-${{ matrix.target }}-${{ needs.check-event.outputs.config }}
|
||||
|
||||
ubuntu-build:
|
||||
name: Build for Ubuntu 🐧
|
||||
runs-on: ubuntu-22.04
|
||||
needs: check-event
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/cache/restore@v3
|
||||
id: ccache-cache
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ runner.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-ccache-x86_64-
|
||||
|
||||
- name: Build OBS Studio 🧱
|
||||
uses: ./.github/actions/build-obs
|
||||
env:
|
||||
TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}
|
||||
TWITCH_HASH: ${{ secrets.TWITCH_HASH }}
|
||||
RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}
|
||||
RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}
|
||||
YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}
|
||||
YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}
|
||||
YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}
|
||||
YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}
|
||||
with:
|
||||
target: x86_64
|
||||
config: ${{ needs.check-event.outputs.config }}
|
||||
|
||||
- name: Package OBS Studio 📀
|
||||
uses: ./.github/actions/package-obs
|
||||
with:
|
||||
target: x86_64
|
||||
config: ${{ needs.check-event.outputs.config }}
|
||||
package: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||
|
||||
- name: Upload Source Tarball 🗜️
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ ! always() }}
|
||||
with:
|
||||
name: obs-studio-*-sources-${{ needs.check-event.outputs.commitHash }}
|
||||
path: ${{ github.workspace }}/build_x86_64/obs-studio-*-sources.*
|
||||
|
||||
- name: Upload Artifacts 📡
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: obs-studio-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}
|
||||
path: ${{ github.workspace }}/build_x86_64/obs-studio-*-x86_64-linux-gnu.*
|
||||
|
||||
- name: Upload Debug Symbol Artifacts 🪲
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||
with:
|
||||
name: obs-studio-ubuntu-22.04-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
|
||||
path: ${{ github.workspace }}/build_x86_64/obs-studio-*-x86_64-linux-gnu-dbgsym.ddeb
|
||||
|
||||
- uses: actions/cache/save@v3
|
||||
if: github.event_name != 'pull_request' && steps.ccache-cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ runner.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }}
|
||||
|
||||
flatpak-build:
|
||||
name: Build Application for Flatpak 📦
|
||||
runs-on: ubuntu-22.04
|
||||
needs: check-event
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:kde-6.4
|
||||
options: --privileged
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
set-safe-directory: ${{ env.GITHUB_WORKSPACE }}
|
||||
|
||||
- name: Set Up Environment 🔧
|
||||
id: setup
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
: Set Up Environment 🔧
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
git config --global --add safe.directory "${GITHUB_WORKSPACE}"
|
||||
|
||||
echo '::group::Install GitHub CLI tool'
|
||||
dnf install -y -q gh
|
||||
gh extension install actions/gh-actions-cache
|
||||
echo '::endgroup::'
|
||||
|
||||
cache_key='flatpak-builder-${{ hashFiles('build-aux/**/*.json') }}'
|
||||
cache_ref='master'
|
||||
read -r key size unit _ ref _ <<< \
|
||||
"$(gh actions-cache list -B ${cache_ref} --key "${cache_key}-x86_64" | head -1)"
|
||||
|
||||
if [[ "${key}" ]]; then
|
||||
echo "cacheKey=${cache_key}" >> $GITHUB_OUTPUT
|
||||
echo "cacheHit=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "cacheHit=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build Flatpak Manifest 🧾
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v5
|
||||
with:
|
||||
build-bundle: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||
bundle: obs-studio-flatpak-${{ needs.check-event.outputs.commitHash }}.flatpak
|
||||
manifest-path: ${{ github.workspace }}/build-aux/com.obsproject.Studio.json
|
||||
cache: ${{ fromJSON(steps.setup.outputs.cacheHit) || (github.event_name == 'push' && github.ref_name == 'master')}}
|
||||
restore-cache: ${{ fromJSON(steps.setup.outputs.cacheHit) }}
|
||||
cache-key: ${{ steps.setup.outputs.cacheKey }}
|
||||
|
||||
windows-build:
|
||||
name: Build for Windows 🪟
|
||||
runs-on: windows-2022
|
||||
needs: check-event
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/cache@v3
|
||||
id: ccache-cache
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ runner.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-ccache-x86_64-
|
||||
|
||||
- name: Build OBS Studio 🧱
|
||||
uses: ./.github/actions/build-obs
|
||||
env:
|
||||
TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}
|
||||
TWITCH_HASH: ${{ secrets.TWITCH_HASH }}
|
||||
RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}
|
||||
RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}
|
||||
YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}
|
||||
YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}
|
||||
YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}
|
||||
YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}
|
||||
GPU_PRIORITY_VAL: ${{ secrets.GPU_PRIORITY_VAL }}
|
||||
with:
|
||||
target: x64
|
||||
config: ${{ needs.check-event.outputs.config }}
|
||||
|
||||
- name: Package OBS Studio 📀
|
||||
uses: ./.github/actions/package-obs
|
||||
with:
|
||||
target: x64
|
||||
config: ${{ needs.check-event.outputs.config }}
|
||||
package: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||
|
||||
- name: Upload Artifacts 📡
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: obs-studio-windows-x64-${{ needs.check-event.outputs.commitHash }}
|
||||
path: ${{ github.workspace }}/build_x64/obs-studio-*-windows-x64.zip
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Check Code Formatting 🛠️
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: clang-format Check 🐉
|
||||
id: clang-format
|
||||
uses: ./.github/actions/run-clang-format
|
||||
with:
|
||||
failCondition: error
|
||||
|
||||
swift-format:
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: swift-format Check 🔥
|
||||
id: swift-format
|
||||
uses: ./.github/actions/run-swift-format
|
||||
with:
|
||||
failCondition: error
|
||||
|
||||
cmake-format:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: cmake-format Check 🎛️
|
||||
id: cmake-format
|
||||
uses: ./.github/actions/run-cmake-format
|
||||
with:
|
||||
failCondition: error
|
||||
|
||||
flatpak-validator:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Flatpak Manifest Check 📦
|
||||
id: flatpak-check
|
||||
uses: ./.github/actions/flatpak-manifest-validator
|
||||
with:
|
||||
failCondition: error
|
||||
|
||||
qt-xml-validator:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Qt XML Check 🖼️
|
||||
id: qt-xml-check
|
||||
uses: ./.github/actions/qt-xml-validator
|
||||
with:
|
||||
failCondition: error
|
||||
@@ -1,27 +0,0 @@
|
||||
name: Clang Format Check
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore: ['**.md']
|
||||
branches-ignore: [master]
|
||||
pull_request:
|
||||
paths-ignore: ['**.md']
|
||||
branches-ignore: [master]
|
||||
|
||||
jobs:
|
||||
clang-format-check:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Install clang format
|
||||
run: |
|
||||
sudo apt-get install -y clang-format-13
|
||||
|
||||
- name: 'Run clang-format'
|
||||
run: |
|
||||
./CI/check-format.sh
|
||||
./CI/check-changes.sh
|
||||
@@ -1,76 +0,0 @@
|
||||
name: Cache Cleanup
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 0 * * *
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
purge:
|
||||
name: Cache Cleanup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Python & Modules
|
||||
run: |
|
||||
sudo apt install -y python3.11
|
||||
python3.11 -m pip install requests
|
||||
|
||||
- name: Clean Caches
|
||||
shell: python3.11 {0}
|
||||
run: |
|
||||
import re
|
||||
from datetime import datetime
|
||||
import requests
|
||||
|
||||
s = requests.session()
|
||||
s.headers['Authorization'] = 'Bearer ${{ secrets.GITHUB_TOKEN }}'
|
||||
|
||||
r = s.get('https://api.github.com/repos/${{ github.repository }}/actions/caches', params=dict(per_page=100))
|
||||
r.raise_for_status()
|
||||
caches = r.json()['actions_caches']
|
||||
|
||||
print(f'There are {len(caches)} total caches.')
|
||||
|
||||
# find latest flatpak cache
|
||||
flatpak_last_ts = None
|
||||
flatpak_key = None
|
||||
for cache in caches:
|
||||
if not cache['ref'] == 'refs/heads/master' or not cache['key'].startswith('flatpak-builder'):
|
||||
continue
|
||||
ts = datetime.fromisoformat(cache['created_at'])
|
||||
if not flatpak_last_ts or ts > flatpak_last_ts:
|
||||
flatpak_key = cache['key']
|
||||
flatpak_last_ts = ts
|
||||
|
||||
if flatpak_key:
|
||||
print(f'Latest flatpak cache: {flatpak_key}')
|
||||
|
||||
now = datetime.utcnow()
|
||||
to_be_removed = []
|
||||
for cache in caches:
|
||||
# add merge queue caches
|
||||
if 'gh-readonly-queue' in cache['ref']:
|
||||
to_be_removed.append(cache)
|
||||
continue
|
||||
|
||||
if flatpak_key and cache['key'].startswith('flatpak-builder'):
|
||||
# add non-master flatpak caches that match latest key
|
||||
if cache['key'] == flatpak_key and not cache['ref'] == 'refs/heads/master':
|
||||
to_be_removed.append(cache)
|
||||
continue
|
||||
# add master flatpak caches that do not match the latest key
|
||||
elif cache['key'] != flatpak_key and cache['ref'] == 'refs/heads/master':
|
||||
to_be_removed.append(cache)
|
||||
continue
|
||||
|
||||
# add dated caches predating today
|
||||
if (cache_date := re.search('[0-9]{4}\-[0-9]{2}\-[0-9]{2}', cache['key'])) is not None:
|
||||
parsed_date = datetime.strptime(cache_date.group(), '%Y-%m-%d')
|
||||
if (now - parsed_date).days > 0:
|
||||
to_be_removed.append(cache)
|
||||
|
||||
print(f'Removing {len(to_be_removed)} caches...')
|
||||
for cache in to_be_removed:
|
||||
print(f'Deleting cache "{cache["key"]}" with ID {cache["id"]}...', end=' ')
|
||||
r = s.delete(f'https://api.github.com/repos/${{ github.repository }}/actions/caches/{cache["id"]}')
|
||||
print(f'[{r.status_code}]')
|
||||
@@ -1,40 +0,0 @@
|
||||
name: Compatibility Data Validator
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "plugins/win-capture/data/compatibility.json"
|
||||
- "plugins/win-capture/data/package.json"
|
||||
pull_request:
|
||||
paths:
|
||||
- "plugins/win-capture/data/compatibility.json"
|
||||
- "plugins/win-capture/data/package.json"
|
||||
|
||||
jobs:
|
||||
schema:
|
||||
name: Schema
|
||||
runs-on: [ubuntu-22.04]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install & Configure Python
|
||||
run: |
|
||||
sudo apt install python3-dev
|
||||
python3 -m pip install jsonschema json_source_map
|
||||
|
||||
- name: Validate Compatibility JSON Schema
|
||||
run: |
|
||||
JSON_FILES=(
|
||||
plugins/win-capture/data/compatibility.json
|
||||
plugins/win-capture/data/package.json
|
||||
)
|
||||
python3 CI/check-jsonschema.py "${JSON_FILES[@]}"
|
||||
|
||||
- name: Annotate Errors
|
||||
if: failure()
|
||||
uses: yuzutech/annotations-action@v0.4.0
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
title: "Compatibility JSON Errors"
|
||||
input: "./validation_errors.json"
|
||||
@@ -1,16 +0,0 @@
|
||||
name: "Crowdin Sync: Import latest translations"
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
crowdin-sync-download:
|
||||
name: Import latest translations
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'obsproject'
|
||||
env:
|
||||
CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.CROWDIN_SYNC_GITHUB_PAT }}
|
||||
- uses: obsproject/obs-crowdin-sync/download@0.2.1
|
||||
@@ -1,20 +0,0 @@
|
||||
name: "Crowdin Sync: Upload English strings"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "**/en-US.ini"
|
||||
jobs:
|
||||
crowdin-sync-upload:
|
||||
name: Upload English strings
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }}
|
||||
GITHUB_EVENT_BEFORE: ${{ github.event.before }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 100
|
||||
- uses: obsproject/obs-crowdin-sync/upload@0.2.1
|
||||
@@ -0,0 +1,146 @@
|
||||
name: Dispatch
|
||||
run-name: Dispatched Repository Actions - ${{ inputs.job }} ⌛️
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
job:
|
||||
description: Dispatch job to run
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- steam
|
||||
- services
|
||||
- translations
|
||||
- documentation
|
||||
ref:
|
||||
description: GitHub reference to use for job
|
||||
type: string
|
||||
required: false
|
||||
customAssetWindows:
|
||||
description: Custom Windows build for Steam Upload
|
||||
type: string
|
||||
required: false
|
||||
customAssetMacOSApple:
|
||||
description: Custom macOS Apple Silicon build for Steam Upload
|
||||
type: string
|
||||
required: false
|
||||
customAssetMacOSIntel:
|
||||
description: Custom macOS Intel build for Steam Upload
|
||||
type: string
|
||||
required: false
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
services-validation:
|
||||
name: Check Services Configuration Files 🕵️
|
||||
if: github.repository_owner == 'obsproject' && inputs.job == 'services'
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check for Defunct Services 📉
|
||||
uses: ./.github/actions/services-validator
|
||||
with:
|
||||
repositorySecret: ${{ secrets.GITHUB_TOKEN }}
|
||||
runSchemaChecks: true
|
||||
runServiceChecks: true
|
||||
createPullRequest: true
|
||||
|
||||
download-language-files:
|
||||
name: Download Language Files 🌐
|
||||
if: github.repository_owner == 'obsproject' && inputs.job == 'translations'
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.CROWDIN_SYNC_GITHUB_PAT }}
|
||||
- uses: obsproject/obs-crowdin-sync/download@0.2.1
|
||||
|
||||
steam-upload:
|
||||
name: Upload Steam Builds 🚂
|
||||
if: github.repository_owner == 'obsproject' && inputs.job == 'steam'
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/steam-upload
|
||||
with:
|
||||
steamSecret: ${{ secrets.STEAM_SHARED_SECRET }}
|
||||
steamUser: ${{ secrets.STEAM_USER }}
|
||||
steamPassword: ${{ secrets.STEAM_PASSWORD }}
|
||||
tagName: ${{ inputs.ref }}
|
||||
customAssetWindows: ${{ inputs.customAssetWindows }}
|
||||
customAssetMacOSApple: ${{ inputs.customAssetMacOSApple }}
|
||||
customAssetMacOSIntel: ${{ inputs.customAssetMacOSIntel }}
|
||||
workflowSecret: ${{ github.token }}
|
||||
preview: false
|
||||
|
||||
update-documentation:
|
||||
name: Update Documentation 📖
|
||||
if: github.repository_owner == 'obsproject' && inputs.job == 'documentation'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Get Commit Information 🆔
|
||||
id: setup
|
||||
run: |
|
||||
: Get Commit Hash 🆔
|
||||
echo "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||
- uses: ./.github/actions/generate-docs
|
||||
with:
|
||||
commitHash: ${{ steps.checks.setup.commitHash }}
|
||||
|
||||
update-documentation-cloudflare:
|
||||
name: Update Documentation for Cloudflare ☁️
|
||||
if: github.repository_owner == 'obsproject' && inputs.job == 'documentation'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Get Commit Information 🆔
|
||||
id: setup
|
||||
run: |
|
||||
: Get Commit Hash 🆔
|
||||
echo "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||
- uses: ./.github/actions/generate-docs
|
||||
with:
|
||||
commitHash: ${{ steps.checks.setup.commitHash }}
|
||||
disableLinkExtensions: true
|
||||
|
||||
deploy-documentation:
|
||||
name: Deploy Documentation to Cloudflare ☁️
|
||||
if: github.repository_owner == 'obsproject' && inputs.job == 'documentation'
|
||||
runs-on: ubuntu-22.04
|
||||
needs: update-documentation-cloudflare
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Get Commit Information 🆔
|
||||
id: setup
|
||||
run: |
|
||||
: Get Commit Hash 🆔
|
||||
echo "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: OBS Studio Docs (No Extensions) ${{ steps.setup.outputs.commitHash }}
|
||||
path: docs
|
||||
|
||||
- name: Set Up Redirects 🔄
|
||||
run: |
|
||||
: Set Up Redirects 🔄
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
echo "/previous/27.2 https://obsproject.com/docs/27.2 302" >> docs/_redirects
|
||||
echo "/previous/:major.:minor https://:major-:minor.${{ vars.CF_PAGES_PROJECT }}.pages.dev 302" >> docs/_redirects
|
||||
|
||||
- name: Publish to Live Page
|
||||
uses: cloudflare/wrangler-action@4c10c1822abba527d820b29e6333e7f5dac2cabd
|
||||
with:
|
||||
workingDirectory: docs
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
command: pages publish . --project-name=${{ vars.CF_PAGES_PROJECT }} --commit-hash='${{ steps.setup.outputs.commitHash }}'
|
||||
@@ -1,136 +0,0 @@
|
||||
name: Generate Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "cmake/**"
|
||||
branches: ['*']
|
||||
tags: ['*']
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/sphinx/**"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
commitHash: ${{ steps.setup.outputs.commitHash }}
|
||||
commitBranch: ${{ steps.setup.outputs.commitBranch }}
|
||||
fullCommitHash: ${{ steps.setup.outputs.fullCommitHash }}
|
||||
env:
|
||||
BUILD_CF_ARTIFACT: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Environment
|
||||
id: setup
|
||||
run: |
|
||||
BRANCH=$(git describe --exact-match --tags 2> /dev/null || git branch --show-current)
|
||||
# Remove patch version from tag
|
||||
BRANCH=$(echo ${BRANCH} | sed -e 's/\.[0-9]*$//')
|
||||
echo "commitBranch=${BRANCH}" >> $GITHUB_OUTPUT
|
||||
echo "commitHash=$(git describe --exact-match --tags 2> /dev/null || git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "fullCommitHash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Bump Version Number
|
||||
shell: bash
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
VERTEST="\#define\sLIBOBS_API_\w+_VER\s([0-9]{1,2})"
|
||||
VER=""
|
||||
MAJOR=""
|
||||
while IFS= read -r l
|
||||
do
|
||||
if [[ $l =~ $VERTEST ]]; then
|
||||
if [[ $VER = '' ]]; then MAJOR="${BASH_REMATCH[1]}"; else VER+="."; fi
|
||||
VER+="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
done < "libobs/obs-config.h"
|
||||
|
||||
SVER="version = '([0-9\.]+)'"
|
||||
RVER="version = '$VER'"
|
||||
SREL="release = '([0-9\.]+)'"
|
||||
RREL="release = '$VER'"
|
||||
SCOPY="copyright = '([A-Za-z0-9, ]+)'"
|
||||
RCOPY="copyright = '2017-$(date +"%Y"), Lain Bailey'"
|
||||
sed -i -E -e "s/${SVER}/${RVER}/g" -e "s/${SREL}/${RREL}/g" -e "s/${SCOPY}/${RCOPY}/g" docs/sphinx/conf.py
|
||||
|
||||
- uses: totaldebug/sphinx-publish-action@1.2.0
|
||||
with:
|
||||
sphinx_src: 'docs/sphinx'
|
||||
build_only: True
|
||||
target_branch: 'master'
|
||||
target_path: '../home/_build'
|
||||
pre_build_commands: 'pip install -Iv sphinx==5.1.1'
|
||||
|
||||
- name: Disable link extensions
|
||||
shell: bash
|
||||
if: ${{ env.BUILD_CF_ARTIFACT == 'true' }}
|
||||
run: |
|
||||
SOPT="html_link_suffix = None"
|
||||
ROPT="html_link_suffix = ''"
|
||||
sed -i -e "s/${SOPT}/${ROPT}/g" docs/sphinx/conf.py
|
||||
|
||||
- uses: totaldebug/sphinx-publish-action@1.2.0
|
||||
if: ${{ env.BUILD_CF_ARTIFACT == 'true' }}
|
||||
with:
|
||||
sphinx_src: 'docs/sphinx'
|
||||
build_only: True
|
||||
target_branch: 'master'
|
||||
target_path: '../home/_build_cf'
|
||||
pre_build_commands: 'pip install -Iv sphinx==5.1.1'
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: 'OBS Studio Docs ${{ steps.setup.outputs.commitHash }}'
|
||||
path: |
|
||||
${{ runner.temp }}/_github_home/_build
|
||||
!${{ runner.temp }}/_github_home/_build/.doctrees
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ env.BUILD_CF_ARTIFACT == 'true' }}
|
||||
with:
|
||||
name: 'CF Pages ${{ steps.setup.outputs.commitHash }}'
|
||||
path: |
|
||||
${{ runner.temp }}/_github_home/_build_cf
|
||||
!${{ runner.temp }}/_github_home/_build_cf/.doctrees
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: docs
|
||||
if: ${{ github.event_name == 'workflow_dispatch' || (github.repository_owner == 'obsproject' && startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request') }}
|
||||
environment:
|
||||
name: cf-pages-deploy
|
||||
concurrency:
|
||||
group: "cf-pages-deployment"
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: 'CF Pages ${{ needs.docs.outputs.commitHash }}'
|
||||
path: docs
|
||||
|
||||
- name: Setup redirects
|
||||
run: |
|
||||
echo "/previous/27.2 https://obsproject.com/docs/27.2 302" >> docs/_redirects
|
||||
echo "/previous/:major.:minor https://:major-:minor.${{ vars.CF_PAGES_PROJECT }}.pages.dev 302" >> docs/_redirects
|
||||
|
||||
- name: Publish to live page
|
||||
if: ${{ !contains(needs.docs.outputs.commitBranch, 'beta') && !contains(needs.docs.outputs.commitBranch, 'rc') }}
|
||||
uses: cloudflare/wrangler-action@4c10c1822abba527d820b29e6333e7f5dac2cabd
|
||||
with:
|
||||
workingDirectory: docs
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
command: pages publish . --project-name=${{ vars.CF_PAGES_PROJECT }} --commit-hash='${{ needs.docs.outputs.fullCommitHash }}'
|
||||
|
||||
- name: Publish to tag alias
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: cloudflare/wrangler-action@4c10c1822abba527d820b29e6333e7f5dac2cabd
|
||||
with:
|
||||
workingDirectory: docs
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
command: pages publish . --project-name=${{ vars.CF_PAGES_PROJECT }} --commit-hash='${{ needs.docs.outputs.fullCommitHash }}' --branch='${{ needs.docs.outputs.commitBranch }}'
|
||||
@@ -1,123 +0,0 @@
|
||||
---
|
||||
|
||||
name: Flatpak
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
branches: [master, 'release/**']
|
||||
|
||||
env:
|
||||
TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}
|
||||
TWITCH_HASH: ${{ secrets.TWITCH_HASH }}
|
||||
RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}
|
||||
RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}
|
||||
YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}
|
||||
YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}
|
||||
YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}
|
||||
YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}
|
||||
|
||||
jobs:
|
||||
check_tag:
|
||||
name: Check release tag
|
||||
runs-on: [ubuntu-latest]
|
||||
outputs:
|
||||
valid_tag: ${{ steps.check_tag.outputs.valid_tag }}
|
||||
matrix: ${{ steps.check_tag.outputs.matrix }}
|
||||
steps:
|
||||
- name: Check the tag
|
||||
id: check_tag
|
||||
run: |
|
||||
shopt -s extglob
|
||||
|
||||
case ${GITHUB_REF##*/} in
|
||||
+([0-9]).+([0-9]).+([0-9]) )
|
||||
echo 'valid_tag=true' >> $GITHUB_OUTPUT
|
||||
echo 'matrix=["beta", "stable"]' >> $GITHUB_OUTPUT
|
||||
;;
|
||||
+([0-9]).+([0-9]).+([0-9])-@(beta|rc)*([0-9]) )
|
||||
echo 'valid_tag=true' >> $GITHUB_OUTPUT
|
||||
echo 'matrix=["beta"]' >> $GITHUB_OUTPUT
|
||||
;;
|
||||
* ) echo 'valid_tag=false' >> $GITHUB_OUTPUT ;;
|
||||
esac
|
||||
|
||||
publish:
|
||||
name: Publish to Flathub
|
||||
runs-on: [ubuntu-latest]
|
||||
needs: check_tag
|
||||
if: fromJSON(needs.check_tag.outputs.valid_tag)
|
||||
env:
|
||||
FLATPAK_BUILD_PATH: flatpak_app/files/share
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:kde-6.4
|
||||
options: --privileged
|
||||
strategy:
|
||||
matrix:
|
||||
branch: ${{ fromJSON(needs.check_tag.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: 'Setup build environment'
|
||||
id: setup
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CACHE_KEY: flatpak-builder-${{ hashFiles('build-aux/**/*.json') }}
|
||||
run: |
|
||||
dnf install -y -q gh
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
|
||||
KEY="$CACHE_KEY-x86_64"
|
||||
CACHE_HIT=$(gh actions-cache list -B master --key $KEY | grep -q $KEY && echo 'true' || echo 'false')
|
||||
|
||||
echo "git_hash=$(git rev-parse --short=9 HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "cache_key=$CACHE_KEY" >> $GITHUB_OUTPUT
|
||||
echo "cache_hit=$CACHE_HIT" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Flatpak Manifest
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1
|
||||
with:
|
||||
bundle: obs-studio-${{ steps.setup.outputs.git_hash }}.flatpak
|
||||
manifest-path: build-aux/com.obsproject.Studio.json
|
||||
cache: ${{ fromJSON(steps.setup.outputs.cache_hit) }}
|
||||
cache-key: ${{ steps.setup.outputs.cache_key }}
|
||||
mirror-screenshots-url: https://dl.flathub.org/repo/screenshots
|
||||
branch: ${{ matrix.branch }}
|
||||
|
||||
- name: Validate AppStream
|
||||
shell: bash
|
||||
working-directory: ${{ env.FLATPAK_BUILD_PATH }}
|
||||
run: |
|
||||
appstream-util validate appdata/com.obsproject.Studio.appdata.xml
|
||||
|
||||
- name: Verify icon and metadata in app-info
|
||||
shell: bash
|
||||
working-directory: ${{ env.FLATPAK_BUILD_PATH }}
|
||||
run: |
|
||||
test -f app-info/icons/flatpak/128x128/com.obsproject.Studio.png || { echo "Missing 128x128 icon in app-info" ; exit 1; }
|
||||
test -f app-info/xmls/com.obsproject.Studio.xml.gz || { echo "Missing com.obsproject.Studio.xml.gz in app-info" ; exit 1; }
|
||||
|
||||
- name: Commit screenshots to the OSTree repository
|
||||
run: |
|
||||
ostree commit --repo=repo --canonical-permissions --branch=screenshots/x86_64 flatpak_app/screenshots
|
||||
|
||||
- name: Publish to Flathub Beta
|
||||
uses: flatpak/flatpak-github-actions/flat-manager@v6.1
|
||||
if: matrix.branch == 'beta'
|
||||
with:
|
||||
flat-manager-url: https://hub.flathub.org/
|
||||
repository: beta
|
||||
token: ${{ secrets.FLATHUB_BETA_TOKEN }}
|
||||
|
||||
- name: Publish to Flathub
|
||||
uses: flatpak/flatpak-github-actions/flat-manager@v6.1
|
||||
if: matrix.branch == 'stable'
|
||||
with:
|
||||
flat-manager-url: https://hub.flathub.org/
|
||||
repository: stable
|
||||
token: ${{ secrets.FLATHUB_TOKEN }}
|
||||
@@ -1,624 +0,0 @@
|
||||
name: 'BUILD'
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore: ['**.md']
|
||||
branches:
|
||||
- master
|
||||
- 'release/**'
|
||||
tags: ['*']
|
||||
pull_request:
|
||||
paths-ignore: ['**.md']
|
||||
branches: [master]
|
||||
merge_group:
|
||||
branches: [master]
|
||||
|
||||
env:
|
||||
CACHE_REVISION: '006'
|
||||
CEF_BUILD_VERSION_LINUX: '5060'
|
||||
CEF_BUILD_VERSION_WIN: '5060'
|
||||
QT_VERSION_MAC: '6.4.3'
|
||||
QT_VERSION_WIN: '6.4.3'
|
||||
DEPS_VERSION_WIN: '2023-06-22'
|
||||
VLC_VERSION_WIN: '3.0.0-git'
|
||||
TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}
|
||||
TWITCH_HASH: ${{ secrets.TWITCH_HASH }}
|
||||
RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}
|
||||
RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}
|
||||
YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}
|
||||
YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}
|
||||
YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}
|
||||
YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}
|
||||
GPU_PRIORITY_VAL: ${{ secrets.GPU_PRIORITY_VAL }}
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
clang_check:
|
||||
name: '01 - Code Format Check'
|
||||
runs-on: [ubuntu-22.04]
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: 'Install clang-format'
|
||||
run: sudo apt-get install -y clang-format-13
|
||||
|
||||
- name: 'Run clang-format'
|
||||
run: |
|
||||
./CI/check-format.sh
|
||||
./CI/check-changes.sh
|
||||
|
||||
- name: 'Install cmake-format'
|
||||
run: sudo pip install cmakelang
|
||||
|
||||
- name: 'Run cmake-format'
|
||||
run: |
|
||||
./CI/check-cmake.sh
|
||||
|
||||
- name: 'Run format-manifest.py'
|
||||
run: |
|
||||
python3 ./build-aux/format-manifest.py
|
||||
./CI/check-changes.sh
|
||||
|
||||
config:
|
||||
name: '01 - Configure Build Jobs'
|
||||
runs-on: [ubuntu-22.04]
|
||||
outputs:
|
||||
create_artifacts: ${{ steps.config.outputs.create_artifacts }}
|
||||
cache_date: ${{ steps.config.outputs.cache_date }}
|
||||
steps:
|
||||
- name: 'Configure Build Jobs'
|
||||
id: config
|
||||
run: |
|
||||
if [[ "${{ github.event_name == 'pull_request' }}" == "true" ]]; then
|
||||
if test -n "$(curl -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -s "${{ github.event.pull_request.url }}" | jq -e '.labels[] | select(.name == "Seeking Testers")')"; then
|
||||
echo 'create_artifacts=true' >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo 'create_artifacts=false' >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo 'create_artifacts=true' >> $GITHUB_OUTPUT
|
||||
fi
|
||||
echo "cache_date=$(date +"%Y-%m-%d")" >> $GITHUB_OUTPUT
|
||||
|
||||
macos_build:
|
||||
name: '02 - macOS'
|
||||
runs-on: [macos-13]
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ['x86_64', 'arm64']
|
||||
if: always()
|
||||
needs: [config, clang_check]
|
||||
env:
|
||||
BLOCKED_FORMULAS: 'speexdsp curl php composer'
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: 'obs-studio'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
path: 'obs-studio'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Restore ccache from cache'
|
||||
id: ccache-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ runner.os }}-ccache-${{ steps.github-check.outputs.generator }}-${{ matrix.arch }}-${{ github.event_name }}-${{ github.head_ref }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-ccache-${{ steps.github-check.outputs.generator }}-${{ matrix.arch }}-push-
|
||||
|
||||
- name: 'Setup build environment'
|
||||
id: setup
|
||||
run: |
|
||||
REMOVE_FORMULAS=""
|
||||
for FORMULA in ${{ env.BLOCKED_FORMULAS }}; do
|
||||
if [ -d "/usr/local/opt/${FORMULA}" ]; then
|
||||
REMOVE_FORMULAS="${REMOVE_FORMULAS}${FORMULA} "
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "${REMOVE_FORMULAS}" ]; then
|
||||
brew uninstall ${REMOVE_FORMULAS}
|
||||
fi
|
||||
|
||||
echo "commitHash=$(git rev-parse --short=9 HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: CI/macos/01_install_dependencies.sh --architecture "${{ matrix.arch }}"
|
||||
|
||||
- name: 'Install Apple Developer Certificate'
|
||||
id: macos-codesign
|
||||
env:
|
||||
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
|
||||
MACOS_SIGNING_CERT: ${{ secrets.MACOS_SIGNING_CERT }}
|
||||
MACOS_SIGNING_CERT_PASSWORD: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}
|
||||
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
MACOS_PROVISIONING_PROFILE: ${{ secrets.MACOS_SIGNING_PROVISIONING_PROFILE }}
|
||||
run: |
|
||||
if [[ "${MACOS_SIGNING_IDENTITY}" && "${MACOS_SIGNING_CERT}" ]]; then
|
||||
CERTIFICATE_PATH="${RUNNER_TEMP}/build_certificate.p12"
|
||||
KEYCHAIN_PATH="${RUNNER_TEMP}/app-signing.keychain-db"
|
||||
|
||||
echo -n "${MACOS_SIGNING_CERT}" | base64 --decode --output="${CERTIFICATE_PATH}"
|
||||
|
||||
: "${MACOS_KEYCHAIN_PASSWORD:="$(echo ${RANDOM} | sha1sum | head -c 32)"}"
|
||||
|
||||
security create-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}"
|
||||
security set-keychain-settings -lut 21600 "${KEYCHAIN_PATH}"
|
||||
security unlock-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}"
|
||||
|
||||
security import "${CERTIFICATE_PATH}" -P "${MACOS_SIGNING_CERT_PASSWORD}" -A \
|
||||
-t cert -f pkcs12 -k "${KEYCHAIN_PATH}" \
|
||||
-T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/xcrun
|
||||
|
||||
security set-key-partition-list -S 'apple-tool:,apple:' -k "${MACOS_KEYCHAIN_PASSWORD}" \
|
||||
"${KEYCHAIN_PATH}" &> /dev/null
|
||||
security list-keychain -d user -s "${KEYCHAIN_PATH}" 'login-keychain'
|
||||
|
||||
echo "CODESIGN_IDENT=${MACOS_SIGNING_IDENTITY}" >> $GITHUB_ENV
|
||||
echo "MACOS_KEYCHAIN_PASSWORD=${MACOS_KEYCHAIN_PASSWORD}" >> $GITHUB_ENV
|
||||
echo "haveCodesignIdent=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "CODESIGN_IDENT=-" >> $GITHUB_ENV
|
||||
echo "haveCodesignIdent=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ "${MACOS_PROVISIONING_PROFILE}" ]]; then
|
||||
PROFILE_PATH="${RUNNER_TEMP}/build_profile.provisionprofile"
|
||||
echo -n "${MACOS_PROVISIONING_PROFILE}" | base64 --decode --output="${PROFILE_PATH}"
|
||||
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
security cms -D -i "${PROFILE_PATH}" -o "${RUNNER_TEMP}/build_profile.plist"
|
||||
UUID="$(plutil -extract UUID raw "${RUNNER_TEMP}/build_profile.plist")"
|
||||
TEAM_ID="$(plutil -extract TeamIdentifier.0 raw -expect string "${RUNNER_TEMP}/build_profile.plist")"
|
||||
|
||||
cp "${PROFILE_PATH}" ~/Library/MobileDevice/Provisioning\ Profiles/${UUID}.provisionprofile
|
||||
echo "provisionprofileUUID=${UUID}" >> $GITHUB_OUTPUT
|
||||
echo "haveProvisioningProfile=true" >> $GITHUB_OUTPUT
|
||||
echo "CODESIGN_TEAM=${TEAM_ID}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "haveProvisioningProfile=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ "${MACOS_NOTARIZATION_USERNAME}" && "${MACOS_NOTARIZATION_PASSWORD}" ]]; then
|
||||
echo "haveNotarizationUser=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "haveNotarizationUser=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: 'Build OBS'
|
||||
env:
|
||||
PROVISIONING_PROFILE: ${{ steps.macos-codesign.outputs.provisionprofileUUID }}
|
||||
run: |
|
||||
sudo xcode-select --switch /Applications/Xcode_14.3.1.app/Contents/Developer
|
||||
if [[ '${{ steps.github-check.outputs.generator }}' == 'Xcode' ]]; then
|
||||
SEEKING_TESTERS=1 CI/macos/02_build_obs.sh --codesign --architecture "${{ matrix.arch }}"
|
||||
else
|
||||
CI/macos/02_build_obs.sh --codesign --architecture "${{ matrix.arch }}"
|
||||
fi
|
||||
|
||||
- name: 'Create build artifact'
|
||||
if: ${{ fromJSON(needs.config.outputs.create_artifacts) }}
|
||||
run: |
|
||||
: ${PACKAGE:=}
|
||||
case "${GITHUB_EVENT_NAME}" in
|
||||
push) if [[ ${GITHUB_REF_NAME} =~ [0-9]+.[0-9]+.[0-9]+(-(rc|beta).+)? ]]; then PACKAGE=1; fi ;;
|
||||
pull_request) PACKAGE=1 ;;
|
||||
esac
|
||||
|
||||
if [[ "${PACKAGE}" ]]; then
|
||||
sudo sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db \
|
||||
"INSERT OR REPLACE INTO access VALUES('kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552);"
|
||||
sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db \
|
||||
"INSERT OR REPLACE INTO access VALUES('kTCCServiceAppleEvents','/usr/local/opt/runner/provisioner/provisioner',1,2,3,1,NULL,NULL,0,'com.apple.finder',X'fade0c000000002c00000001000000060000000200000010636f6d2e6170706c652e66696e64657200000003',NULL,1592919552);"
|
||||
|
||||
CI/macos/03_package_obs.sh --codesign --architecture "${{ matrix.arch }}"
|
||||
ARTIFACT_NAME=$(basename $(/usr/bin/find build_macos -type f -name "obs-studio-*.dmg" -depth 1 | head -1))
|
||||
echo "FILE_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: 'Upload build Artifact'
|
||||
if: ${{ fromJSON(needs.config.outputs.create_artifacts) && env.FILE_NAME != '' }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: 'obs-studio-macos-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}'
|
||||
path: '${{ github.workspace }}/obs-studio/build_macos/${{ env.FILE_NAME }}'
|
||||
|
||||
linux_build:
|
||||
name: '02 - Linux'
|
||||
runs-on: ${{ matrix.ubuntu }}
|
||||
strategy:
|
||||
matrix:
|
||||
ubuntu: ['ubuntu-22.04']
|
||||
if: always()
|
||||
needs: [config, clang_check]
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: 'obs-studio'
|
||||
env:
|
||||
BUILD_FOR_DISTRIBUTION: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' }}
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
path: 'obs-studio'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Restore ccache from cache'
|
||||
id: ccache-cache
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
CACHE_NAME: 'ccache-cache'
|
||||
with:
|
||||
path: ${{ github.workspace }}/.ccache
|
||||
key: ${{ runner.os }}-ccache-${{ matrix.ubuntu }}-${{ github.event_name }}-${{ github.head_ref }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-ccache-${{ matrix.ubuntu }}-push-
|
||||
|
||||
- name: 'Restore Chromium Embedded Framework from cache'
|
||||
id: cef-cache
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
CACHE_NAME: 'cef-cache'
|
||||
with:
|
||||
path: ${{ github.workspace }}/obs-build-dependencies/cef_binary_${{ env.CEF_BUILD_VERSION_LINUX }}_linux64
|
||||
key: ${{ runner.os }}-pr-${{ env.CACHE_NAME }}-${{ env.CEF_BUILD_VERSION_LINUX }}-${{ env.CACHE_REVISION }}
|
||||
|
||||
- name: 'Setup build environment'
|
||||
id: setup
|
||||
run: |
|
||||
echo "commitHash=$(git rev-parse --short=9 HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: 'Install dependencies'
|
||||
env:
|
||||
RESTORED_CEF: ${{ steps.cef-cache.outputs.cache-hit }}
|
||||
run: CI/linux/01_install_dependencies.sh ${{ matrix.script_options }}
|
||||
|
||||
- name: 'Build OBS'
|
||||
run: CI/linux/02_build_obs.sh ${{ matrix.script_options }}
|
||||
|
||||
- name: 'Run tests'
|
||||
if: success()
|
||||
run: cmake --build build -t test
|
||||
|
||||
- name: 'Create build artifact'
|
||||
if: ${{ success() && fromJSON(needs.config.outputs.create_artifacts) }}
|
||||
run: |
|
||||
CI/linux/03_package_obs.sh
|
||||
ARTIFACT_NAME=$(basename $(/usr/bin/find build -maxdepth 1 -type f -name "obs-studio-*.deb" | sort -rn | head -1))
|
||||
echo "FILE_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
|
||||
echo "DEBUG_FILE_NAME=${ARTIFACT_NAME//.deb/-dbgsym.ddeb}" >> $GITHUB_ENV
|
||||
|
||||
- name: 'Upload build Artifact'
|
||||
if: ${{ success() && fromJSON(needs.config.outputs.create_artifacts) }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: 'obs-studio-${{ matrix.ubuntu }}-${{ steps.setup.outputs.commitHash }}'
|
||||
path: '${{ github.workspace }}/obs-studio/build/${{ env.FILE_NAME }}'
|
||||
|
||||
- name: 'Upload debug symbol Artifact'
|
||||
if: ${{ success() && fromJSON(needs.config.outputs.create_artifacts) }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: 'obs-studio-${{ matrix.ubuntu }}-${{ steps.setup.outputs.commitHash }}-dbgsym'
|
||||
path: '${{ github.workspace }}/obs-studio/build/${{ env.DEBUG_FILE_NAME }}'
|
||||
|
||||
windows_build:
|
||||
name: '02 - Windows'
|
||||
runs-on: [windows-2022]
|
||||
needs: [config, clang_check]
|
||||
if: always()
|
||||
env:
|
||||
BUILD_FOR_DISTRIBUTION: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' }}
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Environment
|
||||
id: setup
|
||||
run: |
|
||||
$CommitHash = git rev-parse --short=9 HEAD
|
||||
"commitHash=${CommitHash}" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: 'Build OBS'
|
||||
run: .github/scripts/Build-Windows.ps1 -Target x64 -Configuration RelWithDebInfo
|
||||
|
||||
- name: 'Create build artifact'
|
||||
if: ${{ success() && fromJSON(needs.config.outputs.create_artifacts) }}
|
||||
run: |
|
||||
.github/scripts/Package-Windows.ps1 -Target x64 -Configuration RelWithDebInfo
|
||||
$ArtifactName = Get-ChildItem -filter "build_x64/obs-studio-*-windows-x64.zip" -File
|
||||
Write-Output "FILE_NAME=${ArtifactName}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
- name: 'Upload build artifact'
|
||||
if: ${{ success() && fromJSON(needs.config.outputs.create_artifacts) }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: 'obs-studio-windows-x64-${{ steps.setup.outputs.commitHash }}'
|
||||
path: '${{ env.FILE_NAME }}'
|
||||
|
||||
linux_package:
|
||||
name: '02 - Flatpak'
|
||||
runs-on: [ubuntu-latest]
|
||||
needs: [config, clang_check]
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:kde-6.4
|
||||
options: --privileged
|
||||
steps:
|
||||
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup build environment'
|
||||
id: setup
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CACHE_KEY: flatpak-builder-${{ hashFiles('build-aux/**/*.json') }}
|
||||
run: |
|
||||
dnf install -y -q gh
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||
|
||||
KEY="$CACHE_KEY-x86_64"
|
||||
CACHE_HIT=$(gh actions-cache list -B master --key $KEY | grep -q $KEY && echo 'true' || echo 'false')
|
||||
|
||||
echo "git_hash=$(git rev-parse --short=9 HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "cache_key=$CACHE_KEY" >> $GITHUB_OUTPUT
|
||||
echo "cache_hit=$CACHE_HIT" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Flatpak Manifest
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1
|
||||
with:
|
||||
build-bundle: ${{ fromJSON(needs.config.outputs.create_artifacts) }}
|
||||
bundle: obs-studio-flatpak-${{ steps.setup.outputs.git_hash }}.flatpak
|
||||
manifest-path: build-aux/com.obsproject.Studio.json
|
||||
cache: ${{ fromJSON(steps.setup.outputs.cache_hit) || (github.event_name == 'push' && github.ref == 'refs/heads/master') }}
|
||||
restore-cache: ${{ fromJSON(steps.setup.outputs.cache_hit) }}
|
||||
cache-key: ${{ steps.setup.outputs.cache_key }}
|
||||
|
||||
macos_release:
|
||||
name: '03 - macOS notarized image'
|
||||
runs-on: [macos-13]
|
||||
needs: [macos_build]
|
||||
env:
|
||||
BUILD_FOR_DISTRIBUTION: 'ON'
|
||||
HAVE_SPARKLE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY != '' }}
|
||||
outputs:
|
||||
run_sparkle: ${{ steps.sparkle_check.outputs.run_sparkle }}
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' }}
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ['x86_64', 'arm64']
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: 'Install Apple Developer Certificate'
|
||||
id: macos-codesign
|
||||
env:
|
||||
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
|
||||
MACOS_SIGNING_CERT: ${{ secrets.MACOS_SIGNING_CERT }}
|
||||
MACOS_SIGNING_CERT_PASSWORD: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}
|
||||
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
MACOS_PROVISIONING_PROFILE: ${{ secrets.MACOS_SIGNING_PROVISIONING_PROFILE }}
|
||||
MACOS_NOTARIZATION_USERNAME: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
|
||||
MACOS_NOTARIZATION_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
|
||||
run: |
|
||||
if [[ "${MACOS_SIGNING_IDENTITY}" && "${MACOS_SIGNING_CERT}" ]]; then
|
||||
CERTIFICATE_PATH="${RUNNER_TEMP}/build_certificate.p12"
|
||||
KEYCHAIN_PATH="${RUNNER_TEMP}/app-signing.keychain-db"
|
||||
|
||||
echo -n "${MACOS_SIGNING_CERT}" | base64 --decode --output="${CERTIFICATE_PATH}"
|
||||
|
||||
: "${MACOS_KEYCHAIN_PASSWORD:="$(echo ${RANDOM} | sha1sum | head -c 32)"}"
|
||||
|
||||
security create-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}"
|
||||
security set-keychain-settings -lut 21600 "${KEYCHAIN_PATH}"
|
||||
security unlock-keychain -p "${MACOS_KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}"
|
||||
|
||||
security import "${CERTIFICATE_PATH}" -P "${MACOS_SIGNING_CERT_PASSWORD}" -A \
|
||||
-t cert -f pkcs12 -k "${KEYCHAIN_PATH}" \
|
||||
-T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/xcrun
|
||||
|
||||
security set-key-partition-list -S 'apple-tool:,apple:' -k "${MACOS_KEYCHAIN_PASSWORD}" \
|
||||
"${KEYCHAIN_PATH}" &> /dev/null
|
||||
security list-keychain -d user -s "${KEYCHAIN_PATH}" 'login-keychain'
|
||||
|
||||
echo "CODESIGN_IDENT=${MACOS_SIGNING_IDENTITY}" >> $GITHUB_ENV
|
||||
echo "MACOS_KEYCHAIN_PASSWORD=${MACOS_KEYCHAIN_PASSWORD}" >> $GITHUB_ENV
|
||||
echo "haveCodesignIdent=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "CODESIGN_IDENT=-" >> $GITHUB_ENV
|
||||
echo "haveCodesignIdent=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ "${MACOS_PROVISIONING_PROFILE}" ]]; then
|
||||
PROFILE_PATH="${RUNNER_TEMP}/build_profile.provisionprofile"
|
||||
echo -n "${MACOS_PROVISIONING_PROFILE}" | base64 --decode --output="${PROFILE_PATH}"
|
||||
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
security cms -D -i "${PROFILE_PATH}" -o "${RUNNER_TEMP}/build_profile.plist"
|
||||
UUID="$(plutil -extract UUID raw "${RUNNER_TEMP}/build_profile.plist")"
|
||||
TEAM_ID="$(plutil -extract TeamIdentifier.0 raw -expect string "${RUNNER_TEMP}/build_profile.plist")"
|
||||
|
||||
cp "${PROFILE_PATH}" ~/Library/MobileDevice/Provisioning\ Profiles/${UUID}.provisionprofile
|
||||
echo "provisionprofileUUID=${UUID}" >> $GITHUB_OUTPUT
|
||||
echo "haveProvisioningProfile=true" >> $GITHUB_OUTPUT
|
||||
echo "CODESIGN_TEAM=${TEAM_ID}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "haveProvisioningProfile=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ "${MACOS_NOTARIZATION_USERNAME}" && "${MACOS_NOTARIZATION_PASSWORD}" ]]; then
|
||||
echo "haveNotarizationUser=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "haveNotarizationUser=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: 'Checkout'
|
||||
if: ${{ fromJSON(steps.macos-codesign.outputs.haveCodesignIdent) && fromJSON(steps.macos-codesign.outputs.haveNotarizationUser) }}
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: 'Setup build environment'
|
||||
if: ${{ fromJSON(steps.macos-codesign.outputs.haveCodesignIdent) && fromJSON(steps.macos-codesign.outputs.haveNotarizationUser) }}
|
||||
id: setup
|
||||
run: |
|
||||
echo "commitHash=$(git rev-parse --short=9 HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: 'Determine if Sparkle should run'
|
||||
if: env.HAVE_CODESIGN_IDENTITY == 'true'
|
||||
id: sparkle_check
|
||||
run: |
|
||||
echo 'run_sparkle=${{ env.HAVE_SPARKLE_KEY }}' >> $GITHUB_OUTPUT
|
||||
|
||||
- name: 'Download artifact'
|
||||
if: ${{ fromJSON(steps.macos-codesign.outputs.haveCodesignIdent) && fromJSON(steps.macos-codesign.outputs.haveNotarizationUser) }}
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: 'obs-studio-macos-${{ matrix.arch }}-${{ steps.setup.outputs.commitHash }}'
|
||||
|
||||
- name: 'Create disk image for distribution'
|
||||
if: ${{ fromJSON(steps.macos-codesign.outputs.haveCodesignIdent) && fromJSON(steps.macos-codesign.outputs.haveNotarizationUser) }}
|
||||
env:
|
||||
CODESIGN_IDENT_USER: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}
|
||||
CODESIGN_IDENT_PASS: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}
|
||||
run: |
|
||||
ARTIFACT_NAME=$(/usr/bin/find . -type f -name "obs-studio-*.dmg" -depth 1 | head -1)
|
||||
CI/macos/03_package_obs.sh --notarize-image ${ARTIFACT_NAME}
|
||||
|
||||
echo "FILE_NAME=$(basename ${ARTIFACT_NAME})" >> $GITHUB_ENV
|
||||
|
||||
- name: 'Upload build Artifact'
|
||||
if: ${{ fromJSON(steps.macos-codesign.outputs.haveCodesignIdent) && fromJSON(steps.macos-codesign.outputs.haveNotarizationUser) }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: 'obs-studio-macos-${{ matrix.arch }}-notarized'
|
||||
path: '${{ github.workspace }}/${{ env.FILE_NAME }}'
|
||||
|
||||
macos_sparkle:
|
||||
name: '04 - macOS Sparkle Updates'
|
||||
runs-on: [macos-13]
|
||||
needs: [macos_release]
|
||||
if: fromJSON(needs.macos_release.outputs.run_sparkle)
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ['x86_64', 'arm64']
|
||||
env:
|
||||
SPARKLE_VERSION: '2.3.2'
|
||||
SPARKLE_HASH: '2b3fe6918ca20a83729aad34f8f693a678b714a17d33b5f13ca2d25edfa7eed3'
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: 'repo'
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- name: 'Download artifact'
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: 'obs-studio-macos-${{ matrix.arch }}-notarized'
|
||||
path: 'artifacts'
|
||||
|
||||
- name: 'Install Python requirements'
|
||||
run: pip3 install requests xmltodict
|
||||
|
||||
- name: 'Install Brew requirements'
|
||||
run: brew install coreutils pandoc
|
||||
|
||||
- name: 'Setup Sparkle'
|
||||
run: |
|
||||
curl -L "https://github.com/sparkle-project/Sparkle/releases/download/${{ env.SPARKLE_VERSION }}/Sparkle-${{ env.SPARKLE_VERSION }}.tar.xz" -o Sparkle.tar.xz
|
||||
|
||||
if [[ '${{ env.SPARKLE_HASH }}' != "$(sha256sum Sparkle.tar.xz | cut -d " " -f 1)" ]]; then
|
||||
echo "Sparkle download hash does not match!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir sparkle && cd sparkle
|
||||
tar -xf ../Sparkle.tar.xz
|
||||
|
||||
- name: 'Setup folder structure'
|
||||
run: |
|
||||
mkdir builds
|
||||
mkdir -p output/appcasts/stable
|
||||
mkdir -p output/sparkle_deltas/${{ matrix.arch }}
|
||||
|
||||
- name: 'Determine branch and tag'
|
||||
id: branch
|
||||
run: |
|
||||
pushd repo
|
||||
|
||||
GIT_TAG="$(git describe --tags --abbrev=0)"
|
||||
if [[ ${GIT_TAG} == *'beta'* || ${GIT_TAG} == *'rc'* ]]; then
|
||||
echo "branch=beta" >> $GITHUB_OUTPUT
|
||||
echo "deltas=1" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "branch=stable" >> $GITHUB_OUTPUT
|
||||
echo "deltas=1" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
# Write tag description to file
|
||||
git tag -l --format='%(contents)' ${GIT_TAG} >> ../notes.rst
|
||||
|
||||
- name: 'Download existing Appcast and builds'
|
||||
run: python3 repo/CI/macos/appcast_download.py --branch "${{ steps.branch.outputs.branch }}" --max-old-versions ${{ steps.branch.outputs.deltas }}
|
||||
|
||||
- name: 'Prepare release notes'
|
||||
run: |
|
||||
# Insert underline at line 2 to turn first line into heading
|
||||
sed -i '' '2i\'$'\n''###################################################' notes.rst
|
||||
pandoc -f rst -t html notes.rst -o output/appcasts/notes_${{ steps.branch.outputs.branch }}.html
|
||||
|
||||
- name: 'Setup Sparkle key'
|
||||
run: echo -n "${{ secrets.SPARKLE_PRIVATE_KEY }}" >> eddsa_private.key
|
||||
|
||||
- name: 'Generate Appcast'
|
||||
run: |
|
||||
mv artifacts/*.dmg builds/
|
||||
./sparkle/bin/generate_appcast \
|
||||
--verbose \
|
||||
--ed-key-file ./eddsa_private.key \
|
||||
--download-url-prefix "https://cdn-fastly.obsproject.com/downloads/" \
|
||||
--full-release-notes-url "https://obsproject.com/osx_update/notes_${{ steps.branch.outputs.branch }}.html" \
|
||||
--maximum-versions 0 \
|
||||
--maximum-deltas ${{ steps.branch.outputs.deltas }} \
|
||||
--channel "${{ steps.branch.outputs.branch }}" builds/
|
||||
# Move deltas, if any
|
||||
if compgen -G "builds/*.delta" > /dev/null; then
|
||||
mv builds/*.delta output/sparkle_deltas/${{ matrix.arch }}
|
||||
fi
|
||||
# Move appcasts
|
||||
mv builds/*.xml output/appcasts/
|
||||
|
||||
- name: 'Create 1.x Appcast'
|
||||
run: python3 repo/CI/macos/appcast_convert.py
|
||||
|
||||
- name: 'Upload Appcast and Deltas'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: 'macos-sparkle-updates'
|
||||
path: '${{ github.workspace }}/output'
|
||||
@@ -0,0 +1,94 @@
|
||||
name: Pull Request
|
||||
run-name: ${{ github.event.pull_request.title }} pull request run 🚀
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
branches: [master]
|
||||
types: [ opened, synchronize, reopened, labeled, unlabeled ]
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
check-format:
|
||||
name: Check Formatting 🔍
|
||||
uses: ./.github/workflows/check-format.yaml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
build-project:
|
||||
name: Build Project 🧱
|
||||
uses: ./.github/workflows/build-project.yaml
|
||||
secrets: inherit
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
compatibility-validation:
|
||||
name: Validate Compatibility Data 🕵️
|
||||
if: github.base_ref == 'master'
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for Changed Files ✅
|
||||
uses: ./.github/actions/check-changes
|
||||
id: checks
|
||||
with:
|
||||
baseRef: origin/${{ github.base_ref }}
|
||||
checkGlob: plugins/win-capture/data/*.json
|
||||
|
||||
- name: Check for Invalid Compatibility Data 📉
|
||||
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||
uses: ./.github/actions/compatibility-validator
|
||||
|
||||
services-validation:
|
||||
name: Validate Services Data 🕵️
|
||||
if: github.base_ref == 'master'
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for Changed Files ✅
|
||||
uses: ./.github/actions/check-changes
|
||||
id: checks
|
||||
with:
|
||||
baseRef: origin/${{ github.base_ref }}
|
||||
checkGlob: plugins/rtmp-services/data/*.json
|
||||
|
||||
- name: Check Services JSON Schema 📉
|
||||
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||
uses: ./.github/actions/services-validator
|
||||
with:
|
||||
repositorySecret: ${{ secrets.GITHUB_TOKEN }}
|
||||
runSchemaChecks: true
|
||||
runServiceChecks: false
|
||||
|
||||
update-documentation:
|
||||
name: Update Documentation 📖
|
||||
if: github.repository_owner == 'obsproject' && github.base_ref == 'master'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for Changed Files ✅
|
||||
uses: ./.github/actions/check-changes
|
||||
id: checks
|
||||
with:
|
||||
baseRef: origin/${{ github.base_ref }}
|
||||
checkGlob: docs/sphinx
|
||||
|
||||
- uses: ./.github/actions/generate-docs
|
||||
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||
@@ -0,0 +1,150 @@
|
||||
name: Publish
|
||||
run-name: Publish Repository Actions 🛫
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
branches:
|
||||
- master
|
||||
- 'release/**'
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
check-tag:
|
||||
name: Check Release Tag
|
||||
if: github.repository_owner == 'obsproject'
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
validTag: ${{ steps.check.outputs.validTag }}
|
||||
flatpakMatrix: ${{ steps.check.outputs.flatpakMatrix }}
|
||||
steps:
|
||||
- name: Check Release Tag ☑️
|
||||
id: check
|
||||
run: |
|
||||
: Check Release Tag ☑️
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
shopt -s extglob
|
||||
|
||||
case "${GITHUB_REF_NAME}" in
|
||||
+([0-9]).+([0-9]).+([0-9]) )
|
||||
echo 'validTag=true' >> $GITHUB_OUTPUT
|
||||
echo 'flatpakMatrix=["beta", "stable"]' >> $GITHUB_OUTPUT
|
||||
;;
|
||||
+([0-9]).+([0-9]).+([0-9])-@(beta|rc)*([0-9]) )
|
||||
echo 'validTag=true' >> $GITHUB_OUTPUT
|
||||
echo 'flatpakMatrix=["beta"]' >> $GITHUB_OUTPUT
|
||||
;;
|
||||
*) echo 'validTag=false' >> $GITHUB_OUTPUT ;;
|
||||
esac
|
||||
|
||||
flatpak-publish:
|
||||
name: Publish to Flathub 📦
|
||||
needs: check-tag
|
||||
if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag)
|
||||
runs-on: ubuntu-22.04
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
env:
|
||||
FLATPAK_BUILD_PATH: flatpak_app/files/share
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:kde-6.4
|
||||
options: --privileged
|
||||
strategy:
|
||||
matrix:
|
||||
branch: ${{ fromJSON(needs.check-tag.outputs.flatpakMatrix) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
set-safe-directory: ${{ github.workspace }}
|
||||
|
||||
- name: Set Up Environment 🔧
|
||||
id: setup
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
: Set Up Environment 🔧
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
git config --global --add safe.directory "${GITHUB_WORKSPACE}"
|
||||
|
||||
dnf install -y -q gh
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
cache_key='flatpak-builder-${{ hashFiles('build-aux/**/*.json') }}'
|
||||
cache_ref='master'
|
||||
read -r key size unit _ ref _ <<< \
|
||||
"$(gh actions-cache list -B ${cache_ref} --key "${cache_key}-x86_64" | head -1)"
|
||||
|
||||
if [[ "${key}" ]]; then
|
||||
echo "cacheKey=${cache_key}" >> $GITHUB_OUTPUT
|
||||
echo "cacheHit=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "cacheHit=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
echo "commitHash=$(git rev-parse --short=9 HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build Flatpak Manifest
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v5
|
||||
with:
|
||||
bundle: obs-studio-${{ steps.setup.outputs.commitHash }}.flatpak
|
||||
manifest-path: ${{ github.workspace }}/build-aux/com.obsproject.Studio.json
|
||||
cache: ${{ fromJSON(steps.setup.outputs.cacheHit) }}
|
||||
cache-key: ${{ steps.setup.outputs.cacheKey }}
|
||||
mirror-screenshots-url: https://dl.flathub.org/repo/screenshots
|
||||
branch: ${{ matrix.branch }}
|
||||
|
||||
- name: Validate AppStream
|
||||
working-directory: ${{ env.FLATPAK_BUILD_PATH }}
|
||||
run: |
|
||||
: Validate AppStream
|
||||
appstream-util validate appdata/com.obsproject.Studio.appdata.xml
|
||||
|
||||
- name: Verify Icon and Metadata in app-info
|
||||
working-directory: ${{ env.FLATPAK_BUILD_PATH }}
|
||||
run: |
|
||||
: Verify Icon and Metadata in app-info
|
||||
test -f app-info/icons/flatpak/128x128/com.obsproject.Studio.png || { echo "Missing 128x128 icon in app-info!"; exit 1; }
|
||||
test -f app-info/xmls/com.obsproject.Studio.xml.gz || { echo "Missing com.obsproject.Studio.xml.gz in app-info!"; exit 1; }
|
||||
|
||||
- name: Commit Screenshots to OSTree Repository
|
||||
run: |
|
||||
: Commit Screenshots to OSTree Repository
|
||||
ostree commit --repo=repo --canonical-permissions --branch=screenshots/x86_64 flatpak_app/screenshots
|
||||
|
||||
- name: Publish to Flathub Beta
|
||||
uses: flatpak/flatpak-github-actions/flat-manager@v5
|
||||
if: ${{ matrix.branch == 'beta' }}
|
||||
with:
|
||||
flat-manager-url: https://hub.flathub.org/
|
||||
repository: beta
|
||||
token: ${{ secrets.FLATHUB_BETA_TOKEN }}
|
||||
|
||||
- name: Publish to Flathub
|
||||
uses: flatpak/flatpak-github-actions/flat-manager@v5
|
||||
if: ${{ matrix.branch == 'stable' }}
|
||||
with:
|
||||
flat-manager-url: https://hub.flathub.org/
|
||||
repository: stable
|
||||
token: ${{ secrets.FLATHUB_TOKEN }}
|
||||
|
||||
steam-upload:
|
||||
name: Upload Steam Builds 🚂
|
||||
needs: check-tag
|
||||
if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag)
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/steam-upload
|
||||
with:
|
||||
steamSecret: ${{ secrets.STEAM_SHARED_SECRET }}
|
||||
steamUser: ${{ secrets.STEAM_USER }}
|
||||
steamPassword: ${{ secrets.STEAM_PASSWORD }}
|
||||
workflowSecret: ${{ github.token }}
|
||||
preview: false
|
||||
@@ -0,0 +1,313 @@
|
||||
name: Push to master and release branches
|
||||
run-name: ${{ github.ref_name }} push run 🚀
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
branches:
|
||||
- master
|
||||
- 'release/**'
|
||||
tags:
|
||||
- '*'
|
||||
permissions:
|
||||
contents: write
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} @ ${{ github.ref }}'
|
||||
cancel-in-progress: ${{ github.ref_type == 'tag' }}
|
||||
jobs:
|
||||
check-format:
|
||||
name: Check Formatting 🔍
|
||||
if: github.ref_name == 'master'
|
||||
uses: ./.github/workflows/check-format.yaml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
build-project:
|
||||
name: Build Project 🧱
|
||||
uses: ./.github/workflows/build-project.yaml
|
||||
secrets: inherit
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
compatibility-validation:
|
||||
name: Validate Compatibility Data 🕵️
|
||||
if: github.ref_name == 'master'
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for Changed Files ✅
|
||||
uses: ./.github/actions/check-changes
|
||||
id: checks
|
||||
with:
|
||||
baseRef: ${{ github.event.before }}
|
||||
checkGlob: plugins/win-capture/data/*.json
|
||||
|
||||
- name: Check for Invalid Compatibility Data 📉
|
||||
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||
uses: ./.github/actions/compatibility-validator
|
||||
with:
|
||||
repositorySecret: ${{ github.token }}
|
||||
|
||||
services-validation:
|
||||
name: Validate Service Data 🕵️
|
||||
if: github.ref_name == 'master'
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for Changed Files ✅
|
||||
uses: ./.github/actions/check-changes
|
||||
id: checks
|
||||
with:
|
||||
baseRef: ${{ github.event.before }}
|
||||
checkGlob: plugins/rtmp-services/data/*.json
|
||||
|
||||
- name: Check Services JSON Schema 📉
|
||||
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||
uses: ./.github/actions/services-validator
|
||||
with:
|
||||
repositorySecret: ${{ github.token }}
|
||||
runSchemaChecks: true
|
||||
runServiceChecks: false
|
||||
|
||||
upload-language-files:
|
||||
name: Upload Language Files 🌐
|
||||
if: github.repository_owner == 'obsproject' && github.ref_name == 'master'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for Changed Files ✅
|
||||
uses: ./.github/actions/check-changes
|
||||
id: checks
|
||||
with:
|
||||
baseRef: ${{ github.event.before }}
|
||||
checkGlob: '**/en-US.ini'
|
||||
|
||||
- name: Upload US English Language Files 🇺🇸
|
||||
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||
uses: obsproject/obs-crowdin-sync/upload@0.2.1
|
||||
env:
|
||||
CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }}
|
||||
GITHUB_EVENT_BEFORE: ${{ github.event.before }}
|
||||
|
||||
update-documentation:
|
||||
name: Update Documentation 📖
|
||||
if: github.repository_owner == 'obsproject' && (github.ref_name == 'master' || github.ref_type == 'tag')
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for Changed Files ✅
|
||||
if: github.ref_name != 'tag'
|
||||
uses: ./.github/actions/check-changes
|
||||
id: checks
|
||||
with:
|
||||
baseRef: ${{ github.event.before }}
|
||||
checkGlob: '!(cmake*)'
|
||||
|
||||
- uses: ./.github/actions/generate-docs
|
||||
if: github.ref_type == 'tag' || fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||
with:
|
||||
disableLinkExtensions: ${{ github.ref_type == 'tag' }}
|
||||
|
||||
deploy-documentation:
|
||||
name: Deploy Documentation to Cloudflare ☁️
|
||||
if: github.repository_owner == 'obsproject' && github.ref_type == 'tag'
|
||||
runs-on: ubuntu-22.04
|
||||
needs: update-documentation
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Get Commit Information 🆔
|
||||
id: setup
|
||||
run: |
|
||||
: Get Commit Hash 🆔
|
||||
echo "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: OBS Studio Docs (No Extensions) ${{ steps.setup.outputs.commitHash }}
|
||||
path: docs
|
||||
|
||||
- name: Set Up Redirects 🔄
|
||||
run: |
|
||||
: Set Up Redirects 🔄
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
echo "/previous/27.2 https://obsproject.com/docs/27.2 302" >> docs/_redirects
|
||||
echo "/previous/:major.:minor https://:major-:minor.${{ vars.CF_PAGES_PROJECT }}.pages.dev 302" >> docs/_redirects
|
||||
|
||||
- name: Publish to Live Page
|
||||
uses: cloudflare/wrangler-action@4c10c1822abba527d820b29e6333e7f5dac2cabd
|
||||
with:
|
||||
workingDirectory: docs
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
command: pages publish . --project-name=${{ vars.CF_PAGES_PROJECT }} --commit-hash='${{ steps.setup.outputs.commitHash }}'
|
||||
|
||||
create-appcast:
|
||||
name: Create Sparkle Appcast 🎙️
|
||||
if: github.repository_owner == 'obsproject' && github.ref_type == 'tag'
|
||||
runs-on: macos-13
|
||||
needs: build-project
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [arm64, x86_64]
|
||||
defaults:
|
||||
run:
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- name: Set Up Environment 🔧
|
||||
id: setup
|
||||
run: |
|
||||
: Set Up Environment 🔧
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local channel='stable'
|
||||
if [[ ${GITHUB_REF_NAME} == *(beta|rc)* ]] {
|
||||
channel='beta'
|
||||
}
|
||||
|
||||
local -A arch_names=(x86_64 intel arm64 apple)
|
||||
print "cpuName=${arch_names[${{ matrix.target }}]}" >> $GITHUB_OUTPUT
|
||||
print "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||
print "channel=${channel}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download Artifact 📥
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: obs-studio-macos-${{ matrix.target }}-${{ steps.setup.outputs.commitHash }}
|
||||
|
||||
- name: Generate Appcast 🎙️
|
||||
id: generate-appcast
|
||||
uses: ./.github/actions/sparkle-appcast
|
||||
with:
|
||||
sparklePrivateKey: ${{ secrets.SPARKLE_PRIVATE_KEY }}
|
||||
baseImage: ${{ github.workspace }}/obs-studio-*-macos-${{ steps.setup.outputs.cpuName }}.dmg
|
||||
channel: ${{ steps.setup.outputs.channel }}
|
||||
count: 1
|
||||
urlPrefix: 'https://cdn-fastly.obsproject.com/downloads'
|
||||
customTitle: 'OBS Studio'
|
||||
customLink: 'https://obsproject.com/'
|
||||
|
||||
- name: Upload Artifacts 📡
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: macos-sparkle-update-${{ matrix.target }}
|
||||
path: ${{ github.workspace }}/output
|
||||
|
||||
create-release:
|
||||
name: Create Release 🛫
|
||||
if: github.ref_type == 'tag'
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-project
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Check Release Tag ☑️
|
||||
id: check
|
||||
run: |
|
||||
: Check Release Tag ☑️
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
shopt -s extglob
|
||||
|
||||
case "${GITHUB_REF_NAME}" in
|
||||
+([0-9]).+([0-9]).+([0-9]) )
|
||||
echo 'validTag=true' >> $GITHUB_OUTPUT
|
||||
echo 'prerelease=false' >> $GITHUB_OUTPUT
|
||||
echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
+([0-9]).+([0-9]).+([0-9])-@(beta|rc)*([0-9]) )
|
||||
echo 'validTag=true' >> $GITHUB_OUTPUT
|
||||
echo 'prerelease=true' >> $GITHUB_OUTPUT
|
||||
echo "version=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
*) echo 'validTag=false' >> $GITHUB_OUTPUT ;;
|
||||
esac
|
||||
|
||||
- name: Download Build Artifacts 📥
|
||||
uses: actions/download-artifact@v3
|
||||
if: ${{ fromJSON(steps.check.outputs.validTag) }}
|
||||
|
||||
- name: Rename Files 🏷️
|
||||
if: fromJSON(steps.check.outputs.validTag)
|
||||
run: |
|
||||
: Rename Files 🏷️
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
|
||||
root_dir="${PWD}"
|
||||
|
||||
commit_hash="${GITHUB_SHA:0:9}"
|
||||
macos_arm64_artifact_name="obs-studio-macos-arm64-${commit_hash}"
|
||||
macos_arm64_dsym_artifact_name="obs-studio-macos-arm64-${commit_hash}-dSYMs"
|
||||
macos_intel_artifact_name="obs-studio-macos-x86_64-${commit_hash}"
|
||||
macos_intel_dsym_artifact_name="obs-studio-macos-x86_64-${commit_hash}-dSYMs"
|
||||
ubuntu_x86_64_artifact_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}"
|
||||
ubuntu_x86_64_debug_name="obs-studio-ubuntu-22.04-x86_64-${commit_hash}-dbgsym"
|
||||
|
||||
echo '::group::Renaming Artifacts'
|
||||
mv -v "${macos_arm64_artifact_name}/"obs-studio-*-macos-apple.dmg \
|
||||
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Apple.dmg
|
||||
mv -v "${macos_arm64_dsym_artifact_name}/"obs-studio-*-macos-apple-dSYMs.tar.xz \
|
||||
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Apple-dSYMs.tar.xz
|
||||
mv -v "${macos_intel_artifact_name}/"obs-studio-*-macos-intel.dmg \
|
||||
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel.dmg
|
||||
mv -v "${macos_intel_dsym_artifact_name}/"obs-studio-*-macos-intel-dSYMs.tar.xz \
|
||||
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel-dSYMs.tar.xz
|
||||
mv -v "${ubuntu_x86_64_artifact_name}/"obs-studio-*-x86_64-linux-gnu.deb \
|
||||
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-x86_64.deb
|
||||
mv -v "${ubuntu_x86_64_debug_name}/"obs-studio-*-x86_64-linux-gnu-dbgsym.ddeb \
|
||||
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-x86_64-dbsym.ddeb
|
||||
echo '::endgroup::'
|
||||
|
||||
- name: Generate Checksums 🪪
|
||||
if: fromJSON(steps.check.outputs.validTag)
|
||||
run: |
|
||||
: Generate Checksums 🪪
|
||||
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||
shopt -s extglob
|
||||
|
||||
echo "### Checksums" > ${{ github.workspace }}/CHECKSUMS.txt
|
||||
for file in ${{ github.workspace }}/@(*.deb|*.ddeb|*.dmg|*.tar.xz); do
|
||||
echo " ${file##*/}: $(sha256sum "${file}" | cut -d " " -f 1)" >> ${{ github.workspace }}/CHECKSUMS.txt
|
||||
done
|
||||
|
||||
- name: Create Release 🛫
|
||||
if: fromJSON(steps.check.outputs.validTag)
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@d4e8205d7e959a9107da6396278b2f1f07af0f9b
|
||||
with:
|
||||
draft: true
|
||||
prerelease: ${{ fromJSON(steps.check.outputs.prerelease) }}
|
||||
tag_name: ${{ steps.check.outputs.version }}
|
||||
name: OBS Studio ${{ steps.check.outputs.version }}
|
||||
body_path: ${{ github.workspace }}/CHECKSUMS.txt
|
||||
files: |
|
||||
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-macOS-*.dmg
|
||||
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-macOS-*-dSYMs.tar.xz
|
||||
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-*.deb
|
||||
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-*.ddeb
|
||||
@@ -1,30 +0,0 @@
|
||||
name: UI XML Validator
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "cmake/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "UI/forms/**"
|
||||
|
||||
jobs:
|
||||
qt-xml-validator:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Install xmllint
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install --no-install-recommends -y libxml2-utils
|
||||
|
||||
- name: Register Annotations
|
||||
uses: korelstar/xmllint-problem-matcher@v1
|
||||
|
||||
- name: Validate
|
||||
run: |
|
||||
xmllint --schema UI/forms/XML-Schema-Qt5.15.xsd --noout UI/forms/*.ui UI/forms/**/*.ui
|
||||
@@ -0,0 +1,121 @@
|
||||
name: Scheduled
|
||||
run-name: Scheduled Repository Actions ⏰
|
||||
on:
|
||||
schedule:
|
||||
- cron: 17 0 * * *
|
||||
permissions:
|
||||
contents: write
|
||||
concurrency:
|
||||
group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
services-availability:
|
||||
name: Check Service Availability 🛜
|
||||
if: github.repository_owner == 'obsproject'
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set Up Homebrew 🍺
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
- name: Check for Defunct Services 📉
|
||||
uses: ./.github/actions/services-validator
|
||||
with:
|
||||
repositorySecret: ${{ secrets.GITHUB_TOKEN }}
|
||||
runSchemaChecks: false
|
||||
runServiceChecks: true
|
||||
createPullRequest: true
|
||||
|
||||
cache-cleanup:
|
||||
name: Cache Cleanup 🧹
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
actions: write
|
||||
steps:
|
||||
- name: Remove Stale Ccache Caches
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
: Remove Stale Ccache Caches
|
||||
|
||||
echo '::group::Processing master branch cache entries'
|
||||
while IFS=";" read -r cache_id cache_name; do
|
||||
if [[ "${cache_name}" ]]; then
|
||||
result=true
|
||||
gh api -X DELETE repos/${GITHUB_REPOSITORY}/actions/caches?key=${cache_name} --jq '.total_count' &> /dev/null || result=false
|
||||
|
||||
if ${result}; then
|
||||
echo "Deleted cache entry ${cache_name}"
|
||||
else
|
||||
echo "::warning::Unable to delete cache entry ${cache_name}"
|
||||
fi
|
||||
fi
|
||||
done <<< \
|
||||
"$(gh api repos/${GITHUB_REPOSITORY}/actions/caches \
|
||||
--jq '.actions_caches.[] | select(.ref|test("refs/heads/master")) | select(.key|test(".*-ccache-*")) | {id, key} | join(";")')"
|
||||
echo '::endgroup::'
|
||||
|
||||
|
||||
echo '::group::Processing pull request cache entries'
|
||||
while IFS=";" read -r cache_id cache_name cache_ref; do
|
||||
if [[ "${cache_name}" ]]; then
|
||||
result=true
|
||||
gh api -X DELETE repos/${GITHUB_REPOSITORY}/actions/caches?key=${cache_name} --jq '.total_count' &> /dev/null || result=false
|
||||
|
||||
pr_number=$(echo ${cache_ref} | cut -d '/' -f 3)
|
||||
|
||||
if ${result}; then
|
||||
echo "Deleted PR #${pr_number} cache entry ${cache_name}"
|
||||
else
|
||||
echo "::warning::Unable to delete PR #${pr_number} cache entry ${cache_name}"
|
||||
fi
|
||||
fi
|
||||
done <<< \
|
||||
"$(gh api repos/${GITHUB_REPOSITORY}/actions/caches \
|
||||
--jq '.actions_caches.[] | select(.ref|test("refs/heads/master")|not) | select(.key|test(".*-ccache-*")) | {id, key, ref} | join(";")')"
|
||||
echo '::endgroup::'
|
||||
|
||||
build-project:
|
||||
name: Build Project 🧱
|
||||
uses: ./.github/workflows/build-project.yaml
|
||||
needs: cache-cleanup
|
||||
secrets: inherit
|
||||
|
||||
steam-upload:
|
||||
name: Upload Steam Builds 🚂
|
||||
needs: [build-project]
|
||||
if: github.repository_owner == 'obsproject'
|
||||
runs-on: macos-13
|
||||
defaults:
|
||||
run:
|
||||
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Check Nightly Runs ☑️
|
||||
id: checks
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
: Check Nightly Runs ☑️
|
||||
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||
|
||||
local last_nightly=$(gh run list --workflow scheduled.yaml --limit 1 --json headSha --jq '.[0].headSha')
|
||||
|
||||
if [[ "${GITHUB_SHA}" == "${last_nightly}" ]] {
|
||||
print "passed=false" >> $GITHUB_OUTPUT
|
||||
} else {
|
||||
print "passed=true" >> $GITHUB_OUTPUT
|
||||
}
|
||||
|
||||
- uses: ./.github/actions/steam-upload
|
||||
if: fromJSON(steps.checks.outputs.passed)
|
||||
with:
|
||||
steamSecret: ${{ secrets.STEAM_SHARED_SECRET }}
|
||||
steamUser: ${{ secrets.STEAM_USER }}
|
||||
steamPassword: ${{ secrets.STEAM_PASSWORD }}
|
||||
workflowSecret: ${{ secrets.GITHUB_TOKEN }}
|
||||
preview: ${{ github.repository_owner != 'obsproject' }}
|
||||
@@ -1,93 +0,0 @@
|
||||
name: Services Validator
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "plugins/rtmp-services/data/services.json"
|
||||
- "plugins/rtmp-services/data/package.json"
|
||||
pull_request:
|
||||
paths:
|
||||
- "plugins/rtmp-services/data/services.json"
|
||||
- "plugins/rtmp-services/data/package.json"
|
||||
schedule:
|
||||
- cron: 0 0 * * *
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
schema:
|
||||
name: Schema
|
||||
runs-on: [ubuntu-20.04]
|
||||
if: ${{ github.repository_owner == 'obsproject' || github.event_name != 'schedule' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install & Configure Python
|
||||
run: |
|
||||
sudo apt install python3.9-dev
|
||||
python3.9 -m pip install jsonschema json_source_map
|
||||
|
||||
- name: Validate Service JSON Schema
|
||||
run: |
|
||||
JSON_FILES=(
|
||||
plugins/rtmp-services/data/services.json
|
||||
plugins/rtmp-services/data/package.json
|
||||
)
|
||||
python3.9 CI/check-jsonschema.py "${JSON_FILES[@]}"
|
||||
|
||||
- name: Annotate Errors
|
||||
if: failure()
|
||||
uses: yuzutech/annotations-action@v0.4.0
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
title: "Service JSON Errors"
|
||||
input: "./validation_errors.json"
|
||||
|
||||
service_check:
|
||||
name: Service Check
|
||||
runs-on: macos-latest
|
||||
needs: schema
|
||||
if: ${{ github.repository_owner == 'obsproject' && github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Restore cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/other
|
||||
# Workaround to create a new cache every time, since a cache key is immutable:
|
||||
# https://github.com/actions/cache/blob/main/workarounds.md#update-a-cache
|
||||
key: service-check-${{ github.run_id }}
|
||||
restore-keys: service-check
|
||||
|
||||
- name: Install & Configure Python
|
||||
run: |
|
||||
python3 -m pip install requests
|
||||
|
||||
- name: Check Services
|
||||
id: check
|
||||
run: python3 -u CI/check-services.py
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
WORKFLOW_RUN_ID: ${{ github.run_id }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: timestamps
|
||||
path: ${{ github.workspace }}/other/*
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@36a56dac0739df8d3d8ebb9e6e41026ba248ec27
|
||||
if: steps.check.outputs.make_pr == 'true'
|
||||
with:
|
||||
author: "Service Checker <commits@obsproject.com>"
|
||||
commit-message: "rtmp-services: Remove defunct servers/services"
|
||||
title: "rtmp-services: Remove defunct servers/services"
|
||||
branch: "automated/clean-services"
|
||||
body: ${{ fromJSON(steps.check.outputs.pr_message) }}
|
||||
delete-branch: true
|
||||
@@ -1,266 +0,0 @@
|
||||
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
|
||||
mac_arm_url_override:
|
||||
description: 'Mac ARM build to use (.dmg only)'
|
||||
required: false
|
||||
|
||||
env:
|
||||
WORKFLOW_ID: 583765
|
||||
GIT_NIGHTLY_BRANCH: master
|
||||
STEAM_NIGHTLY_BRANCH: nightly
|
||||
STEAM_STABLE_BRANCH: staging
|
||||
STEAM_BETA_BRANCH: beta_staging
|
||||
STEAM_PLAYTEST_BRANCH: staging
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
name: Steam upload
|
||||
runs-on: macos-latest
|
||||
if: github.repository_owner == 'obsproject'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: source
|
||||
|
||||
- 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(".*.zip")) .browser_download_url' <<< ${ASSETS})"
|
||||
MAC_ASSET_URL="$(jq -r '.[] | select(.name|test(".*x86_64.*.dmg")) .browser_download_url' <<< ${ASSETS})"
|
||||
MAC_ARM_ASSET_URL="$(jq -r '.[] | select(.name|test(".*arm64.*.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(".*windows-x64.*")) .archive_download_url' <<< ${ARTIFACTS})"
|
||||
MAC_ASSET_URL="$(jq -r '.artifacts[] | select(.name|test(".*macos-x86_64.*")) .archive_download_url' <<< ${ARTIFACTS})"
|
||||
MAC_ARM_ASSET_URL="$(jq -r '.artifacts[] | select(.name|test(".*macos-arm64.*")) .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
|
||||
|
||||
if [[ -n '${{ github.event.inputs.mac_arm_url_override }}' ]]; then
|
||||
MAC_ARM_ASSET_URL='${{ github.event.inputs.mac_arm_url_override }}'
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z ${WIN_ASSET_URL} || -z ${MAC_ASSET_URL} || -z ${MAC_ARM_ASSET_URL} ]]; then
|
||||
echo "Missing at least one asset URL!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# set env variables for subsequent steps
|
||||
echo "type=${TYPE}" >> $GITHUB_OUTPUT
|
||||
echo "branch=${BRANCH}" >> $GITHUB_OUTPUT
|
||||
echo "desc=${DESC}" >> $GITHUB_OUTPUT
|
||||
echo "win_url=${WIN_ASSET_URL}" >> $GITHUB_OUTPUT
|
||||
echo "mac_intel_url=${MAC_ASSET_URL}" >> $GITHUB_OUTPUT
|
||||
echo "mac_arm_url=${MAC_ARM_ASSET_URL}" >> $GITHUB_OUTPUT
|
||||
|
||||
- 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 "result=true" >> $GITHUB_OUTPUT
|
||||
if [[ '${{ steps.build-info.outputs.branch }}' == '${{ env.STEAM_BETA_BRANCH }}' ]]; then
|
||||
echo "result_playtest=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "result_playtest=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "result=false" >> $GITHUB_OUTPUT
|
||||
echo "result_playtest=false" >> $GITHUB_OUTPUT
|
||||
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 builds"
|
||||
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
|
||||
|
||||
if [[ '${{ steps.build-info.outputs.mac_arm_url }}' == *'api.github.com'* ]]; then
|
||||
curl -L -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' '${{ steps.build-info.outputs.mac_arm_url }}' -o mac_arm64.dmg.zip
|
||||
else
|
||||
curl -L '${{ steps.build-info.outputs.mac_arm_url }}' -o mac_arm64.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_windows scripts
|
||||
touch disable_updater
|
||||
)
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Extract macOS (x86)"
|
||||
mkdir -p steam-macos/x86
|
||||
# CI builds are zipped
|
||||
if [[ -f ../mac_x86.dmg.zip ]]; then
|
||||
unzip ../mac_x86.dmg.zip
|
||||
else
|
||||
mv ../mac_x86.dmg .
|
||||
fi
|
||||
|
||||
hdiutil attach -noverify -readonly -noautoopen -mountpoint /Volumes/x86 *.dmg
|
||||
cp -R /Volumes/x86/OBS.app steam-macos/x86
|
||||
hdiutil unmount /Volumes/x86
|
||||
rm *.dmg
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Extract and prepare macOS (arm64)"
|
||||
mkdir -p steam-macos/arm64
|
||||
if [[ -f ../mac_arm64.dmg.zip ]]; then
|
||||
unzip ../mac_arm64.dmg.zip
|
||||
else
|
||||
mv ../mac_arm64.dmg .
|
||||
fi
|
||||
|
||||
hdiutil attach -noverify -readonly -noautoopen -mountpoint /Volumes/arm64 *.dmg
|
||||
cp -R /Volumes/arm64/OBS.app steam-macos/arm64
|
||||
hdiutil unmount /Volumes/arm64
|
||||
rm *.dmg
|
||||
|
||||
cp ../source/CI/steam/scripts_macos/launch.sh steam-macos/launch.sh
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Setup steamcmd
|
||||
if: steps.should-run.outputs.result == 'true'
|
||||
uses: CyberAndrii/setup-steamcmd@b786e0da44db3d817e66fa3910a9560cb28c9323
|
||||
|
||||
- name: Generate Steam auth code
|
||||
if: steps.should-run.outputs.result == 'true'
|
||||
id: steam-totp
|
||||
uses: CyberAndrii/steam-totp@c7f636bc64e77f1b901e0420b7890813141508ee
|
||||
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_<branch>-<tag/short hash>", 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: Generate Steam auth code (Playtest)
|
||||
if: steps.should-run.outputs.result_playtest == 'true'
|
||||
id: steam-totp-playtest
|
||||
uses: CyberAndrii/steam-totp@c7f636bc64e77f1b901e0420b7890813141508ee
|
||||
with:
|
||||
shared_secret: ${{ secrets.STEAM_SHARED_SECRET }}
|
||||
|
||||
- name: Upload to Steam (Playtest)
|
||||
if: steps.should-run.outputs.result_playtest == 'true'
|
||||
run: |
|
||||
cd steam
|
||||
echo "::group::Prepare Steam build script"
|
||||
sed 's/@@DESC@@/${{ steps.build-info.outputs.branch }}-${{ steps.build-info.outputs.desc }}/;s/@@BRANCH@@/${{ env.STEAM_PLAYTEST_BRANCH }}/' ../source/CI/steam/obs_playtest_build.vdf > build_playtest.vdf
|
||||
echo "Generated file:"
|
||||
cat build_playtest.vdf
|
||||
echo "::endgroup::"
|
||||
echo "::group::Upload to Steam"
|
||||
steamcmd +login '${{ secrets.STEAM_USER }}' '${{ secrets.STEAM_PASSWORD }}' '${{ steps.steam-totp-playtest.outputs.code }}' +run_app_build "$(pwd)/build_playtest.vdf" +quit
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Upload Steam build logs
|
||||
if: steps.should-run.outputs.result == 'true'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: steam-build-logs
|
||||
path: ${{ github.workspace }}/steam/build/*.log
|
||||
+41
-100
@@ -1,108 +1,49 @@
|
||||
#binaries
|
||||
*.exe
|
||||
*.dll
|
||||
*.dylib
|
||||
*.so
|
||||
*.plugin
|
||||
*.framework
|
||||
*.systemextension
|
||||
# Exclude everything
|
||||
/*
|
||||
|
||||
#cmake
|
||||
/build*/
|
||||
!/build-aux/
|
||||
/release*/
|
||||
/debug*/
|
||||
.vs/
|
||||
*.o.d
|
||||
*.ninja
|
||||
.ninja*
|
||||
.dirstamp
|
||||
/cmake/.CMakeBuildNumber
|
||||
.deps
|
||||
CMakeUserPresets.json
|
||||
# Except for default project files
|
||||
!/.github
|
||||
!/build-aux
|
||||
!/cmake
|
||||
!/deps
|
||||
!/docs
|
||||
!/libobs*
|
||||
!/plugins
|
||||
!/tests
|
||||
!/UI
|
||||
!.cirrus.xml
|
||||
!.clang-format
|
||||
!.cmake-format.json
|
||||
!.editorconfig
|
||||
!.git-blame-ignore-devs
|
||||
!.gitmodules
|
||||
!.gitignore
|
||||
!.mailmap
|
||||
!.swift-format
|
||||
!AUTHORS
|
||||
!buildspec.json
|
||||
!CMakeLists.txt
|
||||
!CMakePresets.json
|
||||
!COC.rst
|
||||
!COMMITMENT
|
||||
!CONTRIBUTING.rst
|
||||
!COPYING
|
||||
!INSTALL
|
||||
!README.rst
|
||||
|
||||
#xcode
|
||||
*.xcodeproj/
|
||||
/xcodebuild/
|
||||
# Exclude lock files
|
||||
*.lock.json
|
||||
|
||||
#clion
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
|
||||
#other stuff (windows stuff, qt moc stuff, etc)
|
||||
Release_MD/
|
||||
Release/
|
||||
Debug/
|
||||
x64/
|
||||
ipch/
|
||||
GeneratedFiles/
|
||||
.moc/
|
||||
/UI/obs.rc
|
||||
.vscode/
|
||||
/CI/include/*.lock.json
|
||||
install_temp/
|
||||
|
||||
/other/
|
||||
|
||||
#make stuff
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
Makefile.in
|
||||
Makefile
|
||||
|
||||
#python
|
||||
__pycache__
|
||||
|
||||
#sphinx
|
||||
# Exclude files generated by Sphinx in-tree
|
||||
/docs/sphinx/_build/*
|
||||
!/docs/sphinx/_build/.gitignore
|
||||
!/docs/sphinx/Makefile
|
||||
|
||||
#random useless file stuff
|
||||
*.dmg
|
||||
*.app
|
||||
.directory
|
||||
.hg
|
||||
.depend
|
||||
tags
|
||||
*.trace
|
||||
*.vsp
|
||||
*.psess
|
||||
*.swp
|
||||
*.dat
|
||||
*.clbin
|
||||
*.log
|
||||
*.tlog
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.xml
|
||||
*.ipch
|
||||
*.css
|
||||
*.xslt
|
||||
*.aps
|
||||
*.suo
|
||||
*.ncb
|
||||
*.user
|
||||
*.lo
|
||||
*.ilk
|
||||
*.la
|
||||
*.o
|
||||
*.obj
|
||||
*.pdb
|
||||
*.res
|
||||
*.dep
|
||||
*.zip
|
||||
*.lnk
|
||||
*.chm
|
||||
*~
|
||||
.DS_Store
|
||||
*/.DS_Store
|
||||
*/**/.DS_Store
|
||||
# Exclude modified Flatpak files
|
||||
build-aux/flatpak-github-action-modified-*
|
||||
|
||||
#flatpak
|
||||
/.flatpak-builder/
|
||||
/_flatpak_build/
|
||||
/flatpak_app/
|
||||
/repo/
|
||||
/CI/flatpak/flatpak-github-action-modified-*
|
||||
# Exclude macOS legacy resource forks
|
||||
.DS_Store
|
||||
|
||||
# Exclude CMake build number cache
|
||||
/cmake/.CMakeBuildNumber
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# Linux full build script
|
||||
##############################################################################
|
||||
#
|
||||
# This script contains all steps necessary to:
|
||||
#
|
||||
# * Build OBS with all default plugins and dependencies
|
||||
# * Package a Linux deb package
|
||||
#
|
||||
# Parameters:
|
||||
# -h, --help : Print usage help
|
||||
# -q, --quiet : Suppress most build process output
|
||||
# -v, --verbose : Enable more verbose build process output
|
||||
# -d, --skip-dependency-checks : Skip dependency checks (default: off)
|
||||
# -p, --portable : Create portable build (default: off)
|
||||
# -pkg, --package : Create distributable disk image
|
||||
# (default: off)
|
||||
# --build-dir : Specify alternative build directory
|
||||
# (default: build)"
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Halt on errors
|
||||
set -eE
|
||||
|
||||
## SET UP ENVIRONMENT ##
|
||||
_RUN_OBS_BUILD_SCRIPT=TRUE
|
||||
PRODUCT_NAME="OBS-Studio"
|
||||
|
||||
CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
|
||||
DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support.sh"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh"
|
||||
|
||||
## DEPENDENCY INSTALLATION
|
||||
source "${CHECKOUT_DIR}/CI/linux/01_install_dependencies.sh"
|
||||
|
||||
## BUILD OBS ##
|
||||
source "${CHECKOUT_DIR}/CI/linux/02_build_obs.sh"
|
||||
|
||||
## PACKAGE OBS AND NOTARIZE ##
|
||||
source "${CHECKOUT_DIR}/CI/linux/03_package_obs.sh"
|
||||
|
||||
## MAIN SCRIPT FUNCTIONS ##
|
||||
print_usage() {
|
||||
echo "build-linux.sh - Build script for OBS-Studio\n"
|
||||
echo -e "Usage: ${0}\n" \
|
||||
"-h, --help : Print this help\n" \
|
||||
"-q, --quiet : Suppress most build process output\n" \
|
||||
"-v, --verbose : Enable more verbose build process output\n" \
|
||||
"-d, --skip-dependency-checks : Skip dependency checks (default: off)\n" \
|
||||
"-p, --portable : Create portable build (default: off)\n" \
|
||||
"-pkg, --package : Create distributable disk image (default: off)\n" \
|
||||
"--disable-pipewire : Disable building with PipeWire support (default: off)\n" \
|
||||
"--build-dir : Specify alternative build directory (default: build)\n"
|
||||
}
|
||||
|
||||
obs-build-main() {
|
||||
while true; do
|
||||
case "${1}" in
|
||||
-h | --help ) print_usage; exit 0 ;;
|
||||
-q | --quiet ) export QUIET=TRUE; shift ;;
|
||||
-v | --verbose ) export VERBOSE=TRUE; shift ;;
|
||||
-d | --skip-dependency-checks ) SKIP_DEP_CHECKS=TRUE; shift ;;
|
||||
-p | --portable ) PORTABLE=TRUE; shift ;;
|
||||
-pkg | --package ) PACKAGE=TRUE; shift ;;
|
||||
--disable-pipewire ) DISABLE_PIPEWIRE=TRUE; shift ;;
|
||||
--build-dir ) BUILD_DIR="${2}"; shift 2 ;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
ensure_dir "${CHECKOUT_DIR}"
|
||||
step "Fetching OBS tags..."
|
||||
git fetch origin --tags
|
||||
|
||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
GIT_HASH=$(git rev-parse --short HEAD)
|
||||
GIT_TAG=$(git describe --tags --abbrev=0)
|
||||
|
||||
if [ "${BUILD_FOR_DISTRIBUTION}" ]; then
|
||||
VERSION_STRING="${GIT_TAG}"
|
||||
else
|
||||
VERSION_STRING="${GIT_TAG}-${GIT_HASH}"
|
||||
fi
|
||||
|
||||
FILE_NAME="obs-studio-${VERSION_STRING}-Linux.deb"
|
||||
|
||||
if [ -z "${SKIP_DEP_CHECKS}" ]; then
|
||||
install_dependencies
|
||||
fi
|
||||
|
||||
build_obs
|
||||
|
||||
if [ "${PACKAGE}" ]; then
|
||||
package_obs
|
||||
fi
|
||||
|
||||
cleanup
|
||||
}
|
||||
|
||||
obs-build-main $*
|
||||
@@ -1,143 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# macOS build script
|
||||
##############################################################################
|
||||
#
|
||||
# This script contains all steps necessary to:
|
||||
#
|
||||
# * Build OBS with all default plugins and dependencies
|
||||
# * Create a macOS application bundle
|
||||
# * Codesign the macOS application bundle
|
||||
# * Package a macOS installation image
|
||||
# * Notarize macOS application bundle and/or installation image
|
||||
#
|
||||
# Parameters:
|
||||
# -h, --help : Print usage help
|
||||
# -q, --quiet : Suppress most build process output
|
||||
# -v, --verbose : Enable more verbose build process output
|
||||
# -d, --skip-dependency-checks : Skip dependency checks (default: off)
|
||||
# -b, --bundle : Create relocatable application bundle
|
||||
# (default: off)
|
||||
# -p, --package : Create distributable disk image
|
||||
# (default: off)
|
||||
# -c, --codesign : Codesign OBS and all libraries
|
||||
# (default: ad-hoc only)
|
||||
# -n, --notarize : Notarize OBS (default: off)
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Halt on errors
|
||||
set -eE
|
||||
|
||||
## SET UP ENVIRONMENT ##
|
||||
_RUN_OBS_BUILD_SCRIPT=TRUE
|
||||
PRODUCT_NAME="OBS-Studio"
|
||||
|
||||
CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
|
||||
DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support.sh"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh"
|
||||
|
||||
## INSTALL DEPENDENCIES ##
|
||||
source "${CHECKOUT_DIR}/CI/macos/01_install_dependencies.sh"
|
||||
|
||||
## BUILD OBS ##
|
||||
source "${CHECKOUT_DIR}/CI/macos/02_build_obs.sh"
|
||||
|
||||
## PACKAGE OBS AND NOTARIZE ##
|
||||
source "${CHECKOUT_DIR}/CI/macos/03_package_obs.sh"
|
||||
|
||||
## MAIN SCRIPT FUNCTIONS ##
|
||||
print_usage() {
|
||||
echo "build-macos.sh - Build script for OBS-Studio"
|
||||
echo -e "Usage: ${0}\n" \
|
||||
"-h, --help : Print this help\n" \
|
||||
"-q, --quiet : Suppress most build process output\n" \
|
||||
"-v, --verbose : Enable more verbose build process output\n" \
|
||||
"-a, --architecture : Specify build architecture (default: x86_64, alternative: arm64)\n" \
|
||||
"-d, --skip-dependency-checks : Skip dependency checks (default: off)\n" \
|
||||
"-b, --bundle : Create relocatable application bundle (default: off)\n" \
|
||||
"-p, --package : Create distributable disk image (default: off)\n" \
|
||||
"-c, --codesign : Codesign OBS and all libraries (default: ad-hoc only)\n" \
|
||||
"-n, --notarize : Notarize OBS (default: off)\n"
|
||||
}
|
||||
|
||||
print_deprecation() {
|
||||
echo -e "DEPRECATION ERROR:\n" \
|
||||
"The '${1}' switch has been deprecated!\n"
|
||||
|
||||
if [ "${1}" = "-s" ]; then
|
||||
echo -e "The macOS build script system has changed:\n" \
|
||||
" - To configure and build OBS, run the script 'CI/macos/02_build_obs.sh'\n" \
|
||||
" - To bundle OBS into a relocatable application bundle, run the script 'CI/macos/02_build_obs.sh --bundle\n" \
|
||||
" - To package OBS, run the script 'CI/macos/03_package_obs.sh'\n" \
|
||||
" - To notarize OBS, run the script 'CI/macos/03_package_obs.sh --notarize'\n"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
obs-build-main() {
|
||||
while true; do
|
||||
case "${1}" in
|
||||
-h | --help ) print_usage; exit 0 ;;
|
||||
-q | --quiet ) export QUIET=TRUE; shift ;;
|
||||
-v | --verbose ) export VERBOSE=TRUE; shift ;;
|
||||
-a | --architecture ) ARCH="${2}"; shift 2 ;;
|
||||
-d | --skip-dependency-checks ) SKIP_DEP_CHECKS=TRUE; shift ;;
|
||||
-p | --package ) PACKAGE=TRUE; shift ;;
|
||||
-c | --codesign ) CODESIGN=TRUE; shift ;;
|
||||
-n | --notarize ) NOTARIZE=TRUE; PACKAGE=TRUE CODESIGN=TRUE; shift ;;
|
||||
-b | --bundle ) BUNDLE=TRUE; shift ;;
|
||||
-s ) print_deprecation ${1}; exit 1 ;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
ensure_dir "${CHECKOUT_DIR}"
|
||||
check_archs
|
||||
check_macos_version
|
||||
step "Fetching OBS tags..."
|
||||
/usr/bin/git fetch origin --tags
|
||||
|
||||
GIT_BRANCH=$(/usr/bin/git rev-parse --abbrev-ref HEAD)
|
||||
GIT_HASH=$(/usr/bin/git rev-parse --short HEAD)
|
||||
GIT_TAG=$(/usr/bin/git describe --tags --abbrev=0)
|
||||
|
||||
if [ "${BUILD_FOR_DISTRIBUTION}" ]; then
|
||||
VERSION_STRING="${GIT_TAG}"
|
||||
else
|
||||
VERSION_STRING="${GIT_TAG}-${GIT_HASH}"
|
||||
fi
|
||||
|
||||
if [ "${ARCH}" = "arm64" ]; then
|
||||
FILE_NAME="obs-studio-${VERSION_STRING}-macOS-Apple.dmg"
|
||||
elif [ "${ARCH}" = "universal" ]; then
|
||||
FILE_NAME="obs-studio-${VERSION_STRING}-macOS.dmg"
|
||||
else
|
||||
FILE_NAME="obs-studio-${VERSION_STRING}-macOS-Intel.dmg"
|
||||
fi
|
||||
|
||||
if [ -z "${SKIP_DEP_CHECKS}" ]; then
|
||||
install_dependencies
|
||||
fi
|
||||
|
||||
build_obs
|
||||
|
||||
if [ "${BUNDLE}" ]; then
|
||||
bundle_obs
|
||||
fi
|
||||
|
||||
if [ "${PACKAGE}" ]; then
|
||||
package_obs
|
||||
fi
|
||||
|
||||
if [ "${NOTARIZE}" ]; then
|
||||
notarize_obs
|
||||
fi
|
||||
|
||||
cleanup
|
||||
}
|
||||
|
||||
obs-build-main $*
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
dirty=$(git ls-files --modified)
|
||||
|
||||
set +x
|
||||
if [[ $dirty ]]; then
|
||||
echo "================================="
|
||||
echo "Files were not formatted properly"
|
||||
echo "$dirty"
|
||||
echo "================================="
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
if [ ${#} -eq 1 -a "${1}" = "VERBOSE" ]; then
|
||||
VERBOSITY="-l debug"
|
||||
else
|
||||
VERBOSITY=""
|
||||
fi
|
||||
|
||||
if [ "${CI}" ]; then
|
||||
MODE="--check"
|
||||
else
|
||||
MODE="-i"
|
||||
fi
|
||||
|
||||
# Runs the formatter in parallel on the code base.
|
||||
# Return codes:
|
||||
# - 1 there are files to be formatted
|
||||
# - 0 everything looks fine
|
||||
|
||||
# Get CPU count
|
||||
OS=$(uname)
|
||||
NPROC=1
|
||||
if [[ ${OS} = "Linux" ]] ; then
|
||||
NPROC=$(nproc)
|
||||
elif [[ ${OS} = "Darwin" ]] ; then
|
||||
NPROC=$(sysctl -n hw.physicalcpu)
|
||||
fi
|
||||
|
||||
# Discover clang-format
|
||||
if ! type cmake-format 2> /dev/null ; then
|
||||
echo "Required cmake-format not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find . -type d \( \
|
||||
-path ./.deps -o \
|
||||
-path ./\*build\* -o \
|
||||
-path ./deps/jansson -o \
|
||||
-path ./plugins/decklink/\*/decklink-sdk -o \
|
||||
-path ./plugins/enc-amf -o \
|
||||
-path ./plugins/mac-syphon/syphon-framework -o \
|
||||
-path ./plugins/obs-outputs/ftl-sdk -o \
|
||||
-path ./plugins/obs-vst -o \
|
||||
-path ./plugins/obs-browser -o \
|
||||
-path ./plugins/win-dshow/libdshowcapture -o \
|
||||
-path ./plugins/obs-websocket/deps \
|
||||
\) -prune -false -type f -o \
|
||||
-name 'CMakeLists.txt' -or \
|
||||
-name '*.cmake' \
|
||||
| xargs -L10 -P ${NPROC} cmake-format ${MODE} ${VERBOSITY}
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
set -o nounset
|
||||
|
||||
if [ ${#} -eq 1 ]; then
|
||||
VERBOSITY="--verbose"
|
||||
else
|
||||
VERBOSITY=""
|
||||
fi
|
||||
|
||||
# Runs the Clang Formatter in parallel on the code base.
|
||||
# Return codes:
|
||||
# - 1 there are files to be formatted
|
||||
# - 0 everything looks fine
|
||||
|
||||
# Get CPU count
|
||||
OS=$(uname)
|
||||
NPROC=1
|
||||
if [[ ${OS} = "Linux" ]] ; then
|
||||
NPROC=$(nproc)
|
||||
elif [[ ${OS} = "Darwin" ]] ; then
|
||||
NPROC=$(sysctl -n hw.physicalcpu)
|
||||
fi
|
||||
|
||||
# Discover clang-format
|
||||
if type clang-format-13 2> /dev/null ; then
|
||||
CLANG_FORMAT=clang-format-13
|
||||
elif type clang-format 2> /dev/null ; then
|
||||
# Clang format found, but need to check version
|
||||
CLANG_FORMAT=clang-format
|
||||
V=$(clang-format --version)
|
||||
if [[ $V != *"version 13.0"* ]]; then
|
||||
echo "clang-format is not 13.0 (returned ${V})"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "No appropriate clang-format found (expected clang-format-13.0.0, or clang-format)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find . -type d \( \
|
||||
-path ./.deps -o \
|
||||
-path ./\*build\* -o \
|
||||
-path ./cmake -o \
|
||||
-path ./plugins/decklink/\*/decklink-sdk -o \
|
||||
-path ./plugins/enc-amf -o \
|
||||
-path ./plugins/obs-outputs/ftl-sdk -o \
|
||||
-path ./plugins/obs-websocket/deps \
|
||||
\) -prune -false -type f -o \
|
||||
-name '*.h' -or \
|
||||
-name '*.hpp' -or \
|
||||
-name '*.m' -or \
|
||||
-name '*.mm' -or \
|
||||
-name '*.c' -or \
|
||||
-name '*.cpp' \
|
||||
| xargs -L100 -P ${NPROC} "${CLANG_FORMAT}" ${VERBOSITY} -i -style=file -fallback-style=none
|
||||
@@ -1,84 +0,0 @@
|
||||
import json
|
||||
from jsonschema import Draft7Validator
|
||||
from json_source_map import calculate
|
||||
from json_source_map.errors import InvalidInputError
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
errors = []
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("JSON path required.")
|
||||
return 1
|
||||
|
||||
for filename in sys.argv[1:]:
|
||||
prep(filename)
|
||||
|
||||
try:
|
||||
with open('validation_errors.json', 'w') as outfile:
|
||||
json.dump(errors, outfile)
|
||||
except OSError as e:
|
||||
print(f'Failed to write validation output to file: {e}')
|
||||
return 1
|
||||
|
||||
if errors:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def prep(filename):
|
||||
try:
|
||||
with open(filename) as json_file:
|
||||
json_string = json_file.read()
|
||||
json_data = json.loads(json_string)
|
||||
except OSError as e:
|
||||
print(f'Failed to load file "{filename}": {e}')
|
||||
return
|
||||
|
||||
schema_filename = json_data.get('$schema')
|
||||
if not schema_filename:
|
||||
print('File has no schema:', filename)
|
||||
return
|
||||
|
||||
file_path = os.path.split(filename)[0]
|
||||
schema_file = os.path.join(file_path, schema_filename)
|
||||
|
||||
try:
|
||||
with open(schema_file) as json_file:
|
||||
schema = json.load(json_file)
|
||||
except OSError as e:
|
||||
print(f'Failed to load schema file "{schema_file}": {e}')
|
||||
return
|
||||
|
||||
validate(filename, json_data, json_string, schema)
|
||||
|
||||
|
||||
def validate(filename, json_data, json_string, schema):
|
||||
try:
|
||||
servicesPaths = calculate(json_string)
|
||||
except InvalidInputError as e:
|
||||
print("Error with file:", e)
|
||||
return
|
||||
|
||||
cls = Draft7Validator(schema)
|
||||
|
||||
for e in sorted(cls.iter_errors(json_data), key=str):
|
||||
print(f'{e}\nIn "{filename}"\n\n')
|
||||
errorPath = '/'.join(str(v) for v in e.absolute_path)
|
||||
errorEntry = servicesPaths['/' + errorPath]
|
||||
errors.append({
|
||||
"file": filename,
|
||||
"start_line": errorEntry.value_start.line + 1,
|
||||
"end_line": errorEntry.value_end.line + 1,
|
||||
"title": "Validation Error",
|
||||
"message": e.message,
|
||||
"annotation_level": "failure"
|
||||
})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# Linux support functions
|
||||
##############################################################################
|
||||
#
|
||||
# This script file can be included in build scripts for Linux.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Setup build environment
|
||||
|
||||
CI_LINUX_CEF_VERSION=$(cat "${CI_WORKFLOW}" | sed -En "s/[ ]+CEF_BUILD_VERSION_LINUX: '([0-9]+)'/\1/p")
|
||||
|
||||
if [ "${TERM-}" -a -z "${CI}" ]; then
|
||||
COLOR_RED=$(tput setaf 1)
|
||||
COLOR_GREEN=$(tput setaf 2)
|
||||
COLOR_BLUE=$(tput setaf 4)
|
||||
COLOR_ORANGE=$(tput setaf 3)
|
||||
COLOR_RESET=$(tput sgr0)
|
||||
else
|
||||
COLOR_RED=""
|
||||
COLOR_GREEN=""
|
||||
COLOR_BLUE=""
|
||||
COLOR_ORANGE=""
|
||||
COLOR_RESET=""
|
||||
fi
|
||||
|
||||
if [ "${CI}" -o "${QUIET}" ]; then
|
||||
export CURLCMD="curl --silent --show-error --location -O"
|
||||
else
|
||||
export CURLCMD="curl --progress-bar --location --continue-at - -O"
|
||||
fi
|
||||
|
||||
_add_ccache_to_path() {
|
||||
if [ "${CMAKE_CCACHE_OPTIONS}" ]; then
|
||||
PATH="/usr/local/opt/ccache/libexec:${PATH}"
|
||||
status "Compiler Info:"
|
||||
local IFS=$'\n'
|
||||
for COMPILER_INFO in $(type cc c++ gcc g++ clang clang++ || true); do
|
||||
info "${COMPILER_INFO}"
|
||||
done
|
||||
fi
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# macOS support functions
|
||||
##############################################################################
|
||||
#
|
||||
# This script file can be included in build scripts for macOS.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Setup build environment
|
||||
WORKFLOW_CONTENT=$(/bin/cat "${CI_WORKFLOW}")
|
||||
|
||||
MACOS_VERSION="$(/usr/bin/sw_vers -productVersion)"
|
||||
MACOS_MAJOR="$(echo ${MACOS_VERSION} | /usr/bin/cut -d '.' -f 1)"
|
||||
MACOS_MINOR="$(echo ${MACOS_VERSION} | /usr/bin/cut -d '.' -f 2)"
|
||||
|
||||
if [ "${TERM-}" -a -z "${CI}" ]; then
|
||||
COLOR_RED=$(/usr/bin/tput setaf 1)
|
||||
COLOR_GREEN=$(/usr/bin/tput setaf 2)
|
||||
COLOR_BLUE=$(/usr/bin/tput setaf 4)
|
||||
COLOR_ORANGE=$(/usr/bin/tput setaf 3)
|
||||
COLOR_RESET=$(/usr/bin/tput sgr0)
|
||||
else
|
||||
COLOR_RED=""
|
||||
COLOR_GREEN=""
|
||||
COLOR_BLUE=""
|
||||
COLOR_ORANGE=""
|
||||
COLOR_RESET=""
|
||||
fi
|
||||
|
||||
## DEFINE UTILITIES ##
|
||||
check_macos_version() {
|
||||
ARCH="${ARCH:-${CURRENT_ARCH}}"
|
||||
|
||||
case "${ARCH}" in
|
||||
x86_64) ;;
|
||||
arm64) ;;
|
||||
*) caught_error "Unsupported architecture '${ARCH}' provided" ;;
|
||||
esac
|
||||
|
||||
step "Check macOS version..."
|
||||
MIN_VERSION="11.0"
|
||||
MIN_MAJOR=$(echo ${MIN_VERSION} | /usr/bin/cut -d '.' -f 1)
|
||||
MIN_MINOR=$(echo ${MIN_VERSION} | /usr/bin/cut -d '.' -f 2)
|
||||
|
||||
if [ "${MACOS_MAJOR}" -lt "11" -a "${MACOS_MINOR}" -lt "${MIN_MINOR}" ]; then
|
||||
error "ERROR: Minimum required macOS version is ${MIN_VERSION}, but running on ${MACOS_VERSION}"
|
||||
fi
|
||||
|
||||
export CODESIGN_LINKER="ON"
|
||||
}
|
||||
|
||||
install_homebrew_deps() {
|
||||
if ! exists brew; then
|
||||
caught_error "Homebrew not found - please install Homebrew (https://brew.sh)"
|
||||
fi
|
||||
|
||||
brew bundle --file "${CHECKOUT_DIR}/CI/include/Brewfile" ${QUIET:+--quiet}
|
||||
|
||||
check_curl
|
||||
}
|
||||
|
||||
check_curl() {
|
||||
if [ "${MACOS_MAJOR}" -lt "11" -a "${MACOS_MINOR}" -lt "15" ]; then
|
||||
if [ ! -d /usr/local/opt/curl ]; then
|
||||
step "Install Homebrew curl..."
|
||||
brew install curl
|
||||
fi
|
||||
|
||||
CURLCMD="/usr/local/opt/curl/bin/curl"
|
||||
else
|
||||
CURLCMD="curl"
|
||||
fi
|
||||
|
||||
if [ "${CI}" -o "${QUIET}" ]; then
|
||||
export CURLCMD="${CURLCMD} --silent --show-error --location -O"
|
||||
else
|
||||
export CURLCMD="${CURLCMD} --progress-bar --location --continue-at - -O"
|
||||
fi
|
||||
}
|
||||
|
||||
check_archs() {
|
||||
step "Check Architecture..."
|
||||
ARCH="${ARCH:-${CURRENT_ARCH}}"
|
||||
if [ "${ARCH}" = "universal" ]; then
|
||||
CMAKE_ARCHS="x86_64;arm64"
|
||||
elif [ "${ARCH}" != "x86_64" -a "${ARCH}" != "arm64" ]; then
|
||||
caught_error "Unsupported architecture '${ARCH}' provided"
|
||||
else
|
||||
CMAKE_ARCHS="${ARCH}"
|
||||
fi
|
||||
}
|
||||
|
||||
_add_ccache_to_path() {
|
||||
if [ "${CMAKE_CCACHE_OPTIONS}" ]; then
|
||||
if [ "${CURRENT_ARCH}" == "arm64" ]; then
|
||||
PATH="/opt/homebrew/opt/ccache/libexec:${PATH}"
|
||||
else
|
||||
PATH="/usr/local/opt/ccache/libexec:${PATH}"
|
||||
fi
|
||||
status "Compiler Info:"
|
||||
local IFS=$'\n'
|
||||
for COMPILER_INFO in $(type cc c++ gcc g++ clang clang++ || true); do
|
||||
info "${COMPILER_INFO}"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
## SET UP CODE SIGNING AND NOTARIZATION CREDENTIALS ##
|
||||
##############################################################################
|
||||
# Apple Developer Identity needed:
|
||||
#
|
||||
# + Signing the code requires a developer identity in the system's keychain
|
||||
# + codesign will look up and find the identity automatically
|
||||
#
|
||||
##############################################################################
|
||||
read_codesign_ident() {
|
||||
if [ -z "${CODESIGN_IDENT}" ]; then
|
||||
step "Set up code signing..."
|
||||
read -p "${COLOR_ORANGE} + Apple developer identity: ${COLOR_RESET}" CODESIGN_IDENT
|
||||
fi
|
||||
CODESIGN_IDENT_SHORT=$(echo "${CODESIGN_IDENT}" | /usr/bin/sed -En "s/.+\((.+)\)/\1/p")
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Apple Developer credentials necessary:
|
||||
#
|
||||
# + Signing for distribution and notarization require an active Apple
|
||||
# Developer membership
|
||||
# + An Apple Development identity is needed for code signing
|
||||
# (i.e. 'Apple Development: YOUR APPLE ID (PROVIDER)')
|
||||
# + Your Apple developer ID is needed for notarization
|
||||
# + An app-specific password is necessary for notarization from CLI
|
||||
# + This password will be stored in your macOS keychain under the identifier
|
||||
# 'OBS-Codesign-Password' with access Apple's 'notarytool' only.
|
||||
##############################################################################
|
||||
|
||||
read_codesign_pass() {
|
||||
step "Set up notarization..."
|
||||
|
||||
if [ -z "${CODESIGN_IDENT_USER}" ]; then
|
||||
read -p "${COLOR_ORANGE} + Apple account id: ${COLOR_RESET}" CODESIGN_IDENT_USER
|
||||
fi
|
||||
|
||||
if [ -z "${CODESIGN_IDENT_PASS}" ]; then
|
||||
CODESIGN_IDENT_PASS=$(stty -echo; read -p "${COLOR_ORANGE} + Apple developer password: ${COLOR_RESET}" secret; stty echo; echo $secret)
|
||||
echo ""
|
||||
fi
|
||||
|
||||
step "Update notarization keychain..."
|
||||
|
||||
echo -n "${COLOR_ORANGE}"
|
||||
/usr/bin/xcrun notarytool store-credentials "OBS-Codesign-Password" --apple-id "${CODESIGN_IDENT_USER}" --team-id "${CODESIGN_IDENT_SHORT}" --password "${CODESIGN_IDENT_PASS}"
|
||||
echo -n "${COLOR_RESET}"
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# Linux dependency management function
|
||||
##############################################################################
|
||||
#
|
||||
# This script file can be included in build scripts for Linux or run directly
|
||||
# with the -s/--standalone switch
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Halt on errors
|
||||
set -eE
|
||||
|
||||
install_build-deps() {
|
||||
shift
|
||||
status "Install OBS build dependencies"
|
||||
trap "caught_error 'install_build-deps'" ERR
|
||||
|
||||
sudo apt-get install --no-install-recommends -y $@
|
||||
}
|
||||
|
||||
install_obs-deps() {
|
||||
shift
|
||||
status "Install OBS dependencies"
|
||||
trap "caught_error 'install_obs-deps'" ERR
|
||||
|
||||
if [ -z "${DISABLE_PIPEWIRE}" ]; then
|
||||
sudo apt-get install --no-install-recommends -y $@ libpipewire-0.3-dev
|
||||
else
|
||||
sudo apt-get install --no-install-recommends -y $@
|
||||
fi
|
||||
}
|
||||
|
||||
install_qt5-deps() {
|
||||
shift
|
||||
status "Install Qt5 dependencies"
|
||||
trap "caught_error 'install_qt5-deps'" ERR
|
||||
|
||||
_QT6_AVAILABLE="$(sudo apt-cache madison qt6-base-dev)"
|
||||
if [ -z "${_QT6_AVAILABLE}" ]; then
|
||||
sudo apt-get install --no-install-recommends -y $@
|
||||
fi
|
||||
}
|
||||
|
||||
install_qt6-deps() {
|
||||
shift
|
||||
status "Install Qt6 dependencies"
|
||||
trap "caught_error 'install_qt6-deps'" ERR
|
||||
|
||||
_QT6_AVAILABLE="$(sudo apt-cache madison ${1})"
|
||||
if [ "${_QT6_AVAILABLE}" ]; then
|
||||
sudo apt-get install --no-install-recommends -y $@
|
||||
fi
|
||||
}
|
||||
|
||||
install_cef() {
|
||||
shift
|
||||
status "Setup for dependency CEF v${1}"
|
||||
ensure_dir "${DEPS_BUILD_DIR}"
|
||||
|
||||
if [ "${CI}" -a "${RESTORED_CEF}" ]; then
|
||||
_SKIP=TRUE
|
||||
elif [ -d "${DEPS_BUILD_DIR}/cef_binary_${1}_linux64" -a -f "${DEPS_BUILD_DIR}/cef_binary_${1}_linux64/build/libcef_dll_wrapper/libcef_dll_wrapper.a" ]; then
|
||||
_SKIP=TRUE
|
||||
fi
|
||||
|
||||
if [ -z "${_SKIP}" ]; then
|
||||
step "Download..."
|
||||
${CURLCMD:-curl -O} https://cdn-fastly.obsproject.com/downloads/cef_binary_${1}_linux64.tar.bz2
|
||||
step "Unpack..."
|
||||
tar -xf cef_binary_${1}_linux64.tar.bz2
|
||||
else
|
||||
step "Found existing Chromium Embedded Framework and loader library..."
|
||||
fi
|
||||
}
|
||||
|
||||
install_plugin-deps() {
|
||||
shift
|
||||
status "Install plugin dependencies"
|
||||
trap "caught_error 'install_plugin-deps'" ERR
|
||||
|
||||
sudo apt-get install --no-install-recommends -y $@
|
||||
}
|
||||
|
||||
install_dependencies() {
|
||||
status "Set up apt"
|
||||
trap "caught_error 'install_dependencies'" ERR
|
||||
|
||||
BUILD_DEPS=(
|
||||
"build-deps cmake ninja-build pkg-config clang clang-format build-essential curl ccache"
|
||||
"obs-deps libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev libswresample-dev \
|
||||
libswscale-dev libx264-dev libcurl4-openssl-dev libmbedtls-dev libgl1-mesa-dev libjansson-dev \
|
||||
libluajit-5.1-dev python3-dev libx11-dev libxcb-randr0-dev libxcb-shm0-dev libxcb-xinerama0-dev \
|
||||
libxcb-composite0-dev libxinerama-dev libxcb1-dev libx11-xcb-dev libxcb-xfixes0-dev swig libcmocka-dev \
|
||||
libpci-dev libxss-dev libglvnd-dev libgles2-mesa libgles2-mesa-dev libwayland-dev \
|
||||
libxkbcommon-dev"
|
||||
"qt5-deps qtbase5-dev qtbase5-private-dev libqt5svg5-dev qtwayland5"
|
||||
"qt6-deps qt6-base-dev qt6-base-private-dev libqt6svg6-dev qt6-wayland"
|
||||
"cef ${LINUX_CEF_BUILD_VERSION:-${CI_LINUX_CEF_VERSION}}"
|
||||
"plugin-deps libasound2-dev libfdk-aac-dev libfontconfig-dev libfreetype6-dev libjack-jackd2-dev \
|
||||
libpulse-dev libsndio-dev libspeexdsp-dev libudev-dev libv4l-dev libva-dev libvlc-dev libdrm-dev \
|
||||
nlohmann-json3-dev libwebsocketpp-dev libasio-dev libvpl-dev libqrcodegencpp-dev"
|
||||
)
|
||||
|
||||
sudo apt-get -qq update
|
||||
|
||||
for DEPENDENCY in "${BUILD_DEPS[@]}"; do
|
||||
set -- ${DEPENDENCY}
|
||||
trap "caught_error ${DEPENDENCY}" ERR
|
||||
FUNC_NAME="install_${1}"
|
||||
${FUNC_NAME} ${@}
|
||||
done
|
||||
}
|
||||
|
||||
install-dependencies-standalone() {
|
||||
CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
|
||||
PRODUCT_NAME="OBS-Studio"
|
||||
DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support.sh"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh"
|
||||
|
||||
status "Setup of OBS build dependencies"
|
||||
install_dependencies
|
||||
}
|
||||
|
||||
print_usage() {
|
||||
echo -e "Usage: ${0}\n" \
|
||||
"-h, --help : Print this help\n" \
|
||||
"-q, --quiet : Suppress most build process output\n" \
|
||||
"-v, --verbose : Enable more verbose build process output\n" \
|
||||
"--disable-pipewire : Disable building with PipeWire support (default: off)\n"
|
||||
}
|
||||
|
||||
install-dependencies-main() {
|
||||
if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
|
||||
while true; do
|
||||
case "${1}" in
|
||||
-h | --help ) print_usage; exit 0 ;;
|
||||
-q | --quiet ) export QUIET=TRUE; shift ;;
|
||||
-v | --verbose ) export VERBOSE=TRUE; shift ;;
|
||||
--disable-pipewire ) DISABLE_PIPEWIRE=TRUE; shift ;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
install-dependencies-standalone
|
||||
fi
|
||||
}
|
||||
|
||||
install-dependencies-main $*
|
||||
@@ -1,135 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# Linux build function
|
||||
##############################################################################
|
||||
#
|
||||
# This script file can be included in build scripts for Linux or run directly
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Halt on errors
|
||||
set -eE
|
||||
|
||||
build_obs() {
|
||||
status "Build OBS"
|
||||
trap "caught_error 'build app'" ERR
|
||||
if [ -z "${CI}" ]; then
|
||||
_backup_artifacts
|
||||
fi
|
||||
|
||||
step "Configure OBS..."
|
||||
_configure_obs
|
||||
|
||||
ensure_dir "${CHECKOUT_DIR}/"
|
||||
step "Build OBS targets..."
|
||||
cmake --build ${BUILD_DIR}
|
||||
}
|
||||
|
||||
# Function to configure OBS build
|
||||
_configure_obs() {
|
||||
ensure_dir "${CHECKOUT_DIR}"
|
||||
status "Configuration of OBS build system..."
|
||||
trap "caught_error 'configure build'" ERR
|
||||
check_ccache
|
||||
|
||||
if [ "${TWITCH_CLIENTID}" -a "${TWITCH_HASH}" ]; then
|
||||
TWITCH_OPTIONS="-DTWITCH_CLIENTID='${TWITCH_CLIENTID}' -DTWITCH_HASH='${TWITCH_HASH}'"
|
||||
fi
|
||||
|
||||
if [ "${RESTREAM_CLIENTID}" -a "${RESTREAM_HASH}" ]; then
|
||||
RESTREAM_OPTIONS="-DRESTREAM_CLIENTID='${RESTREAM_CLIENTID}' -DRESTREAM_HASH='${RESTREAM_HASH}'"
|
||||
fi
|
||||
|
||||
if [ "${YOUTUBE_CLIENTID}" -a "${YOUTUBE_CLIENTID_HASH}" -a "${YOUTUBE_SECRET}" -a "{YOUTUBE_SECRET_HASH}" ]; then
|
||||
YOUTUBE_OPTIONS="-DYOUTUBE_CLIENTID='${YOUTUBE_CLIENTID}' -DYOUTUBE_CLIENTID_HASH='${YOUTUBE_CLIENTID_HASH}' -DYOUTUBE_SECRET='${YOUTUBE_SECRET}' -DYOUTUBE_SECRET_HASH='${YOUTUBE_SECRET_HASH}'"
|
||||
fi
|
||||
|
||||
if [ "${PORTABLE}" ]; then
|
||||
PORTABLE_BUILD="ON"
|
||||
fi
|
||||
|
||||
if [ "${DISABLE_PIPEWIRE}" ]; then
|
||||
PIPEWIRE_OPTION="-DENABLE_PIPEWIRE=OFF"
|
||||
fi
|
||||
|
||||
if [ "${DISABLE_QSV}" ]; then
|
||||
QSV_OPTION="-DENABLE_QSV11=OFF"
|
||||
fi
|
||||
|
||||
cmake -S . -B ${BUILD_DIR} -G Ninja \
|
||||
-DCEF_ROOT_DIR="${DEPS_BUILD_DIR}/cef_binary_${LINUX_CEF_BUILD_VERSION:-${CI_LINUX_CEF_VERSION}}_linux64" \
|
||||
-DCMAKE_BUILD_TYPE=${BUILD_CONFIG} \
|
||||
-DLINUX_PORTABLE=${PORTABLE_BUILD:-OFF} \
|
||||
-DENABLE_AJA=OFF \
|
||||
-DENABLE_NEW_MPEGTS_OUTPUT=OFF \
|
||||
-DENABLE_WEBRTC=OFF \
|
||||
${PIPEWIRE_OPTION} \
|
||||
${QSV_OPTION} \
|
||||
${YOUTUBE_OPTIONS} \
|
||||
${TWITCH_OPTIONS} \
|
||||
${RESTREAM_OPTIONS} \
|
||||
${CI:+-DENABLE_UNIT_TESTS=ON -DBUILD_FOR_DISTRIBUTION=${BUILD_FOR_DISTRIBUTION} -DOBS_BUILD_NUMBER=${GITHUB_RUN_ID}} \
|
||||
${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR}
|
||||
}
|
||||
|
||||
# Function to backup previous build artifacts
|
||||
_backup_artifacts() {
|
||||
ensure_dir "${CHECKOUT_DIR}"
|
||||
if [ -d ${BUILD_DIR} ]; then
|
||||
status "Backup of old OBS build artifacts"
|
||||
|
||||
CUR_DATE=$(date +"%Y-%m-%d@%H%M%S")
|
||||
NIGHTLY_DIR="${CHECKOUT_DIR}/nightly-${CUR_DATE}"
|
||||
PACKAGE_NAME=$(find ${BUILD_DIR} -maxdepth 1 -name "*.deb" | sort -rn | head -1)
|
||||
|
||||
if [ "${PACKAGE_NAME}" ]; then
|
||||
step "Back up $(basename "${PACKAGE_NAME}")..."
|
||||
ensure_dir "${NIGHTLY_DIR}"
|
||||
mv "../${BUILD_DIR}/$(basename "${PACKAGE_NAME}")" ${NIGHTLY_DIR}/
|
||||
info "You can find ${PACKAGE_NAME} in ${NIGHTLY_DIR}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
build-obs-standalone() {
|
||||
CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
|
||||
PRODUCT_NAME="OBS-Studio"
|
||||
DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support.sh"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh"
|
||||
|
||||
build_obs
|
||||
}
|
||||
|
||||
print_usage() {
|
||||
echo -e "Usage: ${0}\n" \
|
||||
"-h, --help : Print this help\n" \
|
||||
"-q, --quiet : Suppress most build process output\n" \
|
||||
"-v, --verbose : Enable more verbose build process output\n" \
|
||||
"-p, --portable : Create portable build (default: off)\n" \
|
||||
"--disable-pipewire : Disable building with PipeWire support (default: off)\n" \
|
||||
"--build-dir : Specify alternative build directory (default: build)\n"
|
||||
}
|
||||
|
||||
build-obs-main() {
|
||||
if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
|
||||
while true; do
|
||||
case "${1}" in
|
||||
-h | --help ) print_usage; exit 0 ;;
|
||||
-q | --quiet ) export QUIET=TRUE; shift ;;
|
||||
-v | --verbose ) export VERBOSE=TRUE; shift ;;
|
||||
-p | --portable ) export PORTABLE=TRUE; shift ;;
|
||||
--disable-pipewire ) DISABLE_PIPEWIRE=TRUE; shift ;;
|
||||
--disable-qsv ) DISABLE_QSV=TRUE; shift ;;
|
||||
--build-dir ) BUILD_DIR="${2}"; shift 2 ;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
build-obs-standalone
|
||||
fi
|
||||
}
|
||||
|
||||
build-obs-main $*
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# Linux libobs plugin package function
|
||||
##############################################################################
|
||||
#
|
||||
# This script file can be included in build scripts for Linux or run directly
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Halt on errors
|
||||
set -eE
|
||||
|
||||
package_obs() {
|
||||
status "Create Linux debian package"
|
||||
trap "caught_error 'package app'" ERR
|
||||
|
||||
ensure_dir "${CHECKOUT_DIR}"
|
||||
|
||||
step "Package OBS..."
|
||||
cmake --build ${BUILD_DIR} -t package
|
||||
|
||||
DEB_NAME=$(find ${BUILD_DIR} -maxdepth 1 -type f -name "obs*.deb" | sort -rn | head -1)
|
||||
DEBUG_NAME="${DEB_NAME//.deb/-dbgsym.ddeb}"
|
||||
|
||||
if [ "${DEB_NAME}" ]; then
|
||||
mv "${DEB_NAME}" "${BUILD_DIR}/${FILE_NAME}"
|
||||
|
||||
if [ "${DEBUG_NAME}" ]; then
|
||||
mv "${DEBUG_NAME}" "${BUILD_DIR}/${FILE_NAME//.deb/-dbgsym.ddeb}"
|
||||
fi
|
||||
else
|
||||
error "ERROR No suitable OBS debian package generated"
|
||||
fi
|
||||
}
|
||||
|
||||
package-obs-standalone() {
|
||||
PRODUCT_NAME="OBS-Studio"
|
||||
|
||||
CHECKOUT_DIR="$(git rev-parse --show-toplevel)"
|
||||
DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support.sh"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh"
|
||||
|
||||
if [ -z "${CI}" ]; then
|
||||
step "Fetch OBS tags..."
|
||||
git fetch --tags origin
|
||||
fi
|
||||
|
||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
GIT_HASH=$(git rev-parse --short=9 HEAD)
|
||||
GIT_TAG=$(git describe --tags --abbrev=0)
|
||||
UBUNTU_VERSION=$(lsb_release -sr)
|
||||
|
||||
if [ "${BUILD_FOR_DISTRIBUTION}" = "true" ]; then
|
||||
VERSION_STRING="${GIT_TAG}"
|
||||
else
|
||||
VERSION_STRING="${GIT_TAG}-${GIT_HASH}"
|
||||
fi
|
||||
|
||||
FILE_NAME="obs-studio-${VERSION_STRING}-ubuntu-${UBUNTU_VERSION}.deb"
|
||||
package_obs
|
||||
}
|
||||
|
||||
print_usage() {
|
||||
echo -e "Usage: ${0}\n" \
|
||||
"-h, --help : Print this help\n" \
|
||||
"-q, --quiet : Suppress most build process output\n" \
|
||||
"-v, --verbose : Enable more verbose build process output\n" \
|
||||
"--build-dir : Specify alternative build directory (default: build)\n"
|
||||
}
|
||||
|
||||
package-obs-main() {
|
||||
if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
|
||||
while true; do
|
||||
case "${1}" in
|
||||
-h | --help ) print_usage; exit 0 ;;
|
||||
-q | --quiet ) export QUIET=TRUE; shift ;;
|
||||
-v | --verbose ) export VERBOSE=TRUE; shift ;;
|
||||
--build-dir ) BUILD_DIR="${2}"; shift 2 ;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
package-obs-standalone
|
||||
fi
|
||||
}
|
||||
|
||||
package-obs-main $*
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# macOS dependency management function
|
||||
##############################################################################
|
||||
#
|
||||
# This script file can be included in build scripts for macOS or run directly.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Halt on errors
|
||||
set -eE
|
||||
|
||||
install_dependencies() {
|
||||
status "Install Homebrew dependencies"
|
||||
trap "caught_error 'install_dependencies'" ERR
|
||||
|
||||
install_homebrew_deps
|
||||
|
||||
}
|
||||
|
||||
install-dependencies-standalone() {
|
||||
CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
|
||||
PRODUCT_NAME="OBS-Studio"
|
||||
DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support.sh"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh"
|
||||
|
||||
status "Setup of OBS build dependencies"
|
||||
check_macos_version
|
||||
check_archs
|
||||
install_dependencies
|
||||
}
|
||||
|
||||
print_usage() {
|
||||
echo -e "Usage: ${0}\n" \
|
||||
"-h, --help : Print this help\n" \
|
||||
"-q, --quiet : Suppress most build process output\n" \
|
||||
"-v, --verbose : Enable more verbose build process output\n" \
|
||||
"-a, --architecture : Specify build architecture (default: x86_64, alternative: arm64)\n"
|
||||
}
|
||||
|
||||
install-dependencies-main() {
|
||||
if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
|
||||
while true; do
|
||||
case "${1}" in
|
||||
-h | --help ) print_usage; exit 0 ;;
|
||||
-q | --quiet ) export QUIET=TRUE; shift ;;
|
||||
-v | --verbose ) export VERBOSE=TRUE; shift ;;
|
||||
-a | --architecture ) ARCH="${2}"; shift 2 ;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
install-dependencies-standalone
|
||||
fi
|
||||
}
|
||||
|
||||
install-dependencies-main $*
|
||||
@@ -1,147 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# macOS build function
|
||||
##############################################################################
|
||||
#
|
||||
# This script file can be included in build scripts for macOS or run directly
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Halt on errors
|
||||
set -eE
|
||||
|
||||
build_obs() {
|
||||
status "Build OBS"
|
||||
trap "caught_error 'build app'" ERR
|
||||
|
||||
step "Configure OBS..."
|
||||
_configure_obs
|
||||
|
||||
ensure_dir "${CHECKOUT_DIR}/"
|
||||
step "Build OBS targets..."
|
||||
|
||||
export NSUnbufferedIO=YES
|
||||
|
||||
: "${PACKAGE:=}"
|
||||
case "${GITHUB_EVENT_NAME}" in
|
||||
push) if [[ ${GITHUB_REF_NAME} =~ [0-9]+.[0-9]+.[0-9]+(-(rc|beta).+)? ]]; then PACKAGE=1; fi ;;
|
||||
pull_request) PACKAGE=1 ;;
|
||||
esac
|
||||
|
||||
pushd "build_macos" > /dev/null
|
||||
|
||||
if [[ "${PACKAGE}" && "${CODESIGN_IDENT:--}" != '-' ]]; then
|
||||
set -o pipefail && xcodebuild ONLY_ACTIVE_ARCH=NO -archivePath "obs-studio.xcarchive" -scheme obs-studio -destination "generic/platform=macOS,name=Any Mac" -parallelizeTargets -hideShellScriptEnvironment archive 2>&1 | xcbeautify
|
||||
set -o pipefail && xcodebuild -exportArchive -archivePath "obs-studio.xcarchive" -exportOptionsPlist "exportOptions.plist" -exportPath "." 2>&1 | xcbeautify
|
||||
else
|
||||
set -o pipefail && xcodebuild ONLY_ACTIVE_ARCH=NO -project obs-studio.xcodeproj -target obs-studio -destination "generic/platform=macOS,name=Any Mac" -parallelizeTargets -configuration RelWithDebInfo -hideShellScriptEnvironment build 2>&1 | xcbeautify
|
||||
|
||||
rm -rf OBS.app && mkdir OBS.app
|
||||
ditto UI/RelWithDebInfo/OBS.app OBS.app
|
||||
fi
|
||||
|
||||
popd > /dev/null
|
||||
|
||||
unset NSUnbufferedIO
|
||||
}
|
||||
|
||||
bundle_obs() {
|
||||
status "Create relocatable macOS application bundle"
|
||||
trap "caught_error 'package app'" ERR
|
||||
|
||||
ensure_dir "${CHECKOUT_DIR}"
|
||||
|
||||
step "Install OBS application bundle..."
|
||||
|
||||
find "build_macos/UI/${BUILD_CONFIG}" -type d -name "OBS.app" | xargs -I{} cp -r {} "build_${ARCH}"/
|
||||
}
|
||||
|
||||
# Function to configure OBS build
|
||||
_configure_obs() {
|
||||
if [ "${CODESIGN}" ]; then
|
||||
read_codesign_ident
|
||||
fi
|
||||
|
||||
ensure_dir "${CHECKOUT_DIR}"
|
||||
status "Configure OBS build system..."
|
||||
trap "caught_error 'configure build'" ERR
|
||||
check_ccache
|
||||
|
||||
if [ "${SPARKLE_APPCAST_URL}" -a "${SPARKLE_PUBLIC_KEY}" ]; then
|
||||
SPARKLE_OPTIONS="-DSPARKLE_APPCAST_URL=\"${SPARKLE_APPCAST_URL}\" -DSPARKLE_PUBLIC_KEY=\"${SPARKLE_PUBLIC_KEY}\""
|
||||
fi
|
||||
|
||||
PRESET="macos"
|
||||
|
||||
if [ "${CI}" ]; then
|
||||
case "${GITHUB_EVENT_NAME}" in
|
||||
push)
|
||||
if [ "${GITHUB_REF_TYPE}" != 'tag' ]; then
|
||||
PRESET="macos-ci"
|
||||
fi
|
||||
;;
|
||||
*) PRESET="macos-ci" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
cmake -S . --preset ${PRESET} \
|
||||
-DCMAKE_OSX_ARCHITECTURES=${ARCH} \
|
||||
-DCMAKE_INSTALL_PREFIX=${BUILD_DIR}/install \
|
||||
-DCMAKE_BUILD_TYPE=${BUILD_CONFIG} \
|
||||
-DOBS_CODESIGN_IDENTITY="${CODESIGN_IDENT:--}" \
|
||||
-DOBS_CODESIGN_TEAM="${CODESIGN_TEAM:-}" \
|
||||
-DOBS_PROVISIONING_PROFILE="${PROVISIONING_PROFILE:-}" \
|
||||
${YOUTUBE_OPTIONS} \
|
||||
${TWITCH_OPTIONS} \
|
||||
${RESTREAM_OPTIONS} \
|
||||
${SPARKLE_OPTIONS} \
|
||||
${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR}
|
||||
}
|
||||
|
||||
build-obs-standalone() {
|
||||
CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
|
||||
PRODUCT_NAME="OBS-Studio"
|
||||
DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support.sh"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh"
|
||||
|
||||
check_archs
|
||||
check_macos_version
|
||||
build_obs
|
||||
|
||||
if [ "${BUNDLE}" ]; then
|
||||
bundle_obs
|
||||
fi
|
||||
}
|
||||
|
||||
print_usage() {
|
||||
echo -e "Usage: ${0}\n" \
|
||||
"-h, --help : Print this help\n" \
|
||||
"-q, --quiet : Suppress most build process output\n" \
|
||||
"-v, --verbose : Enable more verbose build process output\n" \
|
||||
"-a, --architecture : Specify build architecture (default: x86_64, alternative: arm64)\n" \
|
||||
"-c, --codesign : Codesign OBS and all libraries (default: ad-hoc only)\n" \
|
||||
"-b, --bundle : Create relocatable OBS application bundle in build directory (default: build/install/OBS.app)\n"
|
||||
}
|
||||
|
||||
build-obs-main() {
|
||||
if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
|
||||
while true; do
|
||||
case "${1}" in
|
||||
-h | --help ) print_usage; exit 0 ;;
|
||||
-q | --quiet ) export QUIET=TRUE; shift ;;
|
||||
-v | --verbose ) export VERBOSE=TRUE; shift ;;
|
||||
-a | --architecture ) ARCH="${2}"; shift 2 ;;
|
||||
-c | --codesign ) CODESIGN=TRUE; shift ;;
|
||||
-b | --bundle ) BUNDLE=TRUE; shift ;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
build-obs-standalone
|
||||
fi
|
||||
}
|
||||
|
||||
build-obs-main $*
|
||||
@@ -1,184 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# macOS libobs plugin package function
|
||||
##############################################################################
|
||||
#
|
||||
# This script file can be included in build scripts for macOS or run directly
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Halt on errors
|
||||
set -eE
|
||||
|
||||
package_obs() {
|
||||
if [ "${CODESIGN}" ]; then
|
||||
read_codesign_ident
|
||||
fi
|
||||
|
||||
status "Create macOS disk image"
|
||||
trap "caught_error 'package app'" ERR
|
||||
|
||||
ensure_dir "${CHECKOUT_DIR}"
|
||||
|
||||
step "Package OBS..."
|
||||
BUILD_DIR="build_macos"
|
||||
|
||||
root_dir="$(pwd)"
|
||||
|
||||
pushd "${BUILD_DIR}" > /dev/null > /dev/null
|
||||
|
||||
mkdir -p "${FILE_NAME//.dmg/}/.background"
|
||||
cp "${root_dir}/cmake/macos/resources/background.tiff" "${FILE_NAME//.dmg/}/.background/"
|
||||
cp "${root_dir}/cmake/macos/resources/AppIcon.icns" "${FILE_NAME//.dmg/}/.VolumeIcon.icns"
|
||||
ln -s /Applications "${FILE_NAME//.dmg/}/Applications"
|
||||
|
||||
mkdir -p "${FILE_NAME//.dmg/}/OBS.app"
|
||||
ditto OBS.app "${FILE_NAME//.dmg/}/OBS.app"
|
||||
|
||||
hdiutil create -volname "${FILE_NAME//.dmg/}" -srcfolder "${FILE_NAME//.dmg/}" -ov -fs APFS -format UDRW temp.dmg
|
||||
hdiutil attach -noverify -readwrite temp.dmg
|
||||
SetFile -c icnC /Volumes/"${FILE_NAME//.dmg/}"/.VolumeIcon.icns
|
||||
SetFile -a C /Volumes/"${FILE_NAME//.dmg/}"
|
||||
osascript package.applescript "${FILE_NAME//.dmg/}"
|
||||
hdiutil detach "/Volumes/${FILE_NAME//.dmg/}"
|
||||
hdiutil convert -format ULMO -o "${FILE_NAME}" temp.dmg
|
||||
|
||||
rm temp.dmg
|
||||
|
||||
step "Codesign OBS disk image..."
|
||||
/usr/bin/codesign --force --sign "${CODESIGN_IDENT:--}" "${FILE_NAME}"
|
||||
|
||||
rm -rf "${FILE_NAME//.dmg/}"
|
||||
popd > /dev/null
|
||||
}
|
||||
|
||||
notarize_obs() {
|
||||
status "Notarize OBS"
|
||||
trap "caught_error 'notarizing app'" ERR
|
||||
|
||||
if ! exists brew; then
|
||||
error "ERROR Homebrew not found - please install homebrew (https://brew.sh)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_dir "${CHECKOUT_DIR}"
|
||||
|
||||
if [ "${NOTARIZE_IMAGE}" ]; then
|
||||
trap "_caught_error_hdiutil_verify '${NOTARIZE_IMAGE}'" ERR
|
||||
|
||||
step "Verify OBS disk image ${NOTARIZE_IMAGE}..."
|
||||
hdiutil verify "${NOTARIZE_IMAGE}"
|
||||
|
||||
NOTARIZE_TARGET="${NOTARIZE_IMAGE}"
|
||||
elif [ "${NOTARIZE_BUNDLE}" ]; then
|
||||
NOTARIZE_TARGET="${NOTARIZE_BUNDLE}"
|
||||
else
|
||||
OBS_IMAGE="${BUILD_DIR}/${FILE_NAME}"
|
||||
|
||||
if [ -f "${OBS_IMAGE}" ]; then
|
||||
NOTARIZE_TARGET="${OBS_IMAGE}"
|
||||
else
|
||||
error "No notarization application bundle ('OBS.app') or disk image ('${NOTARIZE_IMAGE:-${FILE_NAME}}') found"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$?" -eq 0 ]; then
|
||||
read_codesign_ident
|
||||
read_codesign_pass
|
||||
|
||||
step "Notarize ${NOTARIZE_TARGET}..."
|
||||
/usr/bin/xcrun notarytool submit "${NOTARIZE_TARGET}" --keychain-profile "OBS-Codesign-Password" --wait
|
||||
|
||||
step "Staple the ticket to ${NOTARIZE_TARGET}..."
|
||||
/usr/bin/xcrun stapler staple "${NOTARIZE_TARGET}"
|
||||
fi
|
||||
}
|
||||
|
||||
_caught_error_hdiutil_verify() {
|
||||
error "ERROR during verifying image '${1}'"
|
||||
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
package-obs-standalone() {
|
||||
PRODUCT_NAME="OBS-Studio"
|
||||
|
||||
CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)"
|
||||
DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support.sh"
|
||||
source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh"
|
||||
|
||||
check_archs
|
||||
check_macos_version
|
||||
|
||||
if [ -z "${CI}" ]; then
|
||||
step "Fetch OBS tags..."
|
||||
/usr/bin/git fetch --tags origin
|
||||
fi
|
||||
|
||||
GIT_BRANCH=$(/usr/bin/git rev-parse --abbrev-ref HEAD)
|
||||
GIT_HASH=$(/usr/bin/git rev-parse --short=9 HEAD)
|
||||
GIT_TAG=$(/usr/bin/git describe --tags --abbrev=0)
|
||||
|
||||
if [ "${BUILD_FOR_DISTRIBUTION}" ]; then
|
||||
VERSION_STRING="${GIT_TAG}"
|
||||
else
|
||||
VERSION_STRING="${GIT_TAG}-${GIT_HASH}"
|
||||
fi
|
||||
|
||||
if [ -z "${NOTARIZE_IMAGE}" -a -z "${NOTARIZE_BUNDLE}" ]; then
|
||||
if [ "${ARCH}" = "arm64" ]; then
|
||||
FILE_NAME="obs-studio-${VERSION_STRING}-macos-arm64.dmg"
|
||||
elif [ "${ARCH}" = "universal" ]; then
|
||||
FILE_NAME="obs-studio-${VERSION_STRING}-macos.dmg"
|
||||
else
|
||||
FILE_NAME="obs-studio-${VERSION_STRING}-macos-x86_64.dmg"
|
||||
fi
|
||||
|
||||
package_obs
|
||||
fi
|
||||
|
||||
if [ "${NOTARIZE}" ]; then
|
||||
notarize_obs
|
||||
fi
|
||||
}
|
||||
|
||||
print_usage() {
|
||||
echo -e "Usage: ${0}\n" \
|
||||
"-h, --help : Print this help\n" \
|
||||
"-q, --quiet : Suppress most build process output\n" \
|
||||
"-v, --verbose : Enable more verbose build process output\n" \
|
||||
"-a, --architecture : Specify build architecture (default: x86_64, alternative: arm64)\n" \
|
||||
"-c, --codesign : Codesign OBS and all libraries (default: ad-hoc only)\n" \
|
||||
"-n, --notarize : Notarize OBS (default: off)\n" \
|
||||
"--notarize-image [IMAGE] : Specify existing OBS disk image for notarization\n" \
|
||||
"--notarize-bundle [BUNDLE] : Specify existing OBS application bundle for notarization\n" \
|
||||
"--build-dir : Specify alternative build directory (default: build)\n"
|
||||
}
|
||||
|
||||
package-obs-main() {
|
||||
if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then
|
||||
while true; do
|
||||
case "${1}" in
|
||||
-h | --help ) print_usage; exit 0 ;;
|
||||
-q | --quiet ) export QUIET=TRUE; shift ;;
|
||||
-v | --verbose ) export VERBOSE=TRUE; shift ;;
|
||||
-a | --architecture ) ARCH="${2}"; shift 2 ;;
|
||||
-c | --codesign ) CODESIGN=TRUE; shift ;;
|
||||
-n | --notarize ) NOTARIZE=TRUE; CODESIGN=TRUE; shift ;;
|
||||
--build-dir ) BUILD_DIR="${2}"; shift 2 ;;
|
||||
--notarize-image ) NOTARIZE_IMAGE="${2}"; NOTARIZE=TRUE; CODESIGN=TRUE; shift 2 ;;
|
||||
--notarize-bundle ) NOTARIZE_BUNDLE="${2}"; NOTARIZE=TRUE; CODESIGN=TRUE; shift 2 ;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
package-obs-standalone
|
||||
fi
|
||||
}
|
||||
|
||||
package-obs-main $*
|
||||
@@ -1,96 +0,0 @@
|
||||
import os
|
||||
from copy import deepcopy
|
||||
|
||||
import xmltodict
|
||||
|
||||
|
||||
DELTA_BASE_URL = "https://cdn-fastly.obsproject.com/downloads/sparkle_deltas"
|
||||
|
||||
|
||||
def convert_appcast(filename):
|
||||
print("Converting", filename)
|
||||
in_path = os.path.join("output/appcasts", filename)
|
||||
out_path = os.path.join("output/appcasts/stable", filename.replace("_v2", ""))
|
||||
with open(in_path, "rb") as f:
|
||||
xml_data = f.read()
|
||||
if not xml_data:
|
||||
return
|
||||
|
||||
appcast = xmltodict.parse(xml_data, force_list=("item",))
|
||||
out_appcast = deepcopy(appcast)
|
||||
|
||||
# Remove anything but stable channel items.
|
||||
new_list = []
|
||||
for _item in appcast["rss"]["channel"]["item"]:
|
||||
item = deepcopy(_item)
|
||||
branch = item.pop("sparkle:channel", "stable")
|
||||
if branch != "stable":
|
||||
continue
|
||||
# Remove delta information (incompatible with Sparkle 1.x)
|
||||
item.pop("sparkle:deltas", None)
|
||||
new_list.append(item)
|
||||
|
||||
out_appcast["rss"]["channel"]["item"] = new_list
|
||||
|
||||
with open(out_path, "wb") as f:
|
||||
xmltodict.unparse(out_appcast, output=f, pretty=True)
|
||||
|
||||
# Also create legacy appcast from x86 version.
|
||||
if "x86" in filename:
|
||||
out_path = os.path.join("output/appcasts/stable", "updates.xml")
|
||||
with open(out_path, "wb") as f:
|
||||
xmltodict.unparse(out_appcast, output=f, pretty=True)
|
||||
|
||||
|
||||
def adjust_appcast(filename):
|
||||
print("Adjusting", filename)
|
||||
file_path = os.path.join("output/appcasts", filename)
|
||||
with open(file_path, "rb") as f:
|
||||
xml_data = f.read()
|
||||
if not xml_data:
|
||||
return
|
||||
|
||||
arch = "arm64" if "arm64" in filename else "x86_64"
|
||||
appcast = xmltodict.parse(xml_data, force_list=("item", "enclosure"))
|
||||
|
||||
out_appcast = deepcopy(appcast)
|
||||
out_appcast["rss"]["channel"]["title"] = "OBS Studio"
|
||||
out_appcast["rss"]["channel"]["link"] = "https://obsproject.com/"
|
||||
|
||||
new_list = []
|
||||
for _item in appcast["rss"]["channel"]["item"]:
|
||||
item = deepcopy(_item)
|
||||
# Fix changelog URL
|
||||
# Sparkle doesn't allow us to specify the URL for a specific update,
|
||||
# so we set the full release notes link instead and then rewrite the
|
||||
# appcast. Yay.
|
||||
if release_notes_link := item.pop("sparkle:fullReleaseNotesLink", None):
|
||||
item["sparkle:releaseNotesLink"] = release_notes_link
|
||||
|
||||
# If deltas exist, update their URLs to match server layout
|
||||
# (generate_appcast doesn't allow this).
|
||||
if deltas := item.get("sparkle:deltas", None):
|
||||
for delta_item in deltas["enclosure"]:
|
||||
delta_filename = delta_item["@url"].rpartition("/")[2]
|
||||
delta_item["@url"] = f"{DELTA_BASE_URL}/{arch}/{delta_filename}"
|
||||
|
||||
new_list.append(item)
|
||||
|
||||
out_appcast["rss"]["channel"]["item"] = new_list
|
||||
|
||||
with open(file_path, "wb") as f:
|
||||
xmltodict.unparse(out_appcast, output=f, pretty=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for ac_file in os.listdir("output/appcasts"):
|
||||
if ".xml" not in ac_file:
|
||||
continue
|
||||
if "v2" not in ac_file:
|
||||
# generate_appcast may download legacy appcast files and update them as well.
|
||||
# Those generated files are not backwards-compatible, so delete whatever v1
|
||||
# files it may have created and recreate them manually.
|
||||
os.remove(os.path.join("output/appcasts", ac_file))
|
||||
continue
|
||||
adjust_appcast(ac_file)
|
||||
convert_appcast(ac_file)
|
||||
@@ -1,125 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import plistlib
|
||||
import glob
|
||||
import subprocess
|
||||
import argparse
|
||||
|
||||
import requests
|
||||
import xmltodict
|
||||
|
||||
|
||||
def download_build(url):
|
||||
print(f'Downloading build "{url}"...')
|
||||
filename = url.rpartition("/")[2]
|
||||
r = requests.get(url)
|
||||
if r.status_code == 200:
|
||||
with open(f"artifacts/{filename}", "wb") as f:
|
||||
f.write(r.content)
|
||||
else:
|
||||
print(f"Build download failed, status code: {r.status_code}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def read_appcast(url):
|
||||
print(f"Downloading feed {url} ...")
|
||||
r = requests.get(url)
|
||||
if r.status_code != 200:
|
||||
print(f"Appcast download failed, status code: {r.status_code}")
|
||||
sys.exit(1)
|
||||
|
||||
filename = url.rpartition("/")[2]
|
||||
with open(f"builds/{filename}", "wb") as f:
|
||||
f.write(r.content)
|
||||
|
||||
appcast = xmltodict.parse(r.content, force_list=("item",))
|
||||
|
||||
dl = 0
|
||||
for item in appcast["rss"]["channel"]["item"]:
|
||||
channel = item.get("sparkle:channel", "stable")
|
||||
if channel != target_branch:
|
||||
continue
|
||||
|
||||
if dl == max_old_vers:
|
||||
break
|
||||
download_build(item["enclosure"]["@url"])
|
||||
dl += 1
|
||||
|
||||
|
||||
def get_appcast_url(artifact_dir):
|
||||
dmgs = glob.glob(artifact_dir + "/*.dmg")
|
||||
if not dmgs:
|
||||
raise ValueError("No artifacts!")
|
||||
elif len(dmgs) > 1:
|
||||
raise ValueError("Too many artifacts!")
|
||||
|
||||
dmg = dmgs[0]
|
||||
print(f"Mounting {dmg} ...")
|
||||
out = subprocess.check_output(
|
||||
[
|
||||
"hdiutil",
|
||||
"attach",
|
||||
"-readonly",
|
||||
"-noverify",
|
||||
"-noautoopen",
|
||||
"-plist",
|
||||
dmg,
|
||||
]
|
||||
)
|
||||
d = plistlib.loads(out)
|
||||
|
||||
mountpoint = ""
|
||||
for item in d["system-entities"]:
|
||||
if "mount-point" not in item:
|
||||
continue
|
||||
mountpoint = item["mount-point"]
|
||||
break
|
||||
|
||||
url = None
|
||||
plist_files = glob.glob(mountpoint + "/*.app/Contents/Info.plist")
|
||||
if plist_files:
|
||||
plist_file = plist_files[0]
|
||||
print(f"Reading plist {plist_file} ...")
|
||||
plist = plistlib.load(open(plist_file, "rb"))
|
||||
url = plist.get("SUFeedURL", None)
|
||||
else:
|
||||
print("No Plist file found!")
|
||||
|
||||
print(f"Unmounting {mountpoint}")
|
||||
subprocess.check_call(["hdiutil", "detach", mountpoint])
|
||||
return url
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--artifacts-dir",
|
||||
dest="artifacts",
|
||||
action="store",
|
||||
default="artifacts",
|
||||
help="Folder containing artifact",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--branch",
|
||||
dest="branch",
|
||||
action="store",
|
||||
default="stable",
|
||||
help="Channel/Branch",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-old-versions",
|
||||
dest="max_old_ver",
|
||||
action="store",
|
||||
type=int,
|
||||
default=1,
|
||||
help="Maximum old versions to download",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
target_branch = args.branch
|
||||
max_old_vers = args.max_old_ver
|
||||
url = get_appcast_url(args.artifacts)
|
||||
if not url:
|
||||
raise ValueError("Failed to get Sparkle URL from DMG!")
|
||||
|
||||
read_appcast(url)
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/zsh
|
||||
|
||||
arch_name="$(uname -m)"
|
||||
|
||||
# When the script is launched from Steam, it'll be run through Rosetta.
|
||||
# Manually override arch to arm64 in that case.
|
||||
if [ "$(sysctl -in sysctl.proc_translated)" = "1" ]; then
|
||||
arch_name="arm64"
|
||||
fi
|
||||
|
||||
# Allow users to force Rosetta
|
||||
if [[ "$@" =~ \-\-intel ]]; then
|
||||
arch_name="x86_64"
|
||||
fi
|
||||
|
||||
# legacy app installation
|
||||
if [ -d OBS.app ]; then
|
||||
exec open OBS.app -W --args "$@"
|
||||
fi
|
||||
|
||||
if [ "${arch_name}" = "x86_64" ]; then
|
||||
exec open x86/OBS.app -W --args "$@"
|
||||
elif [ "${arch_name}" = "arm64" ]; then
|
||||
exec open arm64/OBS.app -W --args "$@"
|
||||
else
|
||||
echo "Unknown architecture: ${arch_name}"
|
||||
fi
|
||||
@@ -9,9 +9,7 @@
|
||||
<key>com.obsproject.obs-studio</key>
|
||||
<string>${OBS_PROVISIONING_PROFILE}</string>
|
||||
</dict>
|
||||
<key>signingCertificate</key>
|
||||
<string>${OBS_CODESIGN_IDENTITY}</string>
|
||||
<key>signingStyle</key>
|
||||
<string>manual</string>
|
||||
<string>automatic</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
<dict>
|
||||
<key>method</key>
|
||||
<string>developer-id</string>
|
||||
<key>signingCertificate</key>
|
||||
<string>${OBS_CODESIGN_IDENTITY}</string>
|
||||
<key>signingStyle</key>
|
||||
<string>manual</string>
|
||||
<string>automatic</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f"
|
||||
@@ -0,0 +1,3 @@
|
||||
local icon=' ✖︎ '
|
||||
|
||||
print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}"
|
||||
@@ -0,0 +1,16 @@
|
||||
autoload -Uz log_info
|
||||
|
||||
if (( ! ${+_log_group} )) typeset -g _log_group=0
|
||||
|
||||
if (( ${+CI} )) {
|
||||
if (( _log_group )) {
|
||||
print "::endgroup::"
|
||||
typeset -g _log_group=0
|
||||
}
|
||||
if (( # )) {
|
||||
print "::group::${@}"
|
||||
typeset -g _log_group=1
|
||||
}
|
||||
} else {
|
||||
if (( # )) log_info ${@}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=' =>'
|
||||
|
||||
print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=''
|
||||
|
||||
print -PR " ${(r:5:)icon} ${@}"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=' >'
|
||||
|
||||
print -PR "%F{2} ${(r:5:)icon}%f ${@}"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||
|
||||
if (( _loglevel > 0 )) {
|
||||
local icon=' =>'
|
||||
|
||||
print -PR "${CI:+::warning::}%F{3} ${(r:5:)icon} ${@}%f"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
autoload -Uz log_debug log_error
|
||||
|
||||
local -r _usage="Usage: %B${0}%b <loglevel>
|
||||
|
||||
Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)"
|
||||
|
||||
if (( ! # )); then
|
||||
log_error 'Called without arguments.'
|
||||
log_output ${_usage}
|
||||
return 2
|
||||
elif (( ${1} >= 4 )); then
|
||||
log_error 'Called with loglevel > 3.'
|
||||
log_output ${_usage}
|
||||
fi
|
||||
|
||||
typeset -g -i -r _loglevel=${1}
|
||||
log_debug "Log level set to '${1}'"
|
||||
Executable
+190
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
builtin emulate -L zsh
|
||||
setopt EXTENDED_GLOB
|
||||
setopt PUSHD_SILENT
|
||||
setopt ERR_EXIT
|
||||
setopt ERR_RETURN
|
||||
setopt NO_UNSET
|
||||
setopt PIPE_FAIL
|
||||
setopt NO_AUTO_PUSHD
|
||||
setopt NO_PUSHD_IGNORE_DUPS
|
||||
setopt FUNCTION_ARGZERO
|
||||
|
||||
## Enable for script debugging
|
||||
# setopt WARN_CREATE_GLOBAL
|
||||
# setopt WARN_NESTED_VAR
|
||||
# setopt XTRACE
|
||||
|
||||
autoload -Uz is-at-least && if ! is-at-least 5.2; then
|
||||
print -u2 -PR "%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade zsh to fix this issue."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
invoke_formatter() {
|
||||
if (( # < 1 )) {
|
||||
log_error "Usage invoke_formatter [formatter_name]"
|
||||
exit 2
|
||||
}
|
||||
|
||||
case ${1} {
|
||||
clang)
|
||||
if (( ${+commands[clang-format-13]} )) {
|
||||
local formatter=clang-format-13
|
||||
} elif (( ${+commands[clang-format]} )) {
|
||||
local formatter=clang-format
|
||||
} else {
|
||||
log_error "No viable clang-format version found (required 13.0.1)"
|
||||
exit 2
|
||||
}
|
||||
|
||||
local -a formatter_version=($(${formatter} --version))
|
||||
|
||||
if ! is-at-least 13.0.1 ${formatter_version[-1]}; then
|
||||
log_error "clang-format is not version 13.0.1 or above (found ${formatter_version[-1]}."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! is-at-least ${formatter_version[-1]} 13.0.1; then
|
||||
log_error "clang-format is more recent than version 13.0.1 (found ${formatter_version[-1]})."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
local -a source_files=((libobs|libobs-*|UI|plugins)/**/*.(c|cpp|h|hpp|m|mm)(.N))
|
||||
source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|enc-amf|mac-syphon/syphon-framework|obs-outputs/ftl-sdk)/*})
|
||||
|
||||
local -a format_args=(-style=file -fallback-style=none)
|
||||
if (( _loglevel > 2 )) format_args+=(--verbose)
|
||||
;;
|
||||
cmake)
|
||||
local formatter=cmake-format
|
||||
if (( ${+commands[cmake-format]} )) {
|
||||
local cmake_format_version=$(cmake-format --version)
|
||||
|
||||
if ! is-at-least 0.6.13 ${cmake_format_version}; then
|
||||
log_error "cmake-format is not version 0.6.13 or above (found ${cmake_format_version})."
|
||||
exit 2
|
||||
fi
|
||||
} else {
|
||||
log_error "No viable cmake-format version found (required 0.6.13)"
|
||||
exit 2
|
||||
}
|
||||
|
||||
local -a source_files=((libobs|libobs-*|UI|plugins|cmake)/**/(CMakeLists.txt|*.cmake)(.N))
|
||||
source_files=(${source_files:#*/(obs-outputs/ftl-sdk|jansson|decklink/*/decklink-sdk|enc-amf|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*})
|
||||
|
||||
local -a format_args=()
|
||||
if (( _loglevel > 2 )) format_args+=(--log-level debug)
|
||||
;;
|
||||
swift)
|
||||
local formatter=swift-format
|
||||
if (( ${+commands[swift-format]} )) {
|
||||
local swift_format_version=$(swift-format --version)
|
||||
|
||||
if ! is-at-least 508.0.0 ${swift_format_version}; then
|
||||
log_error "swift-format is not version 508.0.0 or above (found ${swift_format_version})."
|
||||
exit 2
|
||||
fi
|
||||
} else {
|
||||
log_error "No viable swift-format version found (required 508.0.0)"
|
||||
exit 2
|
||||
}
|
||||
|
||||
local -a source_files=((libobs|libobs-*|UI|plugins)/**/*.swift(.N))
|
||||
|
||||
local -a format_args=()
|
||||
;;
|
||||
*) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, cmake-format, and swift-format."; exit 2 ;;
|
||||
}
|
||||
|
||||
local file
|
||||
local -i num_failures=0
|
||||
if (( check_only )) {
|
||||
for file (${source_files}) {
|
||||
if (( _loglevel > 1 )) log_info "Checking format of ${file}..."
|
||||
|
||||
if ! "${formatter}" ${format_args} "${file}" | diff -q "${file}" - &> /dev/null; then
|
||||
log_error "${file} requires formatting changes."
|
||||
|
||||
if (( fail_on_error == 2 )) return 2;
|
||||
num_failures=$(( num_failures + 1 ))
|
||||
else
|
||||
if (( _loglevel > 1 )) log_status "${file} requires no formatting changes."
|
||||
fi
|
||||
}
|
||||
if (( fail_on_error && num_failures )) return 2;
|
||||
} elif (( ${#source_files} )) {
|
||||
format_args+=(-i)
|
||||
"${formatter}" ${format_args} ${source_files}
|
||||
}
|
||||
}
|
||||
|
||||
run_format() {
|
||||
if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}
|
||||
if (( ! ${+FORMATTER_NAME} )) typeset -g FORMATTER_NAME=${${(s:-:)ZSH_ARGZERO:t:r}[2]}
|
||||
|
||||
typeset -g host_os=${${(L)$(uname -s)}//darwin/macos}
|
||||
local -i fail_on_error=0
|
||||
local -i check_only=0
|
||||
local -i verbosity=1
|
||||
local -r _version='1.0.0'
|
||||
|
||||
fpath=("${SCRIPT_HOME}/.functions" ${fpath})
|
||||
autoload -Uz set_loglevel log_info log_error log_output log_status log_warning
|
||||
|
||||
local -r _usage="
|
||||
Usage: %B${functrace[1]%:*}%b <option>
|
||||
|
||||
%BOptions%b:
|
||||
|
||||
%F{yellow} Formatting options%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-c | --check%b Check only, no actual formatting takes place
|
||||
|
||||
%F{yellow} Output options%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-v | --verbose%b Verbose (more detailed output)
|
||||
%B--fail-[never|error] Fail script never/on formatting change - default: %B%F{green}never%f%b
|
||||
%B--debug%b Debug (very detailed and added output)
|
||||
|
||||
%F{yellow} General options%f
|
||||
-----------------------------------------------------------------------------
|
||||
%B-h | --help%b Print this usage help
|
||||
%B-V | --version%b Print script version information"
|
||||
|
||||
local -a args
|
||||
while (( # )) {
|
||||
case ${1} {
|
||||
--)
|
||||
shift
|
||||
args+=($@)
|
||||
break
|
||||
;;
|
||||
-c|--check) check_only=1; shift ;;
|
||||
-v|--verbose) (( verbosity += 1 )); shift ;;
|
||||
-h|--help) log_output ${_usage}; exit 0 ;;
|
||||
-V|--version) print -Pr "${_version}"; exit 0 ;;
|
||||
--debug) verbosity=3; shift ;;
|
||||
--fail-never)
|
||||
fail_on_error=0
|
||||
shift
|
||||
;;
|
||||
--fail-error)
|
||||
fail_on_error=1
|
||||
shift
|
||||
;;
|
||||
--fail-fast)
|
||||
fail_on_error=2
|
||||
shift
|
||||
;;
|
||||
*) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
|
||||
}
|
||||
}
|
||||
|
||||
set -- ${(@)args}
|
||||
set_loglevel ${verbosity}
|
||||
|
||||
invoke_formatter ${FORMATTER_NAME}
|
||||
}
|
||||
|
||||
run_format ${@}
|
||||
@@ -1,40 +1,96 @@
|
||||
import argparse
|
||||
import glob
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
MAIN_MANIFEST_FILENAME = "com.obsproject.Studio.json"
|
||||
|
||||
def main():
|
||||
dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
if not os.path.isfile(os.path.join(dir_path, MAIN_MANIFEST_FILENAME)):
|
||||
print("The script is not ran in the same folder as the manifest")
|
||||
return 1
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Format Flatpak manifest")
|
||||
parser.add_argument(
|
||||
"manifest_file",
|
||||
metavar="FILE",
|
||||
type=str,
|
||||
help="Manifest file to adjust format for",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--check",
|
||||
action="store_true",
|
||||
help="Check for necessary changes only",
|
||||
default=False,
|
||||
required=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--loglevel", type=str, help="Set log level", default="WARNING", required=False
|
||||
)
|
||||
|
||||
for root, dirs, files in os.walk(dir_path):
|
||||
for file in files:
|
||||
if not file.endswith(".json"):
|
||||
continue
|
||||
arguments = parser.parse_args()
|
||||
|
||||
print(f"Formatting {file}")
|
||||
# Load JSON file
|
||||
with open(os.path.join(root, file), "r") as f:
|
||||
j = json.load(f)
|
||||
logging.basicConfig(level=arguments.loglevel, format="%(message)s")
|
||||
logger = logging.getLogger()
|
||||
|
||||
if file == MAIN_MANIFEST_FILENAME:
|
||||
# Sort module files order in the manifest
|
||||
# Assumption: All modules except the last are strings
|
||||
file_modules = j["modules"][0:-1]
|
||||
last_module = j["modules"][-1]
|
||||
file_modules.sort(key=lambda file_name: file_name)
|
||||
j["modules"] = file_modules
|
||||
j["modules"].append(last_module)
|
||||
manifest_file = arguments.manifest_file
|
||||
|
||||
# Overwrite JSON file
|
||||
with open(os.path.join(root, file), "w") as f:
|
||||
json.dump(j, f, indent=4, ensure_ascii=False)
|
||||
f.write("\n")
|
||||
try:
|
||||
with open(manifest_file, "r+") as manifest:
|
||||
manifest_path = os.path.dirname(manifest_file)
|
||||
manifest_string = manifest.read()
|
||||
manifest_data = json.loads(manifest_string)
|
||||
|
||||
module_list = manifest_data.get("modules", [])
|
||||
|
||||
obs_object = module_list[-1]
|
||||
|
||||
if type(obs_object) != dict:
|
||||
logger.error(
|
||||
f"❌ Last element in modules list is not the obs-studio object"
|
||||
)
|
||||
return 2
|
||||
|
||||
new_module_list = []
|
||||
|
||||
for module in module_list:
|
||||
if type(module) == str:
|
||||
if not os.path.isfile(os.path.join(manifest_path, module)):
|
||||
logger.warning(
|
||||
f"⚠️ Specified module {os.path.basename(module)} not found."
|
||||
)
|
||||
continue
|
||||
|
||||
new_module_list.append(module)
|
||||
|
||||
new_module_list.sort()
|
||||
new_module_list.append(obs_object)
|
||||
manifest_data["modules"] = new_module_list
|
||||
|
||||
new_manifest_string = (
|
||||
f"{json.dumps(manifest_data, indent=4, ensure_ascii=False)}\n"
|
||||
)
|
||||
|
||||
if arguments.check:
|
||||
if new_module_list != module_list:
|
||||
logger.error(f"❌ Module list failed order validation")
|
||||
return 2
|
||||
elif new_manifest_string != manifest_string:
|
||||
logger.error(f"❌ Manifest file is not correctly formatted")
|
||||
return 2
|
||||
else:
|
||||
logger.info(f"✅ Module list passed order validation")
|
||||
return 0
|
||||
|
||||
manifest.seek(0)
|
||||
manifest.truncate()
|
||||
manifest.write(new_manifest_string)
|
||||
|
||||
logger.info(f"✅ Updated manifest file '{manifest_file}")
|
||||
except IOError:
|
||||
logger.error(f"❌ Unable to read manifest file '{manifest_file}'")
|
||||
return 2
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
.run-format.zsh
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
.run-format.zsh
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
.run-format.zsh
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/bin/zsh
|
||||
|
||||
arch_name="${CPUTYPE}"
|
||||
is_translated="$(sysctl -in sysctl.proc_translated)"
|
||||
|
||||
if (( is_translated )) arch_name="arm64"
|
||||
if [[ ${@} == *'--intel'* ]] arch_name="x86_64"
|
||||
if [[ -d OBS.app ]] exec open OBS.app -W --args "${@}"
|
||||
|
||||
case ${arch_name} {
|
||||
x86_64) exec open x86_64/OBS.app -W --args "${@}" ;;
|
||||
arm64) exec open arm64/OBS.app -W --args "${@}" ;;
|
||||
*) echo "Unknown architecture: ${arch_name}"; exit 2 ;;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user