ouroboros/build_linux.sh
Ouroboros adac2e0b4e v5.1.0: feat(chat+ci) — selective port from PR #25 + chat bottom-fade layer fix + retire ouroboros-three-layer
Selectively port 5 PR #25 commits into ouroboros (clipboard image
paste, autocorrect-off on chat textarea, integration-test CI tier,
optional macOS code signing & notarization, secrets→env fix for
step-level if-conditions in GitHub Actions. Plus chat bottom
gradient migration from #chat-input-area's background to a dedicated
.chat-bottom-fade sibling layer (z-index 4, pointer-events:none) so
the textarea no longer optically sinks into the dense end of the
gradient. Plus retire ouroboros-three-layer as a dev branch — ouroboros
is now the single dev branch.

(1) Clipboard image paste: web/modules/chat.js registers a paste
listener on #chat-input that scans e.clipboardData.items for image/*,
calls getAsFile(), wraps as File(clipboard-<unix-ts>.<ext>), and
stages via the same pendingAttachment slot the paperclip uses (no
inline upload — uploads when Send/Enter fires). Non-image paste falls
through natively. The paperclip change handler was extracted into a
shared stagePendingFile() helper so both entry points are identical.
The textarea gains autocorrect=off autocapitalize=off
spellcheck=false so code/identifiers/slash-commands are not silently
rewritten by the browser.

(2) Chat bottom-fade layer: web/style.css strips the linear-gradient
background and mask-image from #chat-input-area (which keeps z-index 5),
and adds a new dedicated sibling .chat-bottom-fade (position:absolute;
bottom:0; pointer-events:none; z-index:4; height:200px) below the
input dock. Mobile @media (max-width: 640px) uses
calc(200px + env(safe-area-inset-bottom, 0px)) so the fade fully
covers the iOS-home-indicator safe area at the worst-case input-area
state (attachment + fully-expanded textarea).

(3) Integration tier in CI (Tier 2.5): new integration-test job runs
pytest tests/test_provider_integration.py -m integration on
ubuntu-latest with OPENROUTER_API_KEY/OPENAI_API_KEY/ANTHROPIC_API_KEY
in repo secrets. Triggered on push to main / ouroboros / ouroboros-stable,
on workflow_dispatch, and on tag v*. Locally the  pytest
marker plus addopts -m 'not integration' in pyproject.toml exclude
the tests from default runs.

(4) Optional macOS code signing & notarization (Build tier): when
BUILD_CERTIFICATE_BASE64 / P12_PASSWORD / KEYCHAIN_PASSWORD /
APPLE_TEAM_ID are configured as repo secrets, the build job creates
a temporary keychain, imports the Developer ID certificate, and runs
bash build.sh (which signs .app and .dmg via env-overridable
SIGN_IDENTITY). With APPLE_ID + APPLE_APP_SPECIFIC_PASSWORD also
present, build.sh runs xcrun notarytool submit --wait followed by
xcrun stapler staple. Stapler/notarytool failures are wrapped in
if/else (set -e exempt) so transient Apple-CDN flakes become warnings
instead of dropping the macOS DMG from the release. A NOTARIZE_OUTCOME
enum drives a 4-case summary cascade (success / staple_failed /
submit_failed / unconfigured) plus a defensive *) arm. With no Apple
secrets the build falls back to OUROBOROS_SIGN=0 bash build.sh
(identical to v5.0.0). Cleanup keychain step runs with
if: always() && matrix.os == 'macos-latest' &&
env.BUILD_CERTIFICATE_BASE64 != '' so signing material never persists
across runs and the bash-only security delete-keychain invocation
never fires on Linux/Windows shards. The Import step sets
trap 'rm -f "$CERTIFICATE_PATH"' EXIT so the temporary .p12 is
removed on every exit, including a set -e abort mid-import.

(5) secrets→env fix for step-level if (v4.47.1 lesson): GitHub Actions
rejects secrets.* references inside step-level if expressions
(Unrecognized named-value: secrets). All Apple signing secrets
are mapped at the build job's env: block with a
${{ matrix.os == 'macos-latest' && secrets.X || '' }} guard so
non-macOS shards receive empty strings — Linux/Windows never see the
signing material. Step-level if reads env.* instead. New
docs/DEVELOPMENT.md section GitHub Actions: secrets in step-level if
conditions formalizes the rule with worked examples.

(6) Tests: tests/test_chat_logs_ui.py gains 3 new tests
(test_chat_input_disables_autocorrect, test_clipboard_paste_handler_exists,
test_chat_bottom_fade_is_separate_layer); tests/test_build_scripts.py
gains a new TestMacOSSigning class with 7 contract tests (job-level
secrets env mapping with matrix.os guard, no secrets.* in any if-block,
Import step gates on full secret set, Cleanup keychain always() +
matrix.os + env guard, build.sh SIGN_IDENTITY env override, notarytool
+ stapler optional gate, stapler-failure-as-soft-warning regression);
tests/test_provider_integration.py is added new with 6 tuple-aware
tests (OpenRouter / OpenAI / Anthropic × {basic, isolation}) handling
the post-v4.44.0 LLMClient.chat() (msg, usage) tuple plus Anthropic's
list-of-blocks content. The existing
test_chat_floating_overlays_have_readable_glass_backing was updated
for the migrated bottom-fade contract (asserts no backdrop-filter on
.chat-bottom-fade across base + mobile @media rules).

