Skip to content
Open
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
279 changes: 150 additions & 129 deletions .github/workflows/build-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,155 +3,176 @@ name: build
on:
push:
branches: [develop, main]
tags:
- 'v*'
tags: ['v*']
pull_request:
branches: [develop, main]
workflow_dispatch:
inputs:
GDAL_VER:
description: "GDAL version to use for Docker build"
description: GDAL version to use for Docker build
required: false
default: "3.12.1"
default: '3.12.1'

permissions:
contents: read

jobs:
# ------------------------------------------------------------
# Resolve release tag from ref
# ------------------------------------------------------------
release-meta:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
outputs:
tag: ${{ steps.meta.outputs.tag }}
image: ${{ steps.meta.outputs.image }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: python -m pip install PyYAML
- id: meta
env:
EVENT_NAME: ${{ github.event_name }}
run: |
set -euo pipefail
if [[ "${GITHUB_REF}" == "refs/heads/develop" ]]; then
TAG="latest-dev"
elif [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
TAG="latest"
elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then
TAG="${GITHUB_REF#refs/tags/v}"

# --------------------------------------------------
# Validate against release.yaml
# --------------------------------------------------
RELEASE_VERSION=$(yq -r '.image_version' release.yaml)

if [[ -z "${RELEASE_VERSION}" || "${RELEASE_VERSION}" == "null" ]]; then
echo "image_version not found in release.yaml"
exit 1
fi

if [[ "${TAG}" != "${RELEASE_VERSION}" ]]; then
echo "Git tag v${TAG} does not match release.yaml version ${RELEASE_VERSION}"
exit 1
fi
else
echo "Unsupported ref ${GITHUB_REF}"
exit 1
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"

# ------------------------------------------------------------
# Build, scan, SBOM, push
# ------------------------------------------------------------
build-scan-push:
needs: release-meta
runs-on: ubuntu-latest

python - <<'PY'
import os, yaml
with open('release.yaml') as stream:
release = yaml.safe_load(stream)
ref = os.environ['GITHUB_REF']
if os.environ['EVENT_NAME'] == 'pull_request':
tag = 'pr-test'
elif ref == 'refs/heads/develop':
tag = 'latest-dev'
elif ref == 'refs/heads/main':
tag = 'latest'
elif ref.startswith('refs/tags/v'):
tag = ref.removeprefix('refs/tags/v')
if tag != str(release['image_version']):
raise SystemExit('Git tag does not match release.yaml image_version')
else:
raise SystemExit(f'Unsupported release ref: {ref}')
image = '/'.join(release[key] for key in ('image_registry', 'image_prefix', 'image_name'))
with open(os.environ['GITHUB_OUTPUT'], 'a') as stream:
stream.write(f'tag={tag}\nimage={image}\n')
PY

extension-releases:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Install tooling
run: |
sudo apt-get update
sudo apt-get install -y jq skopeo
curl -sL https://github.com/oras-project/oras/releases/download/v1.2.2/oras_1.2.2_linux_amd64.tar.gz | tar -xz oras
sudo mv oras /usr/local/bin/oras

- name: Configure Docker Hub auth for Kaniko
run: |
if [[ -n "${{ secrets.dockerhub-username }}" ]]; then
mkdir -p $HOME/.docker
cat > $HOME/.docker/config.json <<EOF
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "$(echo -n "${{ secrets.dockerhub-username }}:${{ secrets.dockerhub-token }}" | base64 -w0)"
}
}
}
EOF
fi
- name: Resolve GDAL version
id: gdal
run: |
if [[ -n "${{ github.event.inputs.GDAL_VER }}" ]]; then
GDAL_VER="${{ github.event.inputs.GDAL_VER }}"
else
GDAL_VER="3.12.1"
fi
echo "GDAL_VER=${GDAL_VER}" >> $GITHUB_ENV
- name: Build image with Kaniko (to tar)
uses: docker://gcr.io/kaniko-project/executor:debug
- name: Download the same released VSIXs for both architectures
env:
DOCKER_CONFIG: /github/home/.docker
GITHUB_TOKEN: ${{ github.token }}
run: python3 ci/test-extensions.py download extension-vsix
- uses: actions/upload-artifact@v4
with:
args: >
--context .
--dockerfile Dockerfile
--build-arg GDAL_VER=${{ env.GDAL_VER }}
--no-push
--tar-path image.tar

# --------------------------------------------------------
# Trivy scan (relaxed for now)
# --------------------------------------------------------
- name: Trivy scan (debug mode)
uses: docker://aquasec/trivy:0.50.2
env:
TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db
name: extension-vsix
path: extension-vsix/

build-test:
needs: [release-meta, extension-releases]
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: actions/download-artifact@v4
with:
args: >
image
--input image.tar
--severity HIGH,CRITICAL
--exit-code 0
--no-progress

