Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .github/workflows/pr-demo-bundle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: PR demo bundle

# Builds the golf tiles from the small committed fixture on every push to any
# pull request (drafts included) and uploads a demo bundle artifact: the fresh
# golfTiles.pmtiles + the PR branch's style + a local viewer page. Download it
# from the PR page (Checks -> PR demo bundle -> Artifacts), unzip, and follow
# README.txt to test the PR in your own browser.

on:
pull_request:

# Cancel superseded runs for the same PR.
concurrency:
group: pr-demo-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
build-demo:
runs-on: ubuntu-latest
timeout-minutes: 45 # first uncached tilemaker compile is the long pole
permissions:
contents: read # no write tokens or secrets: safe for fork PRs
steps:
- name: Checkout (tilemaker submodule needed for the image build)
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

# Same GHA build cache as docker-publish.yml, so the expensive tilemaker
# compile layer is shared. Schema/style-only PRs never recompile it: those
# files are copied after the compile layer and shadowed by the bind mount.
- name: Build golftiles image
uses: docker/build-push-action@v6
with:
context: .
push: false
load: true
tags: golftiles:pr
build-args: JOBS=4
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Validate style JSON parses
run: python3 -m json.tool styles/golfTilesStyle.json > /dev/null

- name: Stage fixture into data/
run: cp tests/fixtures/golf-fixture.osm.pbf data/

# Bind-mount the workspace over /app: run_all.sh reads data/*.pbf and
# writes golfTiles.pmtiles to the workspace root, and the PR's
# run_all.sh / process.lua / config.json are used as-is.
- name: Build tiles from fixture
run: docker run --rm -v "$PWD:/app" golftiles:pr

- name: Validate pmtiles output
run: |
python3 - <<'EOF'
import struct
h = open('golfTiles.pmtiles', 'rb').read(127)
assert len(h) == 127, 'file shorter than a PMTiles header'
assert h[:7] == b'PMTiles', 'bad magic bytes'
assert h[7] == 3, f'unexpected spec version {h[7]}'
tile_data_len = struct.unpack_from('<Q', h, 64)[0]
addressed = struct.unpack_from('<Q', h, 72)[0]
assert tile_data_len > 0, 'tile data section is empty'
assert addressed > 0, 'zero addressed tiles'
print(f'OK: {addressed} addressed tiles, {tile_data_len} bytes of tile data')
EOF

- name: Assemble demo bundle
run: |
mkdir -p bundle
cp golfTiles.pmtiles bundle/
cp styles/golfTilesStyle.json bundle/
cp demo/local-viewer.html bundle/index.html
cp demo/BUNDLE_README.txt bundle/README.txt
printf '\nBuilt from PR #%s, commit %s\n' \
"${{ github.event.pull_request.number }}" \
"${{ github.event.pull_request.head.sha }}" >> bundle/README.txt

- name: Short SHA for artifact name
id: vars
env:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: echo "sha_short=${HEAD_SHA:0:7}" >> "$GITHUB_OUTPUT"

