AtlatestRepositorysigil-site

sigil-site / treepublish.sh

1#!/usr/bin/env bash
2set -euo pipefail
3
4# Publish the Sigil website to Codeberg Pages
5#
6# Usage: ./publish.sh [OPTIONS]
7#
8# Options:
9# --local Build only, don't commit/push
10# --serve After building, serve locally (implies --local)
11# --port PORT Port for local server (default: 8000)
12# --skip-build Skip building libraries (use existing build artifacts)
13# --with-repl Force-build the WASM web REPL (on by default for a production
14# publish; use this to also build it under --local)
15# --skip-repl Skip the WASM web REPL build (explicit opt-out; NOT recommended
16# for a production publish — it ships a possibly-stale REPL)
18# The REPL build needs zig (auto-detected from ~/.sigil/toolchain/zig) and
19# wasm-opt/Binaryen (on Guix: run under `guix shell binaryen`). A production
20# publish builds it by default so the live REPL never silently goes stale.
22# Environment:
23# SIGIL Path to sigil binary (default: ~/.sigil/bin/sigil)
24# PARENT_DIR Parent directory where sibling repos live (default: ..)
25# CODEBERG_USER Codeberg username for HTTPS push auth (default: daviwil)
26# CODEBERG_TOKEN When set, uses HTTPS for git operations instead of SSH
28# This script:
29# 1. Ensures the sigil monorepo is present and built
30# 2. Clones/fetches all extracted core library repos
31# 3. Builds each library so API doc JSON is generated
32# 4. Builds the WASM web REPL (default for a production publish; --skip-repl to opt out)
33# 5. Assembles a combined lib directory for with-api-docs
34# 6. Builds the site
35# 7. Optionally deploys to Codeberg Pages
37LOCAL_ONLY=false
38SERVE=false
39PORT=8000
40SKIP_BUILD=false
41WITH_REPL="" # empty = decide after parsing: a production publish builds the REPL, --local skips it
43while [[ $# -gt 0 ]]; do
44 case $1 in
45 --local)
46 LOCAL_ONLY=true
47 shift
48 ;;
49 --serve)
50 LOCAL_ONLY=true
51 SERVE=true
52 shift
53 ;;
54 --port)
55 PORT="$2"
56 shift 2
57 ;;
58 --skip-build)
59 SKIP_BUILD=true
60 shift
61 ;;
62 --with-repl)
63 WITH_REPL=true
64 shift
65 ;;
66 --skip-repl)
67 WITH_REPL=false
68 shift
69 ;;
70 *)
71 echo "Unknown option: $1"
72 echo "Usage: $0 [--local] [--serve] [--port PORT] [--skip-build] [--with-repl] [--skip-repl]"
73 exit 1
74 ;;
75 esac
76done
78# The web REPL is part of a PRODUCTION publish by default, so a real deploy can
79# never silently ship a stale REPL wasm. (It did: usesigil.org sat at 0.17.3
80# while the rest of the site read 0.17.6, because --with-repl was an easy-to-
81# forget opt-in.) A production publish now BUILDS the REPL unless --skip-repl is
82# passed explicitly; local dev previews (--local) skip it by default for fast
83# iteration (opt in with --with-repl). Either way the choice is explicit.
84if [[ -z "$WITH_REPL" ]]; then
85 if [[ "$LOCAL_ONLY" == "true" ]]; then WITH_REPL=false; else WITH_REPL=true; fi
86fi
88SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
89cd "$SCRIPT_DIR"
91SIGIL="${SIGIL:-$HOME/.sigil/bin/sigil}"
92PARENT_DIR="${PARENT_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}"
93PUBLIC_DIR="$SCRIPT_DIR/public"
94COMBINED_LIB="$SCRIPT_DIR/build/combined-lib"
96# Pages deployment target (usesigil.org is hosted from sigil monorepo pages branch)
97# Use HTTPS with token auth when CODEBERG_TOKEN is set (e.g. in CI),
98# otherwise use SSH (local development)
99if [[ -n "${CODEBERG_TOKEN:-}" ]]; then
100 CODEBERG_USER="${CODEBERG_USER:-daviwil}"
101 PAGES_REPO_URL="https://${CODEBERG_USER}:${CODEBERG_TOKEN}@codeberg.org/sigil/sigil.git"
102else
103 PAGES_REPO_URL="[email protected]:sigil/sigil.git"
104fi
105PAGES_BRANCH="pages"
106DOMAIN="usesigil.org"
108# ---------------------------------------------------------------------------
109# Core libraries whose API docs appear on usesigil.org
110# ---------------------------------------------------------------------------
112# Monorepo packages (built from ../sigil) that need a _pkg manifest
113# generated when their custom tasks: skip the default copy-package-docs
114# step. The browser/WASM bridge packages all stage JS assets through
115# custom tasks, so without this list their package pages never render
116# on the site even though their module .json docs generate fine.
117MONOREPO_PACKAGES=(
118 sigil-cli
119 sigil-run
120 sigil-browser
121 sigil-wasm-runtime
122 sigil-wasm-dom
123 sigil-wasm-net
124 sigil-wasm-canvas2d
125 sigil-wasm-gl
128# Extracted repos (cloned as peer directories and built individually).
129# Multi-package repos: sigil-lang (sigil-lib + sigil-stdlib),
130# sigil-build (sigil-package + sigil-deps + sigil-build),
131# sigil-test (sigil-test + sigil-test-runner).
132EXTRACTED_REPOS=(
133 sigil-lang sigil-build
134 sigil-args sigil-ansi sigil-version sigil-docs sigil-git sigil-socket
135 sigil-log sigil-json sigil-http sigil-crypto sigil-tls sigil-sqlite
136 sigil-store sigil-lsp sigil-mcp sigil-repl sigil-nrepl sigil-ffi
137 sigil-format sigil-hooks sigil-test
138 sigil-peg sigil-sxml sigil-markdown sigil-css sigil-fp sigil-match
139 sigil-r7rs sigil-transducers sigil-web sigil-web-client sigil-yaml sigil-later
140 # Core, general-purpose libraries added by the 2026-07 completeness audit.
141 # These are published sigil/* library packages that were missing from the
142 # docs site. (SaaS/API-client packages: forgejo, caldav, jmap, discourse,
143 # postmark, ses, lemonsqueezy, telegram, twitch, youtube, wise, plus the
144 # graphics/audio/game stack, are intentionally excluded; see the audit note.)
145 # sigil-rss was also identified but is NOT release-ready (no tags, its
146 # package.sgl lacks the required `sigil:` field and fails to build), so it
147 # is held back until it is fixed and released. sigil-ledger was evaluated
148 # and intentionally left out as too specialized.
149 sigil-websocket sigil-oauth sigil-jwt sigil-irc sigil-xmpp
150 sigil-org sigil-tui
153# ---------------------------------------------------------------------------
154# Helper functions
155# ---------------------------------------------------------------------------
157info() { echo "=== $* ==="; }
158warn() { echo "WARNING: $*"; }
159die() { echo "ERROR: $*" >&2; exit 1; }
161repo_url() {
162 local name="$1"
163 if [[ -n "${CODEBERG_TOKEN:-}" ]]; then
164 echo "https://codeberg.org/sigil/${name}.git"
165 else
166 echo "[email protected]:sigil/${name}.git"
167 fi
170ensure_repo() {
171 local name="$1"
172 local dir="$PARENT_DIR/$name"
174 if [[ -d "$dir" ]]; then
175 echo " Fetching $name..."
176 git -C "$dir" fetch --quiet --tags 2>/dev/null || true
177 else
178 echo " Cloning $name..."
179 git clone --quiet "$(repo_url "$name")" "$dir"
180 fi
183build_extracted_repo() {
184 local name="$1"
185 local dir="$PARENT_DIR/$name"
187 if [[ ! -d "$dir" ]]; then
188 warn "$name directory not found, skipping"
189 return
190 fi
192 echo " Building $name..."
193 cd "$dir"
195 # Ensure deps are installed
196 if [[ ! -d ".sigil/deps" ]]; then
197 $SIGIL deps install > /dev/null 2>&1 || true
198 fi
200 # Build with --force to ensure JSON docs are generated.
201 # The build may error after compilation due to sigil-lib resolution;
202 # that's OK since compilation (and JSON generation) still succeeds.
203 $SIGIL build --force > /dev/null 2>&1 || true
205 cd "$SCRIPT_DIR"
208# Merge a lib directory into the combined lib.
209# Copies .sgb, .json, and _pkg files. First source wins on conflicts.
210merge_lib() {
211 local src="$1"
212 local label="$2"
214 if [[ ! -d "$src" ]]; then
215 warn "$label lib dir not found: $src"
216 return
217 fi
219 # Copy .sgb and .json files, preserving directory structure
220 find "$src" -type f \( -name "*.sgb" -o -name "*.json" \) | while read -r file; do
221 local rel="${file#$src/}"
222 local dest="$COMBINED_LIB/$rel"
223 if [[ ! -f "$dest" ]]; then
224 mkdir -p "$(dirname "$dest")"
225 cp "$file" "$dest"
226 fi
227 done
229 # Copy _pkg directories (package metadata + docs)
230 if [[ -d "$src/_pkg" ]]; then
231 find "$src/_pkg" -type f | while read -r file; do
232 local rel="${file#$src/}"
233 local dest="$COMBINED_LIB/$rel"
234 if [[ ! -f "$dest" ]]; then
235 mkdir -p "$(dirname "$dest")"
236 cp "$file" "$dest"
237 fi
238 done
239 fi
242# Fix package attribution for extracted repos.
243# When a module exists in both the monorepo (as part of sigil-stdlib) and an
244# extracted repo (as its own package), the monorepo .json says package: "sigil-stdlib"
245# but the extracted repo .json says package: "<repo-name>". We want the extracted
246# repo's attribution so the module appears under the correct package on the site.
247fix_extracted_json() {
248 local name="$1"
249 local src="$PARENT_DIR/$name/build/dev/lib"
251 if [[ ! -d "$src" ]]; then
252 return
253 fi
255 # Overwrite .json files in combined-lib where the extracted repo's version
256 # has the correct package attribution (package matches the repo name)
257 find "$src" -name "*.json" ! -path "*/_pkg/*" | while read -r file; do
258 if grep -q "\"package\": \"$name\"" "$file"; then
259 local rel="${file#$src/}"
260 local dest="$COMBINED_LIB/$rel"
261 mkdir -p "$(dirname "$dest")"
262 cp "$file" "$dest"
263 fi
264 done
267# Ensure a _pkg manifest exists for a monorepo package.
268# Some packages have custom tasks: that skip the default copy-package-docs step,
269# so their _pkg manifest is never created. We generate one from package.sgl.
270ensure_pkg_manifest() {
271 local name="$1"
272 local pkg_dir="$PARENT_DIR/sigil/packages/$name"
273 local manifest_dir="$COMBINED_LIB/_pkg/$name"
274 local manifest_path="$manifest_dir/manifest.json"
276 # Skip if manifest already exists
277 if [[ -f "$manifest_path" ]]; then
278 return
279 fi
281 if [[ ! -f "$pkg_dir/package.sgl" ]]; then
282 warn "No package.sgl for $name, cannot generate manifest"
283 return
284 fi
286 # Extract version and description from package.sgl
287 # Packages may omit version: (inherited from workspace), fall back to workspace version
288 local version description
289 version=$(grep 'version:' "$pkg_dir/package.sgl" | head -1 | sed 's/.*version: *"\(.*\)".*/\1/' || true)
290 if [[ -z "$version" ]]; then
291 version=$(grep 'version:' "$PARENT_DIR/sigil/package.sgl" | head -1 | sed 's/.*version: *"\(.*\)".*/\1/' || true)
292 fi
293 description=$(grep 'description:' "$pkg_dir/package.sgl" | head -1 | sed 's/.*description: *"\(.*\)".*/\1/' || true)
295 mkdir -p "$manifest_dir"
296 cat > "$manifest_path" <<MANIFEST
298 "name": "$name",
299 "version": "$version",
300 "description": "$description"
302MANIFEST
303 echo " Generated manifest for $name"
306# Ensure a _pkg manifest exists for an EXTRACTED repo package.
307# Extracted repos whose package.sgl defines a custom tasks: build task override
308# the default build (which runs copy-package-docs), and if they also ship no
309# docs/ dir they never emit their own _pkg/<name>/manifest.json. Without that
310# manifest, Press's scan-library-docs skips the package entirely: no Libraries
311# sidebar entry and the /docs/lib/<name>/ page 404s (as happened to
312# sigil-crypto, sigil-tls, and sigil-nrepl). This is the same failure mode
313# ensure_pkg_manifest handles for MONOREPO_PACKAGES; generate the manifest from
314# the repo's own package.sgl. The package name is the repo name (matches the
315# convention fix_extracted_json relies on for single-package repos).
316ensure_extracted_pkg_manifest() {
317 local name="$1"
318 local repo_dir="$PARENT_DIR/$name"
319 local pkg_sgl="$repo_dir/package.sgl"
320 local build_lib="$repo_dir/build/dev/lib"
322 if [[ ! -f "$pkg_sgl" ]]; then
323 return
324 fi
326 local manifest_dir="$COMBINED_LIB/_pkg/$name"
327 local manifest_path="$manifest_dir/manifest.json"
329 # Skip if a manifest was already merged from the repo's own build output
330 if [[ -f "$manifest_path" ]]; then
331 return
332 fi
334 # Only synthesize a manifest when the repo actually built modules attributed
335 # to this package. This guards against workspace/multi-package repos (e.g.
336 # sigil-lang, whose package.sgl name is the workspace name "sigil-lang" but
337 # which ships sigil-stdlib/sigil-lib) and against unbuilt/empty repos --
338 # either would otherwise produce a bogus empty package in the sidebar.
339 if [[ ! -d "$build_lib" ]]; then
340 return
341 fi
342 if ! grep -rls "\"package\": \"$name\"" "$build_lib" --include='*.json' >/dev/null 2>&1; then
343 return
344 fi
346 local version description
347 version=$(grep 'version:' "$pkg_sgl" | head -1 | sed 's/.*version: *"\(.*\)".*/\1/' || true)
348 description=$(grep 'description:' "$pkg_sgl" | head -1 | sed 's/.*description: *"\(.*\)".*/\1/' || true)
350 mkdir -p "$manifest_dir"
351 cat > "$manifest_path" <<MANIFEST
353 "name": "$name",
354 "version": "$version",
355 "description": "$description"
357MANIFEST
358 echo " Generated manifest for extracted package $name"
361# ---------------------------------------------------------------------------
362# Step 1: Ensure sigil monorepo is present and built
363# ---------------------------------------------------------------------------
365info "Checking sigil monorepo"
367if [[ ! -d "$PARENT_DIR/sigil" ]]; then
368 die "../sigil not found. Clone the sigil monorepo as a peer directory."
369fi
371if [[ "$SKIP_BUILD" != "true" ]]; then
372 if [[ ! -d "$PARENT_DIR/sigil/build/dev/lib" ]]; then
373 echo " Building monorepo..."
374 cd "$PARENT_DIR/sigil"
375 $SIGIL build
376 cd "$SCRIPT_DIR"
377 else
378 echo " Monorepo build found."
379 fi
380fi
382# ---------------------------------------------------------------------------
383# Step 2: Clone/fetch extracted library repos
384# ---------------------------------------------------------------------------
386info "Ensuring extracted library repos"
388for repo in "${EXTRACTED_REPOS[@]}"; do
389 ensure_repo "$repo"
390done
392# ---------------------------------------------------------------------------
393# Step 3: Build extracted libraries
394# ---------------------------------------------------------------------------
396if [[ "$SKIP_BUILD" != "true" ]]; then
397 info "Building extracted libraries"
399 for repo in "${EXTRACTED_REPOS[@]}"; do
400 build_extracted_repo "$repo"
401 done
402fi
404# ---------------------------------------------------------------------------
405# Step 3.5: Build web REPL WASM (default for production publishes; --skip-repl to opt out)
406# ---------------------------------------------------------------------------
408if [[ "$WITH_REPL" == "true" ]]; then
409 # Auto-detect the sigil-bundled zig so the REPL build works without a manual
410 # PATH setup. sigil's pinned zig is cached under the monorepo's tools/zig
411 # (populated by scripts/with-zig) and symlinked at ~/.sigil/toolchain/zig —
412 # the same toolchain the release build uses. Only look if zig isn't already
413 # on PATH.
414 if ! command -v zig >/dev/null 2>&1; then
415 for zdir in "$HOME/.sigil/toolchain/zig" "${PARENT_DIR:-}/sigil/tools/zig"; do
416 if [[ -n "$zdir" && -x "$zdir/zig" ]]; then
417 info "Using bundled zig at $zdir"
418 export PATH="$zdir:$PATH"
419 break
420 fi
421 done
422 fi
423 if ! command -v zig >/dev/null 2>&1; then
424 die "REPL build requires zig: not on PATH and no bundled zig found at ~/.sigil/toolchain/zig or \$PARENT_DIR/sigil/tools/zig. Install zig, or run the monorepo's scripts/with-zig once to populate the bundled toolchain."
425 fi
427 # wasm-opt (Binaryen) is required for the --asyncify post-link pass that the
428 # monorepo's --config web build runs. Without it that pass is silently
429 # skipped and the REPL wasm ships unresolved asyncify.* imports, so it fails
430 # to instantiate in the browser ("Import asyncify: module is not an object
431 # or function") and the REPL hangs. Fail loudly instead of shipping a broken
432 # REPL. On Guix, wasm-opt is not on PATH by default: re-run inside a guix
433 # shell (zig is auto-detected, so binaryen is the only thing to add):
434 # guix shell binaryen -- ./publish.sh
435 if ! command -v wasm-opt >/dev/null 2>&1; then
436 die "REPL build requires wasm-opt (Binaryen) for the --asyncify pass, not on PATH. On Guix: guix shell binaryen -- ./publish.sh (zig is auto-detected). To publish WITHOUT the REPL, pass --skip-repl explicitly — but that ships whatever REPL wasm is currently staged, which may be stale."
437 fi
439 info "Building web REPL (WASM)"
441 ensure_repo "sigil-web-repl"
442 REPL_SRC="$PARENT_DIR/sigil-web-repl"
443 cd "$REPL_SRC"
445 # Clean build: the bundle step globs everything under build/web/lib,
446 # so orphaned modules from older builds survive incremental rebuilds
447 # and ship stale bytecode (March-era v7 match/sxml modules rode along
448 # this way and broke those REPL examples on the live site).
449 rm -rf build/web
451 # Install deps or use redirects to local monorepo
452 if [[ -d "$PARENT_DIR/sigil" ]]; then
453 REDIR_FILE=$(mktemp)
454 cat > "$REDIR_FILE" << 'REDIR_EOF'
455(redirects
456 repos: (list
457 (for-repo
458 url: "codeberg:sigil/sigil"
459 use: (from-path dir: "../sigil"))))
460REDIR_EOF
461 $SIGIL build --redirects "$REDIR_FILE" --config web
462 rm -f "$REDIR_FILE"
463 else
464 $SIGIL deps install
465 $SIGIL build --config web
466 fi
467 cd "$SCRIPT_DIR"
469 # Copy WASM artifacts into site assets
470 REPL_BUILD="$REPL_SRC/build/web"
471 if [[ -f "$REPL_BUILD/sigil-web-repl.wasm" ]]; then
472 echo " Copying WASM artifacts..."
473 cp "$REPL_BUILD/sigil-web-repl.wasm" "$SCRIPT_DIR/assets/repl/"
474 # The page JS and the wasm are a matched pair: a stale page driving
475 # a fresh wasm hung evaluation on the live site (the eval/input ABI
476 # plumbing had moved on). Always ship the page from the same build.
477 cp "$REPL_BUILD/index.html" "$SCRIPT_DIR/assets/repl/index.html"
479 # Generate merged lib.bundle (web-repl modules + extras for REPL examples).
480 # generate-web-bundle lets LATER dirs override earlier ones, so the
481 # REPL's own lib goes last: it is the coherent, freshly built set,
482 # and a stale peer build must never override its core/stdlib
483 # bytecode (a v7 sigil/core.sgb from an old sigil-sxml build once
484 # shipped over the fresh v9 one and broke the live REPL).
485 BUNDLE_DIRS=""
486 for peer in sigil-sxml sigil-match; do
487 if [[ -d "$PARENT_DIR/$peer/build/dev/lib" ]]; then
488 echo " Adding modules from $peer..."
489 BUNDLE_DIRS="$BUNDLE_DIRS \"$PARENT_DIR/$peer/build/dev/lib\""
490 fi
491 done
492 BUNDLE_DIRS="$BUNDLE_DIRS \"$REPL_BUILD/lib\""
494 echo " Generating lib.bundle..."
495 $SIGIL eval "(import (sigil build))
496 (let ((n (generate-web-bundle
497 output: \"assets/repl/lib.bundle\"
498 dirs: (list $BUNDLE_DIRS))))
499 (display n)
500 (display \" files bundled\n\"))"
501 else
502 warn "WASM build did not produce sigil-web-repl.wasm"
503 fi
504fi
506# ---------------------------------------------------------------------------
507# Step 4: Assemble combined lib directory
508# ---------------------------------------------------------------------------
510info "Assembling combined API docs"
512rm -rf "$COMBINED_LIB"
513mkdir -p "$COMBINED_LIB"
515# Monorepo libs first (primary source)
516merge_lib "$PARENT_DIR/sigil/build/dev/lib" "monorepo"
518# Then extracted repos (only adds modules not already present)
519for repo in "${EXTRACTED_REPOS[@]}"; do
520 merge_lib "$PARENT_DIR/$repo/build/dev/lib" "$repo"
521done
523# Fix package attribution for extracted repos whose modules overlap with monorepo
524for repo in "${EXTRACTED_REPOS[@]}"; do
525 fix_extracted_json "$repo"
526done
528# Generate _pkg manifests for monorepo packages that lack them
529for pkg in "${MONOREPO_PACKAGES[@]}"; do
530 ensure_pkg_manifest "$pkg"
531done
533# Generate _pkg manifests for extracted repos whose custom build tasks skip
534# copy-package-docs (e.g. sigil-crypto, sigil-tls, sigil-nrepl). Without this
535# they never appear in the Libraries sidebar and their pages 404.
536for repo in "${EXTRACTED_REPOS[@]}"; do
537 ensure_extracted_pkg_manifest "$repo"
538done
540echo " Combined lib ready."
542# ---------------------------------------------------------------------------
543# Step 5: Build the site
544# ---------------------------------------------------------------------------
546info "Building site"
548# Ensure press and other deps are installed
549if [[ ! -d ".sigil/deps" ]]; then
550 echo " Installing site dependencies..."
551 $SIGIL deps install
552fi
554# Compile the site explicitly before run. `sigil run`'s auto-compile-hook
555# can produce sub-threshold .sgb stubs in some cases (see sigil-papercut t-2fa9);
556# an explicit `sigil build` produces real bytecode and avoids the failure mode
557# where `(import (sigil-site main))` finds the module but its exports are empty.
558echo " Compiling site sources..."
559$SIGIL build
561# Clear stale output before regenerating. The site build writes pages into
562# public/ but never removes ones that no longer exist, so pages for packages
563# dropped from the arrays (or any renamed/removed content) linger as stale
564# files. Because the deploy step copies public/ verbatim into the fresh pages
565# branch, those stale pages would ship to production on every republish, not
566# just locally. Starting from an empty output tree guarantees public/ reflects
567# exactly the current build.
568rm -rf "$PUBLIC_DIR"
570# Build and run the site
571# -L adds combined-lib to module search paths so Press's load-module-details
572# can find .json doc files for packages not in the site's dependency tree
573$SIGIL -L "$COMBINED_LIB" run -- build
575# Verify
576if [[ ! -d "$PUBLIC_DIR" ]] || [[ ! -f "$PUBLIC_DIR/index.html" ]]; then
577 die "Site build failed - public directory missing or incomplete"
578fi
580# ---------------------------------------------------------------------------
581# Step 6: Deploy or serve
582# ---------------------------------------------------------------------------
584if [[ "$LOCAL_ONLY" == "true" ]]; then
585 echo ""
586 echo "Local build complete! Site is in: $PUBLIC_DIR"
588 if [[ "$SERVE" == "true" ]]; then
589 echo "Starting local server at http://localhost:$PORT"
590 echo "Press Ctrl+C to stop."
591 cd "$PUBLIC_DIR"
592 python3 -m http.server "$PORT"
593 fi
594else
595 info "Publishing site to $DOMAIN"
597 TEMP_DIR=$(mktemp -d)
599 # Initialize a fresh repo with orphan branch
600 cd "$TEMP_DIR"
601 git init --quiet
602 git config user.email "[email protected]"
603 git config user.name "Sigil Pages"
604 git checkout --orphan "$PAGES_BRANCH"
606 # Copy public content
607 cp -r "$PUBLIC_DIR"/* "$TEMP_DIR/"
609 # Create .domains file for Codeberg Pages
610 echo "$DOMAIN" > "$TEMP_DIR/.domains"
612 # Commit and force push
613 git add -A
614 git commit --quiet -m "Update site"
615 git remote add origin "$PAGES_REPO_URL"
616 echo " Force pushing to $PAGES_BRANCH branch..."
617 git push --force origin "$PAGES_BRANCH"
619 echo ""
620 echo "Done! Site will be available at:"
621 echo " https://$DOMAIN/"
622 echo " https://$DOMAIN/docs/"
624 # Cleanup
625 cd "$SCRIPT_DIR"
626 rm -rf "$TEMP_DIR"
627fi