2026-09-18 OBS开源第一个版本
This commit is contained in:
commit
438b2bada3
213
obs-studio/.clang-format
Normal file
213
obs-studio/.clang-format
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
# please use clang-format version 16 or later
|
||||||
|
|
||||||
|
AccessModifierOffset: -8
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllArgumentsOnNextLine: false
|
||||||
|
AllowAllConstructorInitializersOnNextLine: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLambdasOnASingleLine: Inline
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: false
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakStringLiterals: false # apparently unpredictable
|
||||||
|
ColumnLimit: 120
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ConstructorInitializerIndentWidth: 8
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
ForEachMacros:
|
||||||
|
- 'json_object_foreach'
|
||||||
|
- 'json_object_foreach_safe'
|
||||||
|
- 'json_array_foreach'
|
||||||
|
- 'HASH_ITER'
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: 8
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 8
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
|
||||||
|
PenaltyBreakAssignment: 10
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 30
|
||||||
|
PenaltyBreakComment: 10
|
||||||
|
PenaltyBreakFirstLessLess: 0
|
||||||
|
PenaltyBreakString: 10
|
||||||
|
PenaltyExcessCharacter: 100
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: false
|
||||||
|
SkipMacroDefinitionBody: true
|
||||||
|
SortIncludes: false
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
StatementMacros:
|
||||||
|
- 'Q_OBJECT'
|
||||||
|
TabWidth: 8
|
||||||
|
TypenameMacros:
|
||||||
|
- 'DARRAY'
|
||||||
|
UseTab: ForContinuationAndIndentation
|
||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
Standard: c++17
|
||||||
|
InsertBraces: true
|
||||||
|
---
|
||||||
|
Language: ObjC
|
||||||
|
AccessModifierOffset: 2
|
||||||
|
AlignArrayOfStructures: Right
|
||||||
|
AlignConsecutiveAssignments: None
|
||||||
|
AlignConsecutiveBitFields: None
|
||||||
|
AlignConsecutiveDeclarations: None
|
||||||
|
AlignConsecutiveMacros:
|
||||||
|
Enabled: true
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: true
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortEnumsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLambdasOnASingleLine: None
|
||||||
|
AttributeMacros: ['__unused', '__autoreleasing', '_Nonnull', '__bridge']
|
||||||
|
BitFieldColonSpacing: Both
|
||||||
|
#BreakBeforeBraces: Webkit
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: false
|
||||||
|
AfterClass: true
|
||||||
|
AfterControlStatement: Never
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
BeforeLambdaBody: false
|
||||||
|
BeforeWhile: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakAfterAttributes: Never
|
||||||
|
BreakArrays: false
|
||||||
|
BreakBeforeConceptDeclarations: Allowed
|
||||||
|
BreakBeforeInlineASMColon: OnlyMultiline
|
||||||
|
BreakConstructorInitializers: AfterColon
|
||||||
|
BreakInheritanceList: AfterComma
|
||||||
|
ColumnLimit: 120
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
EmptyLineAfterAccessModifier: Never
|
||||||
|
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
IndentAccessModifiers: false
|
||||||
|
IndentCaseBlocks: false
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentExternBlock: Indent
|
||||||
|
IndentGotoLabels: false
|
||||||
|
IndentRequiresClause: true
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: true
|
||||||
|
InsertBraces: false
|
||||||
|
InsertNewlineAtEOF: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
LambdaBodyIndentation: Signature
|
||||||
|
NamespaceIndentation: All
|
||||||
|
ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 4
|
||||||
|
ObjCBreakBeforeNestedBlockParam: false
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
PPIndentWidth: -1
|
||||||
|
PackConstructorInitializers: NextLine
|
||||||
|
QualifierAlignment: Leave
|
||||||
|
ReferenceAlignment: Right
|
||||||
|
RemoveSemicolon: false
|
||||||
|
RequiresClausePosition: WithPreceding
|
||||||
|
RequiresExpressionIndentation: OuterScope
|
||||||
|
SeparateDefinitionBlocks: Leave
|
||||||
|
ShortNamespaceLines: 1
|
||||||
|
SortIncludes: false
|
||||||
|
#SortUsingDeclarations: LexicographicNumeric
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAroundPointerQualifiers: Default
|
||||||
|
SpaceBeforeCaseColon: false
|
||||||
|
SpaceBeforeCpp11BracedList: true
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInLineCommentPrefix:
|
||||||
|
Minimum: 1
|
||||||
|
Maximum: -1
|
||||||
|
Standard: c++17
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
...
|
||||||
63
obs-studio/.editorconfig
Normal file
63
obs-studio/.editorconfig
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
# Since OBS follows the Linux kernel coding style I have started this file to
|
||||||
|
# help with automatically setting various text editors to those requirements.
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file.
|
||||||
|
[*]
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 8
|
||||||
|
|
||||||
|
# As per notr1ch, for 3rd party code that's a part of obs-outputs.
|
||||||
|
[plugins/obs-outputs/librtmp/*.{cpp,c,h}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[CMakeLists.txt]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[**/CMakeLists.txt]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[cmake/**/*.cmake]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[plugins/{rtmp-services,win-capture}/data/**/*.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.qss]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[build-aux/**/*.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.yaml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[{*.zsh,.*.zsh,build-aux/.functions/*,.github/scripts/utils.zsh/*}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.ui]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 1
|
||||||
|
|
||||||
|
[{*.obt,*.oha,*.ovt}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
8
obs-studio/.gersemirc
Normal file
8
obs-studio/.gersemirc
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/BlankSpruce/gersemi/master/gersemi/configuration.schema.json
|
||||||
|
|
||||||
|
definitions: []
|
||||||
|
line_length: 120
|
||||||
|
indent: 2
|
||||||
|
list_expansion: favour-inlining
|
||||||
|
unsafe: false
|
||||||
|
warn_about_unknown_commands: false
|
||||||
2
obs-studio/.github/FUNDING.yml
vendored
Normal file
2
obs-studio/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
open_collective: obsproject
|
||||||
|
patreon: obsproject
|
||||||
121
obs-studio/.github/actions/build-obs/action.yaml
vendored
Normal file
121
obs-studio/.github/actions/build-obs/action.yaml
vendored
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
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: ''
|
||||||
|
provisioningProfileUUID:
|
||||||
|
description: UUID of provisioning profile (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 }}
|
||||||
|
PROVISIONING_PROFILE: ${{ inputs.provisioningProfileUUID }}
|
||||||
|
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 update
|
||||||
|
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 ubuntu-${{ inputs.target }}
|
||||||
|
)
|
||||||
|
if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)
|
||||||
|
|
||||||
|
git fetch origin --no-tags --no-recurse-submodules -q
|
||||||
|
.github/scripts/build-ubuntu ${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: runner.os == 'Linux'
|
||||||
|
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||||
|
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
|
||||||
82
obs-studio/.github/actions/check-changes/action.yaml
vendored
Normal file
82
obs-studio/.github/actions/check-changes/action.yaml
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
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: false
|
||||||
|
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'
|
||||||
|
diffFilter:
|
||||||
|
description: git diff-filter string to use
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
outputs:
|
||||||
|
hasChangedFiles:
|
||||||
|
value: ${{ steps.checks.outputs.hasChangedFiles }}
|
||||||
|
description: True if specified files were changed in comparison to specified git reference
|
||||||
|
changedFiles:
|
||||||
|
value: ${{ 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 }}
|
||||||
|
GITHUB_EVENT_FORCED: ${{ github.event.forced }}
|
||||||
|
GITHUB_REF_BEFORE: ${{ github.event.before }}
|
||||||
|
USE_FALLBACK: ${{ inputs.useFallback }}
|
||||||
|
DIFF_FILTER: ${{ inputs.diffFilter }}
|
||||||
|
run: |
|
||||||
|
: Check for Changed Files ✅
|
||||||
|
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||||
|
shopt -s extglob
|
||||||
|
shopt -s dotglob
|
||||||
|
|
||||||
|
# 4b825dc642cb6eb9a060e54bf8d69288fbee4904 is a "hidden" sha1 hash of
|
||||||
|
# the "empty tree", retrieved via 'git hash-object -t tree /dev/null',
|
||||||
|
# and used here as a last-resort fallback to always provide a valid
|
||||||
|
# git ref.
|
||||||
|
|
||||||
|
if [[ "${GIT_BASE_REF}" ]]; then
|
||||||
|
if ! git cat-file -e "${GIT_BASE_REF}" &> /dev/null; then
|
||||||
|
echo "::warning::Provided base reference ${GIT_BASE_REF} is invalid"
|
||||||
|
if [[ "${USE_FALLBACK}" == 'true' ]]; then
|
||||||
|
GIT_BASE_REF='HEAD~1'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if ! git cat-file -e ${GITHUB_REF_BEFORE} &> /dev/null; then
|
||||||
|
GITHUB_REF_BEFORE='4b825dc642cb6eb9a060e54bf8d69288fbee4904'
|
||||||
|
fi
|
||||||
|
|
||||||
|
GIT_BASE_REF='HEAD~1'
|
||||||
|
case "${GITHUB_EVENT_NAME}" in
|
||||||
|
pull_request) GIT_BASE_REF="origin/${GITHUB_BASE_REF}" ;;
|
||||||
|
push) if [[ "${GITHUB_EVENT_FORCED}" != 'true' ]]; then GIT_BASE_REF="${GITHUB_REF_BEFORE}"; fi ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
changes=($(git diff --name-only --diff-filter="${DIFF_FILTER}" ${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
|
||||||
64
obs-studio/.github/actions/compatibility-validator/action.yaml
vendored
Normal file
64
obs-studio/.github/actions/compatibility-validator/action.yaml
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
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 update
|
||||||
|
brew install --quiet python3
|
||||||
|
|
||||||
|
python3 -m venv .venv
|
||||||
|
|
||||||
|
source .venv/bin/activate
|
||||||
|
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
|
||||||
|
source .venv/bin/activate
|
||||||
|
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@00b2d488bcba3bd01014dc073d276ef4a45d5c6c # v0.6.0
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
repo-token: ${{ inputs.repositorySecret }}
|
||||||
|
title: Compatibility JSON Errors
|
||||||
|
input: ${{ inputs.workingDirectory }}/validation_errors.json
|
||||||
75
obs-studio/.github/actions/flatpak-builder-lint/action.yaml
vendored
Normal file
75
obs-studio/.github/actions/flatpak-builder-lint/action.yaml
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
name: Run flatpak-builder-lint
|
||||||
|
description: Runs flatpak-builder-lint with exceptions
|
||||||
|
inputs:
|
||||||
|
artifact:
|
||||||
|
description: Type of artifact to lint (builddir, repo, manifest, appstream)
|
||||||
|
required: true
|
||||||
|
path:
|
||||||
|
description: Path to flatpak-builder manifest or Flatpak build directory
|
||||||
|
required: true
|
||||||
|
workingDirectory:
|
||||||
|
description: Working directory to clone flatpak-builder-lint
|
||||||
|
required: false
|
||||||
|
default: ${{ github.workspace }}
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Check artifact type
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ inputs.workingDirectory }}
|
||||||
|
run: |
|
||||||
|
: Check artifact input
|
||||||
|
if ! [[ "${{ inputs.artifact }}" =~ builddir|repo|manifest|appstream ]]; then
|
||||||
|
echo "::error::Given artifact type is incorrect"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Run flatpak-builder-lint
|
||||||
|
id: result
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ inputs.workingDirectory }}
|
||||||
|
run: |
|
||||||
|
: Run flatpak-builder-lint
|
||||||
|
|
||||||
|
return=0
|
||||||
|
result="$(flatpak-builder-lint --exceptions --user-exceptions ${GITHUB_ACTION_PATH}/exceptions.json ${{ inputs.artifact }} ${{ inputs.path }})" || return=$?
|
||||||
|
|
||||||
|
if [[ ${return} != 0 && -z "${result}" ]]; then
|
||||||
|
echo "::error::Error while running flatpak-builder-lint"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ inputs.artifact }}" == "appstream" ]]; then
|
||||||
|
echo "${result}"
|
||||||
|
|
||||||
|
if [[ ${return} != 0 ]]; then echo "::error::Flatpak appstream info is not valid"; fi
|
||||||
|
|
||||||
|
exit ${return}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# This jq command selects any available array under the 'warnings' key in the JSON document
|
||||||
|
# or provides an empty array as a fallback if the key is not present. This array is then
|
||||||
|
# piped to the 'map' function to apply a transformation to every element in the array,
|
||||||
|
# converting it to a string prefixed with the output level, the actual element value, and
|
||||||
|
# finally the suffix string defined in 'template'.
|
||||||
|
#
|
||||||
|
# The result of this expression is concatenated with a similar expression doing the same
|
||||||
|
# but for the 'errors' key and its associated array.
|
||||||
|
#
|
||||||
|
# The second jq invocation then selects each element of the array and outputs it directly,
|
||||||
|
# which will be strings of the formats:
|
||||||
|
#
|
||||||
|
# * '::warning:: <original warning> <template text>'
|
||||||
|
# * '::error:: <original error> <template text>'
|
||||||
|
#
|
||||||
|
# If no warnings or errors were found, only empty arrays were used for 'map' and thus
|
||||||
|
# only an empty result array is generated.
|
||||||
|
|
||||||
|
template=" found in the Flatpak ${{ inputs.artifact }}"
|
||||||
|
while read -r log_line; do
|
||||||
|
if [[ "${log_line}" ]]; then echo "${log_line}"; fi
|
||||||
|
done <<< "$(echo "${result}" \
|
||||||
|
| jq -r '(.warnings // [] | try map("::warning:: " + . + "${template}")) + (.errors // [] | try map("::error:: " + . + "${template}"))' \
|
||||||
|
| jq -r '.[]')"
|
||||||
|
|
||||||
|
exit ${return}
|
||||||
8
obs-studio/.github/actions/flatpak-builder-lint/exceptions.json
vendored
Normal file
8
obs-studio/.github/actions/flatpak-builder-lint/exceptions.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"com.obsproject.Studio": [
|
||||||
|
"finish-args-flatpak-spawn-access",
|
||||||
|
"external-gitmodule-url-found",
|
||||||
|
"finish-args-host-filesystem-access",
|
||||||
|
"appstream-flathub-manifest-url-not-reachable"
|
||||||
|
]
|
||||||
|
}
|
||||||
38
obs-studio/.github/actions/flatpak-manifest-validator/action.yaml
vendored
Normal file
38
obs-studio/.github/actions/flatpak-manifest-validator/action.yaml
vendored
Normal file
@ -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::
|
||||||
74
obs-studio/.github/actions/generate-docs/action.yaml
vendored
Normal file
74
obs-studio/.github/actions/generate-docs/action.yaml
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
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:=}"
|
||||||
|
|
||||||
|
# This expression will first try to match all the LIBOBS_API_[...]_VER
|
||||||
|
# lines in 'obs-config.h', before removing the '#define ' prefix and
|
||||||
|
# trimming away whitespace characters and linebreaks.
|
||||||
|
# This will yield a single line with the each major, minor, and patch
|
||||||
|
# version variable name followed by its value, so every even item
|
||||||
|
# in this string will contain a version part.
|
||||||
|
|
||||||
|
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' ' ')"
|
||||||
|
|
||||||
|
# This expression simply replaces the definition of the 'version' and
|
||||||
|
# 'release' variables in the Python script with updated variants using
|
||||||
|
# the version tokens set by the previous expression.
|
||||||
|
# The copyright variable assignment is updated to use the current
|
||||||
|
# local year as the second year value.
|
||||||
|
|
||||||
|
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@a44364271972de3b13c5b63801c2596dfde82413
|
||||||
|
with:
|
||||||
|
sphinx_src: ${{ inputs.sourceDirectory }}/docs/sphinx
|
||||||
|
build_only: true
|
||||||
|
target_branch: master
|
||||||
|
target_path: '../home/_build'
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: ${{ steps.setup.outputs.artifactName }} ${{ steps.setup.outputs.commitHash }}
|
||||||
|
path: |
|
||||||
|
${{ runner.temp }}/_github_home/_build
|
||||||
|
!${{ runner.temp }}/_github_home/_build/.doctrees
|
||||||
115
obs-studio/.github/actions/package-obs/action.yaml
vendored
Normal file
115
obs-studio/.github/actions/package-obs/action.yaml
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
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 update
|
||||||
|
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 ubuntu-${{ inputs.target }}
|
||||||
|
--config ${{ inputs.config }}
|
||||||
|
)
|
||||||
|
if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)
|
||||||
|
|
||||||
|
if [[ '${{ inputs.package }}' == true ]] package_args+=(--package)
|
||||||
|
|
||||||
|
${{ inputs.workingDirectory }}/.github/scripts/package-ubuntu ${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
|
||||||
61
obs-studio/.github/actions/qt-xml-validator/action.yaml
vendored
Normal file
61
obs-studio/.github/actions/qt-xml-validator/action.yaml
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
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@dd2ad21bd8a2de0187cb621419537f345e7e509c # v1.3.0
|
||||||
|
|
||||||
|
- name: Check for Changed Files ✅
|
||||||
|
uses: ./.github/actions/check-changes
|
||||||
|
id: checks
|
||||||
|
with:
|
||||||
|
checkGlob: 'frontend/forms/**/*.ui'
|
||||||
|
|
||||||
|
- name: Validate XML 💯
|
||||||
|
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||||
|
id: result
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}
|
||||||
|
run: |
|
||||||
|
: Validate XML 💯
|
||||||
|
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||||
|
|
||||||
|
CHANGED_FILES=($(echo "${CHANGED_FILES//[\[\]\'\"]/}" | tr "," "\n"))
|
||||||
|
|
||||||
|
if (( ${#CHANGED_FILES[@]} )); then
|
||||||
|
if [[ '${{ inputs.failCondition }}' == never ]]; then set +e; fi
|
||||||
|
xmllint \
|
||||||
|
--schema ${{ github.workspace }}/frontend/forms/XML-Schema-Qt5.15.xsd \
|
||||||
|
--noout "${CHANGED_FILES[@]}"
|
||||||
|
fi
|
||||||
63
obs-studio/.github/actions/run-clang-format/action.yaml
vendored
Normal file
63
obs-studio/.github/actions/run-clang-format/action.yaml
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
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: Check for Changed Files ✅
|
||||||
|
uses: ./.github/actions/check-changes
|
||||||
|
id: checks
|
||||||
|
with:
|
||||||
|
checkGlob: "'*.c' '*.h' '*.cpp' '*.hpp' '*.m' '*.mm'"
|
||||||
|
diffFilter: 'ACM'
|
||||||
|
|
||||||
|
- name: Install Dependencies 🛍️
|
||||||
|
if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||||
|
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@22/bin" >> $GITHUB_PATH
|
||||||
|
brew update
|
||||||
|
brew install --quiet zsh
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
- name: Run clang-format 🐉
|
||||||
|
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||||
|
id: result
|
||||||
|
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||||
|
working-directory: ${{ inputs.workingDirectory }}
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}
|
||||||
|
run: |
|
||||||
|
: Run clang-format 🐉
|
||||||
|
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||||
|
|
||||||
|
print ::group::Install clang-format-22
|
||||||
|
brew update
|
||||||
|
brew trust obsproject/tools
|
||||||
|
brew install --quiet obsproject/tools/clang-format@22
|
||||||
|
print ::endgroup::
|
||||||
|
|
||||||
|
print ::group::Run clang-format-22
|
||||||
|
local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/})
|
||||||
|
./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check ${changes}
|
||||||
|
print ::endgroup::
|
||||||
62
obs-studio/.github/actions/run-gersemi/action.yaml
vendored
Normal file
62
obs-studio/.github/actions/run-gersemi/action.yaml
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
name: Run gersemi
|
||||||
|
description: Runs gersemi 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-gersemi action requires a macOS-based or Linux-based runner."
|
||||||
|
exit 2
|
||||||
|
|
||||||
|
- name: Check for Changed Files ✅
|
||||||
|
uses: ./.github/actions/check-changes
|
||||||
|
id: checks
|
||||||
|
with:
|
||||||
|
checkGlob: "'*.cmake' '*CMakeLists.txt'"
|
||||||
|
diffFilter: 'ACM'
|
||||||
|
|
||||||
|
- name: Install Dependencies 🛍️
|
||||||
|
if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||||
|
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 update
|
||||||
|
brew install --quiet zsh
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
- name: Run gersemi 🎛️
|
||||||
|
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||||
|
id: result
|
||||||
|
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}
|
||||||
|
run: |
|
||||||
|
: Run gersemi 🎛️
|
||||||
|
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||||
|
|
||||||
|
print ::group::Install gersemi
|
||||||
|
brew update
|
||||||
|
brew trust obsproject/tools
|
||||||
|
brew install --quiet obsproject/tools/gersemi@0.25
|
||||||
|
print ::endgroup::
|
||||||
|
|
||||||
|
print ::group::Run gersemi
|
||||||
|
local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/})
|
||||||
|
./build-aux/run-gersemi --fail-${{ inputs.failCondition }} --check ${changes}
|
||||||
|
print ::endgroup::
|
||||||
61
obs-studio/.github/actions/run-swift-format/action.yaml
vendored
Normal file
61
obs-studio/.github/actions/run-swift-format/action.yaml
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
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: Check for Changed Files ✅
|
||||||
|
uses: ./.github/actions/check-changes
|
||||||
|
id: checks
|
||||||
|
with:
|
||||||
|
checkGlob: "'*.swift'"
|
||||||
|
diffFilter: 'ACM'
|
||||||
|
|
||||||
|
- name: Install Dependencies 🛍️
|
||||||
|
if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||||
|
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 update
|
||||||
|
brew install --quiet zsh
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
- name: Run swift-format 🔥
|
||||||
|
if: fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||||
|
id: result
|
||||||
|
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}
|
||||||
|
run: |
|
||||||
|
: Run swift-format 🔥
|
||||||
|
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||||
|
|
||||||
|
print ::group::Install swift-format
|
||||||
|
brew update
|
||||||
|
brew install --quiet swift-format
|
||||||
|
print ::endgroup::
|
||||||
|
|
||||||
|
print ::group::Run swift-format
|
||||||
|
local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/})
|
||||||
|
./build-aux/run-swift-format --fail-${{ inputs.failCondition }} --check ${changes}
|
||||||
|
print ::endgroup::
|
||||||
122
obs-studio/.github/actions/services-validator/action.yaml
vendored
Normal file
122
obs-studio/.github/actions/services-validator/action.yaml
vendored
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
name: Services Validation
|
||||||
|
description: Checks services configuration file and checks for defunct services
|
||||||
|
inputs:
|
||||||
|
repositorySecret:
|
||||||
|
description: GitHub token for API access
|
||||||
|
required: true
|
||||||
|
checkApiSecret:
|
||||||
|
description: Token for server check API
|
||||||
|
required: false
|
||||||
|
checkApiServers:
|
||||||
|
description: Servers for the check API
|
||||||
|
required: false
|
||||||
|
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: |
|
||||||
|
: Configure Python 🐍
|
||||||
|
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||||
|
|
||||||
|
python3 -m venv .venv
|
||||||
|
|
||||||
|
source .venv/bin/activate
|
||||||
|
python3 -m pip install jsonschema json_source_map requests aiohttp
|
||||||
|
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
|
||||||
|
|
||||||
|
source .venv/bin/activate
|
||||||
|
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@00b2d488bcba3bd01014dc073d276ef4a45d5c6c # v0.6.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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
|
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 }}
|
||||||
|
API_KEY: ${{ inputs.checkApiSecret }}
|
||||||
|
API_SERVERS: ${{ inputs.checkApiServers }}
|
||||||
|
run: |
|
||||||
|
: Check for defunct services 📉
|
||||||
|
|
||||||
|
source .venv/bin/activate
|
||||||
|
python3 -u .github/scripts/utils.py/check-services.py
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
if: fromJSON(inputs.runServiceChecks)
|
||||||
|
with:
|
||||||
|
name: timestamps
|
||||||
|
path: ${{ inputs.workingDirectory }}/other/*
|
||||||
|
|
||||||
|
- name: Create pull request 🔧
|
||||||
|
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
|
||||||
|
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
|
||||||
151
obs-studio/.github/actions/setup-macos-codesigning/action.yaml
vendored
Normal file
151
obs-studio/.github/actions/setup-macos-codesigning/action.yaml
vendored
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
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 || steps.codesign.outputs.haveProvisioningProfile }}
|
||||||
|
provisioningProfileUUID:
|
||||||
|
description: UUID of imported provisioning profile
|
||||||
|
value: ${{ steps.provisioning.outputs.provisioningProfileUUID }}
|
||||||
|
haveNotarizationUser:
|
||||||
|
description: True if necessary notarization credentials were found
|
||||||
|
value: ${{ steps.notarization.outputs.haveNotarizationUser || steps.codesign.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 }}
|
||||||
|
MACOS_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} | shasum | 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 "${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'
|
||||||
|
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
|
||||||
|
print 'haveProvisioningProfile=false' >> $GITHUB_OUTPUT
|
||||||
|
print 'haveNotarizationUser=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.outputs.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
|
||||||
|
}
|
||||||
247
obs-studio/.github/actions/sparkle-appcast/action.yaml
vendored
Normal file
247
obs-studio/.github/actions/sparkle-appcast/action.yaml
vendored
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
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 update
|
||||||
|
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 '
|
||||||
|
.configurePresets[]
|
||||||
|
| select(.name=="dependencies")
|
||||||
|
| .vendor["obsproject.com/obs-studio"].tools.sparkle
|
||||||
|
| {version, baseUrl, hash}
|
||||||
|
| join(";")
|
||||||
|
' CMakePresets.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
|
||||||
|
|
||||||
|
- 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}
|
||||||
|
|
||||||
|
# The Xpath Xplained:
|
||||||
|
#
|
||||||
|
# //rss/channel/item - Select every <item> node, under a
|
||||||
|
# <channel> node, under a <rss> node,
|
||||||
|
# which:
|
||||||
|
# [*...] - Has a child node, which
|
||||||
|
# [local-name()='channel'] - Has the local name "channel"
|
||||||
|
# (required to match the
|
||||||
|
# namespaced sparkle:channel node),
|
||||||
|
# which in turn has
|
||||||
|
# [text()='<channel>'] - A text node that contains the
|
||||||
|
# content of inputs.channel
|
||||||
|
# /enclosure/@url - Then select the "url" attribute of
|
||||||
|
# every <enclosure> node under
|
||||||
|
# these matching <item> nodes
|
||||||
|
|
||||||
|
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:subject)' ${GITHUB_REF_NAME} > notes.rst
|
||||||
|
echo '###################################################' >> notes.rst
|
||||||
|
echo '' >> notes.rst
|
||||||
|
git tag -l --format='%(contents:body)' ${GITHUB_REF_NAME} >> 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 }}'
|
||||||
|
local arch=${${${(s:_:)feed_url:t}[2]}//x86/x86_64}
|
||||||
|
local sparkle_options=(
|
||||||
|
--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 }}'
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ '${{ inputs.channel }}' == 'stable' ]] {
|
||||||
|
sparkle_options+=(--phased-rollout-interval 86400)
|
||||||
|
}
|
||||||
|
|
||||||
|
Sparkle/bin/generate_appcast \
|
||||||
|
${sparkle_options} \
|
||||||
|
builds
|
||||||
|
|
||||||
|
local -a deltas=(builds/*.delta(N))
|
||||||
|
|
||||||
|
if (( #deltas )) {
|
||||||
|
mkdir -p output/sparkle_deltas/${arch}
|
||||||
|
mv ${deltas} output/sparkle_deltas/${arch}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
local feed_url='${{ steps.builds.outputs.feedUrl }}'
|
||||||
|
local arch=${${${(s:_:)feed_url:t}[2]}//x86/x86_64}
|
||||||
|
|
||||||
|
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}
|
||||||
|
|
||||||
|
if [[ ${arch} == x86_64 ]] xmllint --format ${legacy} >! output/appcasts/stable/${${appcast:t}//_x86_64_v2.xml/.xml}
|
||||||
|
rm ${legacy}
|
||||||
|
}
|
||||||
81
obs-studio/.github/actions/sparkle-appcast/appcast_adjust.xslt
vendored
Normal file
81
obs-studio/.github/actions/sparkle-appcast/appcast_adjust.xslt
vendored
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?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="*"/>
|
||||||
|
|
||||||
|
<!-- Select /rss/channel/title and store it as default value for 'pCustomTitle' -->
|
||||||
|
<xsl:param name="pCustomTitle" select="/rss/channel/title" />
|
||||||
|
<!-- Select /rss/channel/link and store it as default value for 'pCustomLink' -->
|
||||||
|
<xsl:param name="pCustomLink" select="/rss/channel/link" />
|
||||||
|
<!-- Set empty strings as default values for pSparkleUrl and pDeltaUrl -->
|
||||||
|
<xsl:param name="pSparkleUrl" select="''" />
|
||||||
|
<xsl:param name="pDeltaUrl" select="''" />
|
||||||
|
|
||||||
|
<!-- XSLT identity rule - copy all nodes and their child nodes as well as attributes
|
||||||
|
(attributes are _not_ descendants of the nodes they belong to).
|
||||||
|
This copy rule is applied as the "default" transformation for all nodes.
|
||||||
|
-->
|
||||||
|
<xsl:template match="@* | node()">
|
||||||
|
<xsl:copy>
|
||||||
|
<xsl:apply-templates select="@* | node()" />
|
||||||
|
</xsl:copy>
|
||||||
|
</xsl:template>
|
||||||
|
<!-- Match the <title> under <channel> and <rss> and do not translate it
|
||||||
|
(effectively removes it).
|
||||||
|
-->
|
||||||
|
<xsl:template match="/rss/channel/title" />
|
||||||
|
<!-- Match the <link> under <channel> and <rss> and do not translate it
|
||||||
|
(effectively removes it).
|
||||||
|
-->
|
||||||
|
<xsl:template match="/rss/channel/link" />
|
||||||
|
<!-- Match the <channel> under <rss> and apply a copy translation, which
|
||||||
|
* Creates a new <title> element with a text child node and the text content
|
||||||
|
of the pCustomTitle variable
|
||||||
|
* Creates a new <link> element with a text child node and the text content
|
||||||
|
of the pCustomLink variable
|
||||||
|
* Copies all child nodes and attributes of the original <channel> node
|
||||||
|
(this is why <title> and <link> were explicitly not translated before)
|
||||||
|
-->
|
||||||
|
<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>
|
||||||
|
<!-- Match every url attribute of <enclosure> nodes in <sparkle:deltas> nodes
|
||||||
|
(which themselves are under <item>, <channel>, and <rss> nodes respectively).
|
||||||
|
|
||||||
|
Create a new attribute with the name "url" and then set its content to either
|
||||||
|
* The original value of the attribute if it starts with the value of the
|
||||||
|
pDeltaUrl variable, OR
|
||||||
|
* The actual value of the pDeltaUrl variable, with the value of the
|
||||||
|
pSparkleUrl variable removed in front of the current url value added after
|
||||||
|
|
||||||
|
This effectively updates every url attribute on a delta <enclosure> node
|
||||||
|
that does not start with the current delta url path.
|
||||||
|
-->
|
||||||
|
<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>
|
||||||
|
<!-- Match any <sparkle:fullReleaseNotesLink> node under <item>, <channel>,
|
||||||
|
and <rss> respectively, and replace it with a new node named
|
||||||
|
<sparkle:releaseNotesLink> and populate it with all child nodes and
|
||||||
|
attributes of the original node.
|
||||||
|
-->
|
||||||
|
<xsl:template match="/rss/channel/item/sparkle:fullReleaseNotesLink">
|
||||||
|
<xsl:element name="sparkle:releaseNotesLink"><xsl:apply-templates select="@* | node()" /></xsl:element>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>
|
||||||
33
obs-studio/.github/actions/sparkle-appcast/appcast_legacy.xslt
vendored
Normal file
33
obs-studio/.github/actions/sparkle-appcast/appcast_legacy.xslt
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?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="*"/>
|
||||||
|
|
||||||
|
<!-- XSLT identity rule - copy all nodes and their child nodes as well as attributes
|
||||||
|
(attributes are _not_ descendants of the nodes they belong to).
|
||||||
|
This copy rule is applied as the "default" transformation for all nodes.
|
||||||
|
-->
|
||||||
|
<xsl:template match="@* | node()">
|
||||||
|
<xsl:copy>
|
||||||
|
<xsl:apply-templates select="@* | node()" />
|
||||||
|
</xsl:copy>
|
||||||
|
</xsl:template>
|
||||||
|
<!-- Select every <item> node under a <channel> and <rss> node respectively,
|
||||||
|
which has a <sparkle:channel> child node whose text child node's value
|
||||||
|
is not equal to 'stable', then apply to translation, effectively removing
|
||||||
|
it.
|
||||||
|
-->
|
||||||
|
<xsl:template match="/rss/channel/item[sparkle:channel[text()!='stable']]" />
|
||||||
|
<!-- Select every <sparkle:channel> node under a <item> node which sits under a
|
||||||
|
<channel> and <rss> node respectively, then apply no translation, effectively
|
||||||
|
removing it.
|
||||||
|
-->
|
||||||
|
<xsl:template match="/rss/channel/item/sparkle:channel" />
|
||||||
|
<!-- Select every <sparkle:deltas> node under a <item> node which sits under a
|
||||||
|
<channel> and <rss> node respectively, then apply no translation, effectively
|
||||||
|
removing it.
|
||||||
|
-->
|
||||||
|
<xsl:template match="/rss/channel/item/sparkle:deltas" />
|
||||||
|
</xsl:stylesheet>
|
||||||
291
obs-studio/.github/actions/steam-upload/action.yaml
vendored
Normal file
291
obs-studio/.github/actions/steam-upload/action.yaml
vendored
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
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 Nightly Assets 🌙
|
||||||
|
id: asset-info-nightly
|
||||||
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
if: github.event_name == 'schedule'
|
||||||
|
|
||||||
|
- name: Download Assets 📥
|
||||||
|
id: asset-info
|
||||||
|
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ inputs.workflowSecret }}
|
||||||
|
windows_custom_asset: ${{ inputs.customAssetWindows }}
|
||||||
|
macos_apple_custom_asset: ${{ inputs.customAssetMacOSApple }}
|
||||||
|
macos_intel_custom_asset: ${{ inputs.customAssetMacOSIntel }}
|
||||||
|
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 ${{ inputs.tagName }} \
|
||||||
|
--pattern '*.dmg' \
|
||||||
|
--pattern '*.zip' \
|
||||||
|
--clobber
|
||||||
|
|
||||||
|
IFS=';' read -r is_prerelease description <<< \
|
||||||
|
"$(gh release view ${{ inputs.tagName }} --json isPrerelease,tagName --jq 'join(";")')"
|
||||||
|
;;
|
||||||
|
workflow_dispatch)
|
||||||
|
if [[ '${{ inputs.tagName }}' =~ [0-9]+\.[0-9]+\.[0-9]+(-(rc|beta)[0-9]+)*$ ]] {
|
||||||
|
gh release download ${{ inputs.tagName }} \
|
||||||
|
--pattern '*.dmg' \
|
||||||
|
--pattern '*.zip' \
|
||||||
|
--clobber
|
||||||
|
|
||||||
|
description='${{ inputs.tagName }}'
|
||||||
|
read -r is_prerelease <<< \
|
||||||
|
"$(gh release view ${{ inputs.tagName }} --json isPrerelease --jq '.isPrerelease')"
|
||||||
|
|
||||||
|
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)
|
||||||
|
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-x86_64-${short_hash}/obs-studio-*-macos-intel.dmg \
|
||||||
|
${root_dir}
|
||||||
|
|
||||||
|
description="g${GITHUB_SHA:0:9}"
|
||||||
|
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-*-windows-x64.zip
|
||||||
|
rm ${root_dir}/(#i)obs-studio-*-windows*.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: andriivitiv/setup-steamcmd@afc45f145b95c175c3a3862d3ca84756537d48e8 # v1.3.0
|
||||||
|
|
||||||
|
- name: Generate Steam auth code 🔐
|
||||||
|
id: steam-totp
|
||||||
|
uses: andriivitiv/steam-totp@1661caebf0a84a79be0166020f81837039137101 # v1.2.0
|
||||||
|
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:a} \
|
||||||
|
+quit
|
||||||
|
print '::endgroup'
|
||||||
|
popd
|
||||||
|
|
||||||
|
- name: Generate Steam auth code 🔐
|
||||||
|
id: steam-totp-playtest
|
||||||
|
uses: andriivitiv/steam-totp@1661caebf0a84a79be0166020f81837039137101 # v1.2.0
|
||||||
|
if: ${{ ! fromJSON(inputs.preview) && fromJSON(steps.asset-info.outputs.is_prerelease) }}
|
||||||
|
with:
|
||||||
|
shared_secret: ${{ inputs.steamSecret }}
|
||||||
|
|
||||||
|
- 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 root_dir="${PWD}"
|
||||||
|
local build_file='build_playtest.vdf'
|
||||||
|
local branch_name='${{ inputs.playtestBranch }}'
|
||||||
|
|
||||||
|
pushd steam
|
||||||
|
print '::group::Prepare Steam Build Script'
|
||||||
|
|
||||||
|
sed "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-playtest.outputs.code }}' \
|
||||||
|
+run_app_build ${preview} ${build_file:a} \
|
||||||
|
+quit
|
||||||
|
print '::endgroup'
|
||||||
|
popd
|
||||||
|
|
||||||
|
- name: Upload Steam build logs
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: steam-build-logs
|
||||||
|
path: ${{ github.workspace }}/steam/build/*.log
|
||||||
40
obs-studio/.github/actions/windows-analysis/Invoke-External.ps1
vendored
Normal file
40
obs-studio/.github/actions/windows-analysis/Invoke-External.ps1
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
function Invoke-External {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Invokes a non-PowerShell command.
|
||||||
|
.DESCRIPTION
|
||||||
|
Runs a non-PowerShell command, and captures its return code.
|
||||||
|
Throws an exception if the command returns non-zero.
|
||||||
|
.EXAMPLE
|
||||||
|
Invoke-External 7z x $MyArchive
|
||||||
|
#>
|
||||||
|
|
||||||
|
if ( $args.Count -eq 0 ) {
|
||||||
|
throw 'Invoke-External called without arguments.'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! ( Test-Path function:Log-Information ) ) {
|
||||||
|
. $PSScriptRoot/Logger.ps1
|
||||||
|
}
|
||||||
|
|
||||||
|
$Command = $args[0]
|
||||||
|
$CommandArgs = @()
|
||||||
|
|
||||||
|
if ( $args.Count -gt 1) {
|
||||||
|
$CommandArgs = $args[1..($args.Count - 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
$_EAP = $ErrorActionPreference
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
Log-Debug "Invoke-External: ${Command} ${CommandArgs}"
|
||||||
|
|
||||||
|
& $command $commandArgs
|
||||||
|
$Result = $LASTEXITCODE
|
||||||
|
|
||||||
|
$ErrorActionPreference = $_EAP
|
||||||
|
|
||||||
|
if ( $Result -ne 0 ) {
|
||||||
|
throw "${Command} ${CommandArgs} exited with non-zero code ${Result}."
|
||||||
|
}
|
||||||
|
}
|
||||||
149
obs-studio/.github/actions/windows-analysis/Logger.ps1
vendored
Normal file
149
obs-studio/.github/actions/windows-analysis/Logger.ps1
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
function Log-Debug {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Debug $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Verbose {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Verbose $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Warning {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Warning $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Error {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Error $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Information {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
|
||||||
|
$Icon = ' =>'
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) "
|
||||||
|
Write-Host "${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Group {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(ValueFromPipeline)]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( $Env:CI -ne $null ) {
|
||||||
|
if ( $script:LogGroup ) {
|
||||||
|
Write-Output '::endgroup::'
|
||||||
|
$script:LogGroup = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $Message.count -ge 1 ) {
|
||||||
|
Write-Output "::group::$($Message -join ' ')"
|
||||||
|
$script:LogGroup = $true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( $Message.count -ge 1 ) {
|
||||||
|
Log-Information $Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Status {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })
|
||||||
|
$Icon = ' >'
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) "
|
||||||
|
Write-Host "${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Output {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
|
||||||
|
$Icon = ''
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5
|
||||||
90
obs-studio/.github/actions/windows-analysis/action.yaml
vendored
Normal file
90
obs-studio/.github/actions/windows-analysis/action.yaml
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
name: Run PVS-Studio Analysis
|
||||||
|
inputs:
|
||||||
|
pvsUsername:
|
||||||
|
description: PVS-Studio License Username
|
||||||
|
required: true
|
||||||
|
pvsKey:
|
||||||
|
description: PVS-Studio License Key
|
||||||
|
required: true
|
||||||
|
target:
|
||||||
|
description: Build Target
|
||||||
|
required: true
|
||||||
|
config:
|
||||||
|
description: Build Configuration
|
||||||
|
required: true
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Setup PVS-Studio
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
choco install pvs-studio --version=7.30.81185.980 -y --no-progress
|
||||||
|
|
||||||
|
- name: Activate PVS-Studio
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
|
||||||
|
Invoke-External "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" credentials -u ${{ inputs.pvsUsername }} -n ${{ inputs.pvsKey }}
|
||||||
|
|
||||||
|
- name: Run PVS-Studio Analysis
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
[flags()] Enum PVSErrorCodes {
|
||||||
|
Success = 0
|
||||||
|
AnalyzerCrash = 1
|
||||||
|
GenericError = 2
|
||||||
|
InvalidCommandLine = 4
|
||||||
|
FileNotFound = 8
|
||||||
|
ConfigurationNotFound = 16
|
||||||
|
InvalidProject = 32
|
||||||
|
InvalidExtension = 64
|
||||||
|
LicenseInvalid = 128
|
||||||
|
CodeErrorsFound = 256
|
||||||
|
SuppressionFailed = 512
|
||||||
|
LicenseExpiringSoon = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
$pvsParams = @(
|
||||||
|
"--progress"
|
||||||
|
"--disableLicenseExpirationCheck"
|
||||||
|
"-p", "${{ inputs.target }}"
|
||||||
|
"-c", "${{ inputs.config }}"
|
||||||
|
"-t", "${{ github.workspace }}\build_x64\obs-studio.sln"
|
||||||
|
"-o", "${{ github.workspace }}\analysis.plog"
|
||||||
|
"-C", "${env:GITHUB_ACTION_PATH}\obs.pvsconfig"
|
||||||
|
)
|
||||||
|
& "C:\Program Files (x86)\PVS-Studio\PVS-Studio_Cmd.exe" @pvsParams
|
||||||
|
|
||||||
|
# Success, LicenseExpiringSoon, and CodeErrorsFound are fine as error codes, we only care if it is anything but those
|
||||||
|
$pvs_result = $LASTEXITCODE -band (-bnot ([PVSErrorCodes]::CodeErrorsFound -bor [PVSErrorCodes]::LicenseExpiringSoon))
|
||||||
|
if ($pvs_result -ne 0) {
|
||||||
|
Write-Output "PVS-Studio Errors: $([PVSErrorCodes]$pvs_result)"
|
||||||
|
}
|
||||||
|
exit $pvs_result
|
||||||
|
|
||||||
|
- name: Convert Analysis to SARIF
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
|
||||||
|
|
||||||
|
$conversionParams = @(
|
||||||
|
"-a", "GA:1,2",
|
||||||
|
"-d", "V1042,Renew"
|
||||||
|
"-t", "Sarif"
|
||||||
|
"${{ github.workspace }}\analysis.plog"
|
||||||
|
)
|
||||||
|
Invoke-External "C:\Program Files (x86)\PVS-Studio\PlogConverter.exe" @conversionParams
|
||||||
|
|
||||||
|
- name: Upload PVS-Studio Logs
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: 'pvs-analysis-log'
|
||||||
|
path: |
|
||||||
|
${{ github.workspace }}/analysis.plog
|
||||||
|
${{ github.workspace }}/analysis.plog.sarif
|
||||||
|
|
||||||
|
- name: Upload PVS-Studio Report
|
||||||
|
uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||||
|
with:
|
||||||
|
sarif_file: "${{ github.workspace }}/analysis.plog.sarif"
|
||||||
|
category: 'PVS-Studio (Windows)'
|
||||||
10
obs-studio/.github/actions/windows-analysis/obs.pvsconfig
vendored
Normal file
10
obs-studio/.github/actions/windows-analysis/obs.pvsconfig
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//V_EXCLUDE_PATH */.deps/*
|
||||||
|
//V_EXCLUDE_PATH */blake2/*
|
||||||
|
//V_EXCLUDE_PATH */json11/*
|
||||||
|
//V_EXCLUDE_PATH */simde/*
|
||||||
|
//V_EXCLUDE_PATH */w32-pthreads/*
|
||||||
|
//V_EXCLUDE_PATH */plugins/obs-browser/*
|
||||||
|
//V_EXCLUDE_PATH */plugins/obs-outputs/ftl-sdk/*
|
||||||
|
//V_EXCLUDE_PATH */plugins/obs-websocket/*
|
||||||
|
//V_EXCLUDE_PATH */plugins/win-dshow/libdshowcapture/*
|
||||||
|
//V_EXCLUDE_PATH *_autogen/*
|
||||||
29
obs-studio/.github/actions/windows-patches/Ensure-Location.ps1
vendored
Normal file
29
obs-studio/.github/actions/windows-patches/Ensure-Location.ps1
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
function Ensure-Location {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Ensures current location to be set to specified directory.
|
||||||
|
.DESCRIPTION
|
||||||
|
If specified directory exists, switch to it. Otherwise create it,
|
||||||
|
then switch.
|
||||||
|
.EXAMPLE
|
||||||
|
Ensure-Location "My-Directory"
|
||||||
|
Ensure-Location -Path "Path-To-My-Directory"
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string] $Path
|
||||||
|
)
|
||||||
|
|
||||||
|
if ( ! ( Test-Path $Path ) ) {
|
||||||
|
$_Params = @{
|
||||||
|
ItemType = "Directory"
|
||||||
|
Path = ${Path}
|
||||||
|
ErrorAction = "SilentlyContinue"
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item @_Params | Set-Location
|
||||||
|
} else {
|
||||||
|
Set-Location -Path ${Path}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
obs-studio/.github/actions/windows-patches/Invoke-External.ps1
vendored
Normal file
40
obs-studio/.github/actions/windows-patches/Invoke-External.ps1
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
function Invoke-External {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Invokes a non-PowerShell command.
|
||||||
|
.DESCRIPTION
|
||||||
|
Runs a non-PowerShell command, and captures its return code.
|
||||||
|
Throws an exception if the command returns non-zero.
|
||||||
|
.EXAMPLE
|
||||||
|
Invoke-External 7z x $MyArchive
|
||||||
|
#>
|
||||||
|
|
||||||
|
if ( $args.Count -eq 0 ) {
|
||||||
|
throw 'Invoke-External called without arguments.'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! ( Test-Path function:Log-Information ) ) {
|
||||||
|
. $PSScriptRoot/Logger.ps1
|
||||||
|
}
|
||||||
|
|
||||||
|
$Command = $args[0]
|
||||||
|
$CommandArgs = @()
|
||||||
|
|
||||||
|
if ( $args.Count -gt 1) {
|
||||||
|
$CommandArgs = $args[1..($args.Count - 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
$_EAP = $ErrorActionPreference
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
Log-Debug "Invoke-External: ${Command} ${CommandArgs}"
|
||||||
|
|
||||||
|
& $command $commandArgs
|
||||||
|
$Result = $LASTEXITCODE
|
||||||
|
|
||||||
|
$ErrorActionPreference = $_EAP
|
||||||
|
|
||||||
|
if ( $Result -ne 0 ) {
|
||||||
|
throw "${Command} ${CommandArgs} exited with non-zero code ${Result}."
|
||||||
|
}
|
||||||
|
}
|
||||||
149
obs-studio/.github/actions/windows-patches/Logger.ps1
vendored
Normal file
149
obs-studio/.github/actions/windows-patches/Logger.ps1
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
function Log-Debug {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Debug $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Verbose {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Verbose $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Warning {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Warning $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Error {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Error $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Information {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
|
||||||
|
$Icon = ' =>'
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) "
|
||||||
|
Write-Host "${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Group {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(ValueFromPipeline)]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( $Env:CI -ne $null ) {
|
||||||
|
if ( $script:LogGroup ) {
|
||||||
|
Write-Output '::endgroup::'
|
||||||
|
$script:LogGroup = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $Message.count -ge 1 ) {
|
||||||
|
Write-Output "::group::$($Message -join ' ')"
|
||||||
|
$script:LogGroup = $true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( $Message.count -ge 1 ) {
|
||||||
|
Log-Information $Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Status {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })
|
||||||
|
$Icon = ' >'
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) "
|
||||||
|
Write-Host "${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Output {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
|
||||||
|
$Icon = ''
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5
|
||||||
126
obs-studio/.github/actions/windows-patches/action.yaml
vendored
Normal file
126
obs-studio/.github/actions/windows-patches/action.yaml
vendored
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
name: Run bouf Patch Generation
|
||||||
|
description: Generates OBS updater manifest and patches
|
||||||
|
inputs:
|
||||||
|
gcsAccessKeyId:
|
||||||
|
description: GCS S3 Access Key ID
|
||||||
|
required: true
|
||||||
|
gcsAccessKeySecret:
|
||||||
|
description: GCS S3 Access Key Secret
|
||||||
|
required: true
|
||||||
|
workflowSecret:
|
||||||
|
description: GitHub API token to use for API calls
|
||||||
|
required: true
|
||||||
|
tagName:
|
||||||
|
description: GitHub Release tag
|
||||||
|
required: true
|
||||||
|
channel:
|
||||||
|
description: Update channel
|
||||||
|
required: false
|
||||||
|
default: 'stable'
|
||||||
|
architecture:
|
||||||
|
description: OBS build architecture
|
||||||
|
required: false
|
||||||
|
default: 'x64'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
path: "repo"
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ inputs.tagName }}
|
||||||
|
|
||||||
|
- name: Download Release Artifact
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ inputs.workflowSecret }}
|
||||||
|
run: |
|
||||||
|
# Download OBS release
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
|
||||||
|
Invoke-External gh release download "${{ inputs.tagName }}" -p "OBS-Studio-${{ inputs.tagName }}-Windows-${{ inputs.architecture }}.zip"
|
||||||
|
Invoke-External gh attestation verify "OBS-Studio-${{ inputs.tagName }}-Windows-${{ inputs.architecture }}.zip" --owner obsproject
|
||||||
|
Expand-Archive -Path "OBS-Studio-${{ inputs.tagName }}-Windows-${{ inputs.architecture }}.zip" -DestinationPath "${{ github.workspace }}/build"
|
||||||
|
|
||||||
|
- name: Setup bouf
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
BOUF_TAG: 'v0.6.5'
|
||||||
|
BOUF_HASH: '2e16d5116415579b5dd8cd68b3fe6edaa7513938427567121942f592292605d5'
|
||||||
|
BOUF_NSIS_HASH: 'e323043627cfeebd237d306afc001c8c36a3c37f6ecef2b349a19a3209ae3450'
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
# Download bouf release
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Ensure-Location.ps1
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
|
||||||
|
Ensure-Location bouf
|
||||||
|
$windows_zip = "bouf-windows-${env:BOUF_TAG}.zip"
|
||||||
|
$nsis_zip = "bouf-nsis-${env:BOUF_TAG}.zip"
|
||||||
|
Invoke-External gh release download "${env:BOUF_TAG}" -R "obsproject/bouf" -p $windows_zip -p $nsis_zip
|
||||||
|
|
||||||
|
if ((Get-FileHash $windows_zip -Algorithm SHA256).Hash -ne "${env:BOUF_HASH}") {
|
||||||
|
throw "bouf hash does not match."
|
||||||
|
}
|
||||||
|
if ((Get-FileHash $nsis_zip -Algorithm SHA256).Hash -ne "${env:BOUF_NSIS_HASH}") {
|
||||||
|
throw "NSIS package hash does not match."
|
||||||
|
}
|
||||||
|
|
||||||
|
Expand-Archive -Path $windows_zip -DestinationPath bin
|
||||||
|
Expand-Archive -Path $nsis_zip -DestinationPath nsis
|
||||||
|
|
||||||
|
- name: Install NSIS
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
# Install NSIS
|
||||||
|
winget install --silent --accept-package-agreements --accept-source-agreements --disable-interactivity -e --id NSIS.NSIS
|
||||||
|
|
||||||
|
- name: Install rclone and pandoc
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
choco install rclone pandoc -y --no-progress
|
||||||
|
|
||||||
|
- name: Download Previous Builds
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
RCLONE_TRANSFERS: '100'
|
||||||
|
RCLONE_FAST_LIST: 'true'
|
||||||
|
RCLONE_EXCLUDE: '"{pdbs/**,**/${{ inputs.tagName }}/**}"'
|
||||||
|
RCLONE_S3_PROVIDER: 'GCS'
|
||||||
|
RCLONE_S3_ACCESS_KEY_ID: '${{ inputs.gcsAccessKeyId }}'
|
||||||
|
RCLONE_S3_SECRET_ACCESS_KEY: '${{ inputs.gcsAccessKeySecret }}'
|
||||||
|
RCLONE_S3_ENDPOINT: 'https://storage.googleapis.com'
|
||||||
|
run: |
|
||||||
|
rclone -q copy ":s3:obs-builds/${{ inputs.architecture }}" "${{ github.workspace }}/old_builds"
|
||||||
|
|
||||||
|
- name: Prepare Release Notes
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
# Release notes are just the tag body on Windows
|
||||||
|
Set-Location repo
|
||||||
|
git tag -l --format='%(contents:subject)' ${{ inputs.tagName }} > "${{ github.workspace }}/notes.rst"
|
||||||
|
Write-Output "###################################################" >> "${{ github.workspace }}/notes.rst"
|
||||||
|
Write-Output "" >> "${{ github.workspace }}/notes.rst"
|
||||||
|
git tag -l --format='%(contents:body)' ${{ inputs.tagName }} >> "${{ github.workspace }}/notes.rst"
|
||||||
|
|
||||||
|
- name: Run bouf
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
|
||||||
|
$boufArgs = @(
|
||||||
|
"--config", "${env:GITHUB_ACTION_PATH}/config.toml",
|
||||||
|
"--version", "${{ inputs.tagName }}"
|
||||||
|
"--branch", "${{ inputs.channel }}"
|
||||||
|
"--notes-file", "${{ github.workspace }}/notes.rst"
|
||||||
|
"-i", "${{ github.workspace }}/build"
|
||||||
|
"-p", "${{ github.workspace }}/old_builds"
|
||||||
|
"-o", "${{ github.workspace }}/output"
|
||||||
|
"--updater-data-only"
|
||||||
|
)
|
||||||
|
Invoke-External "${{ github.workspace }}\bouf\bin\bouf.exe" @boufArgs
|
||||||
|
|
||||||
|
- name: Upload Outputs
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: windows-updater-files
|
||||||
|
compression-level: 0
|
||||||
|
path: ${{ github.workspace }}/output
|
||||||
31
obs-studio/.github/actions/windows-patches/config.toml
vendored
Normal file
31
obs-studio/.github/actions/windows-patches/config.toml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
[general]
|
||||||
|
log_level = "trace"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
# On CI these should be in %PATH%
|
||||||
|
sevenzip_path = "7z"
|
||||||
|
makensis_path = "C:/Program Files (x86)/NSIS/makensis.exe"
|
||||||
|
pandoc_path = "pandoc"
|
||||||
|
pdbcopy_path = "C:/Program Files (x86)/Windows Kits/10/Debuggers/x64/pdbcopy.exe"
|
||||||
|
|
||||||
|
[prepare.codesign]
|
||||||
|
skip_sign = true
|
||||||
|
|
||||||
|
[generate]
|
||||||
|
patch_type = "zstd"
|
||||||
|
compress_files = true
|
||||||
|
|
||||||
|
# Prevent some legacy plugins from being marked as deleted
|
||||||
|
exclude_from_removal = [
|
||||||
|
"enc-amf",
|
||||||
|
"win-mf",
|
||||||
|
]
|
||||||
|
|
||||||
|
[package]
|
||||||
|
|
||||||
|
[package.installer]
|
||||||
|
skip = true
|
||||||
|
|
||||||
|
[package.updater]
|
||||||
|
vc_redist_path = "bouf/nsis/VC_redist.x64.exe"
|
||||||
|
skip_sign = true
|
||||||
29
obs-studio/.github/actions/windows-signing/Ensure-Location.ps1
vendored
Normal file
29
obs-studio/.github/actions/windows-signing/Ensure-Location.ps1
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
function Ensure-Location {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Ensures current location to be set to specified directory.
|
||||||
|
.DESCRIPTION
|
||||||
|
If specified directory exists, switch to it. Otherwise create it,
|
||||||
|
then switch.
|
||||||
|
.EXAMPLE
|
||||||
|
Ensure-Location "My-Directory"
|
||||||
|
Ensure-Location -Path "Path-To-My-Directory"
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string] $Path
|
||||||
|
)
|
||||||
|
|
||||||
|
if ( ! ( Test-Path $Path ) ) {
|
||||||
|
$_Params = @{
|
||||||
|
ItemType = "Directory"
|
||||||
|
Path = ${Path}
|
||||||
|
ErrorAction = "SilentlyContinue"
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item @_Params | Set-Location
|
||||||
|
} else {
|
||||||
|
Set-Location -Path ${Path}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
obs-studio/.github/actions/windows-signing/Invoke-External.ps1
vendored
Normal file
40
obs-studio/.github/actions/windows-signing/Invoke-External.ps1
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
function Invoke-External {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Invokes a non-PowerShell command.
|
||||||
|
.DESCRIPTION
|
||||||
|
Runs a non-PowerShell command, and captures its return code.
|
||||||
|
Throws an exception if the command returns non-zero.
|
||||||
|
.EXAMPLE
|
||||||
|
Invoke-External 7z x $MyArchive
|
||||||
|
#>
|
||||||
|
|
||||||
|
if ( $args.Count -eq 0 ) {
|
||||||
|
throw 'Invoke-External called without arguments.'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! ( Test-Path function:Log-Information ) ) {
|
||||||
|
. $PSScriptRoot/Logger.ps1
|
||||||
|
}
|
||||||
|
|
||||||
|
$Command = $args[0]
|
||||||
|
$CommandArgs = @()
|
||||||
|
|
||||||
|
if ( $args.Count -gt 1) {
|
||||||
|
$CommandArgs = $args[1..($args.Count - 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
$_EAP = $ErrorActionPreference
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
Log-Debug "Invoke-External: ${Command} ${CommandArgs}"
|
||||||
|
|
||||||
|
& $command $commandArgs
|
||||||
|
$Result = $LASTEXITCODE
|
||||||
|
|
||||||
|
$ErrorActionPreference = $_EAP
|
||||||
|
|
||||||
|
if ( $Result -ne 0 ) {
|
||||||
|
throw "${Command} ${CommandArgs} exited with non-zero code ${Result}."
|
||||||
|
}
|
||||||
|
}
|
||||||
149
obs-studio/.github/actions/windows-signing/Logger.ps1
vendored
Normal file
149
obs-studio/.github/actions/windows-signing/Logger.ps1
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
function Log-Debug {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Debug $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Verbose {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Verbose $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Warning {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Warning $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Error {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Error $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Information {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
|
||||||
|
$Icon = ' =>'
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) "
|
||||||
|
Write-Host "${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Group {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(ValueFromPipeline)]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( $Env:CI -ne $null ) {
|
||||||
|
if ( $script:LogGroup ) {
|
||||||
|
Write-Output '::endgroup::'
|
||||||
|
$script:LogGroup = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $Message.count -ge 1 ) {
|
||||||
|
Write-Output "::group::$($Message -join ' ')"
|
||||||
|
$script:LogGroup = $true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( $Message.count -ge 1 ) {
|
||||||
|
Log-Information $Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Status {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })
|
||||||
|
$Icon = ' >'
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) "
|
||||||
|
Write-Host "${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Output {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
|
||||||
|
$Icon = ''
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5
|
||||||
149
obs-studio/.github/actions/windows-signing/action.yaml
vendored
Normal file
149
obs-studio/.github/actions/windows-signing/action.yaml
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
name: Run bouf Packaging
|
||||||
|
description: Generates signed OBS install files
|
||||||
|
inputs:
|
||||||
|
gcpWorkloadIdentityProvider:
|
||||||
|
description: GCP Identity Provider Pool ID
|
||||||
|
required: true
|
||||||
|
gcpServiceAccountName:
|
||||||
|
description: Google service account name
|
||||||
|
required: true
|
||||||
|
version:
|
||||||
|
description: Version string (e.g., 30.0.0-rc1)
|
||||||
|
required: true
|
||||||
|
channel:
|
||||||
|
description: Update channel
|
||||||
|
required: false
|
||||||
|
default: 'stable'
|
||||||
|
architecture:
|
||||||
|
description: OBS build architecture
|
||||||
|
required: false
|
||||||
|
default: 'x64'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Extract Artifact
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Expand-Archive -Path build\*.zip -DestinationPath build
|
||||||
|
Remove-Item build\*.zip
|
||||||
|
|
||||||
|
- name: Setup bouf
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
BOUF_TAG: 'v0.6.5'
|
||||||
|
BOUF_HASH: '2e16d5116415579b5dd8cd68b3fe6edaa7513938427567121942f592292605d5'
|
||||||
|
BOUF_NSIS_HASH: 'e323043627cfeebd237d306afc001c8c36a3c37f6ecef2b349a19a3209ae3450'
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
# Download bouf release
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Ensure-Location.ps1
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
|
||||||
|
Ensure-Location bouf
|
||||||
|
$windows_zip = "bouf-windows-${env:BOUF_TAG}.zip"
|
||||||
|
$nsis_zip = "bouf-nsis-${env:BOUF_TAG}.zip"
|
||||||
|
Invoke-External gh release download "${env:BOUF_TAG}" -R "obsproject/bouf" -p $windows_zip -p $nsis_zip
|
||||||
|
|
||||||
|
if ((Get-FileHash $windows_zip -Algorithm SHA256).Hash -ne "${env:BOUF_HASH}") {
|
||||||
|
throw "bouf hash does not match."
|
||||||
|
}
|
||||||
|
if ((Get-FileHash $nsis_zip -Algorithm SHA256).Hash -ne "${env:BOUF_NSIS_HASH}") {
|
||||||
|
throw "NSIS package hash does not match."
|
||||||
|
}
|
||||||
|
|
||||||
|
Expand-Archive -Path $windows_zip -DestinationPath bin
|
||||||
|
Expand-Archive -Path $nsis_zip -DestinationPath nsis
|
||||||
|
|
||||||
|
- name: Install NSIS
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
# Install NSIS
|
||||||
|
winget install --silent --accept-package-agreements --accept-source-agreements --disable-interactivity -e --id NSIS.NSIS
|
||||||
|
|
||||||
|
- name: Setup Google CNG Provider
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
CNG_TAG: 'cng-v1.1'
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
# Download Google CNG provider release from github
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Ensure-Location.ps1
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
|
||||||
|
Ensure-Location gcng
|
||||||
|
|
||||||
|
Invoke-External gh release download "${env:CNG_TAG}" -R "GoogleCloudPlatform/kms-integrations" -p "*amd64.zip"
|
||||||
|
Expand-Archive -Path *.zip
|
||||||
|
$sigPath = Get-ChildItem *.sig -Recurse
|
||||||
|
$msiPath = Get-ChildItem *.msi -Recurse
|
||||||
|
# Verify digital signature against Google's public key
|
||||||
|
Invoke-External openssl dgst -sha384 -verify "${env:GITHUB_ACTION_PATH}/cng-release-signing-key.pem" -signature $sigPath $msiPath
|
||||||
|
# Finally, install the CNG provider
|
||||||
|
Invoke-External msiexec /i $msiPath /qn /norestart
|
||||||
|
|
||||||
|
- name: Install rclone
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
choco install rclone --version=1.69.3 -y --no-progress
|
||||||
|
|
||||||
|
- name: 'Authenticate to Google Cloud'
|
||||||
|
uses: 'google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093' # v3.0.0
|
||||||
|
with:
|
||||||
|
workload_identity_provider: ${{ inputs.gcpWorkloadIdentityProvider }}
|
||||||
|
service_account: ${{ inputs.gcpServiceAccountName }}
|
||||||
|
|
||||||
|
- name: Download Previous Build
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
RCLONE_GCS_ENV_AUTH: 'true'
|
||||||
|
run: |
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Ensure-Location.ps1
|
||||||
|
Ensure-Location "${{ github.workspace }}/old_builds"
|
||||||
|
rclone copy --transfers 100 ":gcs:obs-latest/${{ inputs.architecture }}/${{ inputs.channel }}" .
|
||||||
|
|
||||||
|
- name: Sign Game Capture with RSA cert
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
|
||||||
|
$SignToolExe = "C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe"
|
||||||
|
$signArgs = @(
|
||||||
|
"sign"
|
||||||
|
"/fd", "sha256"
|
||||||
|
"/t", "http://timestamp.digicert.com"
|
||||||
|
"/f", "repo/.github/actions/windows-signing/prod-gc.crt"
|
||||||
|
"/csp", "Google Cloud KMS Provider"
|
||||||
|
"/kc", "projects/ci-signing/locations/global/keyRings/production/cryptoKeys/game-capture-release-sign-hsm/cryptoKeyVersions/1"
|
||||||
|
"${{ github.workspace }}/build/data/obs-plugins/win-capture/*.dll"
|
||||||
|
)
|
||||||
|
Invoke-External $SignToolExe @signArgs
|
||||||
|
|
||||||
|
- name: Run bouf
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
. ${env:GITHUB_ACTION_PATH}\Invoke-External.ps1
|
||||||
|
$boufArgs = @(
|
||||||
|
"--config", "${env:GITHUB_ACTION_PATH}/config_${{ inputs.architecture }}.toml"
|
||||||
|
"--version", "${{ inputs.version }}"
|
||||||
|
"--branch", "${{ inputs.channel }}"
|
||||||
|
"-i", "${{ github.workspace }}/build"
|
||||||
|
"-p", "${{ github.workspace }}/old_builds"
|
||||||
|
"-o", "${{ github.workspace }}/output"
|
||||||
|
"--packaging-only"
|
||||||
|
)
|
||||||
|
Invoke-External "${{ github.workspace }}\bouf\bin\bouf.exe" @boufArgs
|
||||||
|
|
||||||
|
- name: Sync Latest Build
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
RCLONE_INCLUDE: '**/${{ inputs.version }}/**'
|
||||||
|
RCLONE_GCS_ENV_AUTH: 'true'
|
||||||
|
RCLONE_GCS_BUCKET_POLICY_ONLY: 'true'
|
||||||
|
run: |
|
||||||
|
rclone sync --delete-excluded --transfers 100 "${{ github.workspace }}/old_builds" ":gcs:obs-latest/${{ inputs.architecture }}/${{ inputs.channel }}"
|
||||||
|
|
||||||
|
- name: Upload Build to Archive
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
RCLONE_GCS_ENV_AUTH: 'true'
|
||||||
|
RCLONE_GCS_BUCKET_POLICY_ONLY: 'true'
|
||||||
|
run: |
|
||||||
|
rclone copy --transfers 100 "${{ github.workspace }}/old_builds" ":gcs:obs-builds/${{ inputs.architecture }}"
|
||||||
5
obs-studio/.github/actions/windows-signing/cng-release-signing-key.pem
vendored
Normal file
5
obs-studio/.github/actions/windows-signing/cng-release-signing-key.pem
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEtfLbXkHUVc9oUPTNyaEK3hIwmuGRoTtd
|
||||||
|
6zDhwqjJuYaMwNd1aaFQLMawTwZgR0Xn27ymVWtqJHBe0FU9BPIQ+SFmKw+9jSwu
|
||||||
|
/FuqbJnLmTnWMJ1jRCtyHNZawvv2wbiB
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
47
obs-studio/.github/actions/windows-signing/config_arm64.toml
vendored
Normal file
47
obs-studio/.github/actions/windows-signing/config_arm64.toml
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
[general]
|
||||||
|
log_level = "trace"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
# On CI these should be in %PATH%
|
||||||
|
sevenzip_path = "7z"
|
||||||
|
makensis_path = "C:/Program Files (x86)/NSIS/makensis.exe"
|
||||||
|
pandoc_path = "pandoc"
|
||||||
|
pdbcopy_path = "C:/Program Files (x86)/Windows Kits/10/Debuggers/x64/pdbcopy.exe"
|
||||||
|
|
||||||
|
## Preparation steps
|
||||||
|
[prepare]
|
||||||
|
|
||||||
|
[prepare.copy]
|
||||||
|
never_copy = [
|
||||||
|
"bin/32bit",
|
||||||
|
"obs-plugins/32bit",
|
||||||
|
".keepme",
|
||||||
|
]
|
||||||
|
|
||||||
|
[prepare.codesign]
|
||||||
|
sign_cert_file = "repo/.github/actions/windows-signing/prod.crt"
|
||||||
|
sign_kms_key_id = "projects/ci-signing/locations/global/keyRings/production/cryptoKeys/release-sign-hsm/cryptoKeyVersions/1"
|
||||||
|
sign_digest = "sha384"
|
||||||
|
sign_ts_serv = "http://timestamp.digicert.com"
|
||||||
|
sign_ts_algo = "sha256"
|
||||||
|
sign_exts = ['exe', 'dll', 'pyd']
|
||||||
|
sign_append = true
|
||||||
|
|
||||||
|
[prepare.strip_pdbs]
|
||||||
|
# PDBs to not strip
|
||||||
|
exclude = [
|
||||||
|
"obs-frontend-api.pdb",
|
||||||
|
"obs64.pdb",
|
||||||
|
"obs.pdb",
|
||||||
|
]
|
||||||
|
|
||||||
|
[package]
|
||||||
|
[package.installer]
|
||||||
|
skip = true
|
||||||
|
|
||||||
|
[package.zip]
|
||||||
|
name = "OBS-Studio-{version}-arm64.zip"
|
||||||
|
pdb_name = "OBS-Studio-{version}-arm64-pdbs.zip"
|
||||||
|
|
||||||
|
[package.updater]
|
||||||
|
skip_sign = true
|
||||||
47
obs-studio/.github/actions/windows-signing/config_x64.toml
vendored
Normal file
47
obs-studio/.github/actions/windows-signing/config_x64.toml
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
[general]
|
||||||
|
log_level = "trace"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
# On CI these should be in %PATH%
|
||||||
|
sevenzip_path = "7z"
|
||||||
|
makensis_path = "C:/Program Files (x86)/NSIS/makensis.exe"
|
||||||
|
pandoc_path = "pandoc"
|
||||||
|
pdbcopy_path = "C:/Program Files (x86)/Windows Kits/10/Debuggers/x64/pdbcopy.exe"
|
||||||
|
|
||||||
|
## Preparation steps
|
||||||
|
[prepare]
|
||||||
|
|
||||||
|
[prepare.copy]
|
||||||
|
never_copy = [
|
||||||
|
"bin/32bit",
|
||||||
|
"obs-plugins/32bit",
|
||||||
|
".keepme",
|
||||||
|
]
|
||||||
|
|
||||||
|
[prepare.codesign]
|
||||||
|
sign_cert_file = "repo/.github/actions/windows-signing/prod.crt"
|
||||||
|
sign_kms_key_id = "projects/ci-signing/locations/global/keyRings/production/cryptoKeys/release-sign-hsm/cryptoKeyVersions/1"
|
||||||
|
sign_digest = "sha384"
|
||||||
|
sign_ts_serv = "http://timestamp.digicert.com"
|
||||||
|
sign_ts_algo = "sha256"
|
||||||
|
sign_exts = ['exe', 'dll', 'pyd']
|
||||||
|
sign_append = true
|
||||||
|
|
||||||
|
[prepare.strip_pdbs]
|
||||||
|
# PDBs to not strip
|
||||||
|
exclude = [
|
||||||
|
"obs-frontend-api.pdb",
|
||||||
|
"obs64.pdb",
|
||||||
|
"obs.pdb",
|
||||||
|
]
|
||||||
|
|
||||||
|
[package]
|
||||||
|
[package.installer]
|
||||||
|
nsis_script = "bouf/nsis/mp-installer.nsi"
|
||||||
|
|
||||||
|
[package.zip]
|
||||||
|
name = "OBS-Studio-{version}-x64.zip"
|
||||||
|
pdb_name = "OBS-Studio-{version}-pdbs.zip"
|
||||||
|
|
||||||
|
[package.updater]
|
||||||
|
skip_sign = true
|
||||||
42
obs-studio/.github/actions/windows-signing/prod-gc.crt
vendored
Normal file
42
obs-studio/.github/actions/windows-signing/prod-gc.crt
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIHYDCCBUigAwIBAgIQCTRoMeK9NItAQamXbU8PnjANBgkqhkiG9w0BAQsFADBp
|
||||||
|
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMT
|
||||||
|
OERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0
|
||||||
|
IDIwMjEgQ0ExMB4XDTI1MTExNzAwMDAwMFoXDTI4MDExNTIzNTk1OVowaDELMAkG
|
||||||
|
A1UEBhMCVVMxEDAOBgNVBAgTB1d5b21pbmcxETAPBgNVBAcTCFNoZXJpZGFuMRkw
|
||||||
|
FwYDVQQKExBPQlMgUHJvamVjdCwgTExDMRkwFwYDVQQDExBPQlMgUHJvamVjdCwg
|
||||||
|
TExDMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA07e66QJeFjyk8p5l
|
||||||
|
1/hOBt5qXf8paJIFoBsdy38qnkC6ZTJzrmSfERilRBM7UQ7Pzo9aE/On7aUrghdW
|
||||||
|
ZfG/U/3s4KKYZMh+mQscHdx37Y4sUC0Yk/3s+1H3jAz5tEx9FlUgO30MKjSTr3Lc
|
||||||
|
HjqoibokGrZOzqSF2pLqTmSX92/P7E9ii2EnZnTSDWHHLtVmS0YkE6TKQ5v2VHYP
|
||||||
|
ynRVWuOl2wJFNctCYbcZAmBeVFne4k6w443Zvkz70m4lgtaJB24r2y2ay+vyQx2Q
|
||||||
|
gEg3RgcW+3/zh/sqjCQ6RhUjFvdBHP9nPrhCw72P/2J04JrpMnTlHbwUp1ULyH9v
|
||||||
|
rOYDu+8gk2sFgwjgKYGrjuehtwG8IokCppWPxUUyDTklFXbjDVlLQizmoPjwfUKy
|
||||||
|
K6kJd6w6WR3jUdRZYIXuHPzzIQE3G2aB68tSyYANuXjQAOXtVKkFlMiI/KGATIKb
|
||||||
|
FCnhFriqFOlG1vxeKUgqMNQqcaz52V8ZGEtVAOMZVP0FzZIDqrFwvDTQwsRVsRUU
|
||||||
|
c6ACUGZVL5X5nn90XTYIf4oZGFIs7U/P+LmH7Hngb3ZnrvwhurSreaELR554ncOl
|
||||||
|
fLJGpiJlTShkvubXycmYIiM+XLVkdziZlRFlMef5hp02fuT+825ivuWzaNTB0min
|
||||||
|
hMatLBKIwxjO5Xlk6CztRQD6ezMCAwEAAaOCAgMwggH/MB8GA1UdIwQYMBaAFGg3
|
||||||
|
4Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQWBBSNjnGJqRrmOQnj5YyA9Ax8ZpJ/
|
||||||
|
ejA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3
|
||||||
|
LmRpZ2ljZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsG
|
||||||
|
AQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
|
||||||
|
LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIw
|
||||||
|
MjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNl
|
||||||
|
cnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCB
|
||||||
|
lAYIKwYBBQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj
|
||||||
|
ZXJ0LmNvbTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t
|
||||||
|
L0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNB
|
||||||
|
MS5jcnQwCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAgEAKhWQvdUAI3TLwy0j
|
||||||
|
DBZflHdzq2qnsF7v7OCQEiG46CA1/37ZJrBx6B5u23Z4dS7Ruji679WAnhq5oxkC
|
||||||
|
uRVwmjtcDmZ8m1iDmbN0yZzfyhzWYUDQJfOQRtRVJC/3MKy6uPw9IZsInOZDWEeg
|
||||||
|
rVoKLnkrxSbFFRoKtzOrkFqO+bdo+atEUbYsPFAMXzvXTlEvuYqB5iWWbtA2/wN7
|
||||||
|
dFPkB1T9PBp803PU10AeKxwI4q+Ih+Iul3hiPflvFtoY4SX8JBioEWBugcmiL6w9
|
||||||
|
8A88zGnONFcuA2iVFjvJKon6g87mQMulqmZGgBjNmXipDQlsC3rUkdZHJrsKq6mI
|
||||||
|
Pkx5EFfIKJ/ibSQ5f6eKfPboh6hF7JutdoOEZ8Zo2A5M/1WV/kO18IJsKGDPEWBa
|
||||||
|
Y+kvSzdU1j9p7IGU6QRLrBTHO5yUyIoEVAXIPs4g6bRawFHVcO8zebAhg+CmDWsG
|
||||||
|
JT7BDQeiVWuFHs9w6YlQoPQsCl7SL0v8d2JeXajchK65owCyV6dNctBzIIU0/5tW
|
||||||
|
eHGB+jd9Sd6b8z8lgdTBQRBMOxBw5+GGieR6VklPqnLo6nZiX1PVPauTIrp0XP0E
|
||||||
|
Z/n6TCQpUc+EqD7dFAjxRQh0HV3Zx2Xc8QCtLc2cI+ShQrgGXyqo0GlhalNDZkj8
|
||||||
|
WbVFjeU1iU9tPlee5BDXberDjFg=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
25
obs-studio/.github/actions/windows-signing/prod.crt
vendored
Normal file
25
obs-studio/.github/actions/windows-signing/prod.crt
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEIzCCA6mgAwIBAgIQA/1HO4x/Q51pGXj/sSt/ZzAKBggqhkjOPQQDAzBTMQsw
|
||||||
|
CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xKzApBgNVBAMTIkRp
|
||||||
|
Z2lDZXJ0IEc1IENTIEVDQyBTSEEzODQgMjAyMSBDQTEwHhcNMjYwMzA1MDAwMDAw
|
||||||
|
WhcNMjcwMTI3MjM1OTU5WjCBzzETMBEGCysGAQQBgjc8AgEDEwJVUzEYMBYGCysG
|
||||||
|
AQQBgjc8AgECEwdXeW9taW5nMR0wGwYDVQQPDBRQcml2YXRlIE9yZ2FuaXphdGlv
|
||||||
|
bjEXMBUGA1UEBRMOMjAyMy0wMDEyNzIyNTIxCzAJBgNVBAYTAlVTMRAwDgYDVQQI
|
||||||
|
EwdXeW9taW5nMREwDwYDVQQHEwhTaGVyaWRhbjEZMBcGA1UEChMQT0JTIFByb2pl
|
||||||
|
Y3QsIExMQzEZMBcGA1UEAxMQT0JTIFByb2plY3QsIExMQzB2MBAGByqGSM49AgEG
|
||||||
|
BSuBBAAiA2IABKkywk1iHNOnpOU7QEAt5fddCH57pHHdxJxrgbj06UONaLHDY3AR
|
||||||
|
gjqYbVBHYIr59lW59l0Xg/ruWzaX+YHWhJrzriaCI/zTf8lambBEDJBfIkMDQD8k
|
||||||
|
JDxfza4DulU0eaOCAcMwggG/MB8GA1UdIwQYMBaAFNcdx/rG+oJ0IEVxNBIvVdWY
|
||||||
|
QZJeMB0GA1UdDgQWBBTz68Ag8TYugImX7RVRd3qem1tY2zA9BgNVHSAENjA0MDIG
|
||||||
|
BWeBDAEDMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQ
|
||||||
|
UzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgY0GA1UdHwSB
|
||||||
|
hTCBgjA/oD2gO4Y5aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0RzVD
|
||||||
|
U0VDQ1NIQTM4NDIwMjFDQTEuY3JsMD+gPaA7hjlodHRwOi8vY3JsNC5kaWdpY2Vy
|
||||||
|
dC5jb20vRGlnaUNlcnRHNUNTRUNDU0hBMzg0MjAyMUNBMS5jcmwwfgYIKwYBBQUH
|
||||||
|
AQEEcjBwMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wSAYI
|
||||||
|
KwYBBQUHMAKGPGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEc1
|
||||||
|
Q1NFQ0NTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMAoGCCqGSM49BAMDA2gA
|
||||||
|
MGUCMDuU78m1lTDkCHRpMd1nOvvuRCTSglIo+pN9T0G1ziPi+MyeyVmERc2lqXlW
|
||||||
|
TANo+wIxAL+y4Ttsw4Vs9qoFArNpA0EIIz2tKdKgRGNjcPNSZq6ZQkiyVFN4xsjc
|
||||||
|
FCONL0pZJw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
11
obs-studio/.github/actions/windows-signing/test.crt
vendored
Normal file
11
obs-studio/.github/actions/windows-signing/test.crt
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBpDCCAUmgAwIBAgIUXeKu2+AXK2yR8WyuTTRg8+t6p/kwCgYIKoZIzj0EAwIw
|
||||||
|
JzElMCMGA1UEAwwcV2l6YXJkcyBvZiBPQlMgTExDIChURVNUSU5HKTAeFw0yMzEx
|
||||||
|
MDQwMDA3NDBaFw0zMzExMDEwMDA3NDBaMCcxJTAjBgNVBAMMHFdpemFyZHMgb2Yg
|
||||||
|
T0JTIExMQyAoVEVTVElORykwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARVAdL6
|
||||||
|
pkfHYuO8rtndlAjpbz9bMBIVN8elrg4cDJkReAqsjH78ulhKvkor6lfF7rbRzIKA
|
||||||
|
u026v1aDE79Z+bz+o1MwUTAdBgNVHQ4EFgQUsFQqmZEEapghq7ZPqpejjFeu8Dsw
|
||||||
|
HwYDVR0jBBgwFoAUsFQqmZEEapghq7ZPqpejjFeu8DswDwYDVR0TAQH/BAUwAwEB
|
||||||
|
/zAKBggqhkjOPQQDAgNJADBGAiEAmNSMQqZu3U9eMP+PbmssCigkbFKG8mGLNLFN
|
||||||
|
J+KfcdkCIQCVOjg6cqk4zFe7H8EKhB6CwOAIOQrgjGWLihtO2lovCw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
7
obs-studio/.github/scripts/.Aptfile
vendored
Normal file
7
obs-studio/.github/scripts/.Aptfile
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package 'ccache'
|
||||||
|
package 'cmake', bin: '/usr/bin/cmake'
|
||||||
|
package 'curl'
|
||||||
|
package 'git'
|
||||||
|
package 'jq'
|
||||||
|
package 'ninja-build', bin: 'ninja'
|
||||||
|
package 'pkg-config'
|
||||||
4
obs-studio/.github/scripts/.Brewfile
vendored
Normal file
4
obs-studio/.github/scripts/.Brewfile
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
brew "cmake"
|
||||||
|
brew "git"
|
||||||
|
brew "jq"
|
||||||
|
brew "xcbeautify"
|
||||||
2
obs-studio/.github/scripts/.Wingetfile
vendored
Normal file
2
obs-studio/.github/scripts/.Wingetfile
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
package '7zip.7zip', path: '7-zip', bin: '7z'
|
||||||
|
package 'cmake', path: 'Cmake\bin', bin: 'cmake'
|
||||||
239
obs-studio/.github/scripts/.build.zsh
vendored
Normal file
239
obs-studio/.github/scripts/.build.zsh
vendored
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
if (( ! ${+CI} )) {
|
||||||
|
print -u2 -PR "%F{1} ✖︎ ${ZSH_ARGZERO:t:r} requires CI environment.%f"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
autoload -Uz is-at-least && if ! is-at-least 5.9; then
|
||||||
|
print -u2 -PR "%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.9%b is the minimum supported version. Upgrade Zsh to fix this issue."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TRAPZERR() {
|
||||||
|
print -u2 -PR "::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}
|
||||||
|
|
||||||
|
fpath=(${SCRIPT_HOME}/utils.zsh ${fpath})
|
||||||
|
autoload -Uz log_group log_error log_output check_${host_os}
|
||||||
|
|
||||||
|
local -i debug=0
|
||||||
|
|
||||||
|
local target
|
||||||
|
local -r -a _valid_targets=(
|
||||||
|
macos-x86_64
|
||||||
|
macos-arm64
|
||||||
|
ubuntu-x86_64
|
||||||
|
)
|
||||||
|
|
||||||
|
local config='RelWithDebInfo'
|
||||||
|
local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel)
|
||||||
|
local -i codesign=0
|
||||||
|
local -i analyze=0
|
||||||
|
|
||||||
|
local -a args
|
||||||
|
while (( # )) {
|
||||||
|
case ${1} {
|
||||||
|
-t|--target|-c|--config)
|
||||||
|
if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {
|
||||||
|
log_error "Missing value for option %B${1}%b"
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
}
|
||||||
|
case ${1} {
|
||||||
|
--) shift; args+=($@); break ;;
|
||||||
|
-a|--analyze) analyze=1; shift ;;
|
||||||
|
-t|--target)
|
||||||
|
if (( ! ${_valid_targets[(Ie)${2}]} )) {
|
||||||
|
log_error "Invalid value %B${2}%b for option %B${1}%b"
|
||||||
|
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"
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
config=${2}
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-s|--codesign) codesign=1; shift ;;
|
||||||
|
--debug) debug=1; shift ;;
|
||||||
|
*) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
: "${target:="${host_os}-${CPUTYPE}"}"
|
||||||
|
|
||||||
|
set -- ${(@)args}
|
||||||
|
|
||||||
|
check_${host_os}
|
||||||
|
|
||||||
|
if [[ ${host_os} == ubuntu ]] {
|
||||||
|
autoload -Uz setup_ubuntu setup_ccache
|
||||||
|
setup_ccache
|
||||||
|
setup_ubuntu
|
||||||
|
}
|
||||||
|
|
||||||
|
local product_name='obs-studio'
|
||||||
|
|
||||||
|
pushd ${project_root}
|
||||||
|
|
||||||
|
local -a cmake_args=()
|
||||||
|
local -a cmake_build_args=(--build)
|
||||||
|
local -a cmake_install_args=(--install)
|
||||||
|
|
||||||
|
if (( debug )) cmake_args+=(--debug-output)
|
||||||
|
|
||||||
|
case ${target} {
|
||||||
|
macos-*)
|
||||||
|
cmake_args+=(
|
||||||
|
--preset 'macos-ci'
|
||||||
|
-DCMAKE_OSX_ARCHITECTURES:STRING=${target##*-}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (( debug )) {
|
||||||
|
cmake_args+=(CMAKE_XCODE_ATTRIBUTE_COMPILATION_CACHE_ENABLE_DIAGNOSTIC_REMARKS:STRING=YES)
|
||||||
|
}
|
||||||
|
|
||||||
|
typeset -gx NSUnbufferedIO=YES
|
||||||
|
|
||||||
|
typeset -gx CODESIGN_IDENT="${CODESIGN_IDENT:--}"
|
||||||
|
if (( codesign )) && [[ -z ${CODESIGN_TEAM} ]] {
|
||||||
|
typeset -gx CODESIGN_TEAM="$(print "${CODESIGN_IDENT}" | /usr/bin/sed -En 's/.+\((.+)\)/\1/p')"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_group "Configuring ${product_name}..."
|
||||||
|
cmake -S ${project_root} ${cmake_args}
|
||||||
|
|
||||||
|
log_group "Building ${product_name}..."
|
||||||
|
run_xcodebuild() {
|
||||||
|
if (( debug )) {
|
||||||
|
xcodebuild ${@}
|
||||||
|
} else {
|
||||||
|
if [[ ${GITHUB_EVENT_NAME} == push ]] {
|
||||||
|
xcodebuild ${@} 2>&1 | xcbeautify --renderer terminal
|
||||||
|
} else {
|
||||||
|
xcodebuild ${@} 2>&1 | xcbeautify --renderer github-actions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
local -a analyze_args=(
|
||||||
|
CLANG_ANALYZER_OUTPUT=sarif
|
||||||
|
CLANG_ANALYZER_OUTPUT_DIR=${project_root}/analytics
|
||||||
|
-project obs-studio.xcodeproj
|
||||||
|
-target obs-studio
|
||||||
|
-destination "generic/platform=macOS,name=Any Mac"
|
||||||
|
-configuration ${config}
|
||||||
|
-parallelizeTargets
|
||||||
|
-hideShellScriptEnvironment
|
||||||
|
analyze
|
||||||
|
)
|
||||||
|
|
||||||
|
pushd build_macos
|
||||||
|
if (( analyze )) {
|
||||||
|
run_xcodebuild ${analyze_args}
|
||||||
|
} else {
|
||||||
|
if [[ ${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 frontend/${config}/OBS.app OBS.app
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popd
|
||||||
|
;;
|
||||||
|
ubuntu-*)
|
||||||
|
local cmake_bin='/usr/bin/cmake'
|
||||||
|
cmake_args+=(
|
||||||
|
--preset ubuntu-ci
|
||||||
|
-DENABLE_BROWSER:BOOL=ON
|
||||||
|
-DCEF_ROOT_DIR:PATH="${project_root}/.deps/cef_binary_${CEF_VERSION}_${target//ubuntu-/linux_}"
|
||||||
|
)
|
||||||
|
|
||||||
|
cmake_build_args+=(build_${target%%-*} --config ${config} --parallel)
|
||||||
|
cmake_install_args+=(build_${target%%-*} --prefix ${project_root}/build_${target%%-*}/install/${config})
|
||||||
|
|
||||||
|
export CLICOLOR_FORCE=1
|
||||||
|
|
||||||
|
log_group "Configuring ${product_name}..."
|
||||||
|
${cmake_bin} -S ${project_root} ${cmake_args}
|
||||||
|
|
||||||
|
log_group "Building ${product_name}..."
|
||||||
|
if (( debug )) cmake_build_args+=(--verbose)
|
||||||
|
${cmake_bin} ${cmake_build_args}
|
||||||
|
|
||||||
|
log_group "Installing ${product_name}..."
|
||||||
|
if (( debug )) cmake_install_args+=(--verbose)
|
||||||
|
${cmake_bin} ${cmake_install_args}
|
||||||
|
;;
|
||||||
|
}
|
||||||
|
popd
|
||||||
|
log_group
|
||||||
|
}
|
||||||
|
|
||||||
|
build ${@}
|
||||||
251
obs-studio/.github/scripts/.package.zsh
vendored
Normal file
251
obs-studio/.github/scripts/.package.zsh
vendored
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
if (( ! ${+CI} )) {
|
||||||
|
print -u2 -PR "%F{1} ✖︎ ${ZSH_ARGZERO:t:r} requires CI environment%f"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
autoload -Uz is-at-least && if ! is-at-least 5.9; then
|
||||||
|
print -u2 -PR "%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.9%b is the minimum supported version. Upgrade Zsh to fix this issue."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TRAPZERR() {
|
||||||
|
print -u2 -PR "::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}
|
||||||
|
|
||||||
|
fpath=(${SCRIPT_HOME}/utils.zsh ${fpath})
|
||||||
|
autoload -Uz log_error log_output log_group check_${host_os}
|
||||||
|
|
||||||
|
local -i debug=0
|
||||||
|
|
||||||
|
local target
|
||||||
|
local -r -a _valid_targets=(
|
||||||
|
macos-x86_64
|
||||||
|
macos-arm64
|
||||||
|
ubuntu-x86_64
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
local -a args
|
||||||
|
while (( # )) {
|
||||||
|
case ${1} {
|
||||||
|
-t|--target|-c|--config)
|
||||||
|
if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {
|
||||||
|
log_error "Missing value for option %B${1}%b"
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
config=${2}
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-s|--codesign) codesign=1; shift ;;
|
||||||
|
-n|--notarize) notarize=1; shift ;;
|
||||||
|
-p|--package) typeset -g package=1; shift ;;
|
||||||
|
--debug) debug=0; shift ;;
|
||||||
|
*) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
: "${target:="${host_os}-${CPUTYPE}"}"
|
||||||
|
|
||||||
|
set -- ${(@)args}
|
||||||
|
|
||||||
|
check_${host_os}
|
||||||
|
|
||||||
|
local product_name='obs-studio'
|
||||||
|
|
||||||
|
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 ]] {
|
||||||
|
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##*-}]})"
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
typeset -gx CODESIGN_IDENT="${CODESIGN_IDENT:--}"
|
||||||
|
typeset -gx CODESIGN_TEAM="$(print "${CODESIGN_IDENT}" | /usr/bin/sed -En 's/.+\((.+)\)/\1/p')"
|
||||||
|
|
||||||
|
codesign --sign "${CODESIGN_IDENT}" ${output_name}.dmg
|
||||||
|
|
||||||
|
if (( codesign && notarize )) {
|
||||||
|
if ! [[ ${CODESIGN_IDENT} != '-' && ${CODESIGN_TEAM} && ${CODESIGN_IDENT_USER} && ${CODESIGN_IDENT_PASS} ]] {
|
||||||
|
log_error "Notarization requires Apple ID and application password."
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
xcrun notarytool store-credentials 'OBS-Codesign-Password' --apple-id "${CODESIGN_IDENT_USER}" --team-id "${CODESIGN_TEAM}" --password "${CODESIGN_IDENT_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 -cvJf ${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 -cvJf ${output_name}-dSYMs.tar.xz -- *
|
||||||
|
mv ${output_name}-dSYMs.tar.xz ${PWD:h}
|
||||||
|
popd
|
||||||
|
}
|
||||||
|
|
||||||
|
log_group
|
||||||
|
|
||||||
|
} elif [[ ${host_os} == ubuntu ]] {
|
||||||
|
local cmake_bin='/usr/bin/cmake'
|
||||||
|
local -a cmake_args=()
|
||||||
|
if (( debug )) cmake_args+=(--verbose)
|
||||||
|
|
||||||
|
if (( package )) {
|
||||||
|
log_group "Packaging obs-studio..."
|
||||||
|
pushd ${project_root}
|
||||||
|
${cmake_bin} --build build_${target%%-*} --config ${config} --target package ${cmake_args}
|
||||||
|
output_name="${output_name}-${target##*-}-ubuntu-gnu"
|
||||||
|
|
||||||
|
pushd ${project_root}/build_${target%%-*}
|
||||||
|
local -a files=(obs-studio-*-Linux*.(ddeb|deb|ddeb.sha256|deb.sha256))
|
||||||
|
for file (${files}) {
|
||||||
|
mv ${file} ${file//obs-studio-*-Linux/${output_name}}
|
||||||
|
}
|
||||||
|
popd
|
||||||
|
popd
|
||||||
|
} else {
|
||||||
|
log_group "Archiving obs-studio..."
|
||||||
|
output_name="${output_name}-${target##*-}-ubuntu-gnu"
|
||||||
|
|
||||||
|
pushd ${project_root}/build_${target%%-*}/install/${config}
|
||||||
|
XZ_OPT=-T0 tar -cvJf ${project_root}/build_${target%%-*}/${output_name}.tar.xz (bin|lib|share)
|
||||||
|
popd
|
||||||
|
}
|
||||||
|
|
||||||
|
pushd ${project_root}
|
||||||
|
${cmake_bin} --build build_${target%%-*} --config ${config} --target package_source ${cmake_args}
|
||||||
|
output_name="${output_name}-sources"
|
||||||
|
|
||||||
|
pushd ${project_root}/build_${target%%-*}
|
||||||
|
local -a files=(obs-studio-*-sources.tar.*)
|
||||||
|
for file (${files}) {
|
||||||
|
mv ${file} ${file//obs-studio-*-sources/${output_name}}
|
||||||
|
}
|
||||||
|
popd
|
||||||
|
popd
|
||||||
|
|
||||||
|
log_group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package ${@}
|
||||||
89
obs-studio/.github/scripts/Build-Windows.ps1
vendored
Normal file
89
obs-studio/.github/scripts/Build-Windows.ps1
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[ValidateSet('x64', 'arm64')]
|
||||||
|
[string] $Target = 'x64',
|
||||||
|
[ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')]
|
||||||
|
[string] $Configuration = 'RelWithDebInfo'
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
if ( $DebugPreference -eq 'Continue' ) {
|
||||||
|
$VerbosePreference = 'Continue'
|
||||||
|
$InformationPreference = 'Continue'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $env:CI -eq $null ) {
|
||||||
|
throw "Build-Windows.ps1 requires CI environment"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) {
|
||||||
|
throw "obs-studio requires a 64-bit system to build and run."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $PSVersionTable.PSVersion -lt '7.2.0' ) {
|
||||||
|
Write-Warning 'The obs-studio PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6'
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
function Build {
|
||||||
|
trap {
|
||||||
|
Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue'
|
||||||
|
Write-Error $_
|
||||||
|
Log-Group
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
$ScriptHome = $PSScriptRoot
|
||||||
|
$ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.."
|
||||||
|
|
||||||
|
$UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse
|
||||||
|
|
||||||
|
foreach($Utility in $UtilityFunctions) {
|
||||||
|
Write-Debug "Loading $($Utility.FullName)"
|
||||||
|
. $Utility.FullName
|
||||||
|
}
|
||||||
|
|
||||||
|
Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile"
|
||||||
|
|
||||||
|
Push-Location -Stack BuildTemp
|
||||||
|
Ensure-Location $ProjectRoot
|
||||||
|
|
||||||
|
$CmakeArgs = @('--preset', "windows-ci-${Target}")
|
||||||
|
|
||||||
|
$CmakeBuildArgs = @('--build')
|
||||||
|
$CmakeInstallArgs = @()
|
||||||
|
|
||||||
|
if ( $DebugPreference -eq 'Continue' ) {
|
||||||
|
$CmakeArgs += ('--debug-output')
|
||||||
|
$CmakeBuildArgs += ('--verbose')
|
||||||
|
$CmakeInstallArgs += ('--verbose')
|
||||||
|
}
|
||||||
|
|
||||||
|
$CmakeBuildArgs += @(
|
||||||
|
'--preset', "windows-${Target}"
|
||||||
|
'--config', $Configuration
|
||||||
|
'--parallel'
|
||||||
|
'--', '/consoleLoggerParameters:Summary', '/noLogo'
|
||||||
|
)
|
||||||
|
|
||||||
|
$CmakeInstallArgs += @(
|
||||||
|
'--install', "build_${Target}"
|
||||||
|
'--prefix', "${ProjectRoot}/build_${Target}/install"
|
||||||
|
'--config', $Configuration
|
||||||
|
)
|
||||||
|
|
||||||
|
Log-Group "Configuring obs-studio..."
|
||||||
|
Invoke-External cmake @CmakeArgs
|
||||||
|
|
||||||
|
Log-Group "Building obs-studio..."
|
||||||
|
Invoke-External cmake @CmakeBuildArgs
|
||||||
|
|
||||||
|
Log-Group "Installing obs-studio..."
|
||||||
|
Invoke-External cmake @CmakeInstallArgs
|
||||||
|
|
||||||
|
Pop-Location -Stack BuildTemp
|
||||||
|
Log-Group
|
||||||
|
}
|
||||||
|
|
||||||
|
Build
|
||||||
79
obs-studio/.github/scripts/Package-Windows.ps1
vendored
Normal file
79
obs-studio/.github/scripts/Package-Windows.ps1
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[ValidateSet('x64', 'arm64')]
|
||||||
|
[string] $Target = 'x64',
|
||||||
|
[ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')]
|
||||||
|
[string] $Configuration = 'RelWithDebInfo'
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
if ( $DebugPreference -eq 'Continue' ) {
|
||||||
|
$VerbosePreference = 'Continue'
|
||||||
|
$InformationPreference = 'Continue'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $env:CI -eq $null ) {
|
||||||
|
throw "Package-Windows.ps1 requires CI environment"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) {
|
||||||
|
throw "obs-studio requires a 64-bit system to build and run."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $PSVersionTable.PSVersion -lt '7.2.0' ) {
|
||||||
|
Write-Warning 'The obs-studio packaging script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6'
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
function Package {
|
||||||
|
trap {
|
||||||
|
Write-Error $_
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
$ScriptHome = $PSScriptRoot
|
||||||
|
$ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.."
|
||||||
|
|
||||||
|
$UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse
|
||||||
|
|
||||||
|
foreach( $Utility in $UtilityFunctions ) {
|
||||||
|
Write-Debug "Loading $($Utility.FullName)"
|
||||||
|
. $Utility.FullName
|
||||||
|
}
|
||||||
|
|
||||||
|
Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile"
|
||||||
|
|
||||||
|
$GitDescription = Invoke-External git describe --tags --long
|
||||||
|
$Tokens = ($GitDescription -split '-')
|
||||||
|
$CommitVersion = $Tokens[0..$($Tokens.Count - 3)] -join '-'
|
||||||
|
$CommitHash = $($Tokens[-1]).SubString(1)
|
||||||
|
$CommitDistance = $Tokens[-2]
|
||||||
|
|
||||||
|
if ( $CommitDistance -gt 0 ) {
|
||||||
|
$OutputName = "obs-studio-${CommitVersion}-${CommitHash}"
|
||||||
|
} else {
|
||||||
|
$OutputName = "obs-studio-${CommitVersion}"
|
||||||
|
}
|
||||||
|
|
||||||
|
$CpackArgs = @(
|
||||||
|
'-C', "${Configuration}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ( $DebugPreference -eq 'Continue' ) {
|
||||||
|
$CpackArgs += ('--verbose')
|
||||||
|
}
|
||||||
|
|
||||||
|
Log-Group "Packaging obs-studio..."
|
||||||
|
|
||||||
|
Push-Location -Stack PackageTemp "build_${Target}"
|
||||||
|
|
||||||
|
cpack @CpackArgs
|
||||||
|
|
||||||
|
$Package = Get-ChildItem -filter "obs-studio-*-windows-${Target}.zip" -File
|
||||||
|
Move-Item -Path $Package -Destination "${OutputName}-windows-${Target}.zip"
|
||||||
|
|
||||||
|
Pop-Location -Stack PackageTemp
|
||||||
|
}
|
||||||
|
|
||||||
|
Package
|
||||||
1
obs-studio/.github/scripts/build-macos
vendored
Normal file
1
obs-studio/.github/scripts/build-macos
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
./.build.zsh
|
||||||
1
obs-studio/.github/scripts/build-ubuntu
vendored
Normal file
1
obs-studio/.github/scripts/build-ubuntu
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
./.build.zsh
|
||||||
1
obs-studio/.github/scripts/package-macos
vendored
Normal file
1
obs-studio/.github/scripts/package-macos
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
./.package.zsh
|
||||||
1
obs-studio/.github/scripts/package-ubuntu
vendored
Normal file
1
obs-studio/.github/scripts/package-ubuntu
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
./.package.zsh
|
||||||
29
obs-studio/.github/scripts/utils.pwsh/Ensure-Location.ps1
vendored
Normal file
29
obs-studio/.github/scripts/utils.pwsh/Ensure-Location.ps1
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
function Ensure-Location {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Ensures current location to be set to specified directory.
|
||||||
|
.DESCRIPTION
|
||||||
|
If specified directory exists, switch to it. Otherwise create it,
|
||||||
|
then switch.
|
||||||
|
.EXAMPLE
|
||||||
|
Ensure-Location "My-Directory"
|
||||||
|
Ensure-Location -Path "Path-To-My-Directory"
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string] $Path
|
||||||
|
)
|
||||||
|
|
||||||
|
if ( ! ( Test-Path $Path ) ) {
|
||||||
|
$_Params = @{
|
||||||
|
ItemType = "Directory"
|
||||||
|
Path = ${Path}
|
||||||
|
ErrorAction = "SilentlyContinue"
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item @_Params | Set-Location
|
||||||
|
} else {
|
||||||
|
Set-Location -Path ${Path}
|
||||||
|
}
|
||||||
|
}
|
||||||
70
obs-studio/.github/scripts/utils.pwsh/Install-BuildDependencies.ps1
vendored
Normal file
70
obs-studio/.github/scripts/utils.pwsh/Install-BuildDependencies.ps1
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
function Install-BuildDependencies {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Installs required build dependencies.
|
||||||
|
.DESCRIPTION
|
||||||
|
Additional packages might be needed for successful builds. This module contains additional
|
||||||
|
dependencies available for installation via winget and, if possible, adds their locations
|
||||||
|
to the environment path for future invocation.
|
||||||
|
.EXAMPLE
|
||||||
|
Install-BuildDependencies
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string] $WingetFile = "$PSScriptRoot/.Wingetfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ( ! ( Test-Path function:Log-Warning ) ) {
|
||||||
|
. $PSScriptRoot/Logger.ps1
|
||||||
|
}
|
||||||
|
|
||||||
|
$Prefixes = @{
|
||||||
|
'x64' = ${env:ProgramFiles}
|
||||||
|
'x86' = ${env:ProgramFiles(x86)}
|
||||||
|
'arm64' = ${env:ProgramFiles(arm)}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Paths = $env:Path -split [System.IO.Path]::PathSeparator
|
||||||
|
|
||||||
|
$WingetOptions = @('install', '--accept-package-agreements', '--accept-source-agreements')
|
||||||
|
|
||||||
|
if ( $script:Quiet ) {
|
||||||
|
$WingetOptions += '--silent'
|
||||||
|
}
|
||||||
|
|
||||||
|
Log-Group 'Check Windows build requirements'
|
||||||
|
Get-Content $WingetFile | ForEach-Object {
|
||||||
|
$_, $Package, $_, $Path, $_, $Binary, $_, $Version = $_ -replace ',','' -split " +(?=(?:[^\']*\'[^\']*\')*[^\']*$)" -replace "'",''
|
||||||
|
|
||||||
|
$Prefixes.GetEnumerator() | ForEach-Object {
|
||||||
|
$Prefix = $_.value
|
||||||
|
$FullPath = "${Prefix}\${Path}"
|
||||||
|
if ( ( Test-Path $FullPath ) -and ! ( $Paths -contains $FullPath ) ) {
|
||||||
|
$Paths = @($FullPath) + $Paths
|
||||||
|
$env:Path = $Paths -join [System.IO.Path]::PathSeparator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log-Debug "Checking for command ${Binary}"
|
||||||
|
$Found = Get-Command -ErrorAction SilentlyContinue $Binary
|
||||||
|
|
||||||
|
if ( $Found ) {
|
||||||
|
Log-Status "Found dependency ${Binary} as $($Found.Source)"
|
||||||
|
} else {
|
||||||
|
Log-Status "Installing package ${Package} $(if ( $Version -ne $null ) { "Version: ${Version}" } )"
|
||||||
|
|
||||||
|
if ( $Version -ne $null ) {
|
||||||
|
$WingGetOptions += @('--version', ${Version})
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$Params = $WingetOptions + $Package
|
||||||
|
|
||||||
|
winget @Params
|
||||||
|
} catch {
|
||||||
|
throw "Error while installing winget package ${Package}: $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log-Group
|
||||||
|
}
|
||||||
40
obs-studio/.github/scripts/utils.pwsh/Invoke-External.ps1
vendored
Normal file
40
obs-studio/.github/scripts/utils.pwsh/Invoke-External.ps1
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
function Invoke-External {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Invokes a non-PowerShell command.
|
||||||
|
.DESCRIPTION
|
||||||
|
Runs a non-PowerShell command, and captures its return code.
|
||||||
|
Throws an exception if the command returns non-zero.
|
||||||
|
.EXAMPLE
|
||||||
|
Invoke-External 7z x $MyArchive
|
||||||
|
#>
|
||||||
|
|
||||||
|
if ( $args.Count -eq 0 ) {
|
||||||
|
throw 'Invoke-External called without arguments.'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! ( Test-Path function:Log-Information ) ) {
|
||||||
|
. $PSScriptRoot/Logger.ps1
|
||||||
|
}
|
||||||
|
|
||||||
|
$Command = $args[0]
|
||||||
|
$CommandArgs = @()
|
||||||
|
|
||||||
|
if ( $args.Count -gt 1) {
|
||||||
|
$CommandArgs = $args[1..($args.Count - 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
$_EAP = $ErrorActionPreference
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
|
||||||
|
Log-Debug "Invoke-External: ${Command} ${CommandArgs}"
|
||||||
|
|
||||||
|
& $command $commandArgs
|
||||||
|
$Result = $LASTEXITCODE
|
||||||
|
|
||||||
|
$ErrorActionPreference = $_EAP
|
||||||
|
|
||||||
|
if ( $Result -ne 0 ) {
|
||||||
|
throw "${Command} ${CommandArgs} exited with non-zero code ${Result}."
|
||||||
|
}
|
||||||
|
}
|
||||||
149
obs-studio/.github/scripts/utils.pwsh/Logger.ps1
vendored
Normal file
149
obs-studio/.github/scripts/utils.pwsh/Logger.ps1
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
function Log-Debug {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Debug $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Verbose {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Verbose $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Warning {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Warning $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Error {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Error $m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Information {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
|
||||||
|
$Icon = ' =>'
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) "
|
||||||
|
Write-Host "${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Group {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(ValueFromPipeline)]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( $Env:CI -ne $null ) {
|
||||||
|
if ( $script:LogGroup ) {
|
||||||
|
Write-Output '::endgroup::'
|
||||||
|
$script:LogGroup = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $Message.count -ge 1 ) {
|
||||||
|
Write-Output "::group::$($Message -join ' ')"
|
||||||
|
$script:LogGroup = $true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( $Message.count -ge 1 ) {
|
||||||
|
Log-Information $Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Status {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })
|
||||||
|
$Icon = ' >'
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) "
|
||||||
|
Write-Host "${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Log-Output {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string[]] $Message
|
||||||
|
)
|
||||||
|
|
||||||
|
Process {
|
||||||
|
if ( ! ( $script:Quiet ) ) {
|
||||||
|
$StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })
|
||||||
|
$Icon = ''
|
||||||
|
|
||||||
|
foreach($m in $Message) {
|
||||||
|
Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5
|
||||||
131
obs-studio/.github/scripts/utils.py/check-jsonschema.py
vendored
Normal file
131
obs-studio/.github/scripts/utils.py/check-jsonschema.py
vendored
Normal file
@ -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())
|
||||||
372
obs-studio/.github/scripts/utils.py/check-services.py
vendored
Normal file
372
obs-studio/.github/scripts/utils.py/check-services.py
vendored
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from io import BytesIO
|
||||||
|
from typing import List, Dict
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
MINIMUM_PURGE_AGE = 9.75 * 24 * 60 * 60 # slightly less than 10 days
|
||||||
|
TIMEOUT = 10
|
||||||
|
SKIPPED_SERVICES = {"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.
|
||||||
|
|
||||||
|
| Service | Action Taken | Author(s) |
|
||||||
|
| ------- | ------------ | --------- |
|
||||||
|
{table}
|
||||||
|
|
||||||
|
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}"""
|
||||||
|
|
||||||
|
# GQL is great isn't it
|
||||||
|
GQL_QUERY = """{
|
||||||
|
repositoryOwner(login: "obsproject") {
|
||||||
|
repository(name: "obs-studio") {
|
||||||
|
object(expression: "master") {
|
||||||
|
... on Commit {
|
||||||
|
blame(path: "plugins/rtmp-services/data/services.json") {
|
||||||
|
ranges {
|
||||||
|
startingLine
|
||||||
|
endingLine
|
||||||
|
commit {
|
||||||
|
author {
|
||||||
|
user {
|
||||||
|
login
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_artifact():
|
||||||
|
s = requests.session()
|
||||||
|
s.headers["Authorization"] = f'Bearer {os.environ["GITHUB_TOKEN"]}'
|
||||||
|
|
||||||
|
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.raise_for_status()
|
||||||
|
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",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
r.raise_for_status()
|
||||||
|
runs = r.json()
|
||||||
|
if not runs["workflow_runs"]:
|
||||||
|
raise ValueError("No completed workflow runs found")
|
||||||
|
|
||||||
|
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"]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError("No previous artifact found.")
|
||||||
|
|
||||||
|
r = s.get(artifact_url)
|
||||||
|
r.raise_for_status()
|
||||||
|
zip_data = BytesIO()
|
||||||
|
zip_data.write(r.content)
|
||||||
|
|
||||||
|
with zipfile.ZipFile(zip_data) as zip_ref:
|
||||||
|
for info in zip_ref.infolist():
|
||||||
|
if info.filename == "timestamps.json":
|
||||||
|
return json.loads(zip_ref.read(info.filename))
|
||||||
|
|
||||||
|
|
||||||
|
def find_people_to_blame(raw_services: str, servers: list[tuple[str, str]]) -> dict:
|
||||||
|
if not servers:
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
# Fetch Blame data from github
|
||||||
|
s = requests.session()
|
||||||
|
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.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"]
|
||||||
|
|
||||||
|
service_authors = defaultdict(set)
|
||||||
|
for i, line in enumerate(raw_services.splitlines()):
|
||||||
|
if '"url":' not in line:
|
||||||
|
continue
|
||||||
|
for server, service in servers:
|
||||||
|
if server in line and (author := line_author.get(i)):
|
||||||
|
if author not in DO_NOT_PING:
|
||||||
|
service_authors[service].add(author)
|
||||||
|
|
||||||
|
return service_authors
|
||||||
|
|
||||||
|
|
||||||
|
def set_output(name, value):
|
||||||
|
if not GITHUB_OUTPUT_FILE:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
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}")
|
||||||
|
|
||||||
|
|
||||||
|
async def check_servers_task(
|
||||||
|
session: aiohttp.ClientSession, host: str, protocol: str, servers: List[str]
|
||||||
|
) -> List[Dict]:
|
||||||
|
query = [dict(url=h, protocol=protocol) for h in servers]
|
||||||
|
|
||||||
|
async with session.get(
|
||||||
|
f"http://{host}:8999/test_remote_servers", json=query
|
||||||
|
) as resp:
|
||||||
|
return await resp.json()
|
||||||
|
|
||||||
|
|
||||||
|
async def process_services(session: aiohttp.ClientSession, check_servers: List[str]):
|
||||||
|
try:
|
||||||
|
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:
|
||||||
|
package = json.load(package_file)
|
||||||
|
except OSError as 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:
|
||||||
|
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})"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
fail_timestamps = get_last_artifact()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Could not fetch cache file, starting fresh. (Exception: {e})")
|
||||||
|
fail_timestamps = dict()
|
||||||
|
else:
|
||||||
|
print("Fetched cache file from last run artifact.")
|
||||||
|
else:
|
||||||
|
print("Successfully loaded cache file:", CACHE_FILE)
|
||||||
|
|
||||||
|
start_time = int(time.time())
|
||||||
|
affected_services = dict()
|
||||||
|
removed_servers = list()
|
||||||
|
|
||||||
|
# create temporary new list
|
||||||
|
new_services = services.copy()
|
||||||
|
new_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)
|
||||||
|
continue
|
||||||
|
|
||||||
|
service_type = service.get("recommended", {}).get("output", "rtmp_output")
|
||||||
|
if service_type not in {"rtmp_output", "ffmpeg_hls_muxer"}:
|
||||||
|
print("Unknown service type:", service_type)
|
||||||
|
new_services["services"].append(service)
|
||||||
|
continue
|
||||||
|
|
||||||
|
protocol = "rtmp" if service_type == "rtmp_output" else "hls"
|
||||||
|
|
||||||
|
# create a copy to mess with
|
||||||
|
new_service = service.copy()
|
||||||
|
new_service["servers"] = []
|
||||||
|
|
||||||
|
# run checks for all the servers, and store results in timestamp cache
|
||||||
|
try:
|
||||||
|
servers = [s["url"] for s in service["servers"]]
|
||||||
|
tasks = []
|
||||||
|
for host in check_servers:
|
||||||
|
tasks.append(
|
||||||
|
asyncio.create_task(
|
||||||
|
check_servers_task(session, host, protocol, servers)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
results = await asyncio.gather(*tasks)
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
f"❌ Querying server status for \"{service['name']}\" failed with: {e}"
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# go over results
|
||||||
|
for server, result in zip(service["servers"], zip(*results)):
|
||||||
|
failure_count = sum(not res["status"] for res in result)
|
||||||
|
probe_count = len(result)
|
||||||
|
# only treat server as failed if all check servers reported a failure
|
||||||
|
is_ok = failure_count < probe_count
|
||||||
|
|
||||||
|
if not is_ok:
|
||||||
|
failures = {res["comment"] for res in result if not res["status"]}
|
||||||
|
print(
|
||||||
|
f"⚠️ Connecting to server failed: {server['url']} (Reason(s): {failures})"
|
||||||
|
)
|
||||||
|
|
||||||
|
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."
|
||||||
|
)
|
||||||
|
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:
|
||||||
|
# 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"]]
|
||||||
|
|
||||||
|
new_service["servers"].append(server)
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
# remove services with no valid servers
|
||||||
|
if not new_service["servers"]:
|
||||||
|
print(f'💀 Service "{service["name"]}" has no valid servers left, removing!')
|
||||||
|
affected_services[service["name"]] = f"Service removed"
|
||||||
|
else:
|
||||||
|
new_services["services"].append(new_service)
|
||||||
|
|
||||||
|
# wait a bit between services
|
||||||
|
await asyncio.sleep(2.0)
|
||||||
|
|
||||||
|
# write cache file
|
||||||
|
try:
|
||||||
|
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}")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
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
|
||||||
|
|
||||||
|
try:
|
||||||
|
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")
|
||||||
|
|
||||||
|
with open(PACKAGE_FILE, "w", encoding="utf-8") as package_file:
|
||||||
|
json.dump(package, package_file, indent=4)
|
||||||
|
package_file.write("\n")
|
||||||
|
except OSError as 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}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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}")
|
||||||
|
service_authors = dict()
|
||||||
|
|
||||||
|
# set GitHub outputs
|
||||||
|
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("|", "\\|"),
|
||||||
|
action=action,
|
||||||
|
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))
|
||||||
|
else:
|
||||||
|
set_output("make_pr", "false")
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
# check for environment variables
|
||||||
|
try:
|
||||||
|
api_key = os.environ["API_KEY"]
|
||||||
|
servers = os.environ["API_SERVERS"].split(",")
|
||||||
|
if not servers:
|
||||||
|
raise ValueError("No checker servers!")
|
||||||
|
# Mask everything except the region code
|
||||||
|
for server in servers:
|
||||||
|
prefix = server[: server.index(".") + 1]
|
||||||
|
print(f"::add-mask::{prefix}")
|
||||||
|
suffix = server[server.index(".", len(prefix)) :]
|
||||||
|
print(f"::add-mask::{suffix}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Failed getting required environment variables: {e}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# create aiohttp session
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
session.headers["Authorization"] = api_key
|
||||||
|
session.headers["User-Agent"] = "OBS Repo Service Checker/1.0"
|
||||||
|
return await process_services(session, servers)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(asyncio.run(main()))
|
||||||
22
obs-studio/.github/scripts/utils.zsh/check_macos
vendored
Normal file
22
obs-studio/.github/scripts/utils.zsh/check_macos
vendored
Normal file
@ -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 26.0 ${macos_version}; then
|
||||||
|
log_error "Minimum required macOS version is 26.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
|
||||||
54
obs-studio/.github/scripts/utils.zsh/check_ubuntu
vendored
Normal file
54
obs-studio/.github/scripts/utils.zsh/check_ubuntu
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
autoload -Uz log_debug log_group
|
||||||
|
|
||||||
|
log_group 'Check Ubuntu build requirements'
|
||||||
|
log_debug 'Checking Ubuntu distribution name and version...'
|
||||||
|
|
||||||
|
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}")"
|
||||||
|
|
||||||
|
if [[ ${dist_name} != Ubuntu ]] {
|
||||||
|
log_error "Not running on an Ubuntu distribution. Aborting"
|
||||||
|
log_group
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
autoload -Uz is-at-least && if ! is-at-least 24.04 ${dist_version}; then
|
||||||
|
log_error "Not running on a recent-enough Ubuntu distribution. Aborting"
|
||||||
|
log_group
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
} else {
|
||||||
|
log_error "Unable to determine local Linux distribution, but Ubuntu is required. Aborting"
|
||||||
|
log_group
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
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 )) {
|
||||||
|
sudo apt-get -y --no-install-recommends install ${install_list}
|
||||||
|
}
|
||||||
|
|
||||||
|
rehash
|
||||||
|
log_group
|
||||||
84
obs-studio/.github/scripts/utils.zsh/create_diskimage
vendored
Normal file
84
obs-studio/.github/scripts/utils.zsh/create_diskimage
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
autoload -Uz log_debug log_error log_info log_status log_group log_output log_warning
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
trap "safe_hdiutil detach /Volumes/${output_name}; rm temp.dmg; log_group; return 2" ERR
|
||||||
|
|
||||||
|
safe_hdiutil() {
|
||||||
|
local _status=0
|
||||||
|
local -r -a _backoff=(2 5 10 15 30)
|
||||||
|
|
||||||
|
for i ({1..5}) {
|
||||||
|
hdiutil ${@} && _status=0 || _status=1
|
||||||
|
|
||||||
|
if (( _status )) {
|
||||||
|
log_warning "Unable to run 'hdiutil ${@}' (attempt #${i}). Retrying."
|
||||||
|
if (( ${+CI} )) sudo pkill -9 XProtect >/dev/null || true
|
||||||
|
sleep ${_backoff[${i}]}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( _status )) {
|
||||||
|
log_error "Unable to run 'hdiutil ${@}'. Aborting"
|
||||||
|
log_group
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_hdiutil create \
|
||||||
|
-volname "${volume_name}" \
|
||||||
|
-srcfolder ${source} \
|
||||||
|
-ov \
|
||||||
|
-fs HFS+ \
|
||||||
|
-format UDRW \
|
||||||
|
temp.dmg
|
||||||
|
safe_hdiutil attach \
|
||||||
|
-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..."
|
||||||
|
|
||||||
|
safe_hdiutil detach /Volumes/${output_name}
|
||||||
|
|
||||||
|
safe_hdiutil convert \
|
||||||
|
-format ULFO \
|
||||||
|
-ov \
|
||||||
|
-o ${output_name}.dmg temp.dmg
|
||||||
|
|
||||||
|
rm temp.dmg
|
||||||
|
|
||||||
|
trap '' ERR
|
||||||
|
|
||||||
|
log_group
|
||||||
|
|
||||||
|
return 0
|
||||||
1
obs-studio/.github/scripts/utils.zsh/log_debug
vendored
Normal file
1
obs-studio/.github/scripts/utils.zsh/log_debug
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
if (( debug )) print -PR -e "::debug::%F{220}DEBUG: ${@}%f"
|
||||||
1
obs-studio/.github/scripts/utils.zsh/log_error
vendored
Normal file
1
obs-studio/.github/scripts/utils.zsh/log_error
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
print -u2 -PR "::error::%F{1} ✖︎%f ${@}"
|
||||||
12
obs-studio/.github/scripts/utils.zsh/log_group
vendored
Normal file
12
obs-studio/.github/scripts/utils.zsh/log_group
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
autoload -Uz log_info
|
||||||
|
|
||||||
|
if (( ! ${+_log_group} )) typeset -g _log_group=0
|
||||||
|
|
||||||
|
if (( _log_group )) {
|
||||||
|
print "::endgroup::"
|
||||||
|
typeset -g _log_group=0
|
||||||
|
}
|
||||||
|
if (( # )) {
|
||||||
|
print "::group::${@}"
|
||||||
|
typeset -g _log_group=1
|
||||||
|
}
|
||||||
1
obs-studio/.github/scripts/utils.zsh/log_info
vendored
Normal file
1
obs-studio/.github/scripts/utils.zsh/log_info
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
print -PR "%F{4} =>%f %B${@}%b"
|
||||||
1
obs-studio/.github/scripts/utils.zsh/log_output
vendored
Normal file
1
obs-studio/.github/scripts/utils.zsh/log_output
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
print -PR " ${@}"
|
||||||
1
obs-studio/.github/scripts/utils.zsh/log_status
vendored
Normal file
1
obs-studio/.github/scripts/utils.zsh/log_status
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
print -PR "%F{2} >%f ${@}"
|
||||||
1
obs-studio/.github/scripts/utils.zsh/log_warning
vendored
Normal file
1
obs-studio/.github/scripts/utils.zsh/log_warning
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
print -PR "::warning::%F{3} => ${@}%f"
|
||||||
1
obs-studio/.github/scripts/utils.zsh/mkcd
vendored
Normal file
1
obs-studio/.github/scripts/utils.zsh/mkcd
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
[[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1}
|
||||||
37
obs-studio/.github/scripts/utils.zsh/setup_ccache
vendored
Normal file
37
obs-studio/.github/scripts/utils.zsh/setup_ccache
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
autoload -Uz is-at-least log_debug log_warning
|
||||||
|
|
||||||
|
if (( ${+commands[ccache]} )) {
|
||||||
|
log_debug "Found ccache at ${commands[ccache]}"
|
||||||
|
|
||||||
|
local ccache_version=$(ccache --version | head -1 | cut -d ' ' -f 3)
|
||||||
|
if ! is-at-least 4.12 ${ccache_version}; then
|
||||||
|
ccache --set-config=run_second_cpp=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
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}
|
||||||
|
}
|
||||||
|
|
||||||
|
ccache --set-config=cache_dir="${GITHUB_WORKSPACE:-${HOME}}/.ccache"
|
||||||
|
ccache --set-config=max_size="${CCACHE_SIZE:-1G}"
|
||||||
|
ccache -z > /dev/null
|
||||||
|
} else {
|
||||||
|
log_warning "Ccache not available"
|
||||||
|
}
|
||||||
94
obs-studio/.github/scripts/utils.zsh/setup_ubuntu
vendored
Normal file
94
obs-studio/.github/scripts/utils.zsh/setup_ubuntu
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
autoload -Uz log_group log_error log_status log_info log_debug
|
||||||
|
|
||||||
|
if (( ! ${+commands[curl]} )) {
|
||||||
|
log_error 'curl not available.'
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( ! ${+commands[jq]} )) {
|
||||||
|
log_error 'jq not available.'
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
local -a curl_opts=(--show-error --silent --location -O ${@})
|
||||||
|
|
||||||
|
pushd ${project_root}
|
||||||
|
|
||||||
|
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 deps_revision <<< \
|
||||||
|
"$(jq -r --arg target "${target}" '
|
||||||
|
.configurePresets[]
|
||||||
|
| select(.name=="dependencies")
|
||||||
|
| .vendor["obsproject.com/obs-studio"].dependencies["cef"]
|
||||||
|
| {version, baseUrl, "label", "hash": .hashes[$target], "revision": .revision[$target]}
|
||||||
|
| join(";")
|
||||||
|
' ${project_root}/CMakePresets.json)"
|
||||||
|
|
||||||
|
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//ubuntu-/linux_}${deps_revision:+"_v${deps_revision}"}.tar.xz"
|
||||||
|
local _url=${deps_baseurl}/${_filename}
|
||||||
|
local _target="cef_binary_${deps_version}_${target//ubuntu-/linux_}"
|
||||||
|
typeset -g CEF_VERSION=${deps_version}
|
||||||
|
|
||||||
|
log_debug "Running curl ${curl_opts} ${_url}"
|
||||||
|
curl ${curl_opts} ${_url} && \
|
||||||
|
log_status "Downloaded ${deps_label} for ${target}."
|
||||||
|
|
||||||
|
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."
|
||||||
|
popd
|
||||||
|
|
||||||
|
log_group 'Installing obs-studio build dependencies from apt...'
|
||||||
|
|
||||||
|
sudo apt-get install -y --no-install-recommends \
|
||||||
|
build-essential libglib2.0-dev \
|
||||||
|
extra-cmake-modules lsb-release dh-cmake \
|
||||||
|
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 \
|
||||||
|
libwayland-dev \
|
||||||
|
libpipewire-0.3-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libx264-dev \
|
||||||
|
libmbedtls-dev \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
libjansson-dev \
|
||||||
|
uthash-dev \
|
||||||
|
libsimde-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 libatk1.0-dev libatk-bridge2.0-dev libxcomposite-dev libxdamage-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 libqrcodegencpp-dev \
|
||||||
|
libffmpeg-nvenc-dev librist-dev libsrt-openssl-dev \
|
||||||
|
qt6-base-dev libqt6svg6-dev qt6-base-private-dev \
|
||||||
|
libvpl-dev libvpl2
|
||||||
176
obs-studio/.github/workflows/analyze-project.yaml
vendored
Normal file
176
obs-studio/.github/workflows/analyze-project.yaml
vendored
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
name: Analyze Project
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
jobs:
|
||||||
|
windows:
|
||||||
|
name: Windows 🪟 (PVS-Studio)
|
||||||
|
runs-on: windows-2025-vs2026
|
||||||
|
if: false && github.repository_owner == 'obsproject'
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: pwsh
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- 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: Debug
|
||||||
|
|
||||||
|
- name: Run PVS-Studio Analysis
|
||||||
|
uses: ./.github/actions/windows-analysis
|
||||||
|
with:
|
||||||
|
pvsUsername: ${{ secrets.PVS_NAME }}
|
||||||
|
pvsKey: ${{ secrets.PVS_KEY }}
|
||||||
|
target: x64
|
||||||
|
config: Debug
|
||||||
|
|
||||||
|
macos:
|
||||||
|
name: macOS 🍏 (clang-analyze)
|
||||||
|
runs-on: macos-26
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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 26.5'
|
||||||
|
sudo xcode-select --switch /Applications/Xcode_26.5.app/Contents/Developer
|
||||||
|
print '::endgroup::'
|
||||||
|
|
||||||
|
print '::group::Clean Homebrew Environment'
|
||||||
|
local -a unwanted_formulas=()
|
||||||
|
local -a remove_formulas=()
|
||||||
|
for formula (${unwanted_formulas}) {
|
||||||
|
if [[ -d ${HOMEBREW_PREFIX}/Cellar/${formula} ]] remove_formulas+=(${formula})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( #remove_formulas )) brew uninstall --ignore-dependencies ${remove_formulas}
|
||||||
|
print '::endgroup::'
|
||||||
|
|
||||||
|
local xcode_cas_path="${HOME}/Library/Developer/Xcode/DerivedData/CompilationCache.noindex"
|
||||||
|
|
||||||
|
if ! [[ -d ${xcode_cas_path} ]] mkdir -p ${xcode_cas_path}
|
||||||
|
|
||||||
|
print "xcodeCasPath=${xcode_cas_path}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Set Up Code Signing 🔑
|
||||||
|
uses: ./.github/actions/setup-macos-codesigning
|
||||||
|
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 🧱
|
||||||
|
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 }}
|
||||||
|
CODESIGN_IDENT: ${{ steps.codesign.outputs.codesignIdent }}
|
||||||
|
CODESIGN_TEAM: ${{ steps.codesign.outputs.codesignTeam }}
|
||||||
|
PROVISIONING_PROFILE: ${{ steps.codesign.outputs.provisioningProfileUUID }}
|
||||||
|
XCODE_CAS_PATH: ${{ steps.setup.outputs.xcodeCasPath }}
|
||||||
|
run: |
|
||||||
|
: Run macOS Build
|
||||||
|
|
||||||
|
local -a build_args=(
|
||||||
|
--config Debug
|
||||||
|
--target macos-arm64
|
||||||
|
--codesign
|
||||||
|
--analyze
|
||||||
|
)
|
||||||
|
if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)
|
||||||
|
|
||||||
|
git fetch origin --no-tags --no-recurse-submodules -q
|
||||||
|
.github/scripts/build-macos ${build_args}
|
||||||
|
|
||||||
|
- name: Compile Analytics Data 📊
|
||||||
|
run: |
|
||||||
|
: Compile Analytics Data 📊
|
||||||
|
|
||||||
|
local analytics_root='${{ github.workspace }}/analytics'
|
||||||
|
|
||||||
|
local -a analytics_files=(${analytics_root}/StaticAnalyzer/obs-studio/**/*.plist)
|
||||||
|
|
||||||
|
for file (${analytics_files}) {
|
||||||
|
mv ${file} ${analytics_root}/${${file:t}//plist/sarif}
|
||||||
|
}
|
||||||
|
|
||||||
|
pushd ${analytics_root}
|
||||||
|
|
||||||
|
# The jq expression below merges the results of all separate compilation (and
|
||||||
|
# analysis) runs into a single result of all tools, thus meeting the
|
||||||
|
# requirements of the codeql-action's upload job.
|
||||||
|
# The merged sarif file contains a single "runs" array with a single
|
||||||
|
# corresponding "tool" object.
|
||||||
|
#
|
||||||
|
# Each sarif file will have its own "rules" array (depending on which rules the
|
||||||
|
# file violated), so all violated rules across all compilations need to be
|
||||||
|
# collected into this array.
|
||||||
|
#
|
||||||
|
# Next, all "artifacts" array elements and "rules" array elements from all
|
||||||
|
# sarif files have to be added to the same "global" corresponding arrays in the
|
||||||
|
# new single "runs" object.
|
||||||
|
#
|
||||||
|
# Finally, because LLVM produces codeflow regions with invalid "endLine" and
|
||||||
|
# "endColumn" entries (with value 0), those entries are removed from the
|
||||||
|
# regions in the results' codeFlows arrays.
|
||||||
|
|
||||||
|
jq -s '{
|
||||||
|
"$schema": first(.[]."$schema"),
|
||||||
|
"version": first(.[].version),
|
||||||
|
"runs": [{
|
||||||
|
"tool": {
|
||||||
|
"driver": (first(.[].runs[].tool.driver) | del(.rules)) + {
|
||||||
|
"rules": reduce(.[].runs[].tool.driver.rules) as $obj ([]; . + $obj) | unique
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"artifacts": reduce(.[].runs[].artifacts) as $obj ([]; . + $obj) | unique,
|
||||||
|
"results": (reduce(.[].runs[].results) as $obj ([]; . + $obj))
|
||||||
|
| del(
|
||||||
|
.[].codeFlows[].threadFlows[].locations[].location.physicalLocation.region.endLine,
|
||||||
|
.[].codeFlows[].threadFlows[].locations[].location.physicalLocation.region.endColumn
|
||||||
|
| select(. == 0)
|
||||||
|
)
|
||||||
|
}]
|
||||||
|
}' *.sarif > merged.sarif
|
||||||
|
popd
|
||||||
|
|
||||||
|
- name: Upload SARIF report files 📦
|
||||||
|
uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||||
|
with:
|
||||||
|
sarif_file: "${{ github.workspace }}/analytics/merged.sarif"
|
||||||
|
category: 'clang-analyze (macOS Apple Silicon)'
|
||||||
428
obs-studio/.github/workflows/build-project.yaml
vendored
Normal file
428
obs-studio/.github/workflows/build-project.yaml
vendored
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
name: Build Project
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
jobs:
|
||||||
|
check-event:
|
||||||
|
name: Event Data 🔎
|
||||||
|
runs-on: ubuntu-26.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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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 ${{ github.event.number }} --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: macOS 🍏
|
||||||
|
runs-on: macos-26
|
||||||
|
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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 26.5'
|
||||||
|
sudo xcode-select --switch /Applications/Xcode_26.5.app/Contents/Developer
|
||||||
|
print '::endgroup::'
|
||||||
|
|
||||||
|
print '::group::Clean Homebrew Environment'
|
||||||
|
local -a unwanted_formulas=()
|
||||||
|
local -a remove_formulas=()
|
||||||
|
for formula (${unwanted_formulas}) {
|
||||||
|
if [[ -d ${HOMEBREW_PREFIX}/Cellar/${formula} ]] remove_formulas+=(${formula})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( #remove_formulas )) brew uninstall --ignore-dependencies ${remove_formulas}
|
||||||
|
print '::endgroup::'
|
||||||
|
|
||||||
|
local -A arch_names=(x86_64 intel arm64 apple)
|
||||||
|
print "cpuName=${arch_names[${{ matrix.target }}]}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
local xcode_cas_path="${HOME}/Library/Developer/Xcode/DerivedData/CompilationCache.noindex"
|
||||||
|
|
||||||
|
if ! [[ -d ${xcode_cas_path} ]] mkdir -p ${xcode_cas_path}
|
||||||
|
|
||||||
|
print "xcodeCasPath=${xcode_cas_path}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
|
id: xcode-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.setup.outputs.xcodeCasPath }}
|
||||||
|
key: ${{ runner.os }}-xcode-${{ matrix.target }}-${{ needs.check-event.outputs.config }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-xcode-${{ 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 }}
|
||||||
|
XCODE_CAS_PATH: ${{ steps.setup.outputs.xcodeCasPath }}
|
||||||
|
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 }}
|
||||||
|
provisioningProfileUUID: ${{ steps.codesign.outputs.provisioningProfileUUID }}
|
||||||
|
|
||||||
|
- name: Package OBS Studio 📀
|
||||||
|
uses: ./.github/actions/package-obs
|
||||||
|
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||||
|
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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||||
|
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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
|
if: github.event_name != 'pull_request' && steps.xcode-cache.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
path: ${{ steps.setup.outputs.xcodeCasPath }}
|
||||||
|
key: ${{ runner.os }}-xcode-${{ matrix.target }}-${{ needs.check-event.outputs.config }}
|
||||||
|
|
||||||
|
ubuntu-build:
|
||||||
|
name: Ubuntu 🐧
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-26.04]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
needs: check-event
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
|
id: ccache-cache
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/.ccache
|
||||||
|
key: ${{ runner.os }}-${{ matrix.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ matrix.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
|
||||||
|
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||||
|
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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||||
|
with:
|
||||||
|
name: obs-studio-${{ matrix.os }}-sources-${{ needs.check-event.outputs.commitHash }}
|
||||||
|
path: ${{ github.workspace }}/build_ubuntu/obs-studio-*-sources.*
|
||||||
|
|
||||||
|
- name: Upload Artifacts 📡
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||||
|
with:
|
||||||
|
name: obs-studio-${{ matrix.os }}-x86_64-${{ needs.check-event.outputs.commitHash }}
|
||||||
|
path: ${{ github.workspace }}/build_ubuntu/obs-studio-*-x86_64-ubuntu-gnu.*
|
||||||
|
|
||||||
|
- name: Upload Debug Symbol Artifacts 🪲
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||||
|
with:
|
||||||
|
name: obs-studio-${{ matrix.os }}-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym
|
||||||
|
path: ${{ github.workspace }}/build_ubuntu/obs-studio-*-x86_64-ubuntu-gnu-dbgsym.ddeb
|
||||||
|
|
||||||
|
- uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
|
if: github.event_name != 'pull_request' && steps.ccache-cache.outputs.cache-hit != 'true'
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/.ccache
|
||||||
|
key: ${{ runner.os }}-${{ matrix.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }}
|
||||||
|
|
||||||
|
flatpak-build:
|
||||||
|
name: Flatpak 📦
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
needs: check-event
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
FLATPAK_BUILD_SHARE_PATH: flatpak_app/files/share
|
||||||
|
container:
|
||||||
|
image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08
|
||||||
|
options: --privileged
|
||||||
|
volumes:
|
||||||
|
- /usr/local/lib/android:/to_clean/android
|
||||||
|
- /opt/hostedtoolcache/CodeQL:/to_clean/codeql
|
||||||
|
- /usr/local/.ghcup:/to_clean/ghcup
|
||||||
|
- /opt/hostedtoolcache/Python:/to_clean/python
|
||||||
|
- /usr/share/swift:/to_clean/swift
|
||||||
|
- /usr/share/dotnet:/to_clean/dotnet
|
||||||
|
steps:
|
||||||
|
- name: Prepare build space
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
: Prepare build space
|
||||||
|
|
||||||
|
echo ::group::Available storage
|
||||||
|
df -h
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove Android stuff
|
||||||
|
rm -rf /to_clean/android/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove CodeQL stuff
|
||||||
|
rm -rf /to_clean/codeql/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove GHCup stuff
|
||||||
|
rm -rf /to_clean/ghcup/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove Python stuff
|
||||||
|
rm -rf /to_clean/python/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove Swift stuff
|
||||||
|
rm -rf /to_clean/swift/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove .NET stuff
|
||||||
|
rm -rf /to_clean/dotnet/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Available storage
|
||||||
|
df -h
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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}"
|
||||||
|
|
||||||
|
cache_key='flatpak-builder-${{ hashFiles('build-aux/com.obsproject.Studio.json') }}'
|
||||||
|
cache_ref='master'
|
||||||
|
read -r id key size unit created accessed <<< \
|
||||||
|
"$(gh cache list --ref "refs/heads/${cache_ref}" --key "${cache_key}-x86_64" | head -1)"
|
||||||
|
|
||||||
|
if [[ "${key}" ]]; then
|
||||||
|
echo "cacheHit=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "cacheHit=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "cacheKey=${cache_key}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Validate Flatpak manifest
|
||||||
|
uses: ./.github/actions/flatpak-builder-lint
|
||||||
|
with:
|
||||||
|
artifact: manifest
|
||||||
|
path: build-aux/com.obsproject.Studio.json
|
||||||
|
|
||||||
|
- name: Build Flatpak Manifest 🧾
|
||||||
|
uses: flatpak/flatpak-github-actions/flatpak-builder@401fe28a8384095fc1531b9d320b292f0ee45adb # v6.7
|
||||||
|
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 }}
|
||||||
|
mirror-screenshots-url: https://dl.flathub.org/media
|
||||||
|
|
||||||
|
- name: Validate AppStream
|
||||||
|
uses: ./.github/actions/flatpak-builder-lint
|
||||||
|
with:
|
||||||
|
artifact: appstream
|
||||||
|
path: ${{ env.FLATPAK_BUILD_SHARE_PATH }}/metainfo/com.obsproject.Studio.metainfo.xml
|
||||||
|
|
||||||
|
- name: Verify Icon and Metadata in app-info
|
||||||
|
working-directory: ${{ env.FLATPAK_BUILD_SHARE_PATH }}
|
||||||
|
run: |
|
||||||
|
: Verify Icon and Metadata in app-info
|
||||||
|
test -f app-info/icons/flatpak/128x128/com.obsproject.Studio.png || { echo "::error::Missing 128x128 icon in app-info"; exit 1; }
|
||||||
|
test -f app-info/xmls/com.obsproject.Studio.xml.gz || { echo "::error::Missing com.obsproject.Studio.xml.gz in app-info"; exit 1; }
|
||||||
|
|
||||||
|
- name: Validate build directory
|
||||||
|
uses: ./.github/actions/flatpak-builder-lint
|
||||||
|
with:
|
||||||
|
artifact: builddir
|
||||||
|
path: flatpak_app
|
||||||
|
|
||||||
|
- name: Validate repository
|
||||||
|
uses: ./.github/actions/flatpak-builder-lint
|
||||||
|
with:
|
||||||
|
artifact: repo
|
||||||
|
path: repo
|
||||||
|
|
||||||
|
windows-build:
|
||||||
|
name: Windows 🪟
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
architecture: [x64, arm64]
|
||||||
|
runs-on: windows-2025-vs2026
|
||||||
|
needs: check-event
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: pwsh
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- 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: ${{ matrix.architecture }}
|
||||||
|
config: ${{ needs.check-event.outputs.config }}
|
||||||
|
|
||||||
|
- name: Package OBS Studio 📀
|
||||||
|
uses: ./.github/actions/package-obs
|
||||||
|
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.architecture }}
|
||||||
|
config: ${{ needs.check-event.outputs.config }}
|
||||||
|
package: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||||
|
|
||||||
|
- name: Upload Artifacts 📡
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
if: ${{ fromJSON(needs.check-event.outputs.package) }}
|
||||||
|
with:
|
||||||
|
name: obs-studio-windows-${{ matrix.architecture }}-${{ needs.check-event.outputs.commitHash }}
|
||||||
|
path: ${{ github.workspace }}/build_${{ matrix.architecture }}/obs-studio-*-windows-${{ matrix.architecture }}.zip
|
||||||
63
obs-studio/.github/workflows/check-format.yaml
vendored
Normal file
63
obs-studio/.github/workflows/check-format.yaml
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
name: Check Code Formatting 🛠️
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
jobs:
|
||||||
|
clang-format:
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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-26
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: swift-format Check 🔥
|
||||||
|
id: swift-format
|
||||||
|
uses: ./.github/actions/run-swift-format
|
||||||
|
with:
|
||||||
|
failCondition: error
|
||||||
|
|
||||||
|
gersemi:
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: gersemi Check 🎛️
|
||||||
|
id: gersemi
|
||||||
|
uses: ./.github/actions/run-gersemi
|
||||||
|
with:
|
||||||
|
failCondition: error
|
||||||
|
|
||||||
|
flatpak-validator:
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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-26.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Qt XML Check 🖼️
|
||||||
|
id: qt-xml-check
|
||||||
|
uses: ./.github/actions/qt-xml-validator
|
||||||
|
with:
|
||||||
|
failCondition: error
|
||||||
151
obs-studio/.github/workflows/dispatch.yaml
vendored
Normal file
151
obs-studio/.github/workflows/dispatch.yaml
vendored
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
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
|
||||||
|
- patches
|
||||||
|
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
|
||||||
|
channel:
|
||||||
|
description: Channel to use when generating Windows update files
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
jobs:
|
||||||
|
services-validation:
|
||||||
|
name: Validate Services 🕵️
|
||||||
|
if: github.repository_owner == 'obsproject' && inputs.job == 'services'
|
||||||
|
runs-on: macos-26
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- name: Check for Defunct Services 📉
|
||||||
|
uses: ./.github/actions/services-validator
|
||||||
|
with:
|
||||||
|
repositorySecret: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
checkApiSecret: ${{ secrets.CHECK_SERVERS_API_KEY }}
|
||||||
|
checkApiServers: ${{ secrets.CHECK_SERVERS_LIST }}
|
||||||
|
runSchemaChecks: true
|
||||||
|
runServiceChecks: true
|
||||||
|
createPullRequest: true
|
||||||
|
|
||||||
|
download-language-files:
|
||||||
|
name: Download Language Files 🌐
|
||||||
|
if: github.repository_owner == 'obsproject' && inputs.job == 'translations'
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
env:
|
||||||
|
CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
token: ${{ secrets.CROWDIN_SYNC_GITHUB_PAT }}
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: obsproject/obs-crowdin-sync/download@4b488c7ced03aa109d9f12529bd91c26c54b3e89
|
||||||
|
|
||||||
|
steam-upload:
|
||||||
|
name: Upload Steam Builds 🚂
|
||||||
|
if: github.repository_owner == 'obsproject' && inputs.job == 'steam'
|
||||||
|
runs-on: macos-26
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- 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-26.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- uses: ./.github/actions/generate-docs
|
||||||
|
|
||||||
|
update-documentation-cloudflare:
|
||||||
|
name: Update Documentation for Cloudflare ☁️
|
||||||
|
if: github.repository_owner == 'obsproject' && inputs.job == 'documentation'
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- uses: ./.github/actions/generate-docs
|
||||||
|
with:
|
||||||
|
disableLinkExtensions: true
|
||||||
|
|
||||||
|
deploy-documentation:
|
||||||
|
name: Deploy Documentation to Cloudflare ☁️
|
||||||
|
if: github.repository_owner == 'obsproject' && inputs.job == 'documentation'
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
needs: update-documentation-cloudflare
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
environment:
|
||||||
|
name: cf-pages-deploy
|
||||||
|
steps:
|
||||||
|
- name: Get Commit Information 🆔
|
||||||
|
id: setup
|
||||||
|
run: |
|
||||||
|
: Get Commit Hash 🆔
|
||||||
|
echo "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: OBS Studio Docs (No Extensions) ${{ steps.setup.outputs.commitHash }}
|
||||||
|
path: docs
|
||||||
|
|
||||||
|
- name: Publish to Live Page
|
||||||
|
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
|
||||||
|
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 }}'
|
||||||
|
|
||||||
|
windows-patches:
|
||||||
|
name: Create Windows Patches 🩹
|
||||||
|
if: github.repository_owner == 'obsproject' && inputs.job == 'patches'
|
||||||
|
runs-on: windows-2025-vs2026
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- uses: ./.github/actions/windows-patches
|
||||||
|
with:
|
||||||
|
tagName: ${{ inputs.ref }}
|
||||||
|
workflowSecret: ${{ github.token }}
|
||||||
|
channel: ${{ inputs.channel }}
|
||||||
|
gcsAccessKeyId: ${{ secrets.GCS_ACCESS_KEY_ID }}
|
||||||
|
gcsAccessKeySecret: ${{ secrets.GCS_ACCESS_KEY_SECRET }}
|
||||||
94
obs-studio/.github/workflows/pr-pull.yaml
vendored
Normal file
94
obs-studio/.github/workflows/pr-pull.yaml
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
name: Pull
|
||||||
|
run-name: ${{ github.event.pull_request.title }} pull request run 🚀
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
branches: [master]
|
||||||
|
types: [ opened, synchronize, reopened ]
|
||||||
|
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: Format 🔍
|
||||||
|
uses: ./.github/workflows/check-format.yaml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
build-project:
|
||||||
|
name: Build 🧱
|
||||||
|
uses: ./.github/workflows/build-project.yaml
|
||||||
|
secrets: inherit
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
compatibility-validation:
|
||||||
|
name: Validate Compatibility 🕵️
|
||||||
|
if: github.base_ref == 'master'
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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 🕵️
|
||||||
|
if: github.base_ref == 'master'
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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-26.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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)
|
||||||
334
obs-studio/.github/workflows/publish.yaml
vendored
Normal file
334
obs-studio/.github/workflows/publish.yaml
vendored
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
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-26.04
|
||||||
|
outputs:
|
||||||
|
validTag: ${{ steps.check.outputs.validTag }}
|
||||||
|
flatpakMatrix: ${{ steps.check.outputs.flatpakMatrix }}
|
||||||
|
updateChannel: ${{ steps.check.outputs.updateChannel }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
fetch-depth: 0
|
||||||
|
set-safe-directory: ${{ env.GITHUB_WORKSPACE }}
|
||||||
|
|
||||||
|
- name: Check Release Tag ☑️
|
||||||
|
id: check
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
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]) )
|
||||||
|
lastPreRelease="$(gh release list --exclude-drafts --limit 10 --json "publishedAt,tagName,isPrerelease" \
|
||||||
|
--jq '[.[] | select(.isPrerelease == true)] | first | .tagName')"
|
||||||
|
currentRelease="${GITHUB_REF_NAME}"
|
||||||
|
isPreReleaseAhead=false
|
||||||
|
|
||||||
|
printf '%s\n%s\n' "${currentRelease}" "${lastPreRelease}" | sort --version-sort --reverse --check=quiet &&
|
||||||
|
isPreReleaseAhead=false || isPreReleaseAhead=true
|
||||||
|
|
||||||
|
# Edge case: Sort considers the non-suffixed version older than a suffixed one
|
||||||
|
if ${isPreReleaseAhead} && [[ "${currentRelease}" == "${lastPreRelease//-*}" ]]; then
|
||||||
|
isPreReleaseAhead=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 'validTag=true' >> $GITHUB_OUTPUT
|
||||||
|
if ! ${isPreReleaseAhead}; then
|
||||||
|
echo 'flatpakMatrix=["beta", "stable"]' >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo 'flatpakMatrix=["stable"]' >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
echo 'updateChannel=stable' >> $GITHUB_OUTPUT
|
||||||
|
;;
|
||||||
|
+([0-9]).+([0-9]).+([0-9])-@(beta|rc)*([0-9]) )
|
||||||
|
echo 'validTag=true' >> $GITHUB_OUTPUT
|
||||||
|
echo 'flatpakMatrix=["beta"]' >> $GITHUB_OUTPUT
|
||||||
|
echo 'updateChannel=beta' >> $GITHUB_OUTPUT
|
||||||
|
;;
|
||||||
|
*) echo 'validTag=false' >> $GITHUB_OUTPUT ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
flatpak-publish:
|
||||||
|
name: Flathub 📦
|
||||||
|
needs: check-tag
|
||||||
|
if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag)
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
FLATPAK_BUILD_SHARE_PATH: flatpak_app/files/share
|
||||||
|
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 }}
|
||||||
|
container:
|
||||||
|
image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08
|
||||||
|
options: --privileged
|
||||||
|
volumes:
|
||||||
|
- /usr/local/lib/android:/to_clean/android
|
||||||
|
- /opt/hostedtoolcache/CodeQL:/to_clean/codeql
|
||||||
|
- /usr/local/.ghcup:/to_clean/ghcup
|
||||||
|
- /opt/hostedtoolcache/Python:/to_clean/python
|
||||||
|
- /usr/share/swift:/to_clean/swift
|
||||||
|
- /usr/share/dotnet:/to_clean/dotnet
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
branch: ${{ fromJSON(needs.check-tag.outputs.flatpakMatrix) }}
|
||||||
|
steps:
|
||||||
|
- name: Prepare build space
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
: Prepare build space
|
||||||
|
|
||||||
|
echo ::group::Available storage
|
||||||
|
df -h
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove Android stuff
|
||||||
|
rm -rf /to_clean/android/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove CodeQL stuff
|
||||||
|
rm -rf /to_clean/codeql/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove GHCup stuff
|
||||||
|
rm -rf /to_clean/ghcup/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove Python stuff
|
||||||
|
rm -rf /to_clean/python/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove Swift stuff
|
||||||
|
rm -rf /to_clean/swift/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Remove .NET stuff
|
||||||
|
rm -rf /to_clean/dotnet/*
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
echo ::group::Available storage
|
||||||
|
df -h
|
||||||
|
echo ::endgroup::
|
||||||
|
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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}"
|
||||||
|
|
||||||
|
cache_key='flatpak-builder-${{ hashFiles('build-aux/com.obsproject.Studio.json') }}'
|
||||||
|
cache_ref='master'
|
||||||
|
read -r id key size unit created accessed <<< \
|
||||||
|
"$(gh cache list --ref "refs/heads/${cache_ref}" --key "${cache_key}-x86_64" | head -1)"
|
||||||
|
|
||||||
|
if [[ "${key}" ]]; then
|
||||||
|
echo "cacheHit=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "cacheHit=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "cacheKey=${cache_key}" >> $GITHUB_OUTPUT
|
||||||
|
echo "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Validate Flatpak manifest
|
||||||
|
uses: ./.github/actions/flatpak-builder-lint
|
||||||
|
with:
|
||||||
|
artifact: manifest
|
||||||
|
path: build-aux/com.obsproject.Studio.json
|
||||||
|
|
||||||
|
- name: Build Flatpak Manifest
|
||||||
|
uses: flatpak/flatpak-github-actions/flatpak-builder@401fe28a8384095fc1531b9d320b292f0ee45adb # v6.7
|
||||||
|
with:
|
||||||
|
build-bundle: false
|
||||||
|
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/media
|
||||||
|
branch: ${{ matrix.branch }}
|
||||||
|
|
||||||
|
- name: Validate AppStream
|
||||||
|
uses: ./.github/actions/flatpak-builder-lint
|
||||||
|
with:
|
||||||
|
artifact: appstream
|
||||||
|
path: ${{ env.FLATPAK_BUILD_SHARE_PATH }}/metainfo/com.obsproject.Studio.metainfo.xml
|
||||||
|
|
||||||
|
- name: Verify Icon and Metadata in app-info
|
||||||
|
working-directory: ${{ env.FLATPAK_BUILD_SHARE_PATH }}
|
||||||
|
run: |
|
||||||
|
: Verify Icon and Metadata in app-info
|
||||||
|
test -f app-info/icons/flatpak/128x128/com.obsproject.Studio.png || { echo "::error::Missing 128x128 icon in app-info"; exit 1; }
|
||||||
|
test -f app-info/xmls/com.obsproject.Studio.xml.gz || { echo "::error::Missing com.obsproject.Studio.xml.gz in app-info"; exit 1; }
|
||||||
|
|
||||||
|
- name: Validate build directory
|
||||||
|
uses: ./.github/actions/flatpak-builder-lint
|
||||||
|
with:
|
||||||
|
artifact: builddir
|
||||||
|
path: flatpak_app
|
||||||
|
|
||||||
|
- name: Validate repository
|
||||||
|
uses: ./.github/actions/flatpak-builder-lint
|
||||||
|
with:
|
||||||
|
artifact: repo
|
||||||
|
path: repo
|
||||||
|
|
||||||
|
- name: Publish to Flathub Beta
|
||||||
|
uses: flatpak/flatpak-github-actions/flat-manager@401fe28a8384095fc1531b9d320b292f0ee45adb # v6.7
|
||||||
|
if: ${{ matrix.branch == 'beta' }}
|
||||||
|
with:
|
||||||
|
flat-manager-url: https://hub.flathub.org/
|
||||||
|
repository: beta
|
||||||
|
token: ${{ secrets.FLATHUB_BETA_TOKEN }}
|
||||||
|
build-log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
|
- name: Publish to Flathub
|
||||||
|
uses: flatpak/flatpak-github-actions/flat-manager@401fe28a8384095fc1531b9d320b292f0ee45adb # v6.7
|
||||||
|
if: ${{ matrix.branch == 'stable' }}
|
||||||
|
with:
|
||||||
|
flat-manager-url: https://hub.flathub.org/
|
||||||
|
repository: stable
|
||||||
|
token: ${{ secrets.FLATHUB_TOKEN }}
|
||||||
|
build-log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
|
steam-upload:
|
||||||
|
name: Upload Steam Builds 🚂
|
||||||
|
needs: check-tag
|
||||||
|
if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag)
|
||||||
|
runs-on: macos-26
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- uses: ./.github/actions/steam-upload
|
||||||
|
with:
|
||||||
|
steamSecret: ${{ secrets.STEAM_SHARED_SECRET }}
|
||||||
|
steamUser: ${{ secrets.STEAM_USER }}
|
||||||
|
steamPassword: ${{ secrets.STEAM_PASSWORD }}
|
||||||
|
workflowSecret: ${{ github.token }}
|
||||||
|
tagName: ${{ github.ref_name }}
|
||||||
|
preview: false
|
||||||
|
|
||||||
|
windows-patches:
|
||||||
|
name: Create Windows Patches 🩹
|
||||||
|
needs: check-tag
|
||||||
|
if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag)
|
||||||
|
runs-on: windows-2025-vs2026
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- uses: ./.github/actions/windows-patches
|
||||||
|
with:
|
||||||
|
tagName: ${{ github.ref_name }}
|
||||||
|
workflowSecret: ${{ github.token }}
|
||||||
|
channel: ${{ needs.check-tag.outputs.updateChannel }}
|
||||||
|
gcsAccessKeyId: ${{ secrets.GCS_ACCESS_KEY_ID }}
|
||||||
|
gcsAccessKeySecret: ${{ secrets.GCS_ACCESS_KEY_SECRET }}
|
||||||
|
|
||||||
|
create-appcast:
|
||||||
|
name: Create Sparkle Appcast 🎙️
|
||||||
|
if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag)
|
||||||
|
runs-on: macos-26
|
||||||
|
needs: check-tag
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [arm64, x86_64]
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.ref }}
|
||||||
|
|
||||||
|
- name: Set Up Environment 🔧
|
||||||
|
id: setup
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
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
|
||||||
|
|
||||||
|
local workflowRunId="$(gh run list --branch "${GITHUB_REF_NAME}" --event push --limit 1 --json "databaseId" --jq ".[].databaseId")"
|
||||||
|
print "workflowRunId=${workflowRunId}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Download Artifact 📥
|
||||||
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: obs-studio-macos-${{ matrix.target }}-${{ steps.setup.outputs.commitHash }}
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run-id: ${{ steps.setup.outputs.workflowRunId }}
|
||||||
|
|
||||||
|
- 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: 5
|
||||||
|
urlPrefix: 'https://cdn-fastly.obsproject.com/downloads'
|
||||||
|
customTitle: 'OBS Studio'
|
||||||
|
customLink: 'https://obsproject.com/'
|
||||||
|
|
||||||
|
- name: Upload Artifacts 📡
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: macos-sparkle-update-${{ matrix.target }}
|
||||||
|
path: ${{ github.workspace }}/output
|
||||||
|
|
||||||
|
merge-appcasts:
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
needs: create-appcast
|
||||||
|
steps:
|
||||||
|
- name: Merge Appcasts
|
||||||
|
uses: actions/upload-artifact/merge@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: macos-sparkle-update
|
||||||
|
pattern: macos-sparkle-update-*
|
||||||
|
delete-merged: true
|
||||||
265
obs-studio/.github/workflows/push.yaml
vendored
Normal file
265
obs-studio/.github/workflows/push.yaml
vendored
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
name: Push
|
||||||
|
run-name: ${{ github.ref_name }} push run 🚀
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- 'release/**'
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
jobs:
|
||||||
|
check-format:
|
||||||
|
name: Format 🔍
|
||||||
|
if: github.ref_name == 'master'
|
||||||
|
uses: ./.github/workflows/check-format.yaml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
build-project:
|
||||||
|
name: Build 🧱
|
||||||
|
uses: ./.github/workflows/build-project.yaml
|
||||||
|
secrets: inherit
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
compatibility-validation:
|
||||||
|
name: Validate Compatibility 🕵️
|
||||||
|
if: github.ref_name == 'master'
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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 Services 🕵️
|
||||||
|
if: github.ref_name == 'master'
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
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
|
||||||
|
|
||||||
|
update-documentation:
|
||||||
|
name: Update Documentation 📖
|
||||||
|
if: github.repository_owner == 'obsproject' && (github.ref_name == 'master' || github.ref_type == 'tag')
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Check for Changed Files ✅
|
||||||
|
if: github.ref_type != '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-26.04
|
||||||
|
needs: update-documentation
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
environment:
|
||||||
|
name: cf-pages-deploy
|
||||||
|
steps:
|
||||||
|
- name: Get Commit Information 🆔
|
||||||
|
id: setup
|
||||||
|
run: |
|
||||||
|
: Get Commit Hash 🆔
|
||||||
|
echo "commitHash=${GITHUB_SHA:0:9}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
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@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
|
||||||
|
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 }}'
|
||||||
|
|
||||||
|
sign-windows-build:
|
||||||
|
name: Windows Signing ✍️
|
||||||
|
uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@ac19ea663375cd02997be92b7d8ff6dd89a511ad
|
||||||
|
if: github.repository_owner == 'obsproject' && github.ref_type == 'tag'
|
||||||
|
needs: build-project
|
||||||
|
permissions:
|
||||||
|
contents: 'read'
|
||||||
|
id-token: 'write'
|
||||||
|
attestations: 'write'
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
create-release:
|
||||||
|
name: Create Release 🛫
|
||||||
|
if: github.ref_type == 'tag'
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
needs: [build-project, sign-windows-build]
|
||||||
|
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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
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_2604_x86_64_artifact_name="obs-studio-ubuntu-26.04-x86_64-${commit_hash}"
|
||||||
|
ubuntu_2604_x86_64_debug_name="obs-studio-ubuntu-26.04-x86_64-${commit_hash}-dbgsym"
|
||||||
|
ubuntu_2604_sources_name="obs-studio-ubuntu-26.04-sources-${commit_hash}"
|
||||||
|
windows_x64_artifact_name="obs-studio-windows-x64-${{ steps.check.outputs.version }}-signed"
|
||||||
|
windows_x64_installer_name="obs-studio-windows-x64-${{ steps.check.outputs.version }}-installer"
|
||||||
|
windows_x64_debug_name="obs-studio-windows-x64-${{ steps.check.outputs.version }}-pdbs"
|
||||||
|
windows_arm64_artifact_name="obs-studio-windows-arm64-${{ steps.check.outputs.version }}-signed"
|
||||||
|
windows_arm64_debug_name="obs-studio-windows-arm64-${{ steps.check.outputs.version }}-pdbs"
|
||||||
|
|
||||||
|
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_2604_x86_64_artifact_name}/"obs-studio-*-x86_64-ubuntu-gnu.deb \
|
||||||
|
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-26.04-x86_64.deb
|
||||||
|
mv -v "${ubuntu_2604_x86_64_debug_name}/"obs-studio-*-x86_64-ubuntu-gnu-dbgsym.ddeb \
|
||||||
|
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-26.04-x86_64-dbsym.ddeb
|
||||||
|
mv -v "${ubuntu_2604_sources_name}/"obs-studio-*-sources.tar.gz \
|
||||||
|
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Sources.tar.gz
|
||||||
|
mv -v "${windows_x64_artifact_name}/"OBS-Studio-*.zip \
|
||||||
|
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64.zip
|
||||||
|
mv -v "${windows_x64_installer_name}/"OBS-Studio-*.exe \
|
||||||
|
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64-Installer.exe
|
||||||
|
mv -v "${windows_x64_debug_name}/"OBS-Studio-*-pdbs.zip \
|
||||||
|
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64-PDBs.zip
|
||||||
|
mv -v "${windows_arm64_artifact_name}/"OBS-Studio-*.zip \
|
||||||
|
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-arm64.zip
|
||||||
|
mv -v "${windows_arm64_debug_name}/"OBS-Studio-*-pdbs.zip \
|
||||||
|
"${root_dir}"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-arm64-PDBs.zip
|
||||||
|
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|*.tar.gz|*.exe|*.zip); 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@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
||||||
|
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
|
||||||
|
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Sources.tar.gz
|
||||||
|
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64.zip
|
||||||
|
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64-Installer.exe
|
||||||
|
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64-PDBs.zip
|
||||||
|
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-arm64.zip
|
||||||
|
${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-arm64-PDBs.zip
|
||||||
205
obs-studio/.github/workflows/scheduled.yaml
vendored
Normal file
205
obs-studio/.github/workflows/scheduled.yaml
vendored
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
name: Scheduled
|
||||||
|
run-name: Scheduled Repository Actions ⏰
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
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: macos-26
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Check for Defunct Services 📉
|
||||||
|
uses: ./.github/actions/services-validator
|
||||||
|
with:
|
||||||
|
repositorySecret: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
checkApiSecret: ${{ secrets.CHECK_SERVERS_API_KEY }}
|
||||||
|
checkApiServers: ${{ secrets.CHECK_SERVERS_LIST }}
|
||||||
|
runSchemaChecks: false
|
||||||
|
runServiceChecks: true
|
||||||
|
createPullRequest: true
|
||||||
|
|
||||||
|
cache-cleanup:
|
||||||
|
name: Cache Cleanup 🧹
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
permissions:
|
||||||
|
actions: write
|
||||||
|
steps:
|
||||||
|
- name: Remove Stale Ccache Caches
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
: Remove Stale Ccache Caches
|
||||||
|
|
||||||
|
# The jq expressions below use multiple 'select' calls to filter
|
||||||
|
# each item in the array with the 'actions_caches' key.
|
||||||
|
# First it only selects objects whose 'ref' element has the value
|
||||||
|
# 'refs/heads/master', of those objects only those whose 'key'
|
||||||
|
# value matches the specified expression, before finally only
|
||||||
|
# selecting the 'id' and 'key' elements for a new object.
|
||||||
|
# The final 'join' command combines both elements with a semicolon
|
||||||
|
# into a raw string which can then be parsed directly.
|
||||||
|
|
||||||
|
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|xcode)-*")) | {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 🧱
|
||||||
|
uses: ./.github/workflows/build-project.yaml
|
||||||
|
needs: cache-cleanup
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
analyze-project:
|
||||||
|
name: Analyze 🔬
|
||||||
|
uses: ./.github/workflows/analyze-project.yaml
|
||||||
|
needs: cache-cleanup
|
||||||
|
secrets: inherit
|
||||||
|
permissions:
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
upload-language-files:
|
||||||
|
name: Upload Language Files 🌐
|
||||||
|
if: github.repository_owner == 'obsproject' && github.ref_name == 'master'
|
||||||
|
runs-on: ubuntu-26.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Check Nightly Runs ☑️
|
||||||
|
id: nightly-checks
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
: Check Nightly Runs ☑️
|
||||||
|
if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
|
||||||
|
|
||||||
|
# This 'gh' command retrieves the last 2 runs of the workflow defined
|
||||||
|
# by 'scheduled.yaml' and retrieves just the 'headSha' value of the
|
||||||
|
# JSON response payload.
|
||||||
|
#
|
||||||
|
# As this job runs in context of the same workflow, the first element
|
||||||
|
# of the workflow list will be the currently active run.
|
||||||
|
#
|
||||||
|
# The jq expression then selects the 'headSha' element of the second
|
||||||
|
# element in the array, which is the SHA-1 hash of the commit used
|
||||||
|
# for the immediately prior run of this workflow.
|
||||||
|
|
||||||
|
last_nightly=$(gh run list --workflow scheduled.yaml --limit 2 --json headSha --jq '.[1].headSha')
|
||||||
|
|
||||||
|
if [[ "${GITHUB_SHA}" == "${last_nightly}" ]]; then
|
||||||
|
echo "passed=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "passed=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "lastNightly=${last_nightly}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check for Changed Files ✅
|
||||||
|
if: fromJSON(steps.nightly-checks.outputs.passed)
|
||||||
|
uses: ./.github/actions/check-changes
|
||||||
|
id: checks
|
||||||
|
with:
|
||||||
|
baseRef: ${{ steps.nightly-checks.outputs.lastNightly }}
|
||||||
|
checkGlob: '**/en-US.ini'
|
||||||
|
|
||||||
|
- name: Upload US English Language Files 🇺🇸
|
||||||
|
if: steps.checks.outcome == 'success' && fromJSON(steps.checks.outputs.hasChangedFiles)
|
||||||
|
uses: obsproject/obs-crowdin-sync/upload@4b488c7ced03aa109d9f12529bd91c26c54b3e89
|
||||||
|
env:
|
||||||
|
CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }}
|
||||||
|
GITHUB_EVENT_BEFORE: ${{ steps.nightly-checks.outputs.lastNightly }}
|
||||||
|
|
||||||
|
steam-upload:
|
||||||
|
name: Upload Steam Builds 🚂
|
||||||
|
needs: [build-project]
|
||||||
|
if: github.repository_owner == 'obsproject'
|
||||||
|
runs-on: macos-26
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: zsh --no-rcs --errexit --pipefail {0}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- name: Check Nightly Runs ☑️
|
||||||
|
id: checks
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
: Check Nightly Runs ☑️
|
||||||
|
if (( ${+RUNNER_DEBUG} )) setopt XTRACE
|
||||||
|
|
||||||
|
# This 'gh' command retrieves the last 2 runs of the workflow defined
|
||||||
|
# by 'scheduled.yaml' and retrieves just the 'headSha' value of the
|
||||||
|
# JSON response payload.
|
||||||
|
#
|
||||||
|
# As this job runs in context of the same workflow, the first element
|
||||||
|
# of the workflow list will be the currently active run.
|
||||||
|
#
|
||||||
|
# The jq expression then selects the 'headSha' element of the second
|
||||||
|
# element in the array, which is the SHA-1 hash of the commit used
|
||||||
|
# for the immediately prior run of this workflow.
|
||||||
|
|
||||||
|
local last_nightly=$(gh run list --workflow scheduled.yaml --limit 2 --json headSha --jq '.[1].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' }}
|
||||||
84
obs-studio/.github/workflows/sign-windows.yaml
vendored
Normal file
84
obs-studio/.github/workflows/sign-windows.yaml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
name: Sign Windows Project
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
jobs:
|
||||||
|
create-windows-update:
|
||||||
|
name: Sign Windows Build 🥩
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
architecture: [x64, arm64]
|
||||||
|
runs-on: windows-2025-vs2026
|
||||||
|
environment:
|
||||||
|
name: bouf
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: pwsh
|
||||||
|
steps:
|
||||||
|
- name: Parse JWT
|
||||||
|
id: jwt
|
||||||
|
run: |
|
||||||
|
$token = ConvertTo-SecureString -String ${env:ACTIONS_ID_TOKEN_REQUEST_TOKEN} -AsPlainText
|
||||||
|
$jwt = Invoke-WebRequest -Uri "${env:ACTIONS_ID_TOKEN_REQUEST_URL}&audience=ignore" -Authentication Bearer -Token $token
|
||||||
|
$claim_b64 = (($jwt.Content | ConvertFrom-Json -AsHashtable).value -split '\.')[1]
|
||||||
|
$mod = $claim_b64.Length % 4
|
||||||
|
if ($mod -gt 0) {$claim_b64 += '=' * (4 - $mod)}
|
||||||
|
$claim = [Text.Encoding]::Utf8.GetString([Convert]::FromBase64String($claim_b64)) | ConvertFrom-Json -AsHashtable
|
||||||
|
$sha = ${claim}.job_workflow_sha
|
||||||
|
Write-Output "Workflow SHA: ${sha}"
|
||||||
|
"workflow_sha=${sha}" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
path: "repo"
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ steps.jwt.outputs.workflow_sha }}
|
||||||
|
|
||||||
|
- name: Set Up Environment 🔧
|
||||||
|
id: setup
|
||||||
|
run: |
|
||||||
|
$channel = if ($env:GITHUB_REF_NAME -match "(beta|rc)") { "beta" } else { "stable" }
|
||||||
|
$shortHash = $env:GITHUB_SHA.Substring(0,9)
|
||||||
|
"channel=${channel}" >> $env:GITHUB_OUTPUT
|
||||||
|
"commitHash=${shortHash}" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Download Artifact 📥
|
||||||
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: obs-studio-windows-${{ matrix.architecture }}-${{ steps.setup.outputs.commitHash }}
|
||||||
|
path: ${{ github.workspace }}/build
|
||||||
|
|
||||||
|
- name: Run bouf 🥩
|
||||||
|
uses: ./repo/.github/actions/windows-signing
|
||||||
|
with:
|
||||||
|
gcpWorkloadIdentityProvider: ${{ secrets.GCP_IDENTITY_POOL }}
|
||||||
|
gcpServiceAccountName: ${{ secrets.GCP_SERVICE_ACCOUNT_NAME }}
|
||||||
|
version: ${{ github.ref_name }}
|
||||||
|
channel: ${{ steps.setup.outputs.channel }}
|
||||||
|
architecture: ${{ matrix.architecture }}
|
||||||
|
|
||||||
|
- name: Generate artifact attestation
|
||||||
|
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||||
|
with:
|
||||||
|
subject-path: ${{ github.workspace }}/output/*-${{ matrix.architecture }}.zip
|
||||||
|
|
||||||
|
- name: Upload Signed Build
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: obs-studio-windows-${{ matrix.architecture }}-${{ github.ref_name }}-signed
|
||||||
|
compression-level: 0
|
||||||
|
path: ${{ github.workspace }}/output/*-${{ matrix.architecture }}.zip
|
||||||
|
|
||||||
|
- name: Upload PDBs
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: obs-studio-windows-${{ matrix.architecture }}-${{ github.ref_name }}-pdbs
|
||||||
|
compression-level: 0
|
||||||
|
path: ${{ github.workspace }}/output/*-pdbs.zip
|
||||||
|
|
||||||
|
- name: Upload Installer
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
if: matrix.architecture == 'x64'
|
||||||
|
with:
|
||||||
|
name: obs-studio-windows-${{ matrix.architecture }}-${{ github.ref_name }}-installer
|
||||||
|
compression-level: 0
|
||||||
|
path: ${{ github.workspace }}/output/*.exe
|
||||||
51
obs-studio/.gitignore
vendored
Normal file
51
obs-studio/.gitignore
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Exclude everything
|
||||||
|
/*
|
||||||
|
|
||||||
|
# Except for default project files
|
||||||
|
!/.github
|
||||||
|
!/build-aux
|
||||||
|
!/cmake
|
||||||
|
!/deps
|
||||||
|
!/docs
|
||||||
|
!/frontend
|
||||||
|
!/libobs*
|
||||||
|
!/plugins
|
||||||
|
!/shared
|
||||||
|
!/test
|
||||||
|
!.cirrus.xml
|
||||||
|
!.clang-format
|
||||||
|
!.editorconfig
|
||||||
|
!.gersemirc
|
||||||
|
!.git-blame-ignore-devs
|
||||||
|
!.gitmodules
|
||||||
|
!.gitignore
|
||||||
|
!.mailmap
|
||||||
|
!.swift-format
|
||||||
|
!AUTHORS
|
||||||
|
!buildspec.json
|
||||||
|
!CMakeLists.txt
|
||||||
|
!CMakePresets.json
|
||||||
|
!COC.rst
|
||||||
|
!COMMITMENT
|
||||||
|
!CONTRIBUTING.md
|
||||||
|
!CODESTYLE.md
|
||||||
|
!COPYING
|
||||||
|
!INSTALL
|
||||||
|
!README.rst
|
||||||
|
|
||||||
|
# Exclude lock files
|
||||||
|
*.lock.json
|
||||||
|
|
||||||
|
# Exclude files generated by Sphinx in-tree
|
||||||
|
/docs/sphinx/_build/*
|
||||||
|
!/docs/sphinx/_build/.gitignore
|
||||||
|
!/docs/sphinx/Makefile
|
||||||
|
|
||||||
|
# Exclude modified Flatpak files
|
||||||
|
build-aux/flatpak-github-action-modified-*
|
||||||
|
|
||||||
|
# Exclude macOS legacy resource forks
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Exclude CMake build number cache
|
||||||
|
/cmake/.CMakeBuildNumber
|
||||||
9
obs-studio/.gitmodules
vendored
Normal file
9
obs-studio/.gitmodules
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[submodule "plugins/win-dshow/libdshowcapture"]
|
||||||
|
path = deps/libdshowcapture/src
|
||||||
|
url = https://github.com/obsproject/libdshowcapture.git
|
||||||
|
[submodule "plugins/obs-browser"]
|
||||||
|
path = plugins/obs-browser
|
||||||
|
url = https://github.com/obsproject/obs-browser.git
|
||||||
|
[submodule "plugins/obs-websocket"]
|
||||||
|
path = plugins/obs-websocket
|
||||||
|
url = https://github.com/obsproject/obs-websocket.git
|
||||||
107
obs-studio/.mailmap
Normal file
107
obs-studio/.mailmap
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#
|
||||||
|
# Manually defined
|
||||||
|
#
|
||||||
|
Lain <lain@obsproject.com>
|
||||||
|
Lain <obs.jim@gmail.com>
|
||||||
|
Benjamin Klettbach <b.klettbach@gmail.com>
|
||||||
|
BtbN <btbn@btbn.de>
|
||||||
|
kc5nra <jrb@turrettech.com>
|
||||||
|
HomeWorld <homeworld@gmail.com> hwdro <pdarvaru@yahoo.com>
|
||||||
|
Michael Fabian 'Xaymar' Dirks <info@xaymar.com> <Xaymar@users.noreply.github.com>
|
||||||
|
Michael Fabian 'Xaymar' Dirks <info@xaymar.com> <michael.dirks@xaymar.com>
|
||||||
|
Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
||||||
|
Martell Malone <martellmalone@gmail.com>
|
||||||
|
Ford Smith <fordmsmith13@gmail.com> <xtreameprogram2@gmail.com>
|
||||||
|
Ford Smith <fordmsmith13@gmail.com>
|
||||||
|
Sebastian Beckmann <beckmann.sebastian@outlook.de>
|
||||||
|
Rodney <xlnedder@gmail.com>
|
||||||
|
Reboot <reboot@gmx.ch>
|
||||||
|
#
|
||||||
|
# Generated to prevent people with multiple names or emails appearing multiple times, for the manual changes made see below:
|
||||||
|
#
|
||||||
|
jpark37 <jpark37@users.noreply.github.com> <jpark37@gmail.com>
|
||||||
|
jpark37 <jpark37@users.noreply.github.com>
|
||||||
|
DDRBoxman <colin@recursivepenguin.com> <ddrboxman@Colins-MacBook-Pro.local>
|
||||||
|
DDRBoxman <colin@recursivepenguin.com>
|
||||||
|
R1CH <r1ch@r1ch.net> <r.stanway@gmail.com>
|
||||||
|
R1CH <r1ch@r1ch.net> <notr1ch@users.noreply.github.com>
|
||||||
|
R1CH <r1ch@r1ch.net>
|
||||||
|
WizardCM <matt@obsproject.com> <matt@wizardcm.com>
|
||||||
|
WizardCM <matt@obsproject.com> <git@wizardcm.com>
|
||||||
|
WizardCM <matt@obsproject.com>
|
||||||
|
Zachary Lund <admin@computerquip.com> <zachary.lund@streamlabs.com>
|
||||||
|
Zachary Lund <admin@computerquip.com> <Zachary L1und>
|
||||||
|
Zachary Lund <admin@computerquip.com>
|
||||||
|
pkv <pkv.stream@gmail.com> <pkv@obsproject.com>
|
||||||
|
pkv <pkv.stream@gmail.com>
|
||||||
|
PatTheMav <patthemav+github@gmail.com> <PatTheMav@users.noreply.github.com>
|
||||||
|
PatTheMav <patthemav+github@gmail.com>
|
||||||
|
brittneysclark <brittney.s.clark@intel.com> <brittney.s.clark@gmail.com>
|
||||||
|
brittneysclark <brittney.s.clark@intel.com>
|
||||||
|
craftwar <craftwar@users.noreply.github.com> <coo.ky@hotmail.com>
|
||||||
|
craftwar <craftwar@users.noreply.github.com>
|
||||||
|
SCG82 <scg082+github@gmail.com> <scg082@gmail.com>
|
||||||
|
SCG82 <scg082+github@gmail.com>
|
||||||
|
Ed Maste <emaste@FreeBSD.org> <emaste@freebsd.org>
|
||||||
|
Ed Maste <emaste@FreeBSD.org>
|
||||||
|
mntone <sd8@live.jp> <mntone@outlook,jp>
|
||||||
|
mntone <sd8@live.jp>
|
||||||
|
juvester <juvester@cs.helsinki.fi> <juvester@users.noreply.github.com>
|
||||||
|
juvester <juvester@cs.helsinki.fi>
|
||||||
|
Socapex <philippe.groarke@gmail.com> <philippe_groarke@yahoo.ca>
|
||||||
|
Socapex <philippe.groarke@gmail.com>
|
||||||
|
Rat <52238970+ratwithacompiler@users.noreply.github.com> <37424386+ratwithashotgun@users.noreply.github.com>
|
||||||
|
Rat <52238970+ratwithacompiler@users.noreply.github.com>
|
||||||
|
Quinn Damerell <qdamere@microsoft.com> <quinnd@outlook.com>
|
||||||
|
Quinn Damerell <qdamere@microsoft.com>
|
||||||
|
Manuel Kroeber <manuel.kroeber@googlemail.com> <manuel.kroeber@gmail.com>
|
||||||
|
Manuel Kroeber <manuel.kroeber@googlemail.com>
|
||||||
|
Ford Smith <fordmsmith13@gmail.com> <fms34@case.edu>
|
||||||
|
Ford Smith <fordmsmith13@gmail.com>
|
||||||
|
Vainock <contact.vainock@gmail.com> <Vainock@users.noreply.github.com>
|
||||||
|
Vainock <contact.vainock@gmail.com> <39059951+Vainock@users.noreply.github.com>
|
||||||
|
Vainock <contact.vainock@gmail.com>
|
||||||
|
Wahaj Dar <mail@wahajdar.com> <wahajdar@outlook.com>
|
||||||
|
Wahaj Dar <mail@wahajdar.com>
|
||||||
|
David Cooper <david@dtcooper.com> <davidc@eventbrite.com>
|
||||||
|
David Cooper <david@dtcooper.com>
|
||||||
|
dodgepong <ben.torell@gmail.com> <dodgepong@gmail.com>
|
||||||
|
dodgepong <ben.torell@gmail.com>
|
||||||
|
Robin Hielscher <git@robinhielscher.de> <git@jack0r.com>
|
||||||
|
Robin Hielscher <git@robinhielscher.de>
|
||||||
|
stump <git@stump.io> <stump@stump.io>
|
||||||
|
stump <git@stump.io>
|
||||||
|
Gary Kramlich <gary@bitmex.com> <grim@reaperworld.com>
|
||||||
|
Gary Kramlich <gary@bitmex.com>
|
||||||
|
Joshua Rowe <josh@nocoded.com> <joshua@nvisionweb.com>
|
||||||
|
Joshua Rowe <josh@nocoded.com>
|
||||||
|
Matthew McNamara <matt@mattmcn.com> <mattmcnam@gmail.com>
|
||||||
|
Matthew McNamara <matt@mattmcn.com>
|
||||||
|
cg2121 <claytong1214@gmail.com>
|
||||||
|
Gol-D-Ace <Gol-D-Ace@users.noreply.github.com>
|
||||||
|
JohannMG <johannmg@fb.com>
|
||||||
|
sorayuki <sorayuki@live.com>
|
||||||
|
Dead133 <Dead133@gmail.com>
|
||||||
|
Ilya Melamed <ilya77@gmail.com>
|
||||||
|
Michel <michel@sokar.nl>
|
||||||
|
Warchamp7 <warchamp2003@hotmail.com>
|
||||||
|
bl <bl4@postpile.org>
|
||||||
|
Palakis <contact@slepin.fr>
|
||||||
|
paibox <beeanyew@gmail.com>
|
||||||
|
boombatower <jimmy@boombatower.com>
|
||||||
|
cristisilaghi <cristian.silaghi@mozilla.ro>
|
||||||
|
notmark <mark@vaughnsoft.com>
|
||||||
|
univrsal <univrsal@protonmail.ch>
|
||||||
|
Farzad Karkhani <farzad.karkhani@gmail.com>
|
||||||
|
MedicMomcilo <medicmomcilo@gmail.com>
|
||||||
|
#
|
||||||
|
# Manual changes made:
|
||||||
|
#
|
||||||
|
# <matt@wizardcm.com> -> <matt@obsproject.com>
|
||||||
|
# Colin Edwards -> DDRBoxman
|
||||||
|
# Richard Stanway -> R1CH
|
||||||
|
# Matt Gajownik -> WizardCM
|
||||||
|
# pkviet -> pkv
|
||||||
|
# Ben Torrel -> dodgepong
|
||||||
|
# Clayton Groeneveld -> cg2121
|
||||||
|
# Andrew Surzhynskyi -> Dead133
|
||||||
23
obs-studio/.swift-format
Normal file
23
obs-studio/.swift-format
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"lineLength": 120,
|
||||||
|
"indentation": {
|
||||||
|
"spaces": 4
|
||||||
|
},
|
||||||
|
"tabWidth": 4,
|
||||||
|
"maximumBlankLines": 1,
|
||||||
|
"respectsExistingLineBreaks": true,
|
||||||
|
"lineBreakBeforeControlFlowKeywords": false,
|
||||||
|
"lineBreakBeforeEachArgument": false,
|
||||||
|
"lineBreakBeforeEachGenericRequirement": false,
|
||||||
|
"lineBreakBetweenDeclarationAttributes": false,
|
||||||
|
"prioritizeKeepingFunctionOutputTogether": false,
|
||||||
|
"indentConditionalCompilationBlocks": true,
|
||||||
|
"lineBreakAroundMultilineExpressionChainComponents": false,
|
||||||
|
"fileScopedDeclarationPrivacy": {"accessLevel": "private"},
|
||||||
|
"indentSwitchCaseLabels": false,
|
||||||
|
"spacesAroundRangeFormationOperators": false,
|
||||||
|
"noAssignmentInExpressions": { "allowedFunctions" : ["XCTAssertNoThrow"] },
|
||||||
|
"multiElementCollectionTrailingCommas": true,
|
||||||
|
"indentBlankLines": false,
|
||||||
|
}
|
||||||
2894
obs-studio/AUTHORS
Normal file
2894
obs-studio/AUTHORS
Normal file
File diff suppressed because it is too large
Load Diff
37
obs-studio/CMakeLists.txt
Normal file
37
obs-studio/CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.28...3.30)
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake" NO_POLICY_SCOPE)
|
||||||
|
|
||||||
|
project(obs-studio VERSION ${OBS_VERSION_CANONICAL})
|
||||||
|
|
||||||
|
if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||||
|
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/architecture.cmake")
|
||||||
|
if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(compilerconfig)
|
||||||
|
include(defaults)
|
||||||
|
include(helpers)
|
||||||
|
|
||||||
|
option(ENABLE_FRONTEND "Enable building with UI (requires Qt)" ON)
|
||||||
|
option(ENABLE_SCRIPTING "Enable scripting support" ON)
|
||||||
|
option(ENABLE_HEVC "Enable HEVC encoders" ON)
|
||||||
|
|
||||||
|
add_subdirectory(libobs)
|
||||||
|
if(OS_WINDOWS)
|
||||||
|
add_subdirectory(libobs-d3d11)
|
||||||
|
add_subdirectory(libobs-winrt)
|
||||||
|
endif()
|
||||||
|
add_subdirectory(libobs-opengl)
|
||||||
|
if(OS_MACOS)
|
||||||
|
add_subdirectory(libobs-metal)
|
||||||
|
endif()
|
||||||
|
add_subdirectory(plugins)
|
||||||
|
|
||||||
|
add_subdirectory(test/test-input)
|
||||||
|
|
||||||
|
add_subdirectory(frontend)
|
||||||
|
|
||||||
|
message_configuration()
|
||||||
233
obs-studio/CMakePresets.json
Normal file
233
obs-studio/CMakePresets.json
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
{
|
||||||
|
"version": 8,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 3,
|
||||||
|
"minor": 28,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "environmentVars",
|
||||||
|
"hidden": true,
|
||||||
|
"cacheVariables": {
|
||||||
|
"RESTREAM_CLIENTID": {"type": "STRING", "value": "$penv{RESTREAM_CLIENTID}"},
|
||||||
|
"RESTREAM_HASH": {"type": "STRING", "value": "$penv{RESTREAM_HASH}"},
|
||||||
|
"TWITCH_CLIENTID": {"type": "STRING", "value": "$penv{TWITCH_CLIENTID}"},
|
||||||
|
"TWITCH_HASH": {"type": "STRING", "value": "$penv{TWITCH_HASH}"},
|
||||||
|
"YOUTUBE_CLIENTID": {"type": "STRING", "value": "$penv{YOUTUBE_CLIENTID}"},
|
||||||
|
"YOUTUBE_CLIENTID_HASH": {"type": "STRING", "value": "$penv{YOUTUBE_CLIENTID_HASH}"},
|
||||||
|
"YOUTUBE_SECRET": {"type": "STRING", "value": "$penv{YOUTUBE_SECRET}"},
|
||||||
|
"YOUTUBE_SECRET_HASH": {"type": "STRING", "value": "$penv{YOUTUBE_SECRET_HASH}"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dependencies",
|
||||||
|
"hidden": true,
|
||||||
|
"vendor": {
|
||||||
|
"obsproject.com/obs-studio": {
|
||||||
|
"dependencies": {
|
||||||
|
"prebuilt": {
|
||||||
|
"version": "2026-08-26",
|
||||||
|
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
|
||||||
|
"label": "Pre-Built obs-deps",
|
||||||
|
"hashes": {
|
||||||
|
"macos-universal": "166560b13be8deb9a1aa7bfd306377a92c3817cecace931fe9732b9a25f0a97a",
|
||||||
|
"windows-x64": "eb5e67ad110543bcb65a4612bcd654b6427972ea70d26412b48d7e21532dfe67",
|
||||||
|
"windows-x86": "dc21eb9fbc5c08ea0451950e35eeeeece7537e13d1ec7115803b1b59ea96f9b2",
|
||||||
|
"windows-arm64": "0648c0fd0887efcb718e437bf9c446b069a50be58e7fcd585497ad8c11644a79"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"qt6": {
|
||||||
|
"version": "2026-08-26",
|
||||||
|
"baseUrl": "https://github.com/obsproject/obs-deps/releases/download",
|
||||||
|
"label": "Pre-Built Qt6",
|
||||||
|
"hashes": {
|
||||||
|
"macos-universal": "1f69dcfcdd71f6e319d89736de0290a366fe5d161eae692c6818d20fdfa95c25",
|
||||||
|
"windows-x64": "7dd60d2883207ecccceb2270538140fccd1b5022e06beedfbc3ba46c7d2e76ae",
|
||||||
|
"windows-arm64": "a4872414b91252b1d51a3a6d98b6bdd7e3468db19a42c42978670f84eddcb888"
|
||||||
|
},
|
||||||
|
"debugSymbols": {
|
||||||
|
"windows-x64": "c69ad24a76c7986aabff1f0e3860f4e650843dcbbcb2e6699daf63914fa888cd",
|
||||||
|
"windows-arm64": "e39c0ecfdb5fb19a53623aa4914c07e02989f0320e828cc7ed7c18be6b734cc9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cef": {
|
||||||
|
"version": "6533",
|
||||||
|
"baseUrl": "https://cdn-fastly.obsproject.com/downloads",
|
||||||
|
"label": "Chromium Embedded Framework",
|
||||||
|
"hashes": {
|
||||||
|
"macos-x86_64": "37bf7571a48c5dfa8519817e4a90a3503a0eb30f9eadd68f4c3e783e363f272a",
|
||||||
|
"macos-arm64": "429b50e74f6c174dcfe2f14d8204b54add497eaafe117f7b69ce6bb2354d2626",
|
||||||
|
"ubuntu-x86_64": "7963335519a19ccdc5233f7334c5ab023026e2f3e9a0cc417007c09d86608146",
|
||||||
|
"ubuntu-aarch64": "642514469eaa29a5c887891084d2e73f7dc2d7405f7dfa7726b2dbc24b309999",
|
||||||
|
"windows-x64": "922efbda1f2f8be9e5b2754d878a14d90afc81f04e94fc9101a7513e2b5cecc1",
|
||||||
|
"windows-arm64": "df9df4bd85826b4c071c6db404fd59cf93efd9c58ec3ab64e204466ae19bb02a"
|
||||||
|
},
|
||||||
|
"revision": {
|
||||||
|
"macos-x86_64": 5,
|
||||||
|
"macos-arm64": 5,
|
||||||
|
"ubuntu-x86_64": 6,
|
||||||
|
"ubuntu-aarch64": 6,
|
||||||
|
"windows-x64": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"sparkle": {
|
||||||
|
"version": "2.9.2",
|
||||||
|
"baseUrl": "https://github.com/sparkle-project/Sparkle/releases/download",
|
||||||
|
"label": "Sparkle 2",
|
||||||
|
"hash": "1cb340cbbef04c6c0d162078610c25e2221031d794a3449d89f2f56f4df77c95"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "macos",
|
||||||
|
"displayName": "macOS",
|
||||||
|
"description": "Default macOS build (single architecture only)",
|
||||||
|
"inherits": ["environmentVars"],
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Darwin"
|
||||||
|
},
|
||||||
|
"generator": "Xcode",
|
||||||
|
"binaryDir": "${sourceDir}/build_macos",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_OSX_DEPLOYMENT_TARGET": {"type": "STRING", "value": "13.0"},
|
||||||
|
"OBS_CODESIGN_IDENTITY": {"type": "STRING", "value": "$penv{CODESIGN_IDENT}"},
|
||||||
|
"OBS_CODESIGN_TEAM": {"type": "STRING", "value": "$penv{CODESIGN_TEAM}"},
|
||||||
|
"OBS_PROVISIONING_PROFILE": {"type": "STRING", "value": "$penv{PROVISIONING_PROFILE}"},
|
||||||
|
"VIRTUALCAM_DEVICE_UUID": {"type": "STRING", "value": "7626645E-4425-469E-9D8B-97E0FA59AC75"},
|
||||||
|
"VIRTUALCAM_SINK_UUID": {"type": "STRING", "value": "A3F16177-7044-4DD8-B900-72E2419F7A9A"},
|
||||||
|
"VIRTUALCAM_SOURCE_UUID": {"type": "STRING", "value": "A8D7B8AA-65AD-4D21-9C42-66480DBFA8E1"},
|
||||||
|
"SPARKLE_APPCAST_URL": {"type": "STRING", "value": "https://obsproject.com/osx_update/updates_$(ARCHS)_v2.xml"},
|
||||||
|
"SPARKLE_PUBLIC_KEY": {"type": "STRING", "value": "HQ5/Ba9VHOuEWaM0jtVjZzgHKFJX9YTl+HNVpgNF0iM="},
|
||||||
|
"ENABLE_BROWSER": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "macos-ci",
|
||||||
|
"displayName": "macOS (CI)",
|
||||||
|
"description": "CI macOS build (single architecture only)",
|
||||||
|
"inherits": ["macos"],
|
||||||
|
"warnings": {"dev": true, "deprecated": true},
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_COMPILE_WARNING_AS_ERROR": true,
|
||||||
|
"CMAKE_XCODE_ATTRIBUTE_COMPILATION_CACHE_ENABLE_CACHING": "YES",
|
||||||
|
"CMAKE_XCODE_ATTRIBUTE_COMPILATION_CACHE_CAS_PATH": "$penv{XCODE_CAS_PATH}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ubuntu",
|
||||||
|
"displayName": "Ubuntu",
|
||||||
|
"description": "obs-studio for Ubuntu",
|
||||||
|
"inherits": ["environmentVars"],
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Linux"
|
||||||
|
},
|
||||||
|
"binaryDir": "${sourceDir}/build_ubuntu",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"warnings": {"dev": true, "deprecated": true},
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"CMAKE_INSTALL_LIBDIR": "lib/CMAKE_SYSTEM_PROCESSOR-linux-gnu",
|
||||||
|
"ENABLE_AJA": false,
|
||||||
|
"ENABLE_VLC": true,
|
||||||
|
"ENABLE_WAYLAND": true,
|
||||||
|
"ENABLE_WEBRTC": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ubuntu-ci",
|
||||||
|
"inherits": ["ubuntu"],
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
|
||||||
|
"CMAKE_COMPILE_WARNING_AS_ERROR": true,
|
||||||
|
"CMAKE_COLOR_DIAGNOSTICS": true,
|
||||||
|
"ENABLE_CCACHE": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-x64",
|
||||||
|
"displayName": "Windows x64",
|
||||||
|
"description": "Default Windows build (x64)",
|
||||||
|
"inherits": ["environmentVars"],
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Windows"
|
||||||
|
},
|
||||||
|
"architecture": "x64,version=10.0.26100.0",
|
||||||
|
"binaryDir": "${sourceDir}/build_x64",
|
||||||
|
"generator": "Visual Studio 18 2026",
|
||||||
|
"cacheVariables": {
|
||||||
|
"GPU_PRIORITY_VAL": {"type": "STRING", "value": "$penv{GPU_PRIORITY_VAL}"},
|
||||||
|
"VIRTUALCAM_GUID": {"type": "STRING", "value": "A3FCE0F5-3493-419F-958A-ABA1250EC20B"},
|
||||||
|
"ENABLE_BROWSER": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-ci-x64",
|
||||||
|
"displayName": "Windows x64 (CI)",
|
||||||
|
"description": "CI Windows build (x64)",
|
||||||
|
"inherits": ["windows-x64"],
|
||||||
|
"warnings": {"dev": true, "deprecated": true},
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_COMPILE_WARNING_AS_ERROR": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-arm64",
|
||||||
|
"displayName": "Windows ARM64",
|
||||||
|
"description": "Default Windows build (ARM64)",
|
||||||
|
"inherits": ["environmentVars"],
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Windows"
|
||||||
|
},
|
||||||
|
"architecture": "ARM64,version=10.0.26100.0",
|
||||||
|
"binaryDir": "${sourceDir}/build_arm64",
|
||||||
|
"generator": "Visual Studio 18 2026",
|
||||||
|
"cacheVariables": {
|
||||||
|
"GPU_PRIORITY_VAL": {"type": "STRING", "value": "$penv{GPU_PRIORITY_VAL}"},
|
||||||
|
"VIRTUALCAM_GUID": {"type": "STRING", "value": "A3FCE0F5-3493-419F-958A-ABA1250EC20B"},
|
||||||
|
"ENABLE_AJA": false,
|
||||||
|
"ENABLE_BROWSER": true,
|
||||||
|
"ENABLE_SCRIPTING": false,
|
||||||
|
"ENABLE_VST": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-ci-arm64",
|
||||||
|
"displayName": "Windows ARM64 (CI)",
|
||||||
|
"description": "CI Windows build (ARM64)",
|
||||||
|
"inherits": ["windows-arm64"],
|
||||||
|
"warnings": {"dev": true, "deprecated": true},
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_COMPILE_WARNING_AS_ERROR": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{
|
||||||
|
"name": "windows-x64",
|
||||||
|
"configurePreset": "windows-x64",
|
||||||
|
"displayName": "Windows 64-bit",
|
||||||
|
"description": "Windows build for 64-bit (aka x64)",
|
||||||
|
"configuration": "RelWithDebInfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "windows-arm64",
|
||||||
|
"configurePreset": "windows-arm64",
|
||||||
|
"displayName": "Windows on ARM 64-bit",
|
||||||
|
"description": "Windows build for ARM 64-bit (aka ARM64)",
|
||||||
|
"configuration": "RelWithDebInfo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
153
obs-studio/COC.rst
Normal file
153
obs-studio/COC.rst
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
Code of Conduct
|
||||||
|
===============
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
--------
|
||||||
|
|
||||||
|
OBS is made up of many people from all over the world, with different
|
||||||
|
backgrounds and experiences that they bring to the project. In order to
|
||||||
|
continue letting the community grow, feel safe, and to encourage good
|
||||||
|
communication, we are adopting the following Code of Conduct. This
|
||||||
|
document—or any subsequent version—will be enforced from July 31st, 2021
|
||||||
|
onwards.
|
||||||
|
|
||||||
|
Scope
|
||||||
|
-----
|
||||||
|
|
||||||
|
This Code of Conduct applies to anyone who contributes—or wishes to
|
||||||
|
contribute—to the project directly, is taking part in community
|
||||||
|
discussion in official channels, or are in a position where they’ll be
|
||||||
|
seen as a representative of the project, regardless of whether they
|
||||||
|
intend to speak for the project or not.
|
||||||
|
|
||||||
|
A person is considered to be representing the project if they’re
|
||||||
|
speaking as a code contributor, support volunteer, member involved with
|
||||||
|
the project, or if they have some kind of visible role in an official
|
||||||
|
channel for OBS. This does not include roles for Sponsors or Patrons.
|
||||||
|
|
||||||
|
Official channels for OBS include the Discord server, the forum, any
|
||||||
|
GitHub repositories within the OBS Project organization, any social
|
||||||
|
media accounts for OBS Project, any public events where OBS Project has
|
||||||
|
a presence, any presentations given on behalf of OBS Project, or any
|
||||||
|
other situation that can be reasonably interpreted as being hosted,
|
||||||
|
attended or communicated by the OBS Project.
|
||||||
|
|
||||||
|
This Code of Conduct is subject to change, and any changes will have a 2
|
||||||
|
week notice period before taking immediate effect. Changes will not be
|
||||||
|
enforced retroactively unless it directly affects the security, safety
|
||||||
|
or well-being of another individual, or other exceptional circumstances.
|
||||||
|
|
||||||
|
Guidelines
|
||||||
|
----------
|
||||||
|
|
||||||
|
General
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- We are committed to providing a friendly, safe and welcoming
|
||||||
|
environment for all, regardless of level of experience, gender
|
||||||
|
identity and expression, sexual orientation, disability, personal
|
||||||
|
appearance, body size, race, ethnicity, age, religion, nationality,
|
||||||
|
or other similar characteristic.
|
||||||
|
- Please avoid using overtly sexual aliases or other nicknames that
|
||||||
|
might detract from a friendly, safe and welcoming environment for
|
||||||
|
all.
|
||||||
|
- Please be kind and courteous. There’s no need to be mean or rude.
|
||||||
|
- Respect that people will have differences of opinion.
|
||||||
|
- We will exclude you from interaction if you insult, demean or harass
|
||||||
|
anyone. That is not welcome behavior. The definition of harassment is
|
||||||
|
provided in the section on Unacceptable Behavior. In particular, we
|
||||||
|
don’t tolerate behavior that excludes people in socially marginalized
|
||||||
|
groups. This includes private harassment.
|
||||||
|
- Likewise any spamming, trolling, flaming, baiting or other
|
||||||
|
attention-stealing behavior is not welcome.
|
||||||
|
- Being a recognized Contributor, Support Volunteer, Sponsor or Patron
|
||||||
|
does not give you special right to avoid following these guidelines
|
||||||
|
when taking part in community discussions.
|
||||||
|
|
||||||
|
Code Contributors
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- Respect that every design or implementation choice carries a
|
||||||
|
trade-off and numerous costs. There is seldom a right answer.
|
||||||
|
- Please keep unstructured critique to a minimum. If you have solid
|
||||||
|
ideas you want to experiment with, make a fork and see how it works.
|
||||||
|
- Do not post negative or inflammatory comments on code submissions. If
|
||||||
|
you believe that a code submission has problems, provide constructive
|
||||||
|
or actionable feedback, or a reason for why a submission is being
|
||||||
|
rejected. Where it is possible, provide explanations that someone can
|
||||||
|
use to fix things.
|
||||||
|
|
||||||
|
Support Volunteers
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
- There are many users of differing skill levels or abilities. This is
|
||||||
|
never a reason to refuse support for someone, as long as it’s within
|
||||||
|
the scope of OBS support, and the user is willing to cooperate.
|
||||||
|
- Just because something seems obvious to you, doesn’t mean it’s
|
||||||
|
actually obvious. If someone is having difficulty understanding how
|
||||||
|
to use OBS, it should never be treated as their fault.
|
||||||
|
- Bad advice is often worse than no advice. If you don’t know how to
|
||||||
|
address someone’s issue, then get ahold of someone who’ll be able to
|
||||||
|
provide them help. We can only expect so much patience from a user
|
||||||
|
asking for support, and helping without understanding the problem
|
||||||
|
only results in using up that patience.
|
||||||
|
|
||||||
|
Unacceptable Behavior
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The following behaviors are considered harassment and are unacceptable
|
||||||
|
within our community:
|
||||||
|
|
||||||
|
- Violence, threats of violence or violent language directed against
|
||||||
|
another person.
|
||||||
|
- Sexist, racist, homophobic, transphobic, ableist or otherwise
|
||||||
|
discriminatory jokes and language.
|
||||||
|
- Posting or displaying sexually explicit or violent material.
|
||||||
|
- Posting or threatening to post other people’s personally identifying
|
||||||
|
information ("doxing").
|
||||||
|
- Personal insults.
|
||||||
|
- Unwelcome sexual attention. This includes, sexualized comments or
|
||||||
|
jokes, and unwelcome sexual advances.
|
||||||
|
- Deliberate intimidation, stalking or following.
|
||||||
|
- Advocating for, or encouraging, any of the above behavior.
|
||||||
|
|
||||||
|
Enforcement
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Instances of unacceptable behavior may be reported by contacting an
|
||||||
|
enforcement team member at one of the following addresses:
|
||||||
|
coc@obsproject.com. All complaints will be reviewed and investigated,
|
||||||
|
and will result in a response that is deemed necessary and appropriate
|
||||||
|
to the circumstances. The project team is obligated to maintain
|
||||||
|
confidentiality with regard to the reporter of an incident. Further
|
||||||
|
details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
In other cases where behavior is against the general guidelines set out
|
||||||
|
in this Code of Conduct, this may be brought up with a moderator through
|
||||||
|
either a Discord DM, or through an email to an enforcement member. In
|
||||||
|
these cases, it is always assumed in the first instance that a person
|
||||||
|
means well, and action will only be taken in the case of repeated
|
||||||
|
failure to follow the guidelines.
|
||||||
|
|
||||||
|
Feedback
|
||||||
|
--------
|
||||||
|
|
||||||
|
Suggestions for changes can be made by submitting a pull request on the
|
||||||
|
`obsproject/obs-studio <https://github.com/obsproject/obs-studio>`__
|
||||||
|
repo, or if you wish to privately discuss your concerns, you can instead
|
||||||
|
email coc@obsproject.com.
|
||||||
|
|
||||||
|
Attribution
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This Code of Conduct is adapted in part from the Contributor Covenant,
|
||||||
|
version 1.4, in part from the Citizen Code of Conduct (used under
|
||||||
|
CC-BY-SA 3.0), and in part from the Rust Code of Conduct.
|
||||||
|
|
||||||
|
This Code of Conduct is released and licensed under a Creative Commons
|
||||||
|
Attribution-ShareAlike 3.0 License.
|
||||||
|
|
||||||
|
Version History
|
||||||
|
---------------
|
||||||
|
|
||||||
|
V1.0 - First Iteration - July 31st, 2021
|
||||||
425
obs-studio/CODESTYLE.md
Normal file
425
obs-studio/CODESTYLE.md
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
OBS Studio Code Style Guidelines
|
||||||
|
================================
|
||||||
|
|
||||||
|
The project requires all contributions to have their source code formatted using appropriate formatting tools to reduce the potential impact of stylistic changes to a structured “diff” view of code.
|
||||||
|
|
||||||
|
In addition to those automatically enforceable stylistic choices the project also prefers contributions to follow a set of architectural guidelines that are chosen to reduce undefined or unexpected behavior and make code easier to reason about during reviews or maintenance.
|
||||||
|
|
||||||
|
## Reducing Potential For Errors
|
||||||
|
|
||||||
|
The following guidelines have been established as good practices to reduce some common potential for errors or naive coding practices that have lead to such errors in the past:
|
||||||
|
|
||||||
|
* **Always Initialize Variables** - Depending on the programming language, language standard, compiler, and platform, rules for when and how variables are initialized automatically might differ. Thus some variables might have random values at program start and naive code might interpret any value but “0” to mean “correctly initialized” and run into unexpected behavior.
|
||||||
|
* Do not declare or initialize multiple variables on the same line, do not mix declarations and initializations
|
||||||
|
|
||||||
|
```C
|
||||||
|
int i, v = 0; // BAD - v is initialized to 0, i is uninitialized
|
||||||
|
|
||||||
|
|
||||||
|
int i = 0; // GOOD - i is explicitly declared and initialized
|
||||||
|
int v = 0; // GOOD - v is separately declared and initialized
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Do not use “0” as a valid enumeration value by default** - in many cases an enum requires an explicit choice to be made (only one value of a set of “valid” values can be set) and “not making a choice” should not be considered valid
|
||||||
|
|
||||||
|
```C
|
||||||
|
enum state { ACTIVE, // BAD - zero-initialized enum potentially
|
||||||
|
INACTIVE, // leads to implicit state changes.
|
||||||
|
DELETED
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum state { INVALID, // GOOD - zero-initialized enum produces
|
||||||
|
ACTIVE, // an invalid value by default, avoiding
|
||||||
|
INACTIVE, // an implicit state change.
|
||||||
|
DELETED
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum state { ACTIVE = 1, // GOOD, zero-initialization fails because
|
||||||
|
INACTIVE = 2, // it’s not a valid enum value to begin with.
|
||||||
|
DELETED = 3
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Use natural language for variables and types** - expressive code is easier to reason about for maintainers and reviewers and also reduces the mental burden when returning to the same code after even a short absence, as code “says what it does” and variables “tell what they represent”.
|
||||||
|
|
||||||
|
```C
|
||||||
|
int c = 1; // BAD: What does “c” represent?
|
||||||
|
int count = 2; // BAD: Count of “what”?
|
||||||
|
int num_bytes = 3; // GOOD: “Num(ber) of bytes”
|
||||||
|
|
||||||
|
|
||||||
|
bool valid; // BAD: Meaning is ambiguous
|
||||||
|
bool has_valid_key; // GOOD: Describes state of element in an object
|
||||||
|
bool is_valid; // GOOD: Describes state of element itself
|
||||||
|
bool did_send_packet; // GOOD: Describes state of transaction.
|
||||||
|
|
||||||
|
|
||||||
|
float dur = 1.0; // BAD: Unit of duration unknown
|
||||||
|
float duration_ms = 5.0; // GOOD: Unit of duration encoded within name
|
||||||
|
|
||||||
|
|
||||||
|
// GOOD: Function signature communicates the unit of the delay explicitly.
|
||||||
|
start_transition_with_delay(transition_type *transition, float delay_ms);
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Use compound types rather than individual variables** - information like the size or dimension of an object, a timescale, a position in space, should be logically encoded in a compound type and only “unwrapped” when consumed
|
||||||
|
|
||||||
|
```C
|
||||||
|
// GOOD: Function signature communicates the unit of the delay explicitly.
|
||||||
|
start_transition_with_delay(transition_type *transition, float delay_ms);
|
||||||
|
|
||||||
|
|
||||||
|
// EVEN BETTER: Time values encoded as pieces of a fraction (1/1000th of a
|
||||||
|
// second representing a “microsecond”) and explicitly passed to
|
||||||
|
// the function.
|
||||||
|
typedef struct {
|
||||||
|
int_64_t time_value;
|
||||||
|
int_32_t time_scale;
|
||||||
|
} time_unit;
|
||||||
|
|
||||||
|
|
||||||
|
start_transition_with_delay(transition_type *transition, time_unit delay);
|
||||||
|
|
||||||
|
|
||||||
|
// BAD: Behavior encoded in unrelated booleans, maybe even conflicting
|
||||||
|
start_transition(transition_type *transition, bool ignore, bool abort);
|
||||||
|
|
||||||
|
|
||||||
|
// GOOD: Function signature requires more meaningful enum rather than bool
|
||||||
|
enum transition_abort_mode {
|
||||||
|
TRANSITION_ABORT_INVALID, TRANSITION_ABORT_ALL,
|
||||||
|
TRANSITION_ABORT_NEW, TRANSITION_ABORT_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Signature:
|
||||||
|
start_transition(transition_type *transition, enum transition_abort_mode);
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Do not use unnecessary abbreviations** - if a class implements an "Advanced Output", call it AdvancedOutput, not AdvOutput. The same applies to variables holding an instance of the class. Code is read more often than it is written (and indeed the writing part is just the final outcome of a much longer reasoning process) and optimising for the latter incurs a debt on the former.
|
||||||
|
* **Name functions after what they do rather than how they do it** - a function’s signature is effectively its “interface” and should provide a hint about which functionality it provides to the caller. The actual implementation should not be relevant to the caller and exposing this information can actually have a net-negative effect as the caller might try to “out-think” the API.
|
||||||
|
* **Prefer pure functions and non-mutable variables** - side-effects are harder to keep track of and can lead to confusing results, particularly when following the prior rule. This also ties into a later rule about avoiding global scope, as global (and shared) variables tend to encourage writing code that surreptitiously changes global shared scope, which makes code harder to test and reason about.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This obviously does not apply to methods where the implementation detail is a technical need of the API user e.g., for factory methods where the user has data in a specific place that it needs to be loaded from, such as `MyData::loadFromUrl(const std::string &url)` or `MyData::loadFromFile(const std::filesystem::path &path)` where the “what” is essentially intertwined with the “how”.
|
||||||
|
|
||||||
|
* **Do not use specific integer types outside of binary protocols** - for the vast majority of cases using an “int” is sufficient and using a signed type also prevents unexpected overflow issues where a small negative number becomes a massive positive number. Use sized types (and unsigned types) when interacting with binary protocols, bitfields (e.g. to explicitly remove any special meaning from the most-significant bit). Prefer 64-bit integers over unsigned 32-bit integers for large numbers.
|
||||||
|
* **Do not micro-optimize and do not attempt to be overly "clever" with an implementation** - to quote Kernighan:
|
||||||
|
|
||||||
|
> Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?
|
||||||
|
|
||||||
|
* **Use profiling tools to identify actual opportunities for optimization** - the code that the compiler generates can potentially work in different ways than the source code might suggest and might not even be executed in the same order. Premature optimizations might also prevent the compiler from applying its own, leading to slower code overall. Also be mindful of profiling tools themselves potentially influencing the runtime behaviour of code.
|
||||||
|
* **Attempt to actively break code during testing** - do not rely on “happy paths”. Try to be creative and come up with ways to break the assumptions your code might have made. Do not rely on assumptions that data will never be “wrong” or that some other data will always be “there”. As the ISO C++ FAQ calls it:
|
||||||
|
|
||||||
|
> Write code that is guaranteed to work, not code that doesn’t seem to break.
|
||||||
|
|
||||||
|
* **Write code that will be reasonably easy to understand even 6, 12, or 24 months later** - while it is easy to reason about the state of a program while working on it, much of that “inside knowledge” might be gone even a few weeks later. Writing code that is obvious in what it does (and also why it does certain things and which pieces of data it requires) will make it easier to get back into the mental model behind the code. **Write code so that any newcomer to the project will have a reasonably easy time understanding what it does and why it does it the way it’s implemented.**
|
||||||
|
* **Do not rely on global state or singletons** - both are easily abused as shortcuts to avoid implementing a more expressive and safer design. There are valid use cases for global state (like a global “application” instance implemented as a singleton) but those are the exception and not the rule.
|
||||||
|
* **Treat programming as a mental exercise and not just as text manipulation** - existing code in particular will have been designed and built with a set of assumptions and theories in mind, which themselves informed the architectural choices made in its implementation. Some changes might seem “quick and easy” but <u>potentially violate those architectural assumptions</u> and thus could leave the code in a precarious state (and indeed might lead to undefined behaviour when yet other code relies on these assumptions not to be violated).
|
||||||
|
|
||||||
|
The last point cannot be overstated because it is a common source of friction in long-lived software projects. **Neither source code nor documentation can be a full representation of the “theory” behind the code** and indeed both can never be more than an incomplete “snapshot” of one possible implementation of this theory.
|
||||||
|
|
||||||
|
But without a decent understanding of the theory behind the code (or the architectural concerns that went into the current design) one cannot correctly ascertain which changes will be “in line” with the way current code works and might instead violate important principles of it.
|
||||||
|
|
||||||
|
And the more such “naive” code (mainly in the form of hacks and workarounds) is piled on, the more difficult the code becomes to work with, culminating in a code base where even shipping “simple” new features becomes a tough exercise (as the code starts to behave in unpredictable ways due to all the violations of the original “theory”).
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This principle becomes more obvious with less abstract examples: An image-editing program will have a set of considerations and constraints that went into its design (code and user interface) and just adding the ability to decode video files will not make the program able to edit video, which is an entirely separate discipline with potentially opposing needs and considerations.
|
||||||
|
>
|
||||||
|
> And indeed any new feature added to the photo editor now has to potentially contend with the reality that it might be faced with a video file instead, adding even more bits and pieces to unrelated parts of the program to handle a scenario the foundation of the program was conceptually (and explicitly) not built for.
|
||||||
|
|
||||||
|
## Language Specific Guidance
|
||||||
|
|
||||||
|
OBS Studio currently contains code written in the following languages:
|
||||||
|
|
||||||
|
* C
|
||||||
|
* C++
|
||||||
|
* Objective-C/C++
|
||||||
|
* Swift
|
||||||
|
* CMake
|
||||||
|
|
||||||
|
Continuous integration code is mostly based on Powershell, Zsh or Bash scripts, and Python 3.
|
||||||
|
|
||||||
|
While C++ and ObjectiveC/C++ are supersets of the C language, the project prefers to treat them individually with their own rules, conventions, and language standards. Thus rules that apply to “C” do not necessarily apply to these languages and indeed some rules will be replaced for those languages.
|
||||||
|
|
||||||
|
### C
|
||||||
|
|
||||||
|
For “pure” C code the project follows the Linux Kernel Coding Style (https://github.com/torvalds/linux/blob/master/Documentation/process/coding-style.rst). Parts of the guidelines that relate to Emacs, kernel-level allocators, or macros only available in the Linux kernel source code do not apply.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The current C language standard of the project is **C17 without GNU extensions**.
|
||||||
|
|
||||||
|
Some additional notes:
|
||||||
|
|
||||||
|
* Formatting is checked and can be applied by `clang-format`. The formatting generated by it supersedes any rules in the guidelines.
|
||||||
|
* The project uses a line length of 120 characters, and uses tabs for indentation with a tab width of 8 characters.
|
||||||
|
|
||||||
|
### C++
|
||||||
|
|
||||||
|
The project treats C++ as its own language and not just as “C with classes”. The associated code style guidelines are based on the Google C++ Code Style Guide (https://google.github.io/styleguide/cppguide.html) with changes to some aspects, as listed below.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The current C++ language standard of the project is **C++17 without GNU extensions**. A move to C++20 is currently being considered.
|
||||||
|
|
||||||
|
Additions and changes (in the order the associated topics appear in the Google document):
|
||||||
|
|
||||||
|
* **Header Files**
|
||||||
|
* OBS Studio uses the “cpp” suffix for C++ source code files rather than “cc” and “hpp” for C++-specific header files
|
||||||
|
* Header guards are permitted, but not required - a simple `#pragma once` is sufficient
|
||||||
|
* Use of forward declarations is permitted for class or struct types, but be mindful of the caveats mentioned in the Style Guide. **Avoid “Structuring code to enable forward declarations”.**
|
||||||
|
* OBS Studio uses a different header include order, documented in a separate section below
|
||||||
|
* **Classes**
|
||||||
|
* If any constructor or assignment operator is defined, all 5 types need to be either defined, defaulted, or deleted, including the destructor (**“Rule of Five”**)
|
||||||
|
* Ensure that move constructor and move assignment operator are marked “noexcept” (and implemented accordingly) to avoid possible pessimization when using user-defined types with standard library containers like `std::vector`.
|
||||||
|
* Use multiple-inheritance only for the interface/protocol pattern ("implements an interface as defined by" relationship), avoid the diamond pattern and virtual base classes.
|
||||||
|
* **Google-Specific Magic**
|
||||||
|
* OBS Studio does not use `cpplint`
|
||||||
|
* **Other C++ Features**
|
||||||
|
* Exceptions can (but don't have to) be used, particularly when no good "sentinel value" can be provided and also to avoid littering the code with correctness checks after every function call. It is preferred to keep the “scope” of exceptions within the same module (i.e., the same executable or library).
|
||||||
|
* Be aware that in C++ <u>any</u> function that is not a destructor and is not explicitly marked as noexcept can potentially throw an exception as part of normal error handling rather than signaling an actual critical failure.
|
||||||
|
* Instance methods that modify any data, even if it is not their own instance's data, should not be marked `const` to signal that they <u>logically</u> mutate some state.
|
||||||
|
* Do not use comments to declare the types in structured bindings
|
||||||
|
* Any guidelines of C++20 features will be evaluated once the project switches to that language standard.
|
||||||
|
* The project does not allow the use of the Boost library.
|
||||||
|
* However the project does allow the use of the “Disallowed standard library features” and some “Nonstandard Extensions”.
|
||||||
|
* **Naming**
|
||||||
|
* Use the class name (following type name rules) for the file name of its interface header and implementation file.
|
||||||
|
* Use `camelCase` for instance methods, functions, as well as variables. This also aligns with Qt’s code style.
|
||||||
|
* Existing constants in code are permitted to be kept with `UPPERCASE` names, but new constants should follow the new convention of `kCamelCase`.
|
||||||
|
* Namespaces need to follow type name rules, though the project only uses the “`OBS`” namespace for refactoring of application code for the time being.
|
||||||
|
* Do not use underscore prefixes for any names, as these are reserved for the standard library implementation. Use a trailing underscore for private member variables.
|
||||||
|
* **Comments**
|
||||||
|
* Only C++ comment style is permitted for C++ source code, mixing of different styles is not allowed.
|
||||||
|
* Do not use function argument comments.
|
||||||
|
* **Formatting**
|
||||||
|
* Formatting is checked and can be applied by `clang-format`. The formatting generated by it supersedes any rules in the guidelines.
|
||||||
|
* The project uses a line length of 120 characters, and uses tabs for indentation with a tab width of 8 characters.
|
||||||
|
* All new C++ code needs to use curly braces for all looping or branching statements. No exceptions.
|
||||||
|
* Prefer brace initialization to protect against (unexpected) narrowing and prefer it over C-style assignment, except where a functional difference in behavior is needed (e.g. for `std::vector`)
|
||||||
|
* Class visibility labels are not indented.
|
||||||
|
|
||||||
|
Some additional notes:
|
||||||
|
|
||||||
|
* Use the C++ standard template library and C++ algorithms as much as possible, but also be aware of known issues with some of them (e.g. bad runtime performance of `std::regex`).
|
||||||
|
* **Prefer the standard library** over implementing custom loops.
|
||||||
|
* Prefer **C++ collections and filesystem functions** over their C variants (e.g. `std::array` over C arrays)
|
||||||
|
* Prefer **range-based and iterator-based loops** over counter-based loops.
|
||||||
|
* **Prefer C++ types over C types**
|
||||||
|
* Wrap C library code in C++ code to provide a clean C++ interface first.
|
||||||
|
* **Use `class enum` instead of `enum`** for enumeration values and do not use enum as “integer” value aliases.
|
||||||
|
* Do not use inline or static in the same way they are used in C
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The “`static`” keyword is one of the more confusing aspects of modern C++, particularly compared to C. The meaning of the keyword changes depending on whether it is used for a function, a global variable, a function-scope variable, a class method, or a class member.
|
||||||
|
>
|
||||||
|
> In general limit its use in C++ code to describe “storage duration” of variables or for class methods (e.g. factory methods). Be mindful that even though since C++11 function-local static variables are initialized non-concurrently, this does not make consecutive _access_ of the object thread-safe as well. Also be mindful of the “Static Initialization Order Fiasco”. Otherwise all the common established pitfalls and issues of global variables apply.
|
||||||
|
>
|
||||||
|
> `constexpr` definitions do not need to use the static keyword. `constexpr` implies const and const implies static storage duration by default.
|
||||||
|
>
|
||||||
|
> Be careful when mixing `static` and `inline`, as the inline keyword (short for “defined in-line”) allows the same definition to exist in multiple translation units (and thus is allowed to violate the one-definition rule, or “ODR”), enabling the linker to deduplicate all instances of the same function, which is the exact opposite of what “`static`” requires (local visibility and thus a distinct copy of the function in each translation unit).
|
||||||
|
|
||||||
|
#### The Exception: Qt Guidelines
|
||||||
|
|
||||||
|
Many of Qt’s core concepts were invented years before C++ added support for similar ideas, which means that much code interacting with Qt library functions and QObject-based instances requires violating some of the core language principles outlined above:
|
||||||
|
|
||||||
|
* Qt's ownership model predates modern smart pointers or references and thus requires passing around raw pointers:
|
||||||
|
* Any class derived from QWidget needs to be instantiated using bare new.
|
||||||
|
* Parent QObjects take ownership of children and will take handle destructing them appropriately
|
||||||
|
* Thus: **Do not call `delete` on a widget owned by a parent widget. Do not create widgets without attaching them to a parent widget.**
|
||||||
|
* Qt's default string class QString was an early adopter of Unicode (like Windows or Cocoa on macOS) and uses a 2-byte encoding internally (which was adapted to become UTF-16)
|
||||||
|
* A QString needs to be converted between UTF-16 and UTF-8 when passing character data to any C-based API or when interacting with `std::string` instances.
|
||||||
|
* **There Ain’t No Such Thing As Plain Text** (https://tonsky.me/blog/unicode/)
|
||||||
|
* The lifetime of a heap object passed to Qt must be guaranteed to match or exceed the lifetime of the using/owning Qt object.
|
||||||
|
* Lambda expressions can and should be used for callback-style programming as much as possible.
|
||||||
|
* **Be aware of lifetime issues however**: Any reference captured by a lambda needs to be "alive" for as long as Qt might potentially invoke the lambda expression. Use copy-based capture for scalar values.
|
||||||
|
* Note that a pointer is "just" a scalar value and is thus captured by copy, but there is no strong relationship between the copied pointer and the memory it is pointing to. The lifetime of the heap object needs to be ensured. Be mindful of the lifetime of whomever will be invoking the lambda.
|
||||||
|
* A pointer captured by reference can potentially be set to nullptr and checked within the lambda expression's body, but the lifetime of the pointer reference itself now needs to be ensured.
|
||||||
|
|
||||||
|
### C/C++ Header Include Order
|
||||||
|
|
||||||
|
Prefer to include the headers that the implementation itself needs (e.g. because a type or definition is used directly in the implementation) and do not rely on another header possibly having included the same file already. **This follows the “include what you use” rule**.
|
||||||
|
|
||||||
|
When including headers, use the following order:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
// Interface definition or “counterpart” of current file
|
||||||
|
#include “interface.h”
|
||||||
|
|
||||||
|
|
||||||
|
// File in the same directory as the current file _if_ header belongs to the
|
||||||
|
// same “implementation”.
|
||||||
|
#include “file_in_working_directory.h”
|
||||||
|
|
||||||
|
|
||||||
|
// First party dependency from the same larger project that is “linked” with
|
||||||
|
// the implementation
|
||||||
|
#include <first_party_dependency/type_or_interface.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Third party dependency not part of the same project and “linked” with the
|
||||||
|
// implementation.
|
||||||
|
#include <third_party_dependency/type_or_interface.h>
|
||||||
|
|
||||||
|
|
||||||
|
// C++ standard library includes
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
// C standard library includes
|
||||||
|
#include <sys/socket.h>
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This specific order of includes helps in identifying potentially “broken” headers without “masking” the issue by including potential dependencies first.
|
||||||
|
>
|
||||||
|
> That issue can be avoided by following a simple procedure when creating a new interface and implementation pair: The initial implementation should always include the “counterpart” interface header first and should compile just fine even with the “empty” implementation.
|
||||||
|
>
|
||||||
|
> The same applies to any first party and third party library headers: Including one by itself should not result in compilation issues or should not require any standard library headers being included first (if that’s the case, the corresponding library header is badly designed).
|
||||||
|
>
|
||||||
|
> Putting the standard library includes last (and only including them if the implementation needs them) helps expose such malformed header files. While this scheme relies on convention (rather than enforcement by the language) it leads to self-contained headers and a cleaner set of includes.
|
||||||
|
|
||||||
|
Additional rules for this guideline:
|
||||||
|
|
||||||
|
* Includes within each block need to be sorted alphabetically with case-sensitivity enabled.
|
||||||
|
* Do not create platform specific “include blocks”. If a single source file has to include a hodgepodge of platform-specific headers it usually suggests that the source file tries to do “too much” and the “uglyness” that the rule might lead to is the point. To quote the Google C++ Style Guide:<br ><br />**“Instead of using a macro to conditionally compile code ... well, don't do that at all”.**<br /><br />
|
||||||
|
* Refactor the source file to be platform-specific and use CMake to add the appropriate file to the target for a specific platform.
|
||||||
|
* Do not use double quotes to include any file not in the same working directory as the current source or header file. Compilers have been lenient and automatically attempt to use header include paths for all header files, so files might indeed compile nonetheless, but the additional context for the reader (“that header is provided by some other module/target in the project”) is lost.
|
||||||
|
|
||||||
|
Some header files will require breaking these rules. Well-known examples include:
|
||||||
|
|
||||||
|
* `windows.h` might be required very early in an implementation, particularly when relying on the magic `WIN32_LEAN_AND_MEAN` define, as nested includes of the same header might break compilation in severe ways otherwise..
|
||||||
|
* The BSD header `libprocstat.h` requires additional headers to be included in a specific order before its own inclusion (as documented in its man page).
|
||||||
|
* If an impact of include order can be proven through testing, additional headers are allowed to break these rules. This is unavoidable given the legacy and preprocessor architecture of C and C++.
|
||||||
|
|
||||||
|
### Objective-C/C++
|
||||||
|
|
||||||
|
For Objective-C/C++ code the Google Objective-C Style Guide (https://google.github.io/styleguide/objcguide.html) should be followed, which itself is based on Apple’s [Cocoa Coding Guidelines](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html) and [Programming with ObjC Conventions](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html).
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The current Objective-C/C++ language standard of the project is **Objective-C 2.0**.
|
||||||
|
|
||||||
|
The additions and changes to the Google Style Guide are as follows (in the order the associated topics appear in the Google document):
|
||||||
|
|
||||||
|
* **Naming**
|
||||||
|
* Use `camelCase` for functions.
|
||||||
|
* Use of the `g` prefix for global variables in file scope is permitted.
|
||||||
|
* **Types**
|
||||||
|
* Always use the native 64-bit types (i.e., double for CGFloat).
|
||||||
|
* **Comments**
|
||||||
|
* Use Apple’s DocC format for documentation (https://www.swift.org/documentation/docc/).
|
||||||
|
* Use C++ comment style for inline comments.
|
||||||
|
* **Cocoa and Objective-C Features**
|
||||||
|
* Use `#import` for all includes, follow the include order as specified for C/C++.
|
||||||
|
* **Spacing and Formatting**
|
||||||
|
* Formatting is checked and can be applied by `clang-format`. The formatting generated by it supersedes any rules in the guidelines.
|
||||||
|
* Use 4 spaces for indentation, use spaces for alignment.
|
||||||
|
* The maximum line length is 120 characters.
|
||||||
|
* All code needs to use curly braces for all looping or branching statements. No exceptions.
|
||||||
|
|
||||||
|
### Swift
|
||||||
|
|
||||||
|
For Swift code the Google Swift Style Guide (https://google.github.io/swift/) should be followed as well as Apple’s Swift API Guidelines (https://www.swift.org/documentation/api-design-guidelines/).
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> The current Swift language standard of the project is **Swift 6**.
|
||||||
|
|
||||||
|
The additions and changes to the Google Style Guide are as follows (in the order the associated topics appear in the Google document):
|
||||||
|
|
||||||
|
* **General Formatting**
|
||||||
|
* Formatting is checked and can be applied by `swift-format`. The formatting generated by it supersedes any rules in the guidelines.
|
||||||
|
* Use 4 spaces for indentation, use spaces for alignment.
|
||||||
|
* The maximum line length is 120 characters.
|
||||||
|
|
||||||
|
Some additional notes:
|
||||||
|
|
||||||
|
* **Use the Unmanaged type and associated protocols to create retained or unretained opaque pointers to share with C APIs.**
|
||||||
|
* Use `passRetained` to pass an opaque pointer with an incremented reference count to a C API.
|
||||||
|
* Use `takeRetained` to take ownership of that reference count in Swift code and allow normal lifetime management to take over.
|
||||||
|
* Use `passUnretained` and takeUnretained to pass/receive pointers without influencing their reference count.
|
||||||
|
* **Object lifetime has to be manually ensured, and is thus unmanaged**
|
||||||
|
* Use extension to implement protocols in code blocks separate from the core type implementation.
|
||||||
|
|
||||||
|
```Swift
|
||||||
|
class MyType {
|
||||||
|
// Basic implementation
|
||||||
|
fileprivate let memberVariable: String
|
||||||
|
|
||||||
|
|
||||||
|
init(argumentOne: String) {
|
||||||
|
self.memberVariable = argumentOne
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension MyType : SomeProtocol {
|
||||||
|
func someProtocolMethod(argument: String) -> String {
|
||||||
|
return “\(memberVariable) \(argument)”
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Prefer functional patterns as much as possible over C-style loops.**
|
||||||
|
* When a loop is still preferable, use the native Range type and enumerated loops if a counter value is required:
|
||||||
|
|
||||||
|
```Swift
|
||||||
|
for i in 0..<someValue {
|
||||||
|
doTheThing()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (i, theThing) in theCollection.enumerated() {
|
||||||
|
doTheThing(with: theThing);
|
||||||
|
print("I am on iteration \(i) here...")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Objective-C/C++ and Swift
|
||||||
|
|
||||||
|
* **Be mindful of reference counts** when sharing Objective-C/C++ or Swift object instances or their data with external code:
|
||||||
|
* Explicitly increment the reference count on an object to ensure its not deallocated when the current function scope is left and to ensure that the pointer shared with C code is kept alive
|
||||||
|
* Balance that reference count on an object when the external code calls for its "destruction" by decrementing the reference count - **do not manually deallocate the object**, instead let its reference count go to `0`
|
||||||
|
* Wrap code that potentially creates a lot of new heap objects in autoreleasepools within a loop body to allow the reference counts to be evaluated early within each iteration
|
||||||
|
* This can potentially help keeping the memory footprint of each loop iteration at the same level
|
||||||
|
* Prefer language specific collection and string types over C array or character pointers.
|
||||||
|
* For Objective-C/C++, prefer `NSString`, `NSDictionary`, `NSArray`, `NSSet`, and others
|
||||||
|
* For Swift, prefer `String`, `Dictionary`, `Array`, `Set`, and others
|
||||||
|
* Swift and Cocoa types are bridged and interoperable
|
||||||
|
* **Prefer Objective-C/C++ blocks and Swift closures over lambda expressions**
|
||||||
|
|
||||||
|
### CMake
|
||||||
|
|
||||||
|
CMake underwent a big philosophical change with version 3 of the build system generator, which established the notion of “targets” with associated “target properties” that describe a target’s needs, including compiler arguments, linker flags, preprocessor definitions, and more.
|
||||||
|
|
||||||
|
The project’s build system was rewritten a few years ago to make full use of these “modern” CMake patterns and approaches, and thus requires all new CMake code to follow the same principles:
|
||||||
|
|
||||||
|
* Refer to “Modern CMake” (https://cliutils.gitlab.io/modern-cmake/README.html) for general guidelines about how to write modern CMake code.
|
||||||
|
* **Always prefer targets** and the use of target properties over magic global variables
|
||||||
|
* **Prefer generator expressions** over explicit branches in CMake code, if it makes the overall code clearer
|
||||||
|
* **Use 2 spaces for indentation** and spaces for orientation
|
||||||
|
* Use `UPPER_SNAKE_CASE` for cache variables, global variables, and file-local variables
|
||||||
|
* Use `snake_case` for function names and function-local variables
|
||||||
|
* Use an `_UNDERSCORE_PREFIX` for "private" variables that need to exist in the global or file-local scope
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Macros differ from functions in that they do not have a function-local scope. A macro body is effectively put into the scope it is called from and thus shares the variable scope.
|
||||||
|
|
||||||
|
* Pass variables to functions by their name, use `${}` dollar expansion to pass their value
|
||||||
|
* **Always use double quotes for strings**
|
||||||
|
* Prefer using functionality either available via CMake itself or available for specific generators over custom code that runs at configure time
|
||||||
|
* Particularly when multi-config generators for Visual Studio or Xcode are used, code running at configure time does not have access to the "actual" build configuration that will be used by the IDE and thus is incapable of achieving its potential goal anyway.
|
||||||
|
|
||||||
|
It is important to remember that CMake does not represent “the build system”, but rather is a “build system generator” that translates the abstract dependency tree of “targets” into an actual build system or IDE project.
|
||||||
|
|
||||||
|
### JSON and YAML
|
||||||
|
|
||||||
|
Both file formats are predominantly used for build system configuration or continuous integration, but are still subject to a limited set of rules:
|
||||||
|
|
||||||
|
* **Use schema files as much as possible** to ensure that a given JSON or YAML file uses correct structure and - as far as possible - valid values.
|
||||||
|
* Some editors can pull up common schema files when a loaded JSON or YAML file has a specific file name or sits in a directory of a specific name.
|
||||||
|
* Use `camelCase` for variable names
|
||||||
|
* **Exception:** Job names for GitHub action workflows commonly use `dash-case` for the job names.
|
||||||
|
* **Exception:** If the key of a variable is used for an underlying system e.g., to define environment variables in a shell environment that commonly uses `UPPER_SNAKE_CASE` names.
|
||||||
|
* Use **double quotes** for **key names** and **string values** in JSON files.
|
||||||
|
* Use **single quotes** for **complex strings in YAML files** that might otherwise not be correctly interpreted as strings, quoting is otherwise not necessary.
|
||||||
46
obs-studio/COMMITMENT
Normal file
46
obs-studio/COMMITMENT
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
GPL Cooperation Commitment
|
||||||
|
Version 1.0
|
||||||
|
|
||||||
|
Before filing or continuing to prosecute any legal proceeding or claim
|
||||||
|
(other than a Defensive Action) arising from termination of a Covered
|
||||||
|
License, we commit to extend to the person or entity ('you') accused
|
||||||
|
of violating the Covered License the following provisions regarding
|
||||||
|
cure and reinstatement, taken from GPL version 3. As used here, the
|
||||||
|
term 'this License' refers to the specific Covered License being
|
||||||
|
enforced.
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly
|
||||||
|
and finally terminates your license, and (b) permanently, if the
|
||||||
|
copyright holder fails to notify you of the violation by some
|
||||||
|
reasonable means prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you
|
||||||
|
have received notice of violation of this License (for any work)
|
||||||
|
from that copyright holder, and you cure the violation prior to 30
|
||||||
|
days after your receipt of the notice.
|
||||||
|
|
||||||
|
We intend this Commitment to be irrevocable, and binding and
|
||||||
|
enforceable against us and assignees of or successors to our
|
||||||
|
copyrights.
|
||||||
|
|
||||||
|
Definitions
|
||||||
|
|
||||||
|
'Covered License' means the GNU General Public License, version 2
|
||||||
|
(GPLv2), the GNU Lesser General Public License, version 2.1
|
||||||
|
(LGPLv2.1), or the GNU Library General Public License, version 2
|
||||||
|
(LGPLv2), all as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
'Defensive Action' means a legal proceeding or claim that We bring
|
||||||
|
against you in response to a prior proceeding or claim initiated by
|
||||||
|
you or your affiliate.
|
||||||
|
|
||||||
|
'We' means each contributor to this repository as of the date of
|
||||||
|
inclusion of this file, including subsidiaries of a corporate
|
||||||
|
contributor.
|
||||||
|
|
||||||
|
This work is available under a Creative Commons Attribution-ShareAlike
|
||||||
|
4.0 International license (https://creativecommons.org/licenses/by-sa/4.0/).
|
||||||
138
obs-studio/CONTRIBUTING.md
Normal file
138
obs-studio/CONTRIBUTING.md
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
OBS Studio Contribution Guidelines
|
||||||
|
============
|
||||||
|
|
||||||
|
OBS Studio accepts contributions via public **pull requests**, which encompass changes made to the project as well as a written description of the changes made and the motivation behind them.
|
||||||
|
|
||||||
|
Pull requests can be opened by any user with sufficient contribution permissions on the platform chosen by the project. Likewise, any user with sufficient access permissions is able to read, comment, and suggest changes to a pull request, but the ultimate decision for or against adopting the suggested changes lies with **project members**.
|
||||||
|
|
||||||
|
OBS Studio has shown steady growth of popularity, complexity, and scope throughout its existence. Contributions that demonstrate they have taken the aspects outlined in this document into account (including the impact to the maintainability of the project) will potentially, but not necessarily, have a higher chance of adoption.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Past acceptance of a change, including “prior art” that exists in the codebase, does not guarantee that future, similar, changes will be accepted, as the “state of the art” (code style guidelines, best practices, language standard) is constantly evolving and the project consciously chooses to evolve with them.
|
||||||
|
|
||||||
|
These guidelines attempt to provide a set of general rules that any contributor can choose to follow and implement before opening a pull request to reduce review effort and increase its chances for adoption.
|
||||||
|
|
||||||
|
## General Contribution Guidelines
|
||||||
|
|
||||||
|
The canonical language used by the project is “American English”, which mainly applies to source code and non-translated material. Examples include:
|
||||||
|
|
||||||
|
* Commit messages
|
||||||
|
* Names of constants, variables, and types
|
||||||
|
* Source code comments
|
||||||
|
* File names
|
||||||
|
|
||||||
|
Appropriate templates are presented to the user when opening a new issue report or pull request on GitHub which are required to be fully filled out to be taken into consideration by the project.
|
||||||
|
The template might require checking off items that might not be applicable e.g., the requirement to have the code properly formatted does not apply when the contribution has not changed any source code. **It is permissible and preferred to tick the box regardless**.
|
||||||
|
|
||||||
|
Authors of contributions to the OBS Project are expected to abide by the “OBS Project Code of Conduct” available at https://github.com/obsproject/obs-studio/blob/master/COC.rst.
|
||||||
|
|
||||||
|
## Pull Request Review Guidelines
|
||||||
|
|
||||||
|
In general reviews of pull requests are not limited to project members and can be undertaken by contributors and other GitHub users. Project members commonly coordinate reviews of pull requests at their own discretion in line with project needs and available bandwidth.
|
||||||
|
|
||||||
|
To reduce possible friction of the review process, a simple set of rules should be followed:
|
||||||
|
|
||||||
|
* **Do not** request reviews from specific community or project members yourself.
|
||||||
|
* It is permitted to ping other GitHub users to notify them of a PR and kindly ask them if they are willing (and able) to review it, all within reason.
|
||||||
|
* Reviewers might leave comments or ask for changes on specific lines of changed code. Once the comment has been addressed **do not unilaterally resolve** the comment.
|
||||||
|
* It is entirely the reviewer's decision whether they would consider the changes to be sufficient to address their concerns. Just having addressed a comment, does not resolve it.
|
||||||
|
* As a reviewer, make sure to regularly review the comments received on your reviews, check the code changes, and resolve your comments once they have been addressed. **Don't leave pull request authors hanging.**
|
||||||
|
* Project members will usually ping reviewers about unresolved review comments and resolve such comments themselves to unblock PRs from being merged.
|
||||||
|
* When addressing review comments through code changes, try to avoid force-pushes and use temporary commits which can be squashed later (see below).
|
||||||
|
* Unless necessary, avoid rebasing a pull request branch unless it is absolutely necessary. Rebasing will pull in changes from the base branch into the "changes since last push" of the pull request, making it harder for reviewers to check what changes have been pushed.
|
||||||
|
|
||||||
|
## Commit Authoring Guidelines
|
||||||
|
|
||||||
|
The project uses the “50/72” standard for commit messages, which requires that a commit title uses no more than 50 characters, while any consecutive line used for the commit description is allowed to use a maximum of 72 characters (with long lines manually broken up).
|
||||||
|
|
||||||
|
The “anatomy” of a correct commit message with description looks like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
prefix: This is a commit title using 50 characters
|
||||||
|
|
||||||
|
This is a commit description line that is allowed to use up to 72 chars
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefixes are commonly defined by the “module” the commit applies changes to, which has repercussions as to how commit contents are authored (see below).
|
||||||
|
|
||||||
|
* Changes made to code for “libobs” should use the “libobs” prefix, similarly changes made to “obs-ffmpeg” use “obs-ffmpeg”.
|
||||||
|
* Changes made to multiple modules in the “plugins” directory can use the “plugins” prefix, particularly if the changes are of a similar nature
|
||||||
|
* Changes made to files involved in continuous integration (CI) should use the “CI” prefix regardless of the directory name.
|
||||||
|
* Changes made to files used by the CMake build system should use the “cmake” prefix”
|
||||||
|
|
||||||
|
When in doubt about what the correct prefix for a change might be, the existing commit history might be of help, however project guidelines might have changed in the interim and thus a “past” commit message might not be acceptable for new contributions.
|
||||||
|
|
||||||
|
### Commit Messages
|
||||||
|
|
||||||
|
The commit message is the primary place for documentation of changes, not only as a summary of “what” was changed, but even more importantly “why” the changes were made and “why” specifically in the way they have been made.
|
||||||
|
|
||||||
|
As such the commit message should be written in such a way that it could easily pass as the “Description” and “Motivation and Context” required when opening a new pull request (and indeed platforms like GitHub will automatically copy the commit message into the opening post for a pull request with a single commit).
|
||||||
|
|
||||||
|
This ensures that documentation and context are encoded within the source tree and do not require external documents or platforms to ensure this information is not lost.
|
||||||
|
|
||||||
|
Many of these ideas match the ideas outlined in https://chris.beams.io/git-commit.
|
||||||
|
|
||||||
|
### Commit Content Authoring Guidelines
|
||||||
|
|
||||||
|
#### Code Style
|
||||||
|
|
||||||
|
All code contributions to the project need to follow code style guidelines appropriate to the languages used by the changed files. For C and C++ the project has chosen to adopt well-established code style guidelines as its baseline with specific exceptions and changes outlined in a [separate document](https://github.com/obsproject/obs-studio/blob/master/CODESTYLE.md).
|
||||||
|
|
||||||
|
The baseline rules for the common languages used by the project are:
|
||||||
|
|
||||||
|
* C code needs to follow the Linux Kernel Coding Style (https://github.com/torvalds/linux/blob/master/Documentation/process/coding-style.rst)
|
||||||
|
* C++ code needs to follow OBS Studios’ style guide. Familiarity with the Google C++ Style Guide (https://google.github.io/styleguide/cppguide.html) can serve as a baseline.
|
||||||
|
* Objective-C and Objective-C++ code needs to follow Apple’s ObjectiveC Conventions (https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html)
|
||||||
|
* Swift code needs to follow the API Design Guidelines (https://www.swift.org/documentation/api-design-guidelines/)
|
||||||
|
* **The column size limit for all code is 120 characters**
|
||||||
|
|
||||||
|
To help with the formatting of code, the project contains configuration files for code-formatters of several languages:
|
||||||
|
|
||||||
|
* `clang-format` can be used to correctly format C, C++, Objective-C, and Objective-C++ soure code
|
||||||
|
* `swift-format` can be used to correctly format Swift source code
|
||||||
|
* `gersemi` can be used to correctly format CMake source code
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> The “build-aux” directory in the project’s root directory contains scripts that can automate this process and are used by the project’s CI infrastructure to automatically check all contributions that changed corresponding source code files.
|
||||||
|
|
||||||
|
#### Commit Contents
|
||||||
|
|
||||||
|
The changes contained in a commit should attempt to represent logical “units of change” that make sense in isolation and do not necessarily require any preceding or following commits to work. Indeed if a single commit would potentially leave the project in a broken state (e.g., the project cannot be compiled, the application not run, or basic application functionality cannot function) then it is not a full “unit of change”.
|
||||||
|
|
||||||
|
* Changes to misspelled words or purely aesthetic changes should in principle not be individually capable of breaking the project in such a way and are permitted to be grouped into a single “large” commit
|
||||||
|
* If a larger change requires some foundation of fixes to the existing code to make sense, those fixes represent a “unit of change” that should be decoupled from the functional changes made on top of them. Even better, such a change should be encapsulated in its own, separate, pull request.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> During review and collaboration the need for additional changes can arise quickly. It is permitted (and preferred) to temporarily ignore these guidelines to quickly provide changes required to address review comments and add many small, iterative, commits to the pull request.
|
||||||
|
>
|
||||||
|
> Once all review comments have been addressed, the changes need to be squashed into existing commits or alternatively a new set of commits can be created from all accumulated changes to restore compliance with the guidelines again.
|
||||||
|
|
||||||
|
#### Co-Authorship
|
||||||
|
|
||||||
|
Sometimes an open pull request might have been "lost to time" even though its contents might still be a meaningful and positive change. When picking up such a change and authoring a new pull request to "finish it", the original author should be retained as much as possible:
|
||||||
|
|
||||||
|
* The original commit can be cherry-picked, which will retain the author information and only change the committer information
|
||||||
|
* If the original commit needs to be adjusted and changed as it will not apply to an updated code-base, its original author should be added as a "Co-Author" to the commit message.
|
||||||
|
|
||||||
|
```
|
||||||
|
libobs: Refactor audio implementation
|
||||||
|
|
||||||
|
|
||||||
|
Additional commit description
|
||||||
|
<..>
|
||||||
|
Co-authored-by: Name <git@address.TLD>
|
||||||
|
```
|
||||||
|
|
||||||
|
## AI/Machine Learning Policy
|
||||||
|
|
||||||
|
AI/machine learning systems such as Copilot, ChatGPT, and Claude, are prone to generating plausible-sounding, but wrong code that makes incorrect assumptions about OBS internals or APIs it interfaces with.
|
||||||
|
|
||||||
|
This means code generated by such systems will require human review and is likely to require human intervention. If the submitter is unable to undertake that work themselves due to a lack of understanding of the OBS codebase and/or programming, the submission has a high likelihood of being invalid. Such invalid submissions end up taking maintainers' time to review and respond away from legitimate submissions.
|
||||||
|
|
||||||
|
Additionally, such systems have been demonstrated to reproduce code contained in the training data, which may have been originally published under a license that would prohibit its inclusion in OBS.
|
||||||
|
|
||||||
|
Because of the above concerns, we have opted to take the following policy towards submissions with regard to the use of these AI tools:
|
||||||
|
|
||||||
|
* All text and content submitted to our project, which includes code, descriptions, issues, and comments, must be **human written**.
|
||||||
|
* The use of GitHub Copilot and other assistive AI technologies is heavily discouraged.
|
||||||
|
* Low-effort or incorrect submissions that are determined to have been generated by, or created with aid of such systems may lead to a ban from contributing to the repository or project as a whole.
|
||||||
339
obs-studio/COPYING
Normal file
339
obs-studio/COPYING
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
||||||
1
obs-studio/INSTALL
Normal file
1
obs-studio/INSTALL
Normal file
@ -0,0 +1 @@
|
|||||||
|
For install instructions please visit https://github.com/obsproject/obs-studio/wiki/Install-Instructions
|
||||||
79
obs-studio/README.rst
Normal file
79
obs-studio/README.rst
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
OBS Studio <https://obsproject.com>
|
||||||
|
===================================
|
||||||
|
|
||||||
|
.. image:: https://github.com/obsproject/obs-studio/actions/workflows/push.yaml/badge.svg?branch=master
|
||||||
|
:alt: OBS Studio Build Status - GitHub Actions
|
||||||
|
:target: https://github.com/obsproject/obs-studio/actions/workflows/push.yaml?query=branch%3Amaster
|
||||||
|
|
||||||
|
.. image:: https://badges.crowdin.net/obs-studio/localized.svg
|
||||||
|
:alt: OBS Studio Translation Project Progress
|
||||||
|
:target: https://crowdin.com/project/obs-studio
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/discord/348973006581923840.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2
|
||||||
|
:alt: OBS Studio Discord Server
|
||||||
|
:target: https://obsproject.com/discord
|
||||||
|
|
||||||
|
What is OBS Studio?
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
OBS Studio is software designed for capturing, compositing, encoding,
|
||||||
|
recording, and streaming video content, efficiently.
|
||||||
|
|
||||||
|
It's distributed under the GNU General Public License v2 (or any later
|
||||||
|
version) - see the accompanying COPYING file for more details.
|
||||||
|
|
||||||
|
Quick Links
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- Website: https://obsproject.com
|
||||||
|
|
||||||
|
- Help/Documentation/Guides: https://github.com/obsproject/obs-studio/wiki
|
||||||
|
|
||||||
|
- Forums: https://obsproject.com/forum/
|
||||||
|
|
||||||
|
- Build Instructions: https://github.com/obsproject/obs-studio/wiki/Install-Instructions
|
||||||
|
|
||||||
|
- Developer/API Documentation: https://obsproject.com/docs
|
||||||
|
|
||||||
|
- Donating/backing/sponsoring: https://obsproject.com/contribute
|
||||||
|
|
||||||
|
- Bug Tracker: https://github.com/obsproject/obs-studio/issues
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
------------
|
||||||
|
|
||||||
|
- If you would like to help fund or sponsor the project, you can do so
|
||||||
|
via `Patreon <https://www.patreon.com/obsproject>`_, `OpenCollective
|
||||||
|
<https://opencollective.com/obsproject>`_, or `PayPal
|
||||||
|
<https://www.paypal.me/obsproject>`_. See our `contribute page
|
||||||
|
<https://obsproject.com/contribute>`_ for more information.
|
||||||
|
|
||||||
|
- If you wish to contribute code to the project, please make sure to
|
||||||
|
read the coding and commit guidelines:
|
||||||
|
https://github.com/obsproject/obs-studio/blob/master/CONTRIBUTING.md
|
||||||
|
|
||||||
|
- Code for the project follows the code style guidelines, located
|
||||||
|
here: https://github.com/obsproject/obs-studio/blob/master/CODESTYLE.md
|
||||||
|
|
||||||
|
- Developer/API documentation can be found here:
|
||||||
|
https://obsproject.com/docs
|
||||||
|
|
||||||
|
- If you wish to contribute translations, do not submit pull requests.
|
||||||
|
Instead, please use Crowdin. For more information read this page:
|
||||||
|
https://obsproject.com/wiki/How-To-Contribute-Translations-For-OBS
|
||||||
|
|
||||||
|
- Contributors to OBS Studio and related repositories are expected to
|
||||||
|
follow our Code of Conduct, which can be read here:
|
||||||
|
https://github.com/obsproject/obs-studio/blob/master/COC.rst
|
||||||
|
|
||||||
|
- Other ways to contribute are by helping people out with support on
|
||||||
|
our forums or in our community chat. Please limit support to topics
|
||||||
|
you fully understand -- bad advice is worse than no advice. When it
|
||||||
|
comes to something that you don't fully know or understand, please
|
||||||
|
defer to the official help or official channels.
|
||||||
|
|
||||||
|
|
||||||
|
SAST Tools
|
||||||
|
----------
|
||||||
|
|
||||||
|
`PVS-Studio <https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source>`_ - static analyzer for C, C++, C#, and Java code.
|
||||||
3
obs-studio/build-aux/.functions/log_debug
Normal file
3
obs-studio/build-aux/.functions/log_debug
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||||
|
|
||||||
|
if (( _loglevel > 2 )) print -PR -e -- "${CI:+::debug::}%F{220}DEBUG: ${@}%f"
|
||||||
3
obs-studio/build-aux/.functions/log_error
Normal file
3
obs-studio/build-aux/.functions/log_error
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
local icon=' ✖︎ '
|
||||||
|
|
||||||
|
print -u2 -PR "${CI:+::error::}%F{1} ${icon} %f ${@}"
|
||||||
16
obs-studio/build-aux/.functions/log_group
Normal file
16
obs-studio/build-aux/.functions/log_group
Normal file
@ -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 ${@}
|
||||||
|
}
|
||||||
7
obs-studio/build-aux/.functions/log_info
Normal file
7
obs-studio/build-aux/.functions/log_info
Normal file
@ -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"
|
||||||
|
}
|
||||||
7
obs-studio/build-aux/.functions/log_output
Normal file
7
obs-studio/build-aux/.functions/log_output
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||||
|
|
||||||
|
if (( _loglevel > 0 )) {
|
||||||
|
local icon=''
|
||||||
|
|
||||||
|
print -PR " ${(r:5:)icon} ${@}"
|
||||||
|
}
|
||||||
7
obs-studio/build-aux/.functions/log_status
Normal file
7
obs-studio/build-aux/.functions/log_status
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
if (( ! ${+_loglevel} )) typeset -g _loglevel=1
|
||||||
|
|
||||||
|
if (( _loglevel > 0 )) {
|
||||||
|
local icon=' >'
|
||||||
|
|
||||||
|
print -PR "%F{2} ${(r:5:)icon}%f ${@}"
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user