- name: Upload demo bundle
uses: actions/upload-artifact@v4
with:
name: golftiles-demo-pr${{ github.event.pull_request.number }}-${{ steps.vars.outputs.sha_short }}
path: bundle/
retention-days: 14
if-no-files-found: error
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
*.pmtiles
/.vscode
*:Zone.Identifier
!/docs/samples/*.pmtiles
!/docs/samples/*.pmtiles
!/tests/fixtures/*.pbf
25 changes: 25 additions & 0 deletions demo/BUNDLE_README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
golfTiles PR demo bundle
========================

Contents
index.html - local map viewer (opens over Emmaboda GK, zoom 14)
golfTilesStyle.json - the style exactly as committed on this PR branch
golfTiles.pmtiles - tiles built by CI from a small test fixture
(Emmaboda GK + Nybro golfklubb area only)

How to view
1. Unzip this bundle into a folder.
2. From that folder run: npx http-server -p 8080 .
3. Open: http://localhost:8080/

http-server supports HTTP Range Requests, which the pmtiles library
requires. Do NOT use "python -m http.server" - it does not support
Range requests and the map will stay blank.

You can also inspect golfTiles.pmtiles directly with the file picker
at https://pmtiles.io/ (no server needed).

Where this came from
Built by the "PR demo bundle" GitHub Actions workflow. Find it on the
PR page under Checks -> PR demo bundle -> Artifacts (you must be
logged in to GitHub to download artifacts).
44 changes: 44 additions & 0 deletions demo/local-viewer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>golfTiles PR demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Maplibre stylesheet: -->
<link href="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.css" rel="stylesheet" />
<!-- Scripts: -->
<script src="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/pmtiles@3.2.0/dist/pmtiles.js"></script>
<style>html, body { margin: 0; height: 100%; } #map { height: 100%; }</style>
</head>
<body>
<div id='map'></div>
<script type='module'>
// add the PMTiles plugin to the maplibregl global.
const protocol = new pmtiles.Protocol();
maplibregl.addProtocol('pmtiles', protocol.tile);

// Resolve bundle-relative paths against wherever this page is served
// from, so any localhost port or sub-path works.
const tilesUrl = new URL('golfTiles.pmtiles', window.location.href).href;
protocol.add(new pmtiles.PMTiles(tilesUrl));

const style = await (await fetch(new URL('golfTilesStyle.json', window.location.href))).json();
// Point every pmtiles source in the PR's style at the bundled tileset,
// so the style file itself stays byte-identical to the PR branch.
for (const src of Object.values(style.sources)) {
if (src.url && src.url.startsWith('pmtiles://')) {
src.url = 'pmtiles://' + tilesUrl;
}
}

new maplibregl.Map({
container: 'map',
style,
center: [15.59411, 56.52839], // Emmaboda GK - inside the CI fixture.
zoom: 14,
hash: true // shareable / reload-safe position.
});
</script>
</body>
</html>
20 changes: 20 additions & 0 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ If you are using LLM´s tools for development/issue-tracking/discussions, please



# Testing a pull request in your browser: the PR demo bundle
Every pull request (drafts included) automatically gets a downloadable **demo bundle** built by the "PR demo bundle" GitHub Actions workflow on each push. It lets you test a PR's schema ([custom-tilemaker/process.lua](../custom-tilemaker/process.lua)) and style ([styles/golfTilesStyle.json](../styles/golfTilesStyle.json)) changes in your own browser without installing osmium-tool or tilemaker.

The bundle contains:
- ```golfTiles.pmtiles``` — tiles freshly built by CI from a small committed test fixture ([tests/fixtures/golf-fixture.osm.pbf](../tests/fixtures/golf-fixture.osm.pbf), the area around Emmaboda GK and Nybro golfklubb).
- ```golfTilesStyle.json``` — the style exactly as committed on the PR branch.
- ```index.html``` — a local viewer that opens over Emmaboda GK at zoom 14 and points the style at the bundled tiles automatically.
- ```README.txt``` — the same instructions in short form.

To use it:
1. On the PR page go to Checks -> **PR demo bundle** -> Artifacts and download ```golftiles-demo-pr<N>-<sha>``` (you need to be logged in to GitHub).
2. Unzip it, and from that folder run ```npx http-server -p 8080 .```
3. Open http://localhost:8080/

Note that the pmtiles library needs HTTP Range Requests, which http-server supports — ```python -m http.server``` does not, and the map will stay blank with it. You can also inspect the ```golfTiles.pmtiles``` file directly with the file picker on https://pmtiles.io/ without any server.

The fixture is committed in the repo, so CI never downloads anything from Geofabrik; refresh the fixture manually with [tests/fixtures/extract-config.json](../tests/fixtures/extract-config.json) if it ever needs newer OSM data. The first CI run after a change to the Dockerfile or the tilemaker submodule recompiles tilemaker (~15–30 min); other runs reuse the GitHub Actions build cache and finish in a few minutes.



# Set up Maputnik locally in WSL to be able to work on the style:
Because I have not yet uploaded the example .pmtiles or the stle to a bucket which supports CORS some acrobatics with exposing the required things locally is needed:

Expand Down
7 changes: 7 additions & 0 deletions tests/fixtures/extract-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"directory": "tests/fixtures",
"extracts": [
{ "output": "part-emmaboda.osm.pbf", "bbox": [15.52, 56.48, 15.68, 56.58] },
{ "output": "part-nybro.osm.pbf", "bbox": [15.82, 56.70, 16.00, 56.80] }
]
}
Binary file added tests/fixtures/golf-fixture.osm.pbf
Binary file not shown.
Loading