Fix Windows installer stamping itself as beta.3 #22
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build installers | |
| # Builds the three installer artifacts (Windows .exe, macOS .zip, Linux | |
| # .tar.gz) plus SHA256SUMS, then smoke-tests each one on its own OS's GitHub | |
| # runner. These are automated platform tests, not a substitute for a human | |
| # running the installer on their own machine -- see README.md's Testing | |
| # status section for the exact, honest label for each platform. | |
| # | |
| # Every smoke test treats an unexpected non-zero exit from the installer | |
| # script as a real failure -- no `|| true`, no post-extraction `chmod` | |
| # repair. macOS/Linux get a harmless fake `claude` stub on PATH (Chrome is | |
| # already genuinely present on both runner images) so a clean run produces | |
| # an unambiguous, fully-successful result, not one that's silently masked | |
| # by a "Claude Code missing" partial failure. The Windows job additionally | |
| # proves the installed venv came from uv's own *managed* Python 3.12, not | |
| # the runner's pre-installed system Python -- see that job's smoke-test | |
| # steps for exactly how. | |
| # | |
| # Runs on: a manual trigger, an `installer-v*` tag push, or -- so installer | |
| # changes are genuinely built and smoke-tested before merge, not just | |
| # reviewed as text -- any pull request that touches installer files. It | |
| # never runs on every push to main, and it never publishes a GitHub release | |
| # by itself; that stays a manual step (see RELEASE_CHECKLIST.md). | |
| on: | |
| workflow_dispatch: {} | |
| push: | |
| tags: | |
| - "installer-v*" | |
| pull_request: | |
| paths: | |
| - "installer/**" | |
| - ".github/workflows/build-installers.yml" | |
| - "requirements.txt" | |
| jobs: | |
| windows: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Stage repo payload (excluding profiles/, .git, dev artifacts) | |
| shell: pwsh | |
| run: | | |
| New-Item -ItemType Directory -Force -Path payload | Out-Null | |
| $exclude = @('.git', 'profiles', '.venv', '__pycache__', '.pytest_cache', 'dist', 'payload', 'payload-uv', 'exported-docs') | |
| Get-ChildItem -Force -Path . | Where-Object { $exclude -notcontains $_.Name } | ForEach-Object { | |
| Copy-Item -Recurse -Force $_.FullName -Destination "payload/$($_.Name)" | |
| } | |
| New-Item -ItemType Directory -Force -Path payload/profiles | Out-Null | |
| # uv replaces the Windows embeddable-Python approach: the embeddable | |
| # distribution ships without ensurepip, so `venv.EnvBuilder(with_pip=True)` | |
| # cannot bootstrap pip into a fresh venv created from it. uv manages its | |
| # own Python provisioning and has no ensurepip dependency -- see | |
| # installer/bootstrap.py's module docstring for the full reasoning. | |
| # | |
| # Pinned to a specific uv release rather than "latest" so a build is | |
| # reproducible; bump this deliberately (and re-run the smoke test below) | |
| # when updating, not silently. setup.iss's `--python-preference | |
| # only-managed` flag is verified against this exact version's real | |
| # PythonPreference enum -- see setup.iss's [Run] comment. | |
| - name: Install pinned uv | |
| uses: astral-sh/setup-uv@v3 | |
| with: | |
| version: "0.5.11" | |
| - name: Stage uv for bundling | |
| shell: pwsh | |
| run: | | |
| New-Item -ItemType Directory -Force -Path payload-uv | Out-Null | |
| $uvPath = (Get-Command uv).Source | |
| Copy-Item -Force $uvPath payload-uv/uv.exe | |
| # Runs the exact `uv venv --python 3.12 --python-preference only-managed` | |
| # command the packaged installer uses, but directly -- not wrapped in | |
| # Inno Setup's hidden [Run] entry -- with full output streamed to this | |
| # log and a hard timeout. This isolates whether uv's managed-Python | |
| # download itself is slow/network-bound (expected to show real | |
| # progress here) from whether something about Inno Setup's Exec | |
| # mechanism is the problem (e.g. an unread output pipe filling up and | |
| # blocking the child process -- a known class of issue when a parent | |
| # launches a subprocess without draining its stdout/stderr). | |
| - name: Diagnostic -- uv managed-Python provisioning, run directly | |
| shell: pwsh | |
| timeout-minutes: 6 | |
| run: | | |
| $diagVenv = "$env:RUNNER_TEMP\uv-diagnostic-venv" | |
| Write-Host "Running: uv venv --python 3.12 --python-preference only-managed $diagVenv" | |
| & payload-uv\uv.exe venv --python 3.12 --python-preference only-managed $diagVenv | |
| if ($LASTEXITCODE -ne 0) { throw "uv venv failed with exit code $LASTEXITCODE" } | |
| & "$diagVenv\Scripts\python.exe" --version | |
| Write-Host "OK: uv provisioned a managed Python directly, outside Inno Setup, without hanging." | |
| - name: Install Inno Setup | |
| run: choco install innosetup -y --no-progress | |
| - name: Build Socials-Studio-Setup.exe | |
| shell: cmd | |
| timeout-minutes: 5 | |
| run: | | |
| "C:\Program Files (x86)\Inno Setup 6\iscc.exe" installer\windows\setup.iss | |
| - name: Checksum | |
| shell: pwsh | |
| run: | | |
| $hash = Get-FileHash -Algorithm SHA256 dist\Socials-Studio-Setup.exe | |
| "$($hash.Hash.ToLower()) Socials-Studio-Setup.exe" | Out-File -Encoding ascii dist\Socials-Studio-Setup.exe.sha256 | |
| # A second, separate installer build -- never uploaded as an artifact, | |
| # never shipped -- whose bundled requirements.txt names a package that | |
| # cannot exist, so `uv pip install` genuinely fails during a real | |
| # silent install. Used only by "Smoke test -- dependency-install | |
| # failure stops setup cleanly" below, to prove setup.iss's | |
| # RunPythonSetup/PythonSetupFailed error handling actually works, not | |
| # just that it compiles. OutputBaseFilename is overridden via /D so | |
| # this never touches dist\Socials-Studio-Setup.exe or its checksum, | |
| # both already produced above. | |
| - name: Build failure-path test installer (deliberately-broken requirements.txt) | |
| shell: pwsh | |
| timeout-minutes: 5 | |
| run: | | |
| Copy-Item payload\requirements.txt payload\requirements.txt.orig | |
| "this-package-does-not-exist-ss-failure-path-test==0.0.0" | Set-Content -Encoding ascii payload\requirements.txt | |
| & "C:\Program Files (x86)\Inno Setup 6\iscc.exe" /DMyOutputBaseFilename=Socials-Studio-Setup-DepsFailTest installer\windows\setup.iss | |
| if ($LASTEXITCODE -ne 0) { throw "iscc failed to build the failure-path test installer (exit code $LASTEXITCODE)" } | |
| Move-Item -Force payload\requirements.txt.orig payload\requirements.txt | |
| - name: Record the runner's own system Python, for comparison | |
| shell: pwsh | |
| run: | | |
| $systemPython = (Get-Command python -ErrorAction SilentlyContinue).Source | |
| Write-Host "Runner's own system python (if any): $systemPython" | |
| if ($systemPython) { | |
| echo "SYSTEM_PYTHON=$systemPython" >> $env:GITHUB_ENV | |
| } | |
| - name: Smoke test -- silent install | |
| shell: pwsh | |
| timeout-minutes: 6 | |
| run: | | |
| $installDir = "$env:RUNNER_TEMP\ss-install" | |
| $proc = Start-Process -FilePath "dist\Socials-Studio-Setup.exe" ` | |
| -ArgumentList "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/DIR=`"$installDir`"" ` | |
| -PassThru | |
| # Poll ourselves (budget shorter than this step's own timeout-minutes) | |
| # rather than a bare -Wait, so that if the install hangs we can dump | |
| # diagnostics -- what's still running, and however much of | |
| # _setup-python.log got written -- before killing it, all inside this | |
| # same step. A step killed purely by its own timeout-minutes loses | |
| # that chance: the script is torn down mid-line with no opportunity | |
| # to inspect the state it leaves behind. | |
| $deadline = (Get-Date).AddMinutes(4) | |
| while (-not $proc.HasExited -and (Get-Date) -lt $deadline) { | |
| Start-Sleep -Seconds 5 | |
| } | |
| if (-not $proc.HasExited) { | |
| Write-Host "Socials-Studio-Setup.exe did not finish within the polling budget -- still running." | |
| Write-Host "----- processes matching setup/cmd/uv/python under $installDir or dist -----" | |
| Get-CimInstance Win32_Process | | |
| Where-Object { $_.CommandLine -match [regex]::Escape($installDir) -or $_.CommandLine -match "Socials-Studio-Setup" } | | |
| Select-Object ProcessId, Name, CommandLine | Format-List | Out-String | Write-Host | |
| $log = "$installDir\_setup-python.log" | |
| if (Test-Path $log) { | |
| Write-Host "----- $log (partial, install still running) -----" | |
| Get-Content $log | Write-Host | |
| Write-Host "----------------" | |
| } else { | |
| Write-Host "$log does not exist yet." | |
| } | |
| Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue | |
| throw "Smoke test failed: silent install did not complete within the polling budget." | |
| } | |
| $checks = @( | |
| "$installDir\README.md", | |
| "$installDir\.venv\Scripts\python.exe", | |
| "$installDir\launch.bat", | |
| "$installDir\.first-run-pending" | |
| ) | |
| $failed = $false | |
| foreach ($path in $checks) { | |
| if (Test-Path $path) { | |
| Write-Host "OK: $path exists" | |
| } else { | |
| Write-Host "MISSING: $path" | |
| $failed = $true | |
| } | |
| } | |
| if ($failed) { | |
| $log = "$installDir\_setup-python.log" | |
| if (Test-Path $log) { | |
| Write-Host "----- $log -----" | |
| Get-Content $log | Write-Host | |
| Write-Host "----------------" | |
| } else { | |
| Write-Host "$log does not exist either -- the uv [Run] steps may not have executed at all." | |
| } | |
| throw "Smoke test failed: one or more expected files are missing after silent install." | |
| } | |
| - name: Smoke test -- prove the venv is a uv-*managed* Python 3.12, not the runner's own | |
| shell: pwsh | |
| timeout-minutes: 3 | |
| run: | | |
| $installDir = "$env:RUNNER_TEMP\ss-install" | |
| $pyvenvCfgPath = "$installDir\.venv\pyvenv.cfg" | |
| if (-not (Test-Path $pyvenvCfgPath)) { throw "pyvenv.cfg not found -- venv was not created as expected." } | |
| $pyvenvCfg = Get-Content $pyvenvCfgPath -Raw | |
| Write-Host "----- .venv\pyvenv.cfg -----" | |
| Write-Host $pyvenvCfg | |
| Write-Host "----------------------------" | |
| # GitHub-hosted Windows runners keep every pre-installed tool version | |
| # (including Python) under a well-known, documented path containing | |
| # "hostedtoolcache". If the venv's own config mentions it, uv used the | |
| # runner's pre-installed Python instead of provisioning a managed one. | |
| if ($pyvenvCfg -match '(?i)hostedtoolcache') { | |
| throw "venv's pyvenv.cfg references the runner's own hostedtoolcache Python -- uv used the runner's system Python instead of a managed one." | |
| } | |
| if ($env:SYSTEM_PYTHON -and ($pyvenvCfg.ToLower().Contains($env:SYSTEM_PYTHON.ToLower()))) { | |
| throw "venv's pyvenv.cfg matches the runner's own system python path ($env:SYSTEM_PYTHON) -- uv did not provision a managed Python." | |
| } | |
| Write-Host "OK: pyvenv.cfg does not reference the runner's hostedtoolcache or system python -- this venv came from a uv-managed Python." | |
| $venvPythonVersion = & "$installDir\.venv\Scripts\python.exe" --version | |
| Write-Host "venv python reports: $venvPythonVersion" | |
| if ($venvPythonVersion -notmatch '^Python 3\.12\.') { | |
| throw "Expected the managed venv python to report Python 3.12.x, got: $venvPythonVersion" | |
| } | |
| $importCheck = & "$installDir\.venv\Scripts\python.exe" -c "import playwright.sync_api; print('playwright import OK')" | |
| Write-Host $importCheck | |
| if ($importCheck -notmatch 'playwright import OK') { | |
| throw "Dependency import check failed: $importCheck" | |
| } | |
| Write-Host "OK: venv uses a uv-managed Python 3.12 (not the runner's), and playwright imports successfully." | |
| - name: Smoke test -- reinstall preserves profiles/ and the venv still works | |
| shell: pwsh | |
| timeout-minutes: 8 | |
| run: | | |
| $installDir = "$env:RUNNER_TEMP\ss-install" | |
| $profileFile = "$installDir\profiles\fake-session\storage_state.json" | |
| New-Item -ItemType Directory -Force -Path (Split-Path $profileFile) | Out-Null | |
| $originalContent = '{"cookies": "fake-not-real-session-data"}' | |
| Set-Content -Path $profileFile -Value $originalContent -NoNewline | |
| Start-Process -FilePath "dist\Socials-Studio-Setup.exe" ` | |
| -ArgumentList "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/DIR=`"$installDir`"" ` | |
| -Wait -PassThru | Out-Null | |
| $afterContent = Get-Content -Path $profileFile -Raw | |
| if ($afterContent.Trim() -ne $originalContent.Trim()) { | |
| throw "Smoke test failed: profiles/ file changed after reinstall. Before: $originalContent After: $afterContent" | |
| } | |
| Write-Host "OK: profiles/fake-session/storage_state.json unchanged after reinstall" | |
| $venvPythonVersionAfter = & "$installDir\.venv\Scripts\python.exe" --version | |
| if ($venvPythonVersionAfter -notmatch '^Python 3\.12\.') { | |
| throw "After reinstall, expected Python 3.12.x, got: $venvPythonVersionAfter" | |
| } | |
| $importCheckAfter = & "$installDir\.venv\Scripts\python.exe" -c "import playwright.sync_api; print('playwright import OK')" | |
| if ($importCheckAfter -notmatch 'playwright import OK') { | |
| throw "After reinstall, dependency import check failed: $importCheckAfter" | |
| } | |
| Write-Host "OK: venv still reports Python 3.12 and playwright still imports after reinstall." | |
| # Uses the deliberately-broken installer built above (bad | |
| # requirements.txt) to prove setup.iss's RunPythonSetup/ | |
| # PythonSetupFailed handling genuinely stops setup on a real | |
| # dependency-install failure -- not just that setup-python.bat records | |
| # a non-zero exit in its own log, but that Setup itself reports | |
| # failure, never runs bootstrap.py, never writes the first-run | |
| # marker, and never touches profiles/. Does not touch or weaken the | |
| # successful-path smoke tests above -- entirely separate install dir, | |
| # entirely separate installer binary. | |
| - name: Smoke test -- dependency-install failure stops setup cleanly | |
| shell: pwsh | |
| timeout-minutes: 6 | |
| run: | | |
| $installDir = "$env:RUNNER_TEMP\ss-install-fail" | |
| $profileFile = "$installDir\profiles\fake-session\storage_state.json" | |
| New-Item -ItemType Directory -Force -Path (Split-Path $profileFile) | Out-Null | |
| $originalContent = '{"cookies": "fake-not-real-session-data"}' | |
| Set-Content -Path $profileFile -Value $originalContent -NoNewline | |
| $proc = Start-Process -FilePath "dist\Socials-Studio-Setup-DepsFailTest.exe" ` | |
| -ArgumentList "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART", "/DIR=`"$installDir`"" ` | |
| -PassThru | |
| # Same self-polling pattern as the main silent-install smoke test | |
| # above -- if RaiseException somehow doesn't stop Setup promptly, | |
| # this fails within its own bounded budget with diagnostics, | |
| # rather than either hanging for the whole job or silently passing. | |
| $deadline = (Get-Date).AddMinutes(4) | |
| while (-not $proc.HasExited -and (Get-Date) -lt $deadline) { | |
| Start-Sleep -Seconds 5 | |
| } | |
| if (-not $proc.HasExited) { | |
| Write-Host "Socials-Studio-Setup-DepsFailTest.exe did not finish within the polling budget -- still running." | |
| Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue | |
| throw "Failure-path smoke test itself hung waiting for the installer to exit." | |
| } | |
| Write-Host "Installer exit code: $($proc.ExitCode)" | |
| $log = "$installDir\_setup-python.log" | |
| if (Test-Path $log) { | |
| Write-Host "----- $log -----" | |
| Get-Content $log | Write-Host | |
| Write-Host "----------------" | |
| } else { | |
| Write-Host "$log does not exist -- setup-python.bat may not have run at all." | |
| } | |
| $failed = $false | |
| if ($proc.ExitCode -eq 0) { | |
| Write-Host "FAIL: installer reported success (exit code 0) despite the broken requirements.txt." | |
| $failed = $true | |
| } else { | |
| Write-Host "OK: installer reported failure (exit code $($proc.ExitCode)), not success." | |
| } | |
| if ((Test-Path $log) -and (Select-String -Path $log -Pattern "uv pip install exit=0" -Quiet)) { | |
| Write-Host "FAIL: $log shows uv pip install exit=0, but it should have failed." | |
| $failed = $true | |
| } else { | |
| Write-Host "OK: setup-python.bat's own log does not show a successful uv pip install." | |
| } | |
| if (Test-Path "$installDir\.first-run-pending") { | |
| Write-Host "FAIL: .first-run-pending exists -- bootstrap.py ran despite the failed Python setup." | |
| $failed = $true | |
| } else { | |
| Write-Host "OK: .first-run-pending is absent -- bootstrap.py correctly never ran." | |
| } | |
| $afterContent = Get-Content -Path $profileFile -Raw | |
| if ($afterContent.Trim() -ne $originalContent.Trim()) { | |
| Write-Host "FAIL: profiles/fake-session/storage_state.json changed during the failed install. Before: $originalContent After: $afterContent" | |
| $failed = $true | |
| } else { | |
| Write-Host "OK: profiles/fake-session/storage_state.json unchanged after the failed install." | |
| } | |
| if ($failed) { | |
| throw "Dependency-install failure-path smoke test failed -- see FAIL lines above." | |
| } | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-installer | |
| path: | | |
| dist/Socials-Studio-Setup.exe | |
| dist/Socials-Studio-Setup.exe.sha256 | |
| macos: | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Package source + installer scripts | |
| run: | | |
| mkdir -p dist | |
| zip -r dist/Socials-Studio-macOS.zip . \ | |
| -x ".git/*" -x "profiles/*" -x ".venv/*" -x "__pycache__/*" \ | |
| -x ".pytest_cache/*" -x "dist/*" -x "exported-docs/*" | |
| - name: Checksum | |
| run: | | |
| shasum -a 256 dist/Socials-Studio-macOS.zip > dist/Socials-Studio-macOS.zip.sha256 | |
| # Chrome is already genuinely present on macos-latest -- only Claude | |
| # Code is missing (correctly; CI must never install the real thing). | |
| # A harmless stub gets an unambiguous, fully-successful smoke-test | |
| # result instead of one partially masked by "Claude Code missing". | |
| - name: Add a fake claude stub to PATH for an unambiguous result | |
| run: | | |
| mkdir -p "$RUNNER_TEMP/fake-bin" | |
| cat > "$RUNNER_TEMP/fake-bin/claude" <<'EOF' | |
| #!/usr/bin/env bash | |
| echo "claude 0.0.0-fake (smoke-test stub, not real Claude Code)" | |
| EOF | |
| chmod +x "$RUNNER_TEMP/fake-bin/claude" | |
| echo "$RUNNER_TEMP/fake-bin" >> "$GITHUB_PATH" | |
| - name: Smoke test -- extract and install | |
| run: | | |
| set -euo pipefail | |
| WORKDIR="$RUNNER_TEMP/ss-smoke" | |
| INSTALL_DIR="$RUNNER_TEMP/ss-install" | |
| mkdir -p "$WORKDIR" | |
| unzip -q dist/Socials-Studio-macOS.zip -d "$WORKDIR" | |
| # The archive must contain executable scripts AS PACKAGED -- no | |
| # repair here. If this fails, archive creation or extraction lost | |
| # the executable bit; that's a real bug, not something to paper | |
| # over inside the smoke test. | |
| test -x "$WORKDIR/installer/macos/install.sh" | |
| test -x "$WORKDIR/installer/macos/SocialsStudio.command" | |
| echo "OK: install.sh and SocialsStudio.command are executable as extracted" | |
| "$WORKDIR/installer/macos/install.sh" "$INSTALL_DIR" | |
| fail=0 | |
| for f in "$INSTALL_DIR/README.md" "$INSTALL_DIR/.venv/bin/python3" \ | |
| "$INSTALL_DIR/SocialsStudio.command" "$INSTALL_DIR/.first-run-pending"; do | |
| if [ -e "$f" ]; then | |
| echo "OK: $f exists" | |
| else | |
| echo "MISSING: $f" | |
| fail=1 | |
| fi | |
| done | |
| [ "$fail" -eq 0 ] || { echo "Smoke test failed: one or more expected files are missing."; exit 1; } | |
| "$INSTALL_DIR/.venv/bin/python3" -c "import playwright.sync_api; print('playwright import OK')" | |
| - name: Smoke test -- reinstall preserves profiles/ | |
| run: | | |
| set -euo pipefail | |
| WORKDIR="$RUNNER_TEMP/ss-smoke" | |
| INSTALL_DIR="$RUNNER_TEMP/ss-install" | |
| PROFILE_FILE="$INSTALL_DIR/profiles/fake-session/storage_state.json" | |
| mkdir -p "$(dirname "$PROFILE_FILE")" | |
| echo -n '{"cookies": "fake-not-real-session-data"}' > "$PROFILE_FILE" | |
| "$WORKDIR/installer/macos/install.sh" "$INSTALL_DIR" | |
| if ! diff -q <(echo -n '{"cookies": "fake-not-real-session-data"}') "$PROFILE_FILE" >/dev/null; then | |
| echo "Smoke test failed: profiles/ file changed after reinstall." | |
| exit 1 | |
| fi | |
| echo "OK: profiles/fake-session/storage_state.json unchanged after reinstall" | |
| "$INSTALL_DIR/.venv/bin/python3" -c "import playwright.sync_api; print('playwright import OK')" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos-installer | |
| path: | | |
| dist/Socials-Studio-macOS.zip | |
| dist/Socials-Studio-macOS.zip.sha256 | |
| linux: | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Ensure venv/rsync are present | |
| run: | | |
| sudo apt-get update -y | |
| sudo apt-get install -y python3-venv python3-pip rsync | |
| - name: Package source + installer scripts | |
| run: | | |
| mkdir -p dist | |
| tar --exclude='.git' --exclude='profiles' --exclude='.venv' \ | |
| --exclude='__pycache__' --exclude='.pytest_cache' \ | |
| --exclude='dist' --exclude='exported-docs' \ | |
| -czf dist/Socials-Studio-Linux.tar.gz . | |
| - name: Checksum | |
| run: | | |
| sha256sum dist/Socials-Studio-Linux.tar.gz > dist/Socials-Studio-Linux.tar.gz.sha256 | |
| # Chrome is already genuinely present on ubuntu-24.04 -- only Claude | |
| # Code is missing (correctly; CI must never install the real thing). | |
| # A harmless stub gets an unambiguous, fully-successful smoke-test | |
| # result instead of one partially masked by "Claude Code missing". | |
| - name: Add a fake claude stub to PATH for an unambiguous result | |
| run: | | |
| mkdir -p "$RUNNER_TEMP/fake-bin" | |
| cat > "$RUNNER_TEMP/fake-bin/claude" <<'EOF' | |
| #!/usr/bin/env bash | |
| echo "claude 0.0.0-fake (smoke-test stub, not real Claude Code)" | |
| EOF | |
| chmod +x "$RUNNER_TEMP/fake-bin/claude" | |
| echo "$RUNNER_TEMP/fake-bin" >> "$GITHUB_PATH" | |
| - name: Smoke test -- extract and install | |
| run: | | |
| set -euo pipefail | |
| WORKDIR="$RUNNER_TEMP/ss-smoke" | |
| INSTALL_DIR="$RUNNER_TEMP/ss-install" | |
| mkdir -p "$WORKDIR" | |
| tar -xzf dist/Socials-Studio-Linux.tar.gz -C "$WORKDIR" | |
| # The archive must contain executable scripts AS PACKAGED -- no | |
| # repair here. If this fails, archive creation or extraction lost | |
| # the executable bit; that's a real bug, not something to paper | |
| # over inside the smoke test. | |
| test -x "$WORKDIR/installer/linux/install.sh" | |
| test -x "$WORKDIR/installer/linux/socials-studio-launch.sh" | |
| echo "OK: install.sh and socials-studio-launch.sh are executable as extracted" | |
| "$WORKDIR/installer/linux/install.sh" "$INSTALL_DIR" | |
| fail=0 | |
| for f in "$INSTALL_DIR/README.md" "$INSTALL_DIR/.venv/bin/python3" \ | |
| "$INSTALL_DIR/socials-studio-launch.sh" "$INSTALL_DIR/.first-run-pending"; do | |
| if [ -e "$f" ]; then | |
| echo "OK: $f exists" | |
| else | |
| echo "MISSING: $f" | |
| fail=1 | |
| fi | |
| done | |
| [ -f "$HOME/.local/share/applications/socials-studio.desktop" ] \ | |
| && echo "OK: desktop entry installed" \ | |
| || { echo "MISSING: desktop entry"; fail=1; } | |
| [ "$fail" -eq 0 ] || { echo "Smoke test failed: one or more expected files are missing."; exit 1; } | |
| "$INSTALL_DIR/.venv/bin/python3" -c "import playwright.sync_api; print('playwright import OK')" | |
| - name: Smoke test -- reinstall preserves profiles/ | |
| run: | | |
| set -euo pipefail | |
| WORKDIR="$RUNNER_TEMP/ss-smoke" | |
| INSTALL_DIR="$RUNNER_TEMP/ss-install" | |
| PROFILE_FILE="$INSTALL_DIR/profiles/fake-session/storage_state.json" | |
| mkdir -p "$(dirname "$PROFILE_FILE")" | |
| echo -n '{"cookies": "fake-not-real-session-data"}' > "$PROFILE_FILE" | |
| "$WORKDIR/installer/linux/install.sh" "$INSTALL_DIR" | |
| if ! diff -q <(echo -n '{"cookies": "fake-not-real-session-data"}') "$PROFILE_FILE" >/dev/null; then | |
| echo "Smoke test failed: profiles/ file changed after reinstall." | |
| exit 1 | |
| fi | |
| echo "OK: profiles/fake-session/storage_state.json unchanged after reinstall" | |
| "$INSTALL_DIR/.venv/bin/python3" -c "import playwright.sync_api; print('playwright import OK')" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: linux-installer | |
| path: | | |
| dist/Socials-Studio-Linux.tar.gz | |
| dist/Socials-Studio-Linux.tar.gz.sha256 |