diff --git a/.build/prod/.env.example b/.build/prod/.env.example deleted file mode 100644 index 967b4a6..0000000 --- a/.build/prod/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -GITEA_SERVER_URL= -GITEA_REPOSITORY= -GITEA_TOKEN= -GITEA_REF_NAME= diff --git a/.build/prod/.gitignore b/.build/prod/.gitignore deleted file mode 100644 index ccf91ba..0000000 --- a/.build/prod/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -out/ -.env diff --git a/.build/prod/build.sh b/.build/prod/build.sh deleted file mode 100755 index 576b5c3..0000000 --- a/.build/prod/build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# -# Runs INSIDE the .build/prod container. Clones, patches, builds the binary, -# smoke-tests it, and drops it in .build/prod/out/ for the release step. -# -set -euo pipefail -cd /source - -# The repo is bind-mounted from the host (different uid), so mark it safe: -git config --global --add safe.directory '*' - -OUT_DIR="/source/.build/prod/out" -mkdir -p "$OUT_DIR" - -bash scripts/build.sh -bash scripts/smoke.sh dist/httptoolkit - -cp -v dist/httptoolkit "$OUT_DIR/httptoolkit" -echo "==> Artifacts:" -ls -lh "$OUT_DIR" diff --git a/.build/prod/compose.yml b/.build/prod/compose.yml deleted file mode 100644 index b72738f..0000000 --- a/.build/prod/compose.yml +++ /dev/null @@ -1,22 +0,0 @@ -services: - prod: - build: - context: . - dockerfile: Dockerfile - image: htk-prod-builder:local - network_mode: host - working_dir: /source - environment: - - PUPPETEER_SKIP_DOWNLOAD=true - - PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true - volumes: - # The whole repo (with submodules already checked out by the CI step): - - ../../:/source - # Persist package caches between runs to speed up rebuilds: - - htk-npm-cache:/root/.npm - - htk-bun-cache:/root/.bun/install/cache - command: bash .build/prod/build.sh - -volumes: - htk-npm-cache: - htk-bun-cache: diff --git a/.build/prod/release.sh b/.build/prod/release.sh deleted file mode 100755 index 37efc47..0000000 --- a/.build/prod/release.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash -# -# Publish a NEW Gitea release for this build and upload everything in out/. -# Never deletes existing releases/tags - each build gets its own tag. -# -# Config via env (or a local .env next to this script): -# GITEA_SERVER_URL, GITEA_REPOSITORY, PAT_GITEA -# Tag: $1 if given, else derived as v- -# (a build suffix is appended if that tag already exists). -# -set -euo pipefail -cd "$(dirname "$0")" - -# Optional: load credentials from a local .env for manual runs. -[ -f .env ] && source ./.env - -: "${GITEA_SERVER_URL:?}" -: "${GITEA_REPOSITORY:?}" -: "${PAT_GITEA:?}" - -ROOT="$(git rev-parse --show-toplevel)" -SHA="$(git rev-parse HEAD)" -SHORT="$(git rev-parse --short HEAD)" -OUT_DIR="$(pwd)/out" -REPO_API="${GITEA_SERVER_URL}/api/v1/repos/${GITEA_REPOSITORY}" -AUTH=(-H "Authorization: token ${PAT_GITEA}") - -# Resolve a unique release tag: -TAG="${1:-}" -if [ -z "$TAG" ]; then - VER="$(jq -r .version "$ROOT/httptoolkit-server/package.json")" - TAG="v${VER}-${SHORT}" - # If a release for this commit already exists (e.g. a scheduled rebuild of an - # unchanged commit), keep the old one and make this tag unique instead. - if curl -sf "${AUTH[@]}" "${REPO_API}/releases/tags/${TAG}" > /dev/null 2>&1; then - TAG="${TAG}-${GITHUB_RUN_NUMBER:-$(date -u +%Y%m%d%H%M%S)}" - fi -fi - -echo "==> Creating release $TAG @ $SHORT" -RESP=$(curl -s -X POST "${REPO_API}/releases" "${AUTH[@]}" \ - -H "Content-Type: application/json" \ - -d "{ - \"tag_name\": \"${TAG}\", - \"target_commitish\": \"${SHA}\", - \"name\": \"HTTP Toolkit ${TAG}\", - \"body\": \"Self-hosted single-file build from ${SHORT}.\", - \"draft\": false, - \"prerelease\": false - }") - -RID=$(echo "$RESP" | jq -r '.id // empty') -if [ -z "$RID" ]; then - echo "!! Failed to create release:"; echo "$RESP"; exit 1 -fi -echo "==> Release id $RID" - -# Upload artifacts. -shopt -s nullglob -for f in "$OUT_DIR"/*; do - [ -f "$f" ] || continue - name=$(basename "$f") - echo "==> Uploading $name" - curl -s -X POST "${REPO_API}/releases/${RID}/assets?name=${name}" "${AUTH[@]}" \ - -H "Content-Type: application/octet-stream" \ - --data-binary "@${f}" > /dev/null -done -echo "==> Release complete." diff --git a/.build/prod/Dockerfile b/Dockerfile similarity index 50% rename from .build/prod/Dockerfile rename to Dockerfile index e6f74c5..d379b24 100644 --- a/.build/prod/Dockerfile +++ b/Dockerfile @@ -1,7 +1,8 @@ -# Builder image for the self-hosted HTTP Toolkit single-file binary (Linux x64). -# Provides Node, Bun, and the toolchain needed to build the server's native modules. FROM node:22-bookworm +ENV PUPPETEER_SKIP_DOWNLOAD=true +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true + RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ python3 \ @@ -14,8 +15,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ jq \ && rm -rf /var/lib/apt/lists/* -# Install Bun (used for the single-file `--compile` step) -RUN curl -fsSL https://bun.sh/install | bash -ENV PATH="/root/.bun/bin:${PATH}" - WORKDIR /source + +CMD /source/build.sh diff --git a/README.md b/README.md deleted file mode 100644 index e6a6aac..0000000 --- a/README.md +++ /dev/null @@ -1,128 +0,0 @@ -# Self-hosted HTTP Toolkit - -A private distribution repo that builds [HTTP Toolkit](https://httptoolkit.com) into a -**single self-contained binary** that runs the backend and serves the web UI locally: - -```bash -./httptoolkit --port 7070 -# → starts the proxy server and opens the web UI at http://localhost:7070 -``` - -No Electron, no hosted UI, no account/login. Upstream is tracked as **git submodules** -and all local modifications live in this repo as **patches** + an **overlay**, so -updating to a new upstream release is a controlled, reviewable step. - -## Layout - -``` -. -├── httptoolkit-ui/ # submodule → github.com/httptoolkit/httptoolkit-ui (pinned) -├── httptoolkit-server/ # submodule → github.com/httptoolkit/httptoolkit-server (pinned) -├── patches/ -│ ├── ui/ # diffs against upstream UI files -│ └── server/ # diffs against upstream server files -├── overlay/server/ # NEW files copied into the server submodule (never conflict) -│ ├── htk-entry.ts # single-file binary entry (Bun) -│ └── src/ -│ ├── htk-app.ts # shared: run backend + serve UI + open browser -│ └── commands/app.ts # oclif fallback command -├── scripts/ -│ ├── apply.sh # reset submodules → apply patches → copy overlay -│ ├── build.sh # build UI → embed → bun compile → dist/ -│ └── smoke.sh # launch binary & verify it serves + backend is up -├── .build/prod/ # Docker builder (Gitea `docker-compose` runner) -│ ├── Dockerfile # Node + Bun + native toolchain -│ ├── compose.yml # mounts repo, runs build.sh -│ ├── build.sh # build + smoke → out/ -│ └── release.sh # Gitea-API release + upload -└── .gitea/workflows/build.yml -``` - -## What the patches change - -| Kind | File | Change | -|------|------|--------| -| UI patch | `src/model/account/account-store.ts` | injects a synthetic active Pro subscription | -| UI patch | `src/components/settings/settings-page.tsx` | removes the account/subscription card | -| UI patch | `src/components/app.tsx` | removes the "Give feedback" sidebar button | -| Server patch | `src/constants.ts` | allow `localhost`/`127.0.0.1` origins in prod builds | -| Overlay | `htk-entry.ts`, `src/htk-app.ts`, `src/commands/app.ts` | serve the UI locally + `--port` | - -**Security note:** upstream restricts a packaged build's API to `https://app.httptoolkit.tech` -so no *other* local page can drive your proxy. Because we self-host the UI on localhost we -must allow localhost origins (this matches upstream's own dev-mode allowlist). Trade-off: any -local HTTP page could talk to the server while it's running. Acceptable for a personal tool. - -## Build locally - -Requires **node ≥22**, **npm**, **bun**, and **tar**. - -```bash -git submodule update --init --recursive -bash scripts/build.sh # → dist/httptoolkit -bash scripts/smoke.sh # optional: verify it runs -``` - -Run it: - -```bash -./dist/httptoolkit --port 7070 # UI on :7070 -./dist/httptoolkit --no-open # don't auto-open a browser -./dist/httptoolkit --help -``` - -## Updating to a new upstream release - -```bash -# bump a submodule to a new tag -cd httptoolkit-ui && git fetch && git checkout v && cd .. -git add httptoolkit-ui - -# re-apply and see if patches still fit -bash scripts/apply.sh -``` - -If a patch no longer applies, `apply.sh` fails and names the patch. Fix it: - -```bash -cd httptoolkit-ui -git checkout -- . # reset -# hand-apply the change, then regenerate the patch: -git diff -- > ../patches/ui/000X-....patch -``` - -Overlay files never conflict (they're new files), so only the small `patches/` diffs -ever need attention on upgrade. - -## Packaging notes / risks - -- **Single file via Bun `--compile`.** The server pulls in native `.node` addons - (`node-datachannel`, `registry-js`, `adbkit`, …). These **cannot be cross-compiled**, - so the binary is built **per host platform**. `scripts/smoke.sh` is the guard: if a - native addon won't load from the embedded FS, it fails there. -- **Linux only, for now.** CI builds inside a Linux Docker container, which produces a - trustworthy Linux x64 binary. Windows/macOS binaries are **not** reliably buildable - from Linux Docker (foreign-platform native addons can't be produced or smoke-tested - there, and macOS needs signing) — they'd need real Windows/macOS runners. -- The UI is embedded as `ui.tar.gz` and extracted to `$TMPDIR/httptoolkit-ui-` - on first run. -- No `HTK_SERVER_TOKEN` is set, so the local UI talks to the backend without a token. - -## CI (Gitea Actions, Docker) - -Matches the `docker-compose` runner convention: - -- **`.gitea/workflows/build.yml`** — on a `v*` tag (or manual dispatch): checks out with - submodules, runs `docker compose run --rm prod` in `.build/prod/`, then creates a Gitea - release and uploads the artifacts via the Gitea API (`secrets.PAT_GITEA`). -- **`.build/prod/`** — the Docker builder: - - `Dockerfile` — Node 22 + Bun + native-build toolchain - - `compose.yml` — mounts the repo at `/source`, caches npm/bun - - `build.sh` — runs `scripts/build.sh` + `scripts/smoke.sh`, drops the binary in `out/` - - `release.sh` — Gitea-API release + asset upload - -Tag a release: - -```bash -git tag v1.27.1 && git push origin v1.27.1 -``` diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..0ef7478 --- /dev/null +++ b/build.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -euo pipefail +cd /source + + +OUT_DIR="/source/builds" +mkdir -p "$OUT_DIR" + +git clone https://github.com/httptoolkit/httptoolkit-ui +git clone https://github.com/httptoolkit/httptoolkit-server + +## +## add the patch codes here +## +cp -v dist/httptoolkit "$OUT_DIR/httptoolkit" diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..7810620 --- /dev/null +++ b/compose.yml @@ -0,0 +1,7 @@ +services: + release-httptoolkit: + build: + context: . + dockerfile: Dockerfile + volumes: + - ./:/source