From a4c816f2ef623809b602f70ea692a7a7fff5a5fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsk=C3=BD?= Date: Sat, 6 Jun 2026 13:57:49 +0200 Subject: [PATCH 1/7] staticExport: add `yarn hacky-static-export` --- .github/workflows/build-static-export.sh | 53 +++++++++++++++++++++ .github/workflows/publish-gh-pages-hack.yml | 29 ++--------- .gitignore | 1 + package.json | 1 + 4 files changed, 59 insertions(+), 25 deletions(-) create mode 100755 .github/workflows/build-static-export.sh diff --git a/.github/workflows/build-static-export.sh b/.github/workflows/build-static-export.sh new file mode 100755 index 000000000..8d4c74798 --- /dev/null +++ b/.github/workflows/build-static-export.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +OUT="$PROJECT_ROOT/static-export" + +cd "$PROJECT_ROOT" + +export VERCEL_GIT_COMMIT_SHA="$(git rev-parse HEAD)" +export VERCEL_GIT_COMMIT_MESSAGE="$(git log -1 --pretty=%B)" +export NEXT_PUBLIC_FAKE_STATIC_EXPORT=true +export NEXT_PUBLIC_FORCE_PROJECT=osmapp.org +export NEXT_PUBLIC_OG_IMAGE_FETCHER_HOST=https://osmapp-preview.vercel.app + +yarn build + +rm -rf "$OUT" +cp -r public "$OUT" +mkdir -p "$OUT/_next" +cp -r .next/static "$OUT/_next" + +if lsof -iTCP:3000 -sTCP:LISTEN -t > /dev/null 2>&1; then + echo "Error: port 3000 is already in use." >&2 + exit 1 +fi + +yarn start & +SERVER_PID=$! + +cleanup() { + kill "$SERVER_PID" 2>/dev/null || true +} +trap cleanup EXIT + +echo "Waiting for server to start..." +for i in $(seq 1 20); do + curl -sf http://localhost:3000 > /dev/null 2>&1 && break + sleep 1 +done + +curl --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/index.html" +curl --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/404.html" + +for lang in $(node --input-type=module <<<'import { LANGUAGES } from "./src/config.mjs"; process.stdout.write(Object.keys(LANGUAGES).join("\n")+"\n");'); do + mkdir -p "$OUT/$lang" + curl --cookie "hideHomepage=yes; lang=$lang" http://localhost:3000 > "$OUT/$lang/index.html" +done + +mkdir -p "$OUT/node/1601837931" +curl --cookie "hideHomepage=yes" http://localhost:3000/node/1601837931 > "$OUT/node/1601837931/index.html" + +echo "Done. Output in: $OUT" diff --git a/.github/workflows/publish-gh-pages-hack.yml b/.github/workflows/publish-gh-pages-hack.yml index b333b9020..071d63573 100644 --- a/.github/workflows/publish-gh-pages-hack.yml +++ b/.github/workflows/publish-gh-pages-hack.yml @@ -31,38 +31,17 @@ jobs: run: yarn install --frozen-lockfile - name: Build next.js (hacky static export) - run: | - export VERCEL_GIT_COMMIT_SHA="$(git rev-parse HEAD)" - export VERCEL_GIT_COMMIT_MESSAGE="$(git log -1 --pretty=%B)" - export NEXT_PUBLIC_FAKE_STATIC_EXPORT=true - export NEXT_PUBLIC_FORCE_PROJECT=osmapp.org - export NEXT_PUBLIC_OG_IMAGE_FETCHER_HOST=https://osmapp-preview.vercel.app - yarn build - mkdir public/_next - cp -r .next/static public/_next - #echo -e "User-agent: *\nDisallow: /" > public/robots.txt - - yarn start & - sleep 5 # server usually starts in 1 second - - curl --cookie "hideHomepage=yes" http://localhost:3000 > public/index.html - curl --cookie "hideHomepage=yes" http://localhost:3000 > public/404.html - for lang in $(node --input-type=module <<<'import { LANGUAGES } from "./src/config.mjs";process.stdout.write(Object.keys(LANGUAGES).join("\n")+"\n");'); do - mkdir public/$lang - curl --cookie "hideHomepage=yes; lang=$lang" http://localhost:3000 > public/$lang/index.html - done; - - # cache SSR version of some pages (eg. Prague, english-only) - mkdir -p public/node/1601837931 - curl --cookie "hideHomepage=yes" http://localhost:3000/node/1601837931 > public/node/1601837931/index.html + run: yarn hacky-static-export - name: Setup Pages id: pages uses: actions/configure-pages@v5 + - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: - path: ./public + path: ./static-export + - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index ed84fc933..98640524c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ yarn-error.log* .env.local .env.sentry-build-plugin /public/icons-indoor +static-export diff --git a/package.json b/package.json index 68420516e..8d72cdfed 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "build": "next build", "start": "next start", "dist": "next build && next export -o dist", + "hacky-static-export": "bash .github/workflows/build-static-export.sh", "postinstall": "copyfiles -u 2 node_modules/maplibre-gl-indoorequal/sprite/* public/icons-indoor/" }, "husky": { From d123479bc01d827449e6e86d02605a0aa57933a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsk=C3=BD?= Date: Tue, 9 Jun 2026 10:32:07 +0200 Subject: [PATCH 2/7] refac --- .github/workflows/publish-gh-pages-hack.yml | 2 +- package.json | 2 +- .../hacky-static-export.sh | 36 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) rename .github/workflows/build-static-export.sh => scripts/hacky-static-export.sh (55%) diff --git a/.github/workflows/publish-gh-pages-hack.yml b/.github/workflows/publish-gh-pages-hack.yml index 071d63573..432268bb8 100644 --- a/.github/workflows/publish-gh-pages-hack.yml +++ b/.github/workflows/publish-gh-pages-hack.yml @@ -30,7 +30,7 @@ jobs: - name: Install deps run: yarn install --frozen-lockfile - - name: Build next.js (hacky static export) + - name: Run hacky-static-export run: yarn hacky-static-export - name: Setup Pages diff --git a/package.json b/package.json index 8d72cdfed..b7d38bf7b 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build": "next build", "start": "next start", "dist": "next build && next export -o dist", - "hacky-static-export": "bash .github/workflows/build-static-export.sh", + "hacky-static-export": "bash scripts/hacky-static-export.sh", "postinstall": "copyfiles -u 2 node_modules/maplibre-gl-indoorequal/sprite/* public/icons-indoor/" }, "husky": { diff --git a/.github/workflows/build-static-export.sh b/scripts/hacky-static-export.sh similarity index 55% rename from .github/workflows/build-static-export.sh rename to scripts/hacky-static-export.sh index 8d4c74798..406df7c7f 100755 --- a/.github/workflows/build-static-export.sh +++ b/scripts/hacky-static-export.sh @@ -1,8 +1,13 @@ #!/usr/bin/env bash set -euo pipefail +if lsof -iTCP:3000 -sTCP:LISTEN -t > /dev/null 2>&1; then + echo "Error: port 3000 is already in use." >&2 + exit 1 +fi + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" OUT="$PROJECT_ROOT/static-export" cd "$PROJECT_ROOT" @@ -12,6 +17,7 @@ export VERCEL_GIT_COMMIT_MESSAGE="$(git log -1 --pretty=%B)" export NEXT_PUBLIC_FAKE_STATIC_EXPORT=true export NEXT_PUBLIC_FORCE_PROJECT=osmapp.org export NEXT_PUBLIC_OG_IMAGE_FETCHER_HOST=https://osmapp-preview.vercel.app +export NEXT_PUBLIC_API_KEY_MAPTILER=yd6CSMQpAwnAcPVZwWQ6 yarn build @@ -20,34 +26,28 @@ cp -r public "$OUT" mkdir -p "$OUT/_next" cp -r .next/static "$OUT/_next" -if lsof -iTCP:3000 -sTCP:LISTEN -t > /dev/null 2>&1; then - echo "Error: port 3000 is already in use." >&2 - exit 1 -fi - yarn start & SERVER_PID=$! -cleanup() { - kill "$SERVER_PID" 2>/dev/null || true -} +cleanup() { kill "$SERVER_PID" 2>/dev/null || true; } trap cleanup EXIT -echo "Waiting for server to start..." -for i in $(seq 1 20); do - curl -sf http://localhost:3000 > /dev/null 2>&1 && break - sleep 1 -done +echo "> Waiting for server to start..." +for i in $(seq 1 9); do curl -sf http://localhost:3000 > /dev/null 2>&1 && break; sleep 1; done -curl --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/index.html" -curl --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/404.html" +echo "> Get index.html and 404.html..." +curl -s --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/index.html" +curl -s --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/404.html" for lang in $(node --input-type=module <<<'import { LANGUAGES } from "./src/config.mjs"; process.stdout.write(Object.keys(LANGUAGES).join("\n")+"\n");'); do + echo "> Get language $lang..." mkdir -p "$OUT/$lang" - curl --cookie "hideHomepage=yes; lang=$lang" http://localhost:3000 > "$OUT/$lang/index.html" + curl -s --cookie "hideHomepage=yes; lang=$lang" http://localhost:3000 > "$OUT/$lang/index.html" done +echo "> Get Prague node page..." mkdir -p "$OUT/node/1601837931" -curl --cookie "hideHomepage=yes" http://localhost:3000/node/1601837931 > "$OUT/node/1601837931/index.html" +curl -s --cookie "hideHomepage=yes" http://localhost:3000/node/1601837931 > "$OUT/node/1601837931/index.html" +echo "" echo "Done. Output in: $OUT" From bbcc68d3e3f8ababd03195960c6a0e02032f06c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsk=C3=BD?= Date: Tue, 9 Jun 2026 10:34:27 +0200 Subject: [PATCH 3/7] ssr --- scripts/hacky-static-export.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/hacky-static-export.sh b/scripts/hacky-static-export.sh index 406df7c7f..3a39d3504 100755 --- a/scripts/hacky-static-export.sh +++ b/scripts/hacky-static-export.sh @@ -35,6 +35,9 @@ trap cleanup EXIT echo "> Waiting for server to start..." for i in $(seq 1 9); do curl -sf http://localhost:3000 > /dev/null 2>&1 && break; sleep 1; done +echo "> Checking SSR..." +curl --silent --fail localhost:3000/node/6 | grep -q "Originally Detonátor route (this message used for SSR check)" && echo "SSR OK" + echo "> Get index.html and 404.html..." curl -s --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/index.html" curl -s --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/404.html" @@ -50,4 +53,4 @@ mkdir -p "$OUT/node/1601837931" curl -s --cookie "hideHomepage=yes" http://localhost:3000/node/1601837931 > "$OUT/node/1601837931/index.html" echo "" -echo "Done. Output in: $OUT" +echo "Output in: $OUT" From b715da0f9529b99641f528711ef28212f314c0b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsk=C3=BD?= Date: Tue, 9 Jun 2026 10:51:13 +0200 Subject: [PATCH 4/7] exit --- scripts/hacky-static-export.sh | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/scripts/hacky-static-export.sh b/scripts/hacky-static-export.sh index 3a39d3504..2b8d9260b 100755 --- a/scripts/hacky-static-export.sh +++ b/scripts/hacky-static-export.sh @@ -28,29 +28,26 @@ cp -r .next/static "$OUT/_next" yarn start & SERVER_PID=$! - cleanup() { kill "$SERVER_PID" 2>/dev/null || true; } trap cleanup EXIT - -echo "> Waiting for server to start..." -for i in $(seq 1 9); do curl -sf http://localhost:3000 > /dev/null 2>&1 && break; sleep 1; done +for i in $(seq 1 5); do sleep 1; curl -sf localhost:3000 > /dev/null 2>&1 && break; done echo "> Checking SSR..." -curl --silent --fail localhost:3000/node/6 | grep -q "Originally Detonátor route (this message used for SSR check)" && echo "SSR OK" +curl --silent --fail localhost:3000/node/6 | grep -q "Originally Detonátor route (this message used for SSR check)" && echo "SSR OK" || exit 1 echo "> Get index.html and 404.html..." -curl -s --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/index.html" -curl -s --cookie "hideHomepage=yes" http://localhost:3000 > "$OUT/404.html" +curl -s --cookie "hideHomepage=yes" localhost:3000 > "$OUT/index.html" +curl -s --cookie "hideHomepage=yes" localhost:3000 > "$OUT/404.html" for lang in $(node --input-type=module <<<'import { LANGUAGES } from "./src/config.mjs"; process.stdout.write(Object.keys(LANGUAGES).join("\n")+"\n");'); do echo "> Get language $lang..." mkdir -p "$OUT/$lang" - curl -s --cookie "hideHomepage=yes; lang=$lang" http://localhost:3000 > "$OUT/$lang/index.html" + curl -s --cookie "hideHomepage=yes; lang=$lang" localhost:3000 > "$OUT/$lang/index.html" done echo "> Get Prague node page..." mkdir -p "$OUT/node/1601837931" -curl -s --cookie "hideHomepage=yes" http://localhost:3000/node/1601837931 > "$OUT/node/1601837931/index.html" +curl -s --cookie "hideHomepage=yes" localhost:3000/node/1601837931 > "$OUT/node/1601837931/index.html" echo "" echo "Output in: $OUT" From a3169bbf26fdbc5a27233ae45ef560028cda423c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsk=C3=BD?= Date: Tue, 9 Jun 2026 11:04:39 +0200 Subject: [PATCH 5/7] =?UTF-8?q?ssr=20failuje=20nev=C3=ADm=20pro=C4=8D,=20p?= =?UTF-8?q?=C5=99itom=20ru=C4=8Dn=C4=9B=20projde?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/hacky-static-export.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/hacky-static-export.sh b/scripts/hacky-static-export.sh index 2b8d9260b..5fa94871e 100755 --- a/scripts/hacky-static-export.sh +++ b/scripts/hacky-static-export.sh @@ -26,14 +26,18 @@ cp -r public "$OUT" mkdir -p "$OUT/_next" cp -r .next/static "$OUT/_next" -yarn start & +yarn start > /dev/null 2>&1 & SERVER_PID=$! cleanup() { kill "$SERVER_PID" 2>/dev/null || true; } trap cleanup EXIT -for i in $(seq 1 5); do sleep 1; curl -sf localhost:3000 > /dev/null 2>&1 && break; done +for i in $(seq 1 5); do curl -sf localhost:3000/node/6 > /dev/null 2>&1 && break; sleep 5; done echo "> Checking SSR..." -curl --silent --fail localhost:3000/node/6 | grep -q "Originally Detonátor route (this message used for SSR check)" && echo "SSR OK" || exit 1 +if curl --silent --fail localhost:3000/node/6 | grep -q "Originally Detonátor route (this message used for SSR check)"; then + echo "SSR OK" +else + echo "SSR FAILED"; exit 1 +fi echo "> Get index.html and 404.html..." curl -s --cookie "hideHomepage=yes" localhost:3000 > "$OUT/index.html" From bf4fe3796a18d4f72ad9e3e353356f360b26e49b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 07:50:24 +0000 Subject: [PATCH 6/7] fix(hacky-static-export): make SSR check reliable + kill orphaned server The SSR check piped `curl` into `grep -q`. With `set -o pipefail`, grep -q closes the pipe on its first match, so curl gets SIGPIPE and exits non-zero while still writing the body - the pipeline then fails and reports "SSR FAILED" even though the string was present (it passed when run by hand without pipefail). Capture the response first and grep via a here-string, no pipe. Also fix cleanup: killing only the yarn wrapper left the next-server listener orphaned on port 3000, so a stale server could answer the next run's check. Now kill the actual port-3000 listener and its children too. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01BNZNaqq5GCWzbHccySTxSE --- scripts/hacky-static-export.sh | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/scripts/hacky-static-export.sh b/scripts/hacky-static-export.sh index 5fa94871e..0d3c45886 100755 --- a/scripts/hacky-static-export.sh +++ b/scripts/hacky-static-export.sh @@ -28,12 +28,30 @@ cp -r .next/static "$OUT/_next" yarn start > /dev/null 2>&1 & SERVER_PID=$! -cleanup() { kill "$SERVER_PID" 2>/dev/null || true; } +# Killing only the yarn wrapper ($SERVER_PID) leaves the actual `next-server` +# listener orphaned on port 3000. A stale server then keeps answering and makes +# the SSR check below validate outdated output on the next run. So also kill +# whatever is really listening on the port, plus its render-worker children. +cleanup() { + local listener + listener="$(lsof -tiTCP:3000 -sTCP:LISTEN 2>/dev/null | head -n1 || true)" + if [ -n "$listener" ]; then + pkill -P "$listener" 2>/dev/null || true + kill "$listener" 2>/dev/null || true + fi + kill "$SERVER_PID" 2>/dev/null || true +} trap cleanup EXIT for i in $(seq 1 5); do curl -sf localhost:3000/node/6 > /dev/null 2>&1 && break; sleep 5; done echo "> Checking SSR..." -if curl --silent --fail localhost:3000/node/6 | grep -q "Originally Detonátor route (this message used for SSR check)"; then +# Capture the whole response before grepping: piping curl into `grep -q` makes +# grep close the pipe on its first match, so curl gets SIGPIPE and exits non-zero +# while still writing the body. Under `set -o pipefail` that fails the pipeline +# and reports "SSR FAILED" even though the string was found (passes when run by +# hand without pipefail). A here-string avoids the pipe entirely. +SSR_HTML="$(curl --silent --fail localhost:3000/node/6 || true)" +if grep -q "Originally Detonátor route (this message used for SSR check)" <<< "$SSR_HTML"; then echo "SSR OK" else echo "SSR FAILED"; exit 1 From 7a0ceef5b7927950383230ca47f7fc6f083abe6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Zbytovsk=C3=BD?= Date: Sat, 20 Jun 2026 12:55:08 +0200 Subject: [PATCH 7/7] fix + set -m --- scripts/hacky-static-export.sh | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/scripts/hacky-static-export.sh b/scripts/hacky-static-export.sh index 0d3c45886..710ec9d48 100755 --- a/scripts/hacky-static-export.sh +++ b/scripts/hacky-static-export.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -euo pipefail +set -euom pipefail if lsof -iTCP:3000 -sTCP:LISTEN -t > /dev/null 2>&1; then echo "Error: port 3000 is already in use." >&2 @@ -28,28 +28,12 @@ cp -r .next/static "$OUT/_next" yarn start > /dev/null 2>&1 & SERVER_PID=$! -# Killing only the yarn wrapper ($SERVER_PID) leaves the actual `next-server` -# listener orphaned on port 3000. A stale server then keeps answering and makes -# the SSR check below validate outdated output on the next run. So also kill -# whatever is really listening on the port, plus its render-worker children. -cleanup() { - local listener - listener="$(lsof -tiTCP:3000 -sTCP:LISTEN 2>/dev/null | head -n1 || true)" - if [ -n "$listener" ]; then - pkill -P "$listener" 2>/dev/null || true - kill "$listener" 2>/dev/null || true - fi - kill "$SERVER_PID" 2>/dev/null || true -} +cleanup() { kill -- -"$SERVER_PID" 2>/dev/null || true; } # works thanks to `set -m` trap cleanup EXIT + for i in $(seq 1 5); do curl -sf localhost:3000/node/6 > /dev/null 2>&1 && break; sleep 5; done echo "> Checking SSR..." -# Capture the whole response before grepping: piping curl into `grep -q` makes -# grep close the pipe on its first match, so curl gets SIGPIPE and exits non-zero -# while still writing the body. Under `set -o pipefail` that fails the pipeline -# and reports "SSR FAILED" even though the string was found (passes when run by -# hand without pipefail). A here-string avoids the pipe entirely. SSR_HTML="$(curl --silent --fail localhost:3000/node/6 || true)" if grep -q "Originally Detonátor route (this message used for SSR check)" <<< "$SSR_HTML"; then echo "SSR OK"