- name: Generate SBOM (SPDX JSON)
uses: docker://aquasec/trivy:0.50.2
name: extension-vsix
path: extension-vsix
- name: Build and load architecture image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/${{ matrix.arch }}
load: true
tags: pde-extension-test:ci
build-args: GDAL_VER=${{ inputs.GDAL_VER || '3.12.1' }}
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
provenance: false
- name: Test tools, identity, extensions, and entrypoint
env:
PLATFORM: linux/${{ matrix.arch }}
run: bash ci/test-image.sh pde-extension-test:ci "$PLATFORM" "$PWD/extension-vsix"
- name: Test mastering-app-package initialization and chart startup
run: bash ci/test-mastering.sh pde-extension-test:ci linux/${{ matrix.arch }}
- name: Test nested rootless Podman pull, build, and execution
run: bash ci/test-podman.sh pde-extension-test:ci linux/${{ matrix.arch }}
- name: Save tested image
run: docker save pde-extension-test:ci -o image.tar
- name: Install pinned scan tooling
env:
ARCH: ${{ matrix.arch }}
run: |
set -euo pipefail
case "$ARCH" in amd64) TRIVY_ARCH=64bit ;; arm64) TRIVY_ARCH=ARM64 ;; esac
curl -fsSL "https://github.com/aquasecurity/trivy/releases/download/v0.74.0/trivy_0.74.0_Linux-${TRIVY_ARCH}.tar.gz" | tar -xz trivy
sudo install trivy /usr/local/bin/trivy
- name: Trivy scan (relaxed for now)
env:
TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db
run: trivy image --input image.tar --severity HIGH,CRITICAL --exit-code 0 --no-progress
- name: Generate architecture SBOM
run: trivy image --input image.tar --format spdx-json --output sbom.spdx.json
- name: Upload tested image and its SBOM
uses: actions/upload-artifact@v4
with:
args: >
image
--input image.tar
--format spdx-json
--output sbom.spdx.json

# --------------------------------------------------------
# Push image and attach SBOM
# --------------------------------------------------------
- name: Push image with Skopeo
id: push
name: image-${{ matrix.arch }}
path: |
image.tar
sbom.spdx.json
retention-days: 1

# Neither architecture is published until both native build/test jobs pass.
publish:
if: github.event_name != 'pull_request'
needs: [release-meta, build-test]
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: image-*
path: images
- uses: docker/setup-buildx-action@v3
- name: Install publishing tools
run: |
set -euo pipefail

TAG=${{ needs.release-meta.outputs.tag }}
REGISTRY=$(yq -r '.image_registry' release.yaml)
IMAGE_PREFIX=$(yq -r '.image_prefix' release.yaml)
IMAGE=$(yq -r '.image_name' release.yaml)

IMAGE_REF="${REGISTRY}/${IMAGE_PREFIX}/${IMAGE}:${TAG}"

skopeo copy \
--dest-creds="${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}" \
docker-archive:image.tar \
docker://${IMAGE_REF}

DIGEST=$(skopeo inspect \
--creds="${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}" \
docker://${IMAGE_REF} \
| jq -r .Digest)

echo "${REGISTRY}/${IMAGE_PREFIX}/${IMAGE}@${DIGEST}" > image.digest
sudo apt-get update
sudo apt-get install -y skopeo jq
ARCH=$(dpkg --print-architecture)
curl -fsSL "https://github.com/oras-project/oras/releases/download/v1.2.2/oras_1.2.2_linux_${ARCH}.tar.gz" | tar -xz oras
sudo install oras /usr/local/bin/oras
- name: Publish architecture images, attach SBOMs, and create combined manifest
env:
IMAGE: ${{ needs.release-meta.outputs.image }}
TAG: ${{ needs.release-meta.outputs.tag }}
REGISTRY_TOKEN: ${{ github.token }}
REGISTRY_USER: ${{ github.actor }}
run: |
set -euo pipefail
REGISTRY="${IMAGE%%/*}"
printf '%s' "$REGISTRY_TOKEN" | docker login "$REGISTRY" --username "$REGISTRY_USER" --password-stdin
printf '%s' "$REGISTRY_TOKEN" | oras login "$REGISTRY" --username "$REGISTRY_USER" --password-stdin
refs=()
for arch in amd64 arm64; do
ARCH_REF="${IMAGE}:${TAG}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${arch}"
skopeo copy --dest-creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" \
"docker-archive:images/image-${arch}/image.tar" "docker://${ARCH_REF}"
DIGEST=$(skopeo inspect --creds "${REGISTRY_USER}:${REGISTRY_TOKEN}" "docker://${ARCH_REF}" | jq -r .Digest)
# Attach each SBOM to the exact image digest it describes, not the index.
(cd "images/image-${arch}" && oras attach --artifact-type application/spdx+json \
"${IMAGE}@${DIGEST}" sbom.spdx.json:application/spdx+json)
refs+=("${IMAGE}@${DIGEST}")
done
docker buildx imagetools create --tag "${IMAGE}:${TAG}" "${refs[@]}"
docker buildx imagetools inspect "${IMAGE}:${TAG}"
72 changes: 71 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,74 @@ Notable project changes are recorded here for users and contributors.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).


