[CmdletBinding()]
|
param(
|
[Parameter(Mandatory = $true)]
|
[string]$Python,
|
[Parameter(Mandatory = $true)]
|
[string]$PyInstallerExecutable,
|
[Parameter(Mandatory = $true)]
|
[string]$ApprovedBuilderReceipt,
|
[Parameter(Mandatory = $true)]
|
[string]$OutputRoot,
|
[Parameter(Mandatory = $true)]
|
[string]$ApprovedSourceReceipt,
|
[string]$SourceArtifactManifest = (Join-Path (Split-Path -Parent $PSCommandPath) 'source-artifact-manifest.json'),
|
[switch]$ValidateOnly
|
)
|
|
$ErrorActionPreference = 'Stop'
|
$sourceRoot = (Resolve-Path -LiteralPath (Split-Path -Parent $PSCommandPath)).Path
|
$expectedManifestPath = Join-Path $sourceRoot 'source-artifact-manifest.json'
|
$resolvedSourceManifest = (Resolve-Path -LiteralPath $SourceArtifactManifest).Path
|
if ($resolvedSourceManifest -cne $expectedManifestPath) {
|
throw 'Only the bundled reviewed source artifact manifest is accepted.'
|
}
|
|
function Get-StrictJson([string]$Path) {
|
$text = [System.IO.File]::ReadAllText($Path, [System.Text.UTF8Encoding]::new($false, $true))
|
return $text | ConvertFrom-Json
|
}
|
|
function Test-PathWithin([string]$Candidate, [string]$Root) {
|
$prefix = $Root.TrimEnd([System.IO.Path]::DirectorySeparatorChar) + [System.IO.Path]::DirectorySeparatorChar
|
return $Candidate.Equals($Root, [StringComparison]::OrdinalIgnoreCase) -or
|
$Candidate.StartsWith($prefix, [StringComparison]::OrdinalIgnoreCase)
|
}
|
|
function Assert-ExactSourceTree([string]$Root, [string]$ManifestPath, [string[]]$ExpectedFiles) {
|
$rootItem = Get-Item -LiteralPath $Root
|
if ($rootItem.Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
throw 'Source root must not be a reparse path.'
|
}
|
$actualFiles = @()
|
foreach ($item in @(Get-ChildItem -LiteralPath $Root -Force -Recurse)) {
|
if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
throw 'Source tree contains a reparse path.'
|
}
|
if (-not $item.PSIsContainer -and $item.FullName -cne $ManifestPath) {
|
$actualFiles += $item.FullName.Substring($Root.Length + 1).Replace('\', '/')
|
}
|
}
|
if (Compare-Object -CaseSensitive ($ExpectedFiles | Sort-Object) @($actualFiles | Sort-Object)) {
|
throw 'Actual source tree file set does not exactly match the approved manifest.'
|
}
|
}
|
|
function Assert-ExactSourceSnapshot([string]$Root, [string]$ManifestPath, [string[]]$ExpectedFiles, [object]$Manifest) {
|
Assert-ExactSourceTree $Root $ManifestPath $ExpectedFiles
|
foreach ($entry in $Manifest.files) {
|
if ($entry.path -notmatch '^[A-Za-z0-9._/-]+$' -or $entry.path.Contains('..') -or
|
$entry.sha256 -notmatch '^[A-F0-9]{64}$' -or $entry.bytes -lt 1) {
|
throw 'Source artifact manifest contains an invalid entry.'
|
}
|
$candidate = [System.IO.Path]::GetFullPath((Join-Path $Root $entry.path))
|
if (-not $candidate.StartsWith($Root + [System.IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) {
|
throw 'Source artifact path escaped its root.'
|
}
|
$item = Get-Item -LiteralPath $candidate
|
if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
|
$item.Length -ne $entry.bytes -or
|
(Get-FileHash -Algorithm SHA256 -LiteralPath $candidate).Hash -cne $entry.sha256) {
|
throw 'Source artifact hash mismatch.'
|
}
|
}
|
}
|
|
$resolvedOutput = [System.IO.Path]::GetFullPath($OutputRoot)
|
if (-not [System.IO.Path]::IsPathRooted($resolvedOutput) -or $resolvedOutput.StartsWith('\\')) {
|
throw 'OutputRoot must be an absolute local path.'
|
}
|
$sourceReceiptPath = (Resolve-Path -LiteralPath $ApprovedSourceReceipt).Path
|
$sourceReceiptItem = Get-Item -LiteralPath $sourceReceiptPath
|
if ($sourceReceiptItem.PSIsContainer -or ($sourceReceiptItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
|
(Test-PathWithin $sourceReceiptPath $sourceRoot) -or (Test-PathWithin $sourceReceiptPath $resolvedOutput)) {
|
throw 'Approved source receipt must be an external regular non-reparse file.'
|
}
|
$sourceReceipt = Get-StrictJson $sourceReceiptPath
|
$expectedSourceReceiptKeys = @(
|
'schema', 'task_id', 'approval_scope', 'approved_by_role', 'status',
|
'source_artifact_manifest_bytes', 'source_artifact_manifest_sha256'
|
)
|
if ((Compare-Object ($expectedSourceReceiptKeys | Sort-Object) @($sourceReceipt.PSObject.Properties.Name | Sort-Object)) -or
|
$sourceReceipt.schema -ne 1 -or
|
$sourceReceipt.task_id -cne 'DEV-PROJECT-INFO-BILI-AUTHENTICATED-SESSION-DOWNLOAD-20260805-001' -or
|
$sourceReceipt.approval_scope -cne 'controlled-build-source-manifest' -or
|
$sourceReceipt.approved_by_role -notin @('dev.reviewer.project', 'project.admin') -or
|
$sourceReceipt.status -cne 'APPROVED' -or
|
$sourceReceipt.source_artifact_manifest_sha256 -notmatch '^[A-F0-9]{64}$' -or
|
$sourceReceipt.source_artifact_manifest_bytes -ne (Get-Item -LiteralPath $resolvedSourceManifest).Length -or
|
$sourceReceipt.source_artifact_manifest_sha256 -cne (Get-FileHash -Algorithm SHA256 -LiteralPath $resolvedSourceManifest).Hash) {
|
throw 'External approved source receipt does not match the source manifest.'
|
}
|
|
$expectedSourceFiles = @(
|
'__init__.py', 'background.js', 'build_host.ps1', 'config.example.json',
|
'constants.py', 'dependencies/dependency-artifact-manifest.json',
|
'dependencies/yt_dlp-2026.7.4-py3-none-any.whl',
|
'formal_legacy_identity_manifest.py', 'install_native_host.ps1', 'job.py', 'manifest.json',
|
'native-host-manifest.template.json', 'native_host.py', 'protocol.py',
|
'queue-producer.example.json', 'queue_producer.py', 'queue_state.py',
|
'sidepanel.css', 'sidepanel.html', 'sidepanel.js', 'worker.py'
|
)
|
Assert-ExactSourceTree $sourceRoot $resolvedSourceManifest $expectedSourceFiles
|
$dependencyManifestPath = Join-Path $sourceRoot 'dependencies/dependency-artifact-manifest.json'
|
$dependencyManifestItem = Get-Item -LiteralPath $dependencyManifestPath
|
$sourceManifest = Get-StrictJson $resolvedSourceManifest
|
if ($sourceManifest.schema -ne 1 -or $sourceManifest.scope -cne 'generic-bilibili-queue' -or
|
$sourceManifest.extension_id -cne 'oidmclckpdmpabbfedplkbdplmfcenbb' -or
|
$sourceManifest.extension_build -cne 'project-info-bili-auth-ingress/1.2.25+20260829.generic.v027' -or
|
$sourceManifest.host_build -cne 'project-info-bili-auth-native-host/1.2.25+20260829.generic.v027' -or
|
$sourceManifest.dependency_artifact_manifest_bytes -ne $dependencyManifestItem.Length -or
|
$sourceManifest.dependency_artifact_manifest_sha256 -cne (Get-FileHash -Algorithm SHA256 -LiteralPath $dependencyManifestPath).Hash) {
|
throw 'Source artifact manifest identity mismatch.'
|
}
|
$metadataContract = $sourceManifest.archive_metadata_contract
|
$expectedMetadataContractKeys = @(
|
'schema', 'root', 'relative_files', 'distribution_name', 'distribution_version',
|
'allowed_type_codes', 'source_date_epoch', 'tree_hash_algorithm', 'canonical_tree_sha256'
|
)
|
if ($null -eq $metadataContract -or
|
(Compare-Object ($expectedMetadataContractKeys | Sort-Object) @($metadataContract.PSObject.Properties.Name | Sort-Object)) -or
|
$metadataContract.schema -ne 1 -or
|
$metadataContract.root -cne 'yt_dlp-2026.7.4.dist-info' -or
|
(Compare-Object -CaseSensitive @(
|
'INSTALLER', 'METADATA', 'RECORD', 'REQUESTED', 'WHEEL',
|
'entry_points.txt', 'licenses/LICENSE'
|
) @($metadataContract.relative_files)) -or
|
$metadataContract.distribution_name -cne 'yt-dlp' -or
|
$metadataContract.distribution_version -cne '2026.7.4' -or
|
(Compare-Object -CaseSensitive @('b', 'x') @($metadataContract.allowed_type_codes)) -or
|
$metadataContract.source_date_epoch -ne 1786207924 -or
|
$metadataContract.tree_hash_algorithm -cne 'sha256(path-utf8,nul,decimal-bytes-ascii,nul,payload-sha256-lower-hex-ascii,lf)-upper-hex-v1' -or
|
$metadataContract.canonical_tree_sha256 -notmatch '^[A-F0-9]{64}$') {
|
throw 'Source artifact metadata contract mismatch.'
|
}
|
$actualSourceFiles = @($sourceManifest.files | ForEach-Object { $_.path } | Sort-Object)
|
if (Compare-Object -CaseSensitive ($expectedSourceFiles | Sort-Object) $actualSourceFiles) {
|
throw 'Source artifact manifest file set mismatch.'
|
}
|
Assert-ExactSourceSnapshot $sourceRoot $resolvedSourceManifest $expectedSourceFiles $sourceManifest
|
|
$dependencyManifest = Get-StrictJson $dependencyManifestPath
|
$expectedDependencyKeys = @(
|
'schema', 'task_id', 'python_implementation', 'distribution', 'version',
|
'design_version', 'acquisition', 'unconditional_runtime_dependencies', 'wheels'
|
)
|
$expectedAcquisitionKeys = @('source', 'index', 'method', 'acquired_at')
|
$expectedWheelKeys = @('filename', 'distribution', 'version', 'bytes', 'sha256')
|
if ((Compare-Object ($expectedDependencyKeys | Sort-Object) @($dependencyManifest.PSObject.Properties.Name | Sort-Object)) -or
|
(Compare-Object ($expectedAcquisitionKeys | Sort-Object) @($dependencyManifest.acquisition.PSObject.Properties.Name | Sort-Object)) -or
|
$dependencyManifest.schema -ne 1 -or
|
$dependencyManifest.task_id -cne 'DEV-PROJECT-INFO-BILI-AUTHENTICATED-SESSION-DOWNLOAD-20260805-001' -or
|
$dependencyManifest.python_implementation -cne 'CPython' -or
|
$dependencyManifest.distribution -cne 'yt-dlp' -or
|
$dependencyManifest.version -cne '2026.7.4' -or
|
$dependencyManifest.design_version -cne '2026.07.04' -or
|
$dependencyManifest.acquisition.source -cne 'PyPI' -or
|
$dependencyManifest.acquisition.index -cne 'https://pypi.org/simple' -or
|
$dependencyManifest.acquisition.method -cne 'pip download --no-deps --only-binary=:all:' -or
|
@($dependencyManifest.unconditional_runtime_dependencies).Count -ne 0 -or
|
@($dependencyManifest.wheels).Count -ne 1) {
|
throw 'Dependency artifact manifest identity or closure mismatch.'
|
}
|
$wheelEntry = @($dependencyManifest.wheels)[0]
|
if ((Compare-Object ($expectedWheelKeys | Sort-Object) @($wheelEntry.PSObject.Properties.Name | Sort-Object)) -or
|
$wheelEntry.filename -cne 'yt_dlp-2026.7.4-py3-none-any.whl' -or
|
$wheelEntry.distribution -cne 'yt-dlp' -or
|
$wheelEntry.version -cne '2026.7.4' -or
|
$wheelEntry.bytes -lt 1 -or
|
$wheelEntry.sha256 -notmatch '^[A-F0-9]{64}$') {
|
throw 'Dependency wheel entry mismatch.'
|
}
|
$dependencyWheelhouse = Split-Path -Parent $dependencyManifestPath
|
$wheelPath = Join-Path $dependencyWheelhouse $wheelEntry.filename
|
$wheelItem = Get-Item -LiteralPath $wheelPath
|
$wheelhouseFiles = @(Get-ChildItem -LiteralPath $dependencyWheelhouse -File -Force)
|
if ($wheelhouseFiles.Count -ne 2 -or
|
(Compare-Object @('dependency-artifact-manifest.json', $wheelEntry.filename) @($wheelhouseFiles.Name | Sort-Object)) -or
|
($wheelItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
|
$wheelItem.Length -ne $wheelEntry.bytes -or
|
(Get-FileHash -Algorithm SHA256 -LiteralPath $wheelPath).Hash -cne $wheelEntry.sha256) {
|
throw 'Dependency wheelhouse file set or hash mismatch.'
|
}
|
|
$entry = Join-Path $sourceRoot 'native_host.py'
|
$resolvedPython = (Resolve-Path -LiteralPath $Python).Path
|
$pythonItem = Get-Item -LiteralPath $resolvedPython
|
$resolvedPyInstallerExecutable = (Resolve-Path -LiteralPath $PyInstallerExecutable).Path
|
$pyInstallerItem = Get-Item -LiteralPath $resolvedPyInstallerExecutable
|
$builderReceiptPath = (Resolve-Path -LiteralPath $ApprovedBuilderReceipt).Path
|
$builderReceiptItem = Get-Item -LiteralPath $builderReceiptPath
|
$expectedBuilderReceiptBytes = 2027
|
$expectedBuilderReceiptSha256 = 'B65F4184E8782394F2CC27C47CB8656C942E366FD80E8FA7A548CE5A3367BACF'
|
$expectedBuilderPythonBytes = 262144
|
$expectedBuilderPythonSha256 = '5912D0884B23C0343983A864C6064242391E2265536F50B88624857E353882C9'
|
$expectedPyInstallerBytes = 108469
|
$expectedPyInstallerSha256 = 'D5DC4427C5E5D417457DAE6FD8B50EF2AFA0A4CE5FDFD5767AB20C5B56555C39'
|
if (($pythonItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -or $pythonItem.PSIsContainer -or
|
($pyInstallerItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -or $pyInstallerItem.PSIsContainer -or
|
$builderReceiptItem.PSIsContainer -or ($builderReceiptItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
|
(Test-PathWithin $builderReceiptPath $sourceRoot) -or (Test-PathWithin $builderReceiptPath $resolvedOutput) -or
|
$pythonItem.Length -ne $expectedBuilderPythonBytes -or
|
(Get-FileHash -Algorithm SHA256 -LiteralPath $resolvedPython).Hash -cne $expectedBuilderPythonSha256 -or
|
$pyInstallerItem.Length -ne $expectedPyInstallerBytes -or
|
(Get-FileHash -Algorithm SHA256 -LiteralPath $resolvedPyInstallerExecutable).Hash -cne $expectedPyInstallerSha256 -or
|
$builderReceiptItem.Length -ne $expectedBuilderReceiptBytes -or
|
(Get-FileHash -Algorithm SHA256 -LiteralPath $builderReceiptPath).Hash -cne $expectedBuilderReceiptSha256) {
|
throw 'Pinned builder executables or external provision receipt do not match the approved environment.'
|
}
|
$builderReceiptText = [System.IO.File]::ReadAllText($builderReceiptPath, [System.Text.UTF8Encoding]::new($false, $true))
|
if (-not $builderReceiptText.Contains('request_handoff: HANDOFF-INFOADMIN-MGADMIN-BILI-PYINSTALLER-6-15-0-OFFLINE-ENV-PROVISION-20260806-001') -or
|
-not $builderReceiptText.Contains("environment_python: ``$resolvedPython``") -or
|
-not $builderReceiptText.Contains("environment_python_bytes_sha256: ``$expectedBuilderPythonBytes/$($expectedBuilderPythonSha256.ToLowerInvariant())``") -or
|
-not $builderReceiptText.Contains("pyinstaller_executable: ``$resolvedPyInstallerExecutable``") -or
|
-not $builderReceiptText.Contains("pyinstaller_executable_bytes_sha256: ``$expectedPyInstallerBytes/$($expectedPyInstallerSha256.ToLowerInvariant())``")) {
|
throw 'External builder provision receipt does not bind the supplied executables.'
|
}
|
if (Test-Path -LiteralPath $resolvedOutput) {
|
throw 'OutputRoot already exists; overwrite is forbidden.'
|
}
|
$pythonEnvironment = @{}
|
foreach ($variable in @(Get-ChildItem Env: | Where-Object { $_.Name -like 'PYTHON*' })) {
|
$pythonEnvironment[$variable.Name] = $variable.Value
|
}
|
$outputCreated = $false
|
try {
|
foreach ($name in @($pythonEnvironment.Keys)) {
|
Remove-Item -LiteralPath "Env:$name" -ErrorAction SilentlyContinue
|
}
|
$env:PYTHONDONTWRITEBYTECODE = '1'
|
$pyInstallerVersion = & $resolvedPython -I -B -c "import importlib.metadata; print(importlib.metadata.version('PyInstaller'))"
|
if ($LASTEXITCODE -ne 0 -or $pyInstallerVersion.Trim() -cne '6.15.0') {
|
throw 'PyInstaller 6.15.0 is required; no unpinned builder is allowed.'
|
}
|
$pipVersion = & $resolvedPython -I -B -m pip --version
|
if ($LASTEXITCODE -ne 0 -or -not $pipVersion) {
|
throw 'The pinned builder must provide pip for isolated offline dependency installation.'
|
}
|
$wheelProbeScript = @'
|
import email.parser
|
import json
|
import sys
|
import zipfile
|
|
with zipfile.ZipFile(sys.argv[1]) as wheel:
|
metadata_entries = [name for name in wheel.namelist() if name.endswith('.dist-info/METADATA')]
|
if len(metadata_entries) != 1:
|
raise SystemExit(7)
|
metadata = email.parser.BytesParser().parsebytes(wheel.read(metadata_entries[0]))
|
requirements = metadata.get_all('Requires-Dist') or []
|
print(json.dumps({
|
'metadata_entry': metadata_entries[0],
|
'name': metadata.get('Name'),
|
'version': metadata.get('Version'),
|
'unconditional': [item for item in requirements if 'extra ==' not in item],
|
}))
|
'@
|
$wheelProbeJson = & $resolvedPython -I -B -c $wheelProbeScript $wheelPath
|
if ($LASTEXITCODE -ne 0) {
|
throw 'Dependency wheel metadata could not be verified.'
|
}
|
$wheelProbe = $wheelProbeJson | ConvertFrom-Json
|
if ($wheelProbe.metadata_entry -cne 'yt_dlp-2026.7.4.dist-info/METADATA' -or
|
$wheelProbe.name -cne 'yt-dlp' -or $wheelProbe.version -cne '2026.7.4' -or
|
@($wheelProbe.unconditional).Count -ne 0) {
|
throw 'Dependency wheel metadata or unconditional closure mismatch.'
|
}
|
if ($ValidateOnly) {
|
[pscustomobject]@{
|
result = 'VALIDATION_PASS_ONLY'
|
pyinstaller_version = '6.15.0'
|
yt_dlp_version = '2026.7.4'
|
builder_provision_receipt_sha256 = $expectedBuilderReceiptSha256
|
dependency_artifact_manifest_sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $dependencyManifestPath).Hash
|
} | ConvertTo-Json -Compress
|
return
|
}
|
[System.IO.Directory]::CreateDirectory($resolvedOutput) | Out-Null
|
$outputCreated = $true
|
$workRoot = Join-Path $resolvedOutput '.build'
|
$specRoot = Join-Path $resolvedOutput '.spec'
|
$dependencyRoot = Join-Path $resolvedOutput '.deps'
|
$previousSourceDateEpoch = [Environment]::GetEnvironmentVariable('SOURCE_DATE_EPOCH', 'Process')
|
try {
|
$env:SOURCE_DATE_EPOCH = [string]$metadataContract.source_date_epoch
|
& $resolvedPython -I -B -m pip install `
|
--isolated `
|
--disable-pip-version-check `
|
--no-index `
|
--find-links $dependencyWheelhouse `
|
--no-deps `
|
--no-compile `
|
--only-binary=:all: `
|
--target $dependencyRoot `
|
'yt-dlp==2026.7.4'
|
$pipExitCode = $LASTEXITCODE
|
} finally {
|
if ($null -eq $previousSourceDateEpoch) {
|
Remove-Item Env:SOURCE_DATE_EPOCH -ErrorAction SilentlyContinue
|
} else {
|
$env:SOURCE_DATE_EPOCH = $previousSourceDateEpoch
|
}
|
}
|
if ($pipExitCode -ne 0) {
|
throw "Offline yt-dlp installation failed with exit code $pipExitCode."
|
}
|
$dependencyRootItem = Get-Item -LiteralPath $dependencyRoot
|
if (($dependencyRootItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
|
$dependencyRootItem.PSIsContainer -eq $false -or
|
(Split-Path -Parent $dependencyRootItem.FullName) -cne $resolvedOutput) {
|
throw 'Temporary dependency root is not the owned canonical .deps directory.'
|
}
|
foreach ($dependencyItem in @(Get-ChildItem -LiteralPath $dependencyRoot -Force -Recurse)) {
|
if ($dependencyItem.Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
throw 'Temporary dependency tree contains a reparse path.'
|
}
|
}
|
$dependencyTreeProbeScript = @'
|
import base64
|
import csv
|
import hashlib
|
import importlib.metadata as metadata
|
import io
|
import json
|
import pathlib
|
import sys
|
import zipfile
|
|
root = pathlib.Path(sys.argv[1]).resolve(strict=True)
|
owned = pathlib.Path(sys.argv[2]).resolve(strict=True)
|
wheel = pathlib.Path(sys.argv[3]).resolve(strict=True)
|
if root.name != '.deps' or root.parent != owned:
|
raise SystemExit(31)
|
|
expected = {}
|
record_name = 'yt_dlp-2026.7.4.dist-info/RECORD'
|
with zipfile.ZipFile(wheel) as archive:
|
for name in archive.namelist():
|
if name.endswith('/'):
|
continue
|
payload = archive.read(name)
|
if '.data/data/' in name:
|
relative = name.split('.data/data/', 1)[1]
|
elif '.data/scripts/' in name:
|
continue
|
elif '.data/' in name:
|
raise SystemExit(32)
|
else:
|
relative = name
|
if relative != record_name:
|
expected[relative] = (len(payload), hashlib.sha256(payload).digest())
|
|
for relative, payload in {
|
'yt_dlp-2026.7.4.dist-info/INSTALLER': b'pip\n',
|
'yt_dlp-2026.7.4.dist-info/REQUESTED': b'',
|
}.items():
|
expected[relative] = (len(payload), hashlib.sha256(payload).digest())
|
|
record_path = root / record_name
|
if not record_path.is_file():
|
raise SystemExit(33)
|
rows = list(csv.reader(io.StringIO(record_path.read_text(encoding='utf-8'))))
|
if any(len(row) != 3 for row in rows):
|
raise SystemExit(34)
|
recorded = {}
|
for relative, encoded_hash, raw_size in rows:
|
normalized = relative.replace('\\', '/')
|
if normalized.startswith('../../'):
|
normalized = normalized[6:]
|
if normalized.startswith('/') or '..' in pathlib.PurePosixPath(normalized).parts or normalized in recorded:
|
raise SystemExit(35)
|
recorded[normalized] = (encoded_hash, raw_size)
|
|
expected_names = set(expected) | {record_name, 'bin/yt-dlp.exe'}
|
if set(recorded) != expected_names:
|
raise SystemExit(36)
|
actual_names = {
|
path.relative_to(root).as_posix()
|
for path in root.rglob('*')
|
if path.is_file()
|
}
|
if actual_names != expected_names:
|
raise SystemExit(37)
|
|
for relative, (encoded_hash, raw_size) in recorded.items():
|
target = root / pathlib.PurePosixPath(relative)
|
if not target.is_file():
|
raise SystemExit(38)
|
payload = target.read_bytes()
|
if relative == record_name:
|
if encoded_hash or raw_size:
|
raise SystemExit(39)
|
continue
|
expected_hash = 'sha256=' + base64.urlsafe_b64encode(hashlib.sha256(payload).digest()).rstrip(b'=').decode('ascii')
|
if encoded_hash != expected_hash or raw_size != str(len(payload)):
|
raise SystemExit(40)
|
|
for relative, (size, digest) in expected.items():
|
payload = (root / pathlib.PurePosixPath(relative)).read_bytes()
|
if len(payload) != size or hashlib.sha256(payload).digest() != digest:
|
raise SystemExit(41)
|
|
distributions = list(metadata.distributions(path=[str(root)]))
|
identities = [(item.metadata.get('Name'), item.version) for item in distributions]
|
if identities != [('yt-dlp', '2026.7.4')]:
|
raise SystemExit(42)
|
if any(
|
part.lower() == 'pyinstaller' or part.lower().startswith('pyinstaller-')
|
for relative in actual_names
|
for part in pathlib.PurePosixPath(relative).parts
|
):
|
raise SystemExit(43)
|
print(json.dumps({'distribution': 'yt-dlp', 'version': '2026.7.4', 'files': len(actual_names)}))
|
'@
|
$dependencyTreeJson = & $resolvedPython -I -S -B -c $dependencyTreeProbeScript $dependencyRoot $resolvedOutput $wheelPath
|
if ($LASTEXITCODE -ne 0) {
|
throw 'Temporary dependency tree does not exactly match the pinned wheel-derived installation.'
|
}
|
$dependencyTree = $dependencyTreeJson | ConvertFrom-Json
|
if ($dependencyTree.distribution -cne 'yt-dlp' -or
|
$dependencyTree.version -cne '2026.7.4' -or
|
$dependencyTree.files -lt 1) {
|
throw 'Temporary dependency metadata discovery mismatch.'
|
}
|
$pyInstallerLaunchScript = @'
|
import importlib.metadata as metadata
|
import pathlib
|
import runpy
|
import sys
|
|
root = pathlib.Path(sys.argv[1]).resolve(strict=True)
|
owned = pathlib.Path(sys.argv[2]).resolve(strict=True)
|
arguments = sys.argv[3:]
|
if root.name != '.deps' or root.parent != owned:
|
raise SystemExit(51)
|
sys.path.insert(0, str(root))
|
if metadata.version('yt-dlp') != '2026.7.4':
|
raise SystemExit(52)
|
sys.argv = ['PyInstaller', *arguments]
|
runpy.run_module('PyInstaller', run_name='__main__', alter_sys=True)
|
'@
|
& $resolvedPython -I -B -c $pyInstallerLaunchScript $dependencyRoot $resolvedOutput `
|
--noconfirm `
|
--clean `
|
--noupx `
|
--onefile `
|
--paths $dependencyRoot `
|
--collect-all yt_dlp `
|
--copy-metadata yt-dlp `
|
--name project-info-bili-auth-native-host `
|
--distpath $resolvedOutput `
|
--workpath $workRoot `
|
--specpath $specRoot `
|
$entry
|
if ($LASTEXITCODE -ne 0) {
|
throw "PyInstaller failed with exit code $LASTEXITCODE."
|
}
|
$hostExecutable = Join-Path $resolvedOutput 'project-info-bili-auth-native-host.exe'
|
if (-not (Test-Path -LiteralPath $hostExecutable -PathType Leaf)) {
|
throw 'Expected one-file host executable was not produced.'
|
}
|
$requiredArchiveModules = @(
|
'bili_authenticated_extension.worker', 'yt_dlp', 'yt_dlp.downloader',
|
'yt_dlp.globals', 'yt_dlp.plugins', 'yt_dlp.version'
|
)
|
$expectedMetadataEntry = $metadataContract.root + '/METADATA'
|
$archiveProbeScript = @'
|
import email.parser
|
import hashlib
|
import json
|
import pathlib
|
import re
|
import sys
|
|
from PyInstaller.archive.readers import CArchiveReader
|
|
REQUIRED_MODULES = {
|
'bili_authenticated_extension.worker', 'yt_dlp', 'yt_dlp.downloader',
|
'yt_dlp.globals', 'yt_dlp.plugins', 'yt_dlp.version',
|
}
|
|
|
class GateError(Exception):
|
def __init__(self, code, message):
|
super().__init__(message)
|
self.code = code
|
|
|
def fail(code, message):
|
raise GateError(code, message)
|
|
|
def normalize(raw):
|
if not isinstance(raw, str) or not raw or any(ord(ch) < 32 for ch in raw):
|
fail('E_ARCHIVE_PATH', 'Archive contains an invalid entry name.')
|
value = raw.replace('\\', '/')
|
parts = value.split('/')
|
if value.startswith('/') or any(part in ('', '.', '..') for part in parts) or ':' in parts[0]:
|
fail('E_ARCHIVE_PATH', 'Archive contains a non-canonical entry path.')
|
return '/'.join(parts)
|
|
|
def identity(payload):
|
try:
|
message = email.parser.BytesParser().parsebytes(payload)
|
except Exception as exc:
|
fail('E_ARCHIVE_METADATA_IDENTITY', f'Cannot parse package metadata: {type(exc).__name__}.')
|
names = message.get_all('Name', [])
|
versions = message.get_all('Version', [])
|
if len(names) != 1 or len(versions) != 1:
|
fail('E_ARCHIVE_METADATA_IDENTITY', 'Package metadata identity is missing or ambiguous.')
|
return names[0], versions[0]
|
|
|
def tree_hash(files):
|
digest = hashlib.sha256()
|
for relative in sorted(files):
|
payload = files[relative]
|
digest.update(relative.encode('utf-8'))
|
digest.update(b'\0')
|
digest.update(str(len(payload)).encode('ascii'))
|
digest.update(b'\0')
|
digest.update(hashlib.sha256(payload).hexdigest().encode('ascii'))
|
digest.update(b'\n')
|
return digest.hexdigest().upper()
|
|
|
def main():
|
source_manifest = pathlib.Path(sys.argv[3]).resolve(strict=True)
|
contract = json.loads(source_manifest.read_text(encoding='utf-8'))['archive_metadata_contract']
|
expected_root = contract['root']
|
expected_files = set(contract['relative_files'])
|
allowed_metadata_types = set(contract['allowed_type_codes'])
|
expected_name = contract['distribution_name']
|
expected_version = contract['distribution_version']
|
expected_tree_hash = contract['canonical_tree_sha256']
|
executable = pathlib.Path(sys.argv[1]).resolve(strict=True)
|
dependency_root = pathlib.Path(sys.argv[2]).resolve(strict=True)
|
expected_root_path = (dependency_root / expected_root).resolve(strict=True)
|
if expected_root_path.parent != dependency_root or not expected_root_path.is_dir():
|
fail('E_EXPECTED_METADATA_TREE', 'Validated dependency metadata root is unavailable.')
|
expected = {
|
path.relative_to(expected_root_path).as_posix(): path.read_bytes()
|
for path in expected_root_path.rglob('*')
|
if path.is_file()
|
}
|
if set(expected) != expected_files:
|
fail('E_EXPECTED_METADATA_TREE', 'Validated dependency metadata file set is unexpected.')
|
if identity(expected['METADATA']) != (expected_name, expected_version):
|
fail('E_EXPECTED_METADATA_IDENTITY', 'Validated dependency metadata identity is unexpected.')
|
|
archive = CArchiveReader(str(executable))
|
normalized = {}
|
for raw, entry in archive.toc.items():
|
canonical = normalize(raw)
|
if canonical in normalized:
|
fail('E_ARCHIVE_DUPLICATE_PATH', 'Archive contains duplicate normalized paths.')
|
normalized[canonical] = (raw, entry[-1])
|
|
modules = set()
|
for canonical, (raw, typecode) in normalized.items():
|
modules.add(canonical)
|
if typecode == 'z':
|
embedded = archive.open_embedded_archive(raw)
|
for module_name in embedded.toc:
|
modules.add(normalize(module_name))
|
missing_modules = sorted(REQUIRED_MODULES - modules)
|
if missing_modules:
|
fail('E_ARCHIVE_MODULE_MISSING', 'Static archive is missing required module: ' + missing_modules[0])
|
|
roots = {}
|
for canonical, value in normalized.items():
|
parts = canonical.split('/')
|
for index, part in enumerate(parts):
|
if part.lower().endswith('.dist-info'):
|
root = '/'.join(parts[:index + 1])
|
relative = '/'.join(parts[index + 1:])
|
if not relative or relative in roots.setdefault(root, {}):
|
fail('E_ARCHIVE_DUPLICATE_METADATA', 'Archive package metadata paths are ambiguous.')
|
roots[root][relative] = value
|
break
|
|
candidates = set()
|
for root, files in roots.items():
|
basename = root.rsplit('/', 1)[-1]
|
if re.match(r'^yt[-_.]dlp-', basename, flags=re.IGNORECASE):
|
candidates.add(root)
|
metadata_entry = files.get('METADATA')
|
if metadata_entry is not None:
|
raw, _ = metadata_entry
|
payload = archive.extract(raw)
|
try:
|
name, _ = identity(payload)
|
except GateError:
|
if root in candidates:
|
raise
|
else:
|
if re.sub(r'[-_.]+', '-', name).lower() == 'yt-dlp':
|
candidates.add(root)
|
if len(candidates) == 0:
|
fail('E_ARCHIVE_METADATA_MISSING', 'Static archive is missing pinned yt-dlp package metadata.')
|
if candidates != {expected_root}:
|
fail('E_ARCHIVE_METADATA_DUPLICATE_OR_PATH', 'Static archive yt-dlp metadata root is duplicated or unexpected.')
|
|
archive_files = roots[expected_root]
|
if set(archive_files) != expected_files:
|
fail('E_ARCHIVE_METADATA_FILE_SET', 'Static archive yt-dlp metadata file set is incomplete or unexpected.')
|
actual = {}
|
metadata_types = set()
|
for relative, (raw, typecode) in archive_files.items():
|
if typecode not in allowed_metadata_types:
|
fail('E_ARCHIVE_METADATA_TYPE', 'Static archive metadata is not stored as an approved data representation.')
|
# PyInstaller 6.15.0 promotes DATA to CArchive type "b" on Windows
|
# when os.access(source, X_OK) is true. This loop is reached only after
|
# the unique metadata root and exact relative file set are frozen, so
|
# "b" is never treated as data outside these seven pinned members.
|
metadata_types.add(typecode)
|
actual[relative] = archive.extract(raw)
|
if identity(actual['METADATA']) != (expected_name, expected_version):
|
fail('E_ARCHIVE_METADATA_IDENTITY', 'Static archive yt-dlp Name/Version mismatch.')
|
for relative in sorted(expected_files):
|
if actual[relative] != expected[relative]:
|
fail('E_ARCHIVE_METADATA_BYTES', 'Static archive yt-dlp metadata bytes mismatch: ' + relative)
|
computed_expected_tree_hash = tree_hash(expected)
|
if computed_expected_tree_hash != expected_tree_hash:
|
fail('E_EXPECTED_METADATA_TREE_HASH', 'Validated dependency metadata tree does not match the approved canonical contract.')
|
if tree_hash(actual) != expected_tree_hash:
|
fail('E_ARCHIVE_METADATA_TREE_HASH', 'Static archive yt-dlp metadata tree hash mismatch.')
|
return {
|
'status': 'PASS',
|
'method': 'PyInstaller 6.15.0 CArchiveReader exact metadata bytes',
|
'required_modules': sorted(REQUIRED_MODULES),
|
'metadata_entry': expected_root + '/METADATA',
|
'metadata_files': len(expected),
|
'metadata_type_codes': sorted(metadata_types),
|
'metadata_tree_sha256': expected_tree_hash,
|
}
|
|
|
try:
|
print(json.dumps(main(), sort_keys=True, separators=(',', ':')))
|
except GateError as exc:
|
print(json.dumps({'status': 'FAIL', 'code': exc.code, 'message': str(exc)}, sort_keys=True, separators=(',', ':')))
|
'@
|
$archiveProbeJson = & $resolvedPython -I -B -c $archiveProbeScript $hostExecutable $dependencyRoot $resolvedSourceManifest
|
if ($LASTEXITCODE -ne 0) {
|
throw 'Structured static PyInstaller archive inspection failed.'
|
}
|
$archiveProbe = $archiveProbeJson | ConvertFrom-Json
|
if ($archiveProbe.status -cne 'PASS') {
|
throw "Static archive verification failed [$($archiveProbe.code)]: $($archiveProbe.message)"
|
}
|
if ($archiveProbe.method -cne 'PyInstaller 6.15.0 CArchiveReader exact metadata bytes' -or
|
$archiveProbe.metadata_entry -cne $expectedMetadataEntry -or
|
$archiveProbe.metadata_files -ne 7 -or
|
$null -eq $archiveProbe.metadata_type_codes -or
|
@($archiveProbe.metadata_type_codes).Count -lt 1 -or
|
@($archiveProbe.metadata_type_codes | Where-Object { $_ -cnotin @($metadataContract.allowed_type_codes) }).Count -ne 0 -or
|
$archiveProbe.metadata_tree_sha256 -cne $metadataContract.canonical_tree_sha256 -or
|
(Compare-Object $requiredArchiveModules @($archiveProbe.required_modules))) {
|
throw 'Structured static archive verification result mismatch.'
|
}
|
foreach ($temporaryPath in @($workRoot, $specRoot, $dependencyRoot)) {
|
if (Test-Path -LiteralPath $temporaryPath) {
|
Remove-Item -LiteralPath $temporaryPath -Recurse -Force
|
}
|
}
|
$unexpected = @(Get-ChildItem -LiteralPath $resolvedOutput -Force | Where-Object { $_.Name -cne 'project-info-bili-auth-native-host.exe' })
|
if ($unexpected.Count -ne 0) {
|
throw 'One-file build produced an unexpected dependency tree.'
|
}
|
$hostItem = Get-Item -LiteralPath $hostExecutable
|
$buildManifest = [ordered]@{
|
schema = 2
|
scope = 'generic-bilibili-queue'
|
extension_id = 'oidmclckpdmpabbfedplkbdplmfcenbb'
|
extension_build = 'project-info-bili-auth-ingress/1.2.25+20260829.generic.v027'
|
host_build = 'project-info-bili-auth-native-host/1.2.25+20260829.generic.v027'
|
packaging = 'pyinstaller-onefile'
|
pyinstaller_version = '6.15.0'
|
yt_dlp_version = '2026.7.4'
|
builder_python_sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $resolvedPython).Hash
|
pyinstaller_executable_bytes = $pyInstallerItem.Length
|
pyinstaller_executable_sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $resolvedPyInstallerExecutable).Hash
|
builder_provision_receipt_bytes = $builderReceiptItem.Length
|
builder_provision_receipt_sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $builderReceiptPath).Hash
|
build_script_sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $PSCommandPath).Hash
|
source_artifact_manifest_bytes = (Get-Item -LiteralPath $resolvedSourceManifest).Length
|
source_artifact_manifest_sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $resolvedSourceManifest).Hash
|
dependency_artifact_manifest_bytes = $dependencyManifestItem.Length
|
dependency_artifact_manifest_sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $dependencyManifestPath).Hash
|
yt_dlp_wheel_sha256 = $wheelEntry.sha256
|
archive_verification = [ordered]@{
|
status = 'PASS'
|
method = $archiveProbe.method
|
required_modules = $requiredArchiveModules
|
metadata_entry = $expectedMetadataEntry
|
metadata_files = $archiveProbe.metadata_files
|
metadata_type_codes = @($archiveProbe.metadata_type_codes)
|
metadata_tree_sha256 = $archiveProbe.metadata_tree_sha256
|
}
|
files = @([ordered]@{
|
path = $hostItem.Name
|
bytes = $hostItem.Length
|
sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $hostExecutable).Hash
|
})
|
}
|
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
|
[System.IO.File]::WriteAllText(
|
(Join-Path $resolvedOutput 'build-artifact-manifest.json'),
|
($buildManifest | ConvertTo-Json -Depth 5),
|
$utf8NoBom
|
)
|
Assert-ExactSourceSnapshot $sourceRoot $resolvedSourceManifest $expectedSourceFiles $sourceManifest
|
} catch {
|
$caught = $_
|
if ($outputCreated -and (Test-Path -LiteralPath $resolvedOutput)) {
|
Remove-Item -LiteralPath $resolvedOutput -Recurse -Force
|
}
|
try {
|
Assert-ExactSourceSnapshot $sourceRoot $resolvedSourceManifest $expectedSourceFiles $sourceManifest
|
} catch {
|
throw "Post-build source snapshot drifted: $($_.Exception.Message)"
|
}
|
throw $caught
|
} finally {
|
Remove-Item -LiteralPath 'Env:PYTHONDONTWRITEBYTECODE' -ErrorAction SilentlyContinue
|
foreach ($name in @($pythonEnvironment.Keys)) {
|
Set-Item -LiteralPath "Env:$name" -Value $pythonEnvironment[$name]
|
}
|
}
|