sharp does the images now; ffmpeg keeps the video. each does the job it is for — sharp is libvips in-process, no process to spawn and no pipe, which is an order of magnitude less work for the common case of a folder of photographs. a video has no frame until something decodes one, and that is ffmpeg's and nobody else's. added through the procedure bunfig.toml describes, since there is no --no-frozen-lockfile flag in bun 1.3.14: package.json edited, frozenLockfile commented out for ONE command, lockfile diff read, setting put back, and a frozen install confirmed clean afterwards. WHAT THE LOCKFILE DIFF ACTUALLY SAID, because the friction is only worth anything if somebody reads it. everything is inside sharp's tree: sharp 0.33.5 -> 0.35.4 at top level, @img/colour added, and the per-platform libvips binaries moved 1.2.4 -> 1.3.3. two SHARED packages moved with it — semver 7.7.4 -> 7.8.5 and @emnapi/runtime 1.8.1 -> 1.11.3 — both forced by sharp's own ranges, and both previous consumers (next, editorconfig) kept pinned copies of what they had. next keeps its own sharp at 0.33.5 entirely. nothing outside that moved. ^0.35.3 to match what av-club declares. 0.x caret pins the minor, so ^0.33 and ^0.35 do not overlap — the plugin intersection would have resolved two native libvips trees onto one machine for no reason. THE IMAGE LIST GREW: libvips reads HEIC and SVG and ffmpeg does not, and neither is exotic — HEIC is what every recent iPhone writes, and a folder of them drawing generic file icons was the most visible gap this had. `failOn: 'none'` and `limitInputPixels: false` are both deliberate. libvips rejects a file with any warning in it, and a truncated JPEG — an interrupted copy, a phone that died mid-write — is exactly that; refusing it would make this browser stricter than the viewer somebody opens the file with. the pixel ceiling is a DoS guard for an upload endpoint, and these are the owner's own files already on the owner's own disk: a panorama is not an attack. SETUP, both halves: ffmpeg was never in the package list at all, on any of the four package managers, despite this router shelling out to it for subtitles, audio extraction and audio tracks long before thumbnails existed. it worked because every machine set up by hand already had it. now declared. sharp gets a LOAD CHECK beside node-pty's build check, and reported separately from it. same failure shape — an install that succeeds while the native module does not — but the opposite cause: node-pty compiles and can fail to, sharp downloads a libvips binary for this exact platform and arch and can fail to resolve one. a combined "native modules" verdict would send somebody to rebuild the wrong thing. loading it is the only test worth running; a directory proves nothing, and `versions.vips` proves libvips is bound. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
122 lines
5.9 KiB
Bash
122 lines
5.9 KiB
Bash
#!/bin/bash
|
|
# =============================================================================
|
|
# officer-setup — the repository
|
|
# =============================================================================
|
|
#
|
|
# Definitions only.
|
|
#
|
|
# ── Cloned as the owner, never as root ──
|
|
#
|
|
# A repository cloned by root is one the owner cannot pull, cannot commit in, and
|
|
# whose node_modules they cannot write. Every git operation here runs as the
|
|
# account, from a directory that account can stat.
|
|
|
|
[[ -n "${OFFICER_SETUP_REPO_LOADED:-}" ]] && return 0
|
|
OFFICER_SETUP_REPO_LOADED=1
|
|
|
|
# Public HTTPS, which is what this needed all along.
|
|
#
|
|
# It was ssh://git@gitea.pastilhas.dev:2222/... until 2026-08-14, and the reason was
|
|
# that the repository was private: an HTTPS clone of a private repo prompts for a
|
|
# username, and under sudo with no interactive terminal that hangs or dies with
|
|
# "could not read Username". The note here said "back to HTTPS when the repository is
|
|
# public", and it now is — verified with an anonymous `git ls-remote`.
|
|
#
|
|
# The change matters more than a URL swap. An SSH default cannot clone on a genuinely
|
|
# fresh machine: the key machine-setup generates there is brand new and Gitea has
|
|
# never seen it, so `--repo` was effectively mandatory on a first install. HTTPS needs
|
|
# no key and no agent, so the default now works on a blank box.
|
|
#
|
|
# If this ever goes private again, SSH is the answer and the constraint above is the
|
|
# reason — plus one more: the clone runs as the OWNER, and sudo drops SSH_AUTH_SOCK,
|
|
# so a passphrase-protected key has no agent to answer it.
|
|
OFFICER_REPO="${OFFICER_REPO:-https://gitea.officer.dev/officerdev/platform.git}"
|
|
|
|
platform_dir() { echo "${OFFICER_ROOT}/platform"; }
|
|
|
|
repo_exists() { [[ -d "$(platform_dir)/.git" ]]; }
|
|
|
|
repo_remote() { (cd "$(platform_dir)" 2>/dev/null && git remote get-url origin 2>/dev/null) || true; }
|
|
repo_branch() { (cd "$(platform_dir)" 2>/dev/null && git branch --show-current 2>/dev/null) || true; }
|
|
repo_is_dirty() { [[ -n "$(cd "$(platform_dir)" 2>/dev/null && git status --porcelain 2>/dev/null)" ]]; }
|
|
|
|
# Split an ssh:// URL into host and port, for the reachability check below.
|
|
repo_ssh_host() { sed -E 's|^ssh://[^@]*@([^:/]+).*|\1|' <<<"$1"; }
|
|
repo_ssh_port() { sed -nE 's|^ssh://[^@]*@[^:]+:([0-9]+)/.*|\1|p' <<<"$1"; }
|
|
|
|
# Can this account actually clone it?
|
|
#
|
|
# `git ls-remote` is the real question — not "does the host answer" but "can this
|
|
# account read this repository". Both prompts are disabled, because neither fails
|
|
# cleanly on its own: over https git asks for a username nobody is there to type,
|
|
# and over ssh it asks for a password or stops on host-key verification. With
|
|
# both off, an unreachable or unreadable repository is an immediate non-zero
|
|
# instead of a hang.
|
|
repo_reachable() {
|
|
as_owner "GIT_TERMINAL_PROMPT=0 \
|
|
GIT_SSH_COMMAND='ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=8' \
|
|
timeout 20 git ls-remote '$1' >/dev/null 2>&1" /
|
|
}
|
|
|
|
# The https form of the same repository, for a machine with no key.
|
|
repo_https_url() {
|
|
sed -E 's|^ssh://[^@]*@([^:/]+)(:[0-9]+)?/|https://\1/|' <<<"$1"
|
|
}
|
|
|
|
clone_repo() {
|
|
local url="$1" dest
|
|
dest="$(platform_dir)"
|
|
install -d -m 0755 -o "$USERNAME" -g "$(user_group)" "$OFFICER_ROOT"
|
|
as_owner "GIT_TERMINAL_PROMPT=0 git clone '${url}' '${dest}'" /
|
|
}
|
|
|
|
pull_repo() { as_owner "git -C '$(platform_dir)' pull --ff-only" /; }
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Dependencies
|
|
# -----------------------------------------------------------------------------
|
|
#
|
|
# ── The lockfile is frozen, and that is the point ──
|
|
#
|
|
# bunfig.toml sets [install] frozenLockfile = true, so `bun install` resolves from
|
|
# bun.lock and nothing else. A package.json that disagrees with the lockfile is a
|
|
# hard failure rather than a quiet resolution — which is deliberate: the friction
|
|
# exists so that an unexplained lockfile change shows up in a diff. See the
|
|
# supply-chain note in CLAUDE.md.
|
|
#
|
|
# So a failure here is usually one of two things, and they need different
|
|
# answers: the lockfile genuinely disagrees with package.json, or node-pty failed
|
|
# to build. Both are reported as such rather than as "install failed".
|
|
|
|
deps_installed() { [[ -d "$(platform_dir)/node_modules" ]]; }
|
|
|
|
# node-pty has no Linux prebuild, so `bun install` compiles it every time. This is
|
|
# the artefact that proves it worked, and its absence is why the terminal sidecar
|
|
# would not start.
|
|
node_pty_built() { compgen -G "$(platform_dir)/node_modules/node-pty/build/Release/*.node" >/dev/null 2>&1; }
|
|
|
|
# sharp is libvips, and it arrives as a PREBUILT BINARY chosen by platform and
|
|
# architecture — @img/sharp-linux-x64 and its libvips, or the arm64 pair, or the
|
|
# musl ones. bun picks them from the lockfile's optional dependencies.
|
|
#
|
|
# Which means the install can succeed and the module can still be unusable: a
|
|
# lockfile written on x64 carries the x64 binaries, and on a machine where the
|
|
# right optional package did not resolve, `import('sharp')` throws at the first
|
|
# call rather than at install time. Same failure shape as node-pty above, and the
|
|
# same reason to check it here — the symptom otherwise is a file browser drawing
|
|
# generic icons over a folder of photographs, which nobody reads as "a native
|
|
# module is missing".
|
|
#
|
|
# Loading it is the only test worth running. A directory being present proves
|
|
# nothing; sharp's own `versions.vips` proves libvips is there and bound.
|
|
sharp_loads() {
|
|
as_owner "cd '$(platform_dir)' && bun -e \"import('sharp').then(m => { if (!m.default.versions.vips) process.exit(1) }).catch(() => process.exit(1))\"" >/dev/null 2>&1
|
|
}
|
|
|
|
# What libvips sharp found, for the report. Empty when it does not load at all.
|
|
sharp_version() {
|
|
as_owner "cd '$(platform_dir)' && bun -e \"import('sharp').then(m => console.log(m.default.versions.vips)).catch(() => {})\"" 2>/dev/null | tr -d '\r'
|
|
}
|
|
|
|
install_deps() { as_owner "cd '$(platform_dir)' && bun install 2>&1"; }
|