## Unreleased

- Remove GDAL's temporary build toolchain and unused development dependencies
in the build layer, retaining PROJ/data and linked runtime libraries. Add a
compressed-raster and reprojection regression check.

- Give rootless Podman's UID/GID mapping helpers the setuid permissions needed
to unpack images containing additional user/group IDs, including Python slim.
- Test actual nested rootless Podman builds and non-root execution on both CI
architectures, using a disposable workspace volume.

## [1.4.0] - 2026-11-02

### Added
- AMD64 and ARM64 binary downloads and native CI build/test jobs, with a combined
platform manifest and architecture-specific SBOM attachments.
- Local Buildx tasks for either platform and a multi-platform OCI archive, plus
checks for tool execution, user identity, extensions, and entrypoint startup.
- Pull-request CI validation on both architectures, using the same downloaded
extension releases for AMD64 and ARM64.
- Pinned CWL and transpiler tooling for EOAP extension runtime requirements.
- CI installation checks for the latest released EOAP Validator, CWL Metadata
Editor, and CWL UML Viewer VSIXs, with Python dependency checks, validator
environment setup, and rendering checks for all available UML diagram types.
Failures block image publication; tested release metadata is saved as an artifact.
- A Taskfile for local AMD64 image builds with image tag and GDAL version overrides.
- Standalone local operation through `JHSINGLE_NATIVE_PROXY_AUTHTYPE=none`,
with OAuth retained as the default.

### Changed

- Install Node.js 22.15.0 through NVM 0.40.3 instead of distro Node.js packages.
- Require the OpenJDK 17 headless JDK, replacing the optional Java runtime and
removing the `INSTALL_JRE` build argument.
- Install Calrissian 0.18.1 and its pinned `cwl-utils` 0.40 dependency in an
isolated virtual environment to avoid conflicts with the image's CWL tooling.
- Document local image builds, standalone usage, and extension compatibility checks.
- Replace Kaniko builds with Docker Buildx and publish the tested images only
after both native architecture build/test jobs pass.
- Align the `UID` environment variable with the actual `jovyan` UID of 1001.
- Use Trivy 0.74.0 for CI scans, matching the pinned image scanner; both platform
assets are available, while the previous 0.50.2 binary asset URLs returned 404.
### Fixed
- Migrate the runtime OS to Fedora 44 and code-server to 4.144.0, retaining
the Python 3.12 Conda/Jupyter environment and PDE tools, identity and paths.
- Probe code-server `/healthz` with a 120-second readiness window so cold
editor startup does not stall the native proxy.
- Retain Java 17 through Temurin and use Fedora's Nextcloud client packages.
- Add an integration gate on both architectures for the mastering-app-package
init script, extensions, Python environment, S3 setup and direct editor startup.
- Trivy Transition to 0.76.1
- Include the C++ compiler and XZ utilities required for the GDAL source build.
- Validate proxy authentication modes and quote entrypoint arguments.
- Install Calrissian from commit `a95d87dd213d51b11faea7c6def4ced46d466a4a`
with compatible isolated dependencies and bundled Dask resources.
- Preinstall Hatch 1.16.5 and its runtime dependencies in an isolated environment,
avoiding a first-use download and fixing interpreter discovery with virtualenv
21; add offline Hatch environment creation/activation and Calrissian smoke checks.
- Set `VIRTUAL_ENV_DISABLE_PROMPT=1` for the Bash kernel only: when `~/.bashrc` activates a
virtual environment, activating another one in a notebook cell restored the pre-kernel
prompt and the cell never finished. Terminals keep the `(venv)` prompt.
- Ensure the default workspace is writable by jovyan for tool configuration, and
remove the root-owned build cache that blocked `uv` and pip caches there.
- Store Hatch configuration, data, and caches in writable directories under
`/home/jovyan`, including when a volume is mounted at `/workspace`.
- Make Mike Farah's pinned `yq` the default command while retaining the Python
`yq` dependency and `tomlq`/`xq` launchers; smoke-test YAML and TOML processing.
## [1.3.0] - 2026-10-06

### Added
Expand Down Expand Up @@ -65,6 +133,8 @@ at the earliest Git tag.
tooling versions, and install code-server 4.108.1 from its release tarball.
- Refactor the CI container build and publishing workflow.

[Unreleased]: https://github.com/EOEPCA/pde-code-server/compare/v1.2.0...HEAD
[Unreleased]: https://github.com/EOEPCA/pde-code-server/compare/v1.4.0...HEAD
[1.4.0]: https://github.com/EOEPCA/pde-code-server/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/EOEPCA/pde-code-server/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/EOEPCA/pde-code-server/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/EOEPCA/pde-code-server/releases/tag/v1.1.0
Loading
Loading