feat: workspace tooling for the MesaStack fork

Add one-shot scripts driving both forks from the workspace root:

- build-image:   frontend build + thin-overlay image build + verification
- verify-image:  static and runtime smoke tests (brand, API key prefix,
                 login, real key creation), always cleans up its container
- sync-upstream: bump both forks to a new upstream release, cutting fresh
                 v<version>-lofyer branches and replaying fork-only commits
- bootstrap:     clone the two forks into place after cloning this repo

Fork-only commit detection deliberately goes beyond 'git log --cherry-pick
--right-only', which re-replays upstream commits that were rebased or
squashed into the new release; commits contained by any upstream ref or tag
are dropped instead.

The forks stay as separate repositories rather than submodules so each keeps
a clean upstream history to rebase onto new releases.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
lofyer
2026-07-27 09:16:05 +08:00
co-authored by factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
commit 004d18db92
7 changed files with 882 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env bash
#
# build-image: one-shot build of the MesaStack control-plane image from the
# workspace root. Builds the rebranded frontend, overlays it plus our patched
# backend sources onto the matching official release image, then (by default)
# smoke tests the result.
#
# It requires both forks to sit on the *same* v<version>-lofyer branch, so a
# build can never mix a frontend and backend from different upstream releases.
#
# Usage:
# scripts/build-image # build + verify
# scripts/build-image --no-verify # build only
# scripts/build-image --skip-ui-build # reuse existing frontend dist/
# scripts/build-image --push # push after a successful build
# scripts/build-image --dry-run # show what would happen
#
# Env knobs (also see gpustack-lofyer/hack/package-lofyer):
# NAMESPACE / REPOSITORY / TAG / IMAGE / BASE_IMAGE
# SMOKE_PORT host port used by the smoke test (default 18080)
#
set -o errexit
set -o nounset
set -o pipefail
source "$(dirname "${BASH_SOURCE[0]}")/lib/common.sh"
UI_BUILD=1
VERIFY=1
PUSH=0
DRY_RUN=0
SMOKE_PORT="${SMOKE_PORT:-18080}"
while [[ $# -gt 0 ]]; do
case "${1}" in
--skip-ui-build) UI_BUILD=0 ;;
--no-verify) VERIFY=0 ;;
--verify) VERIFY=1 ;;
--push) PUSH=1 ;;
--dry-run) DRY_RUN=1 ;;
-h|--help) sed -n '2,26p' "${0}"; exit 0 ;;
*) fail "unknown argument: ${1}" ;;
esac
shift
done
require_cmd docker git
require_repos
VERSION="$(resolve_workspace_version)"
TAG="${TAG:-${VERSION}}"
IMAGE="${IMAGE:-${NAMESPACE}/${REPOSITORY}:${TAG}}"
log "workspace version : ${VERSION}"
log "target image : ${IMAGE}"
# 1. Frontend.
step "Building frontend"
if [[ "${UI_BUILD}" == "1" ]]; then
require_cmd npm
if [[ "${DRY_RUN}" == "1" ]]; then
echo "(dry-run) (cd ${UI_REPO} && npm run build)"
else
(cd "${UI_REPO}" && npm run build)
fi
else
log "skipping frontend build (--skip-ui-build)"
[[ -f "${UI_REPO}/dist/index.html" ]] \
|| fail "no existing frontend dist at ${UI_REPO}/dist (drop --skip-ui-build)"
fi
# 2. Image. Delegates to the backend packaging script, which stages the dist
# and only the python files that differ from the upstream tag.
step "Building image ${IMAGE}"
PACK_ENV=(
"NAMESPACE=${NAMESPACE}"
"REPOSITORY=${REPOSITORY}"
"TAG=${TAG}"
"IMAGE=${IMAGE}"
"UI_REPO=${UI_REPO}"
"PUSH=${PUSH}"
"DRY_RUN=${DRY_RUN}"
)
[[ -n "${BASE_IMAGE:-}" ]] && PACK_ENV+=("BASE_IMAGE=${BASE_IMAGE}")
env "${PACK_ENV[@]}" "${BACKEND_REPO}/hack/package-lofyer"
if [[ "${DRY_RUN}" == "1" ]]; then
log "dry-run complete"
exit 0
fi
# 3. Verify.
if [[ "${VERIFY}" == "1" ]]; then
step "Verifying ${IMAGE}"
IMAGE="${IMAGE}" SMOKE_PORT="${SMOKE_PORT}" "$(dirname "${BASH_SOURCE[0]}")/verify-image"
fi
step "Done"
log "image: ${IMAGE}"
log "run it with:"
echo " docker run -d --name mesastack -p 80:80 -v /var/run/docker.sock:/var/run/docker.sock ${IMAGE}"