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
7 changes: 5 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,12 @@ updates:
- minor
- patch

# The base images of the Dockerfile that self-hosts the web app, pinned by tag and digest.
# The base images of the Docker images (the web app's Dockerfile at the root, the command line's
# in packaging/docker), pinned by tag and digest.
- package-ecosystem: docker
directory: "/"
directories:
- "/"
- "/packaging/docker"
schedule:
interval: weekly
open-pull-requests-limit: 2
Expand Down
302 changes: 302 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
# Builds RustGIS's Docker images and publishes them to the GitHub Container Registry, for
# linux/amd64 and linux/arm64 (see docs/installation.md › Self-host the web app with Docker, and
# The command line in Docker):
#
# ghcr.io/opengeos/rustgis the web app, served by nginx (the Dockerfile at the root)
# ghcr.io/opengeos/rustgis-cli the command line (packaging/docker/cli.Dockerfile)
#
# with the tags
#
# edge, sha-<commit> every update to main
# <version>, <major>.<minor>, every published release (a pre-release gets only its version)
# latest
#
# A pull request that changes an image builds and smoke-tests both without publishing. The web app
# is WebAssembly, so its build stage runs once, on the runner's platform, for both platforms; the
# command line is native code, so each platform is built on a runner of its own and the two are
# then published under one multi-platform tag.
name: Docker images

