Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ceab38a
feat(helm): add minimal docker-compose-aligned eoapi umbrella chart
lhoupert Jun 30, 2026
04dacfe
feat(helm): make eoapi-workshop chart deployable on remote clusters
lhoupert Jun 30, 2026
d525404
ci(workshop): publish JupyterLab image to GHCR
lhoupert Jul 1, 2026
e6fb0b2
feat(helm): per-participant JupyterLab environments
lhoupert Jul 1, 2026
51b12f0
feat(helm): add stac-manager UI (routed at /manager)
lhoupert Jul 1, 2026
839fa62
feat(helm): subdomain-per-service routing (*.eoapi-workshop.ds.io)
lhoupert Jul 1, 2026
8a45034
feat(helm): rework deploy.sh for subdomain routing + participant tokens
lhoupert Jul 1, 2026
1904e42
docs(helm): rewrite README for subdomain routing + Labs + stac-manager
lhoupert Jul 1, 2026
544988f
ci(workshop): also build the image on the workshop branch
lhoupert Jul 1, 2026
9217657
fix(helm): point stac-auth-proxy OIDC_DISCOVERY_URL at the in-cluster…
lhoupert Jul 1, 2026
6dfc6a2
ci(workshop): tag the image :latest on every build
lhoupert Jul 1, 2026
df7d26b
Merge remote-tracking branch 'origin/foss4geu' into foss4geu-helmchart
lhoupert Jul 1, 2026
2bfa221
fix(helm): seed Lab home with cp -R (not cp -a)
lhoupert Jul 1, 2026
a14fa7d
fix(helm): serve Lab notebooks fresh from the image (persist only work/)
lhoupert Jul 1, 2026
5bc5173
chore(helm): address review — stale comments, pull-secret, dep repos,…
lhoupert Jul 1, 2026
e865816
ci(helm)+chore: validate chart in CI and print URLs via NOTES.txt
lhoupert Jul 1, 2026
bc9a8f8
docs(helm): simplify README (~230→152 lines)
lhoupert Jul 1, 2026
f20d073
fix(helm): make workshop notebooks 03/05 run in-cluster (data + tipg …
lhoupert Jul 1, 2026
1c0452f
fix(helm)+docs: hand browser-reachable URLs to the notebook IFrame cells
lhoupert Jul 1, 2026
78300d6
fix(compose)+docs+helm: run the same service versions everywhere
lhoupert Jul 1, 2026
025de8e
feat(data): load the glad STAC collection in compose and the chart
lhoupert Jul 1, 2026
431d835
fix(docs): prefill notebook 04's username from the catalog
lhoupert Jul 1, 2026
35f9313
fix(docs): prefill notebook 03's username from the catalog
lhoupert Jul 2, 2026
a1c4fee
fix(compose)+docs: make the notebooks run inside the compose jupyterh…
lhoupert Jul 2, 2026
55ca7c7
style: ruff-format the notebooks and stac_auth helper
lhoupert Jul 2, 2026
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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,20 @@ jobs:
run: uv run ruff check
- name: Format
run: uv run ruff format --check

helm:
name: Helm chart
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: azure/setup-helm@v4
- name: Register dependency repos
run: |
helm repo add eoapi https://developmentseed.org/eoapi-k8s/
helm repo add stac-manager https://stac-manager.ds.io/
- name: Build chart dependencies
run: helm dependency build infrastructure/charts/eoapi-workshop
- name: Lint
run: helm lint infrastructure/charts/eoapi-workshop
- name: Render checks
run: ./infrastructure/charts/eoapi-workshop/tests/render-checks.sh
53 changes: 53 additions & 0 deletions .github/workflows/publish-workshop-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Publish workshop image

# Builds the JupyterLab workshop image (Dockerfile.local + environment.yml) and
# pushes it to GHCR. Consumed by the Helm chart's per-participant Labs
# (infrastructure/charts/eoapi-workshop, values key `jupyter.image`).
on:
push:
branches:
- main
- foss4geu-helmchart # workshop branch — image is built from here for now
paths:
- Dockerfile.local
- environment.yml
- docs/**
- .github/workflows/publish-workshop-image.yml
workflow_dispatch: {} # manual run (once this workflow is on the default branch)

jobs:
publish:
name: Build and push
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v5

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Image metadata
id: meta
uses: docker/metadata-action@v5
with:
# Portable: resolves to ghcr.io/developmentseed/eoapi-workshop in this repo.
images: ghcr.io/${{ github.repository_owner }}/eoapi-workshop
tags: |
type=raw,value=latest
type=sha,format=long
type=ref,event=branch

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.local
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,8 @@ cdk.context.json
node_modules/

config.yaml

# Helm: pulled chart dependencies (rebuilt with `helm dependency update`).
# Chart.lock IS tracked for reproducible builds.
charts/*/charts/
charts/**/*.tgz
84 changes: 73 additions & 11 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
database:
image: ghcr.io/stac-utils/pgstac:v0.9.8
image: ghcr.io/stac-utils/pgstac:v0.9.10
environment:
- POSTGRES_USER=username
- POSTGRES_PASSWORD=password
Expand Down Expand Up @@ -59,8 +59,34 @@ services:
start_period: 120s


# Load a public STAC collection from the MAAP STAC into pgstac so notebook 04
# §4.5 has data — mirrors the 2i2c deploy step and the Helm chart's
# features-loader Job (stac-loader container). Idempotent: skips if present.
stac-loader:
image: python:3.12-slim
depends_on:
database:
condition: service_started
environment:
- PGHOST=database
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
- PGPORT=5432
restart: "no"
command: >
bash -c '
set -euo pipefail &&
pip install -q "pypgstac[psycopg]==0.9.10" &&
sleep 5 &&
python3 /opt/load_stac.py
'
configs:
- source: load_stac
target: /opt/load_stac.py

stac-fastapi:
image: ghcr.io/stac-utils/stac-fastapi-pgstac:6.0.2
image: ghcr.io/stac-utils/stac-fastapi-pgstac:6.2.2
ports:
- 8081:8081
environment:
Expand Down Expand Up @@ -107,16 +133,16 @@ services:

titiler-pgstac:
platform: linux/amd64
image: ghcr.io/stac-utils/titiler-pgstac:1.9.0
image: ghcr.io/stac-utils/titiler-pgstac:3.0.0
ports:
- 8082:8082
environment:
# Postgres connection
- POSTGRES_USER=username
- POSTGRES_PASS=password
- POSTGRES_DBNAME=postgis
- POSTGRES_HOST=database
- POSTGRES_PORT=5432
# Postgres connection — titiler-pgstac 3.x reads PG* (POSTGRES_* was dropped)
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
- PGHOST=database
- PGPORT=5432
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=10
# - DB_MAX_QUERIES=10
Expand All @@ -134,9 +160,11 @@ services:
# TiTiler Config
- MOSAIC_CONCURRENCY=1
- TITILER_PGSTAC_API_ENABLE_EXTERNAL_DATASET_ENDPOINTS=True
# AWS S3 endpoint config
# AWS S3 endpoint config — the workshop's s3:// assets (glad collection)
# are in public buckets; read unsigned unless real credentials are provided
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
- AWS_NO_SIGN_REQUEST=${AWS_NO_SIGN_REQUEST:-YES}
depends_on:
database:
condition: service_started
Expand All @@ -146,7 +174,7 @@ services:
bash -c "uvicorn titiler.pgstac.main:app --host 0.0.0.0 --port 8082"

tipg:
image: ghcr.io/developmentseed/tipg:1.1.2
image: ghcr.io/developmentseed/tipg:1.4.0
ports:
- 8083:8083
environment:
Expand Down Expand Up @@ -178,6 +206,8 @@ services:
- database

stac-manager:
# no arm64 manifest published — run the amd64 image under emulation
platform: linux/amd64
image: ghcr.io/developmentseed/stac-manager:1.0.3
ports:
- 8086:8080
Expand Down Expand Up @@ -215,6 +245,38 @@ services:
- STAC_BROWSER_ENDPOINT=http://localhost:8080
- STAC_MANAGER_ENDPOINT=http://localhost:8086

configs:
# same loader as the Helm chart's stac-loader container — keep in sync
load_stac:
content: |
import json
import sys
import urllib.request

from pypgstac.db import PgstacDB
from pypgstac.load import Loader, Methods

SRC = "https://stac.maap-project.org"
COLLECTION = "glad-global-forest-change-1.11"
LIMIT = 100

db = PgstacDB()
if db.query_one("select 1 from pgstac.collections where id=%s", [COLLECTION]):
print(f"{COLLECTION} already present -- skipping load.")
sys.exit(0)

urllib.request.urlretrieve(f"{SRC}/collections/{COLLECTION}", "/tmp/collection.json")
with urllib.request.urlopen(f"{SRC}/search?collections={COLLECTION}&limit={LIMIT}") as r:
features = json.load(r)["features"]
with open("/tmp/items.ndjson", "w") as f:
for feat in features:
f.write(json.dumps(feat) + "\n")

loader = Loader(db=db)
loader.load_collections("/tmp/collection.json", Methods.upsert)
loader.load_items("/tmp/items.ndjson", Methods.upsert)
print(f"{COLLECTION}: collection + {len(features)} items loaded.")

volumes:
pgdata:
feature-loader-state:
Expand Down
Loading
Loading