(7) Branch consolidation: ouroboros-three-layer is retired as a dev
branch. ouroboros is now the single dev branch.
.github/workflows/ci.yml (Tier 1 quick-test trigger + path-filter
branches list + build job's OUROBOROS_MANAGED_SOURCE_BRANCH default),
build.sh / build_linux.sh / build_windows.ps1 (each script's
${OUROBOROS_MANAGED_SOURCE_BRANCH:-...} default), and four test files
(test_release_workflow.py, test_launcher_sync.py,
test_git_ops_recovery.py, test_build_repo_bundle.py — 21 occurrences
total) all switch from ouroboros-three-layer to ouroboros. Historical
references in older changelog rows (v4.50.0-rc.7) and in ouroboros/*
module comments about the Phase 2/3 three-layer architecture refactor
are intentionally preserved — those describe the architectural
refactor, not the dev branch name. The remote
managed/ouroboros-three-layer branch is deleted in the same release.

Adversarial multimodel review (gemini-2.5/gpt-5.5/claude-opus-4.7
critics in parallel, full-context, 4 rounds): 32 findings total → 18
fixed, 14 rejected/deferred with explicit per-finding reasoning. All
three critics independently reach SAFE TO COMMIT after round 4.

Ouroboros triad+scope review (production code path
parallel_review.run_parallel_review with full-repo pack, 2 rounds):
4 findings (2 scope-critical + 1 scope-advisory + 1 triad-advisory)
→ all 4 fixed. Round 1 caught matrix-shard secret leak; round 2
caught documentation/runtime command mismatch + cert-file cleanup
gap on set -e failure.

VERSION 5.0.0 → 5.1.0 (MINOR: additive features + UX/CI polish, no
breaking change). Release invariant synchronised: VERSION,
pyproject.toml [project].version, README badge, docs/ARCHITECTURE.md
header — all 5.1.0.

Note on changelog rolloff: the v4.50.0-rc.2 minor entry is rolled
off proactively to keep one slot below the P7 5-minor-row cap. Its
full body remains at git tag v4.50.0-rc.2.
EOF
)
2026-04-26 17:57:30 +03:00

65 lines
2.1 KiB
Bash

#!/bin/bash
set -e
VERSION=$(tr -d '[:space:]' < VERSION)
ARCHIVE_NAME="Ouroboros-${VERSION}-linux-$(uname -m).tar.gz"
MANAGED_SOURCE_BRANCH="${OUROBOROS_MANAGED_SOURCE_BRANCH:-ouroboros}"
RELEASE_TAG="v${VERSION}"
PYTHON_CMD="${PYTHON_CMD:-python3}"
if ! command -v "$PYTHON_CMD" >/dev/null 2>&1; then
PYTHON_CMD=python
fi
echo "=== Building Ouroboros for Linux (v${VERSION}) ==="
if [ ! -f "python-standalone/bin/python3" ]; then
echo "ERROR: python-standalone/ not found."
echo "Run first: bash scripts/download_python_standalone.sh"
exit 1
fi
echo "--- Installing launcher dependencies ---"
"$PYTHON_CMD" -m pip install -q -r requirements-launcher.txt
echo "--- Installing agent dependencies into python-standalone ---"
python-standalone/bin/pip3 install -q -r requirements.txt
rm -rf build dist
export PYINSTALLER_CONFIG_DIR="$PWD/.pyinstaller-cache"
mkdir -p "$PYINSTALLER_CONFIG_DIR"
echo "--- Installing Chromium for browser tools (bundled into python-standalone) ---"
PLAYWRIGHT_BROWSERS_PATH=0 python-standalone/bin/python3 -m playwright install chromium
echo "--- Building embedded managed repo bundle ---"
if ! git rev-parse -q --verify "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then
echo "ERROR: packaging requires git tag $RELEASE_TAG to exist."
exit 1
fi
TAG_TYPE="$(git cat-file -t "refs/tags/$RELEASE_TAG" 2>/dev/null || true)"
if [ "$TAG_TYPE" != "tag" ]; then
echo "ERROR: packaging requires annotated git tag $RELEASE_TAG (got '$TAG_TYPE'). Recreate with: git tag -a $RELEASE_TAG -m 'Release $RELEASE_TAG'"
exit 1
fi
if ! git tag --points-at HEAD | grep -Fx "$RELEASE_TAG" >/dev/null 2>&1; then
echo "ERROR: packaging requires HEAD to be tagged with $RELEASE_TAG."
exit 1
fi
"$PYTHON_CMD" scripts/build_repo_bundle.py --source-branch "$MANAGED_SOURCE_BRANCH"
echo "--- Running PyInstaller ---"
"$PYTHON_CMD" -m PyInstaller Ouroboros.spec --clean --noconfirm
echo ""
echo "=== Creating archive ==="
cd dist
tar -czf "$ARCHIVE_NAME" Ouroboros/
cd ..
echo ""
echo "=== Done ==="
echo "Archive: dist/$ARCHIVE_NAME"
echo ""
echo "To run: extract and execute ./Ouroboros/Ouroboros"