on:
push:
branches: [main]
release:
types: [published]
# The images' own files and what changes how they build: the dependencies, the build tasks and
# the two apps. A change to the crates' code is built natively and for wasm by
# ci.yml already; a new build dependency of theirs arrives through Cargo.lock.
pull_request:
paths:
Comment thread
giswqs marked this conversation as resolved.
Resolved
- Dockerfile
- .dockerignore
- packaging/docker/**
- .github/workflows/docker.yml
Comment thread
giswqs marked this conversation as resolved.
Resolved
- Cargo.toml
- Cargo.lock
- .cargo/**
- xtask/**
- apps/rustgis-web/**
- apps/rustgis-cli/**
Comment thread
giswqs marked this conversation as resolved.
Resolved
- LICENSE
- THIRD-PARTY-LICENSES.txt
workflow_dispatch:
Comment thread
giswqs marked this conversation as resolved.
Resolved

Comment thread
giswqs marked this conversation as resolved.
Resolved
permissions:
contents: read

# One run at a time per ref, so pushes and manual runs on main publish `edge` in order. A publishing
# run always finishes (a cancelled one could leave the CLI's tags half updated); a newer push only
# replaces a run that is still waiting. A pull request's newer push cancels its older run.
concurrency:
group: docker-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
WEB_IMAGE: ghcr.io/opengeos/rustgis
CLI_IMAGE: ghcr.io/opengeos/rustgis-cli
# The tags of both images (docker/metadata-action); `latest` follows releases, not main.
TAGS: |
type=edge,branch=main
type=sha,enable=${{ github.event_name != 'release' }}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable=${{ github.event_name == 'release' && !github.event.release.prerelease }}

jobs:
web:
name: Web app image
if: github.repository == 'opengeos/RustGIS'
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
packages: write
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
steps:
- name: Checkout repository
uses: actions/checkout@v7

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security (low, medium confidence): these jobs hold packages: write, and the actions are pinned by tag (@v7, @v4, @v6). package-managers.yml pins its third-party actions by full commit SHA with a version comment. Pinning the docker/* actions the same way would match that convention. The repo also mixes styles (Swatinem/rust-cache@v2, PyO3/maturin-action@v1), so this is a consistency question, not a defect.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this open for the maintainer, as in the earlier round. It's a repo-wide pinning-policy question: most workflows use tags for actions/* and other widely used publishers, and docker/* follows that.

with:
persist-credentials: false

- uses: docker/setup-buildx-action@v4

- name: Image tags and labels
id: meta
uses: docker/metadata-action@v6
env:
# The registry shows the index's annotations for a multi-platform image.
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
with:
images: ${{ env.WEB_IMAGE }}
flavor: latest=false
tags: ${{ env.TAGS }}
# The Dockerfile's own, ahead of the ones metadata-action takes from the repository.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quality (nit, high confidence): the title, description and source strings are repeated in five places: the web Dockerfile's LABEL, this job's labels and annotations, cli.Dockerfile's LABEL, the CLI meta labels, and the manifest job's --annotation flags. A change to one will drift from the others. Consider one env value per image that the workflow steps reuse.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this open as a marginal nit. Each Dockerfile has to carry its own LABELs for local builds, and the manifest annotations have to be passed explicitly, so a shared env value would only remove two of the five copies.

labels: |
org.opencontainers.image.title=RustGIS web app
org.opencontainers.image.description=The RustGIS web app (WebAssembly), served by nginx
org.opencontainers.image.url=https://rustgis.app
annotations: |
org.opencontainers.image.title=RustGIS web app
org.opencontainers.image.description=The RustGIS web app (WebAssembly), served by nginx
org.opencontainers.image.url=https://rustgis.app

# The image for the runner's platform, loaded into Docker to check that it serves the app.
- name: Build for the smoke test
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64
load: true
tags: rustgis:test
cache-from: type=gha,scope=web
cache-to: type=gha,scope=web,mode=max

- name: Smoke test
run: |
set -euo pipefail
docker run -d --name rustgis -p 8080:8080 rustgis:test
for _ in $(seq 30); do
curl -sSf http://127.0.0.1:8080/healthz >/dev/null && break
sleep 1
done
curl -sSf -o "$RUNNER_TEMP/index.html" http://127.0.0.1:8080/
grep -q '_bg.wasm' "$RUNNER_TEMP/index.html"
curl -sSf -o /dev/null http://127.0.0.1:8080/sw.js
curl -sSf -o /dev/null http://127.0.0.1:8080/manifest.webmanifest
echo "The image serves the web app."

- name: Smoke test logs
if: failure()
run: docker logs rustgis || true

- name: Sign in to the GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Both platforms; the build stage is the smoke test's, from the builder's cache.
- name: Publish
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
cache-from: type=gha,scope=web

cli:
name: Command line image (${{ matrix.arch }})
if: github.repository == 'opengeos/RustGIS'
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-latest
- arch: arm64
runner: ubuntu-24.04-arm
steps:
- name: Checkout repository
uses: actions/checkout@v7
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
with:
persist-credentials: false

- uses: docker/setup-buildx-action@v4

- name: Image labels
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.CLI_IMAGE }}
flavor: latest=false
tags: ${{ env.TAGS }}
# The Dockerfile's own, ahead of the ones metadata-action takes from the repository.
labels: |
org.opencontainers.image.title=RustGIS command line
org.opencontainers.image.description=The RustGIS command line: run commands and geoprocessing, render maps, inspect data
org.opencontainers.image.url=https://rustgis.app

- name: Build for the smoke test
uses: docker/build-push-action@v7
with:
context: .
file: packaging/docker/cli.Dockerfile
platforms: linux/${{ matrix.arch }}
load: true
tags: rustgis-cli:test
cache-from: type=gha,scope=cli-${{ matrix.arch }}
cache-to: type=gha,scope=cli-${{ matrix.arch }},mode=max

# Runs the binary as the image runs it; drawing the logo and rendering the built-in sample
# exercise the renderer, the fonts and the embedded data.
- name: Smoke test
run: |
set -euo pipefail
docker run --rm rustgis-cli:test tools > "$RUNNER_TEMP/tools.txt"
grep -q buffer "$RUNNER_TEMP/tools.txt"
docker run --rm rustgis-cli:test logo logo.png --size 64 # into /data, unmounted
docker run --rm --entrypoint sh rustgis-cli:test -c \
'rustgis-cli run --sample --cmd "share.exportMap={\"path\":\"/tmp/map.png\"}" && test -s /tmp/map.png \
&& test -s /usr/share/doc/rustgis/THIRD-PARTY-LICENSES.txt'
echo "The image runs the command line."

- name: Sign in to the GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Pushed by digest only; the manifest job tags the two platforms' images together.
- name: Publish by digest
id: push
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v7
with:
context: .
file: packaging/docker/cli.Dockerfile
platforms: linux/${{ matrix.arch }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.CLI_IMAGE }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=cli-${{ matrix.arch }}

- name: Record the digest
if: github.event_name != 'pull_request'
env:
DIGEST: ${{ steps.push.outputs.digest }}
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/digests"
touch "$RUNNER_TEMP/digests/${DIGEST#sha256:}"

- name: Upload the digest
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v7
with:
name: cli-digest-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1

cli-manifest:
name: Command line image (publish)
needs: cli
if: github.repository == 'opengeos/RustGIS' && github.event_name != 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
packages: write
steps:
- name: Download the digests
uses: actions/download-artifact@v8
with:
path: ${{ runner.temp }}/digests
pattern: cli-digest-*
merge-multiple: true

- uses: docker/setup-buildx-action@v4

- name: Image tags
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.CLI_IMAGE }}
flavor: latest=false
tags: ${{ env.TAGS }}

- name: Sign in to the GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Publish the multi-platform tags
working-directory: ${{ runner.temp }}/digests
run: |
set -euo pipefail
tags=()
while IFS= read -r tag; do
[ -n "$tag" ] && tags+=(-t "$tag")
done <<< "$DOCKER_METADATA_OUTPUT_TAGS"
sources=()
for digest in *; do sources+=("$CLI_IMAGE@sha256:$digest"); done
docker buildx imagetools create "${tags[@]}" \
--annotation "index:org.opencontainers.image.title=RustGIS command line" \
--annotation "index:org.opencontainers.image.description=The RustGIS command line: run commands and geoprocessing, render maps, inspect data" \
--annotation "index:org.opencontainers.image.source=https://github.com/opengeos/RustGIS" \
--annotation "index:org.opencontainers.image.licenses=Apache-2.0" \
"${sources[@]}"
docker buildx imagetools inspect "$CLI_IMAGE:$DOCKER_METADATA_OUTPUT_VERSION"
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
# The RustGIS web app, built from this checkout and served by nginx (see docs/installation.md ›
# Self-host the web app with Docker):
#
# docker build -t rustgis-web .
# docker run --rm -p 8080:8080 rustgis-web # http://localhost:8080
# docker build -t rustgis .
# docker run --rm -p 8080:8080 rustgis # http://localhost:8080
#
# It builds as .github/workflows/web-deploy.yml does, with three differences: no analytics tag
# (only the official deploy adds one), relative addresses (as the docs preview's build) so it can
Expand Down
5 changes: 3 additions & 2 deletions GEOLIBRE_PARITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,9 @@ deployment-capabilities.md, ui-profiles.md_
GeoLibre, which covers field work on phones.
- [ ] **PWA and offline basemap tile pre-caching**. Partial: the web app is installable with
offline start; offline data areas with sync. Tile pre-caching unverified.
- [ ] **Docker self-hosting image with runtime config**. Partial: a Dockerfile for the web app; a
published image and runtime config (settings from the container's environment) are missing.
- [ ] **Docker self-hosting image with runtime config**. Partial: a Dockerfile for the web app and
published images (ghcr.io/opengeos/rustgis, and rustgis-cli for the command line); runtime config (settings from the container's
environment) is missing.
Comment thread
giswqs marked this conversation as resolved.
Resolved
- [ ] **Deployment capabilities, policies and UI profiles**: restrict edit, add, run and export per
deployment. Missing.
- [ ] **Package-manager and store channels**: winget, a Homebrew Cask, the Mac App Store. Partial:
Expand Down
3 changes: 2 additions & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ rustgis-cli skills # the AI Assistant's skills (also: add, re
```

`commands` lists the engine commands and their JSON parameters; `tools` lists geoprocessing
tools and their parameters. See [Installation](installation.md) for the CLI packages.
tools and their parameters. See [Installation](installation.md) for the CLI packages, and
[The command line in Docker](installation.md#the-command-line-in-docker) for its image.

## Rendering

Expand Down
Loading
Loading