From 4624923f15e8cc9c111f6cf653bd2c5f0c79342d Mon Sep 17 00:00:00 2001 From: Jean-Luc Davern Date: Mon, 17 Aug 2026 16:50:49 -0500 Subject: [PATCH 1/5] fix(images/p3-sandbox-ide): upgraded code-server to restore quick-fix clicks - Updated the sandbox IDE image to use code-server 4.132.0 instead of 4.22.0. - Restored mouse click handling for code actions in the quick-fix menu. Signed-off-by: Jean-Luc Davern --- images/p3-sandbox-ide/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/p3-sandbox-ide/Dockerfile b/images/p3-sandbox-ide/Dockerfile index 2ce7dd0..8d932ab 100644 --- a/images/p3-sandbox-ide/Dockerfile +++ b/images/p3-sandbox-ide/Dockerfile @@ -1,4 +1,4 @@ -FROM codercom/code-server:4.22.0 +FROM codercom/code-server:4.132.0 COPY --chown=1000:1000 settings.json /home/coder/.local/share/code-server/Machine/settings.json COPY code-server.yaml /etc/code-server.yaml From b395679576a615bdd14f8df53512690bcb113956 Mon Sep 17 00:00:00 2001 From: Jean-Luc Davern Date: Mon, 17 Aug 2026 16:50:50 -0500 Subject: [PATCH 2/5] feat(images/p3-sandbox-ide-java): added Java 21 IDE variant - Added a Java 21-based sandbox IDE image with the Red Hat Java extension installed. - Configured code-server and Maven to use the mounted workspace repository offline. - Added contract tests and documentation for the new image variant. Signed-off-by: Jean-Luc Davern --- images/p3-sandbox-ide-java/CHANGELOG.md | 1 + images/p3-sandbox-ide-java/Dockerfile | 29 +++++++++ images/p3-sandbox-ide-java/README.md | 24 ++++++++ images/p3-sandbox-ide-java/maven-settings.xml | 8 +++ images/p3-sandbox-ide-java/settings.json | 26 ++++++++ .../tests/image-contract.test.sh | 60 +++++++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 images/p3-sandbox-ide-java/CHANGELOG.md create mode 100644 images/p3-sandbox-ide-java/Dockerfile create mode 100644 images/p3-sandbox-ide-java/README.md create mode 100644 images/p3-sandbox-ide-java/maven-settings.xml create mode 100644 images/p3-sandbox-ide-java/settings.json create mode 100755 images/p3-sandbox-ide-java/tests/image-contract.test.sh diff --git a/images/p3-sandbox-ide-java/CHANGELOG.md b/images/p3-sandbox-ide-java/CHANGELOG.md new file mode 100644 index 0000000..825c32f --- /dev/null +++ b/images/p3-sandbox-ide-java/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/images/p3-sandbox-ide-java/Dockerfile b/images/p3-sandbox-ide-java/Dockerfile new file mode 100644 index 0000000..dcabe47 --- /dev/null +++ b/images/p3-sandbox-ide-java/Dockerfile @@ -0,0 +1,29 @@ +# syntax=docker/dockerfile:1.7 + +FROM eclipse-temurin:21.0.8_9-jdk@sha256:7d1d666ddafac14da0ded6b4b076becf76cf88b31f9d7953a76555cc82f86511 AS java + +FROM ghcr.io/lf-certification/p3-sandbox-ide:v1.3.0@sha256:0cf8f45ca811cb45aa4d828b0ff95e5cf7bd9f8541256f1f7436382e542b8c95 + +USER root +COPY --from=java /opt/java/openjdk /opt/java/openjdk +ENV JAVA_HOME=/opt/java/openjdk +ENV PATH="${JAVA_HOME}/bin:${PATH}" + +# code-server 4.22.0 embeds VS Code 1.87.0. redhat.java 1.55.0 +# declares engines.vscode ^1.77.0, so the pinned host is compatible. +USER 1000:1000 +RUN curl --fail --location --silent --show-error \ + https://open-vsx.org/api/redhat/java/1.55.0/file/redhat.java-1.55.0.vsix \ + --output /tmp/redhat.java.vsix \ + && echo "011639c3ee347b9591895bfc77d8cf28f836c053d0a49ae4a83efe9dc473a603 /tmp/redhat.java.vsix" \ + | sha256sum --check --strict \ + && code-server --install-extension /tmp/redhat.java.vsix \ + && rm /tmp/redhat.java.vsix + +# The sandbox operator mounts the target user's home at shared/workspace, so +# jdt.ls resolves the prepared Maven repository directly from the VM. +COPY --chown=1000:1000 maven-settings.xml /home/coder/.m2/settings.xml + +# Keep the base image's terminal and watcher defaults at Machine scope. Java +# policy lives at User scope so code-server merges both settings layers. +COPY --chown=1000:1000 settings.json /home/coder/.local/share/code-server/User/settings.json diff --git a/images/p3-sandbox-ide-java/README.md b/images/p3-sandbox-ide-java/README.md new file mode 100644 index 0000000..fc7f8d3 --- /dev/null +++ b/images/p3-sandbox-ide-java/README.md @@ -0,0 +1,24 @@ +# P3 Sandbox Java IDE + +Java 21 variant of [`p3-sandbox-ide`](../p3-sandbox-ide/README.md) for sandbox workspaces that need Java language intelligence. + +## Included tooling + +- Eclipse Temurin JDK 21 +- Red Hat Java extension for code-server +- Maven project import and dependency-source indexing + +The image inherits the base IDE entrypoint, SSHFS workspace behavior, integrated terminal, and environment-variable contract. Sandbox blueprints select it through `ide.version: java-v1`; the sandbox operator maps that version to this image. + +## Offline contract + +Candidate sessions are not expected to download JDKs, extensions, Maven artifacts, plugins, or sources. The content build must prewarm the target VM user's Maven repository. The sandbox operator copies that prepared repository into writable pod-local storage mounted at `/home/coder/.m2/repository` before the IDE starts. + +Maven import runs offline in the IDE. Authoritative Maven builds and tests run through the integrated terminal on the target VM. + +## Local verification + +```bash +docker build --tag p3-sandbox-ide-java:dev images/p3-sandbox-ide-java +images/p3-sandbox-ide-java/tests/image-contract.test.sh +``` diff --git a/images/p3-sandbox-ide-java/maven-settings.xml b/images/p3-sandbox-ide-java/maven-settings.xml new file mode 100644 index 0000000..a9f9a20 --- /dev/null +++ b/images/p3-sandbox-ide-java/maven-settings.xml @@ -0,0 +1,8 @@ + + + /home/coder/shared/workspace/.m2/repository + true + diff --git a/images/p3-sandbox-ide-java/settings.json b/images/p3-sandbox-ide-java/settings.json new file mode 100644 index 0000000..3434c0c --- /dev/null +++ b/images/p3-sandbox-ide-java/settings.json @@ -0,0 +1,26 @@ +{ + "extensions.autoCheckUpdates": false, + "extensions.autoUpdate": false, + "telemetry.telemetryLevel": "off", + "redhat.telemetry.enabled": false, + "java.jdt.ls.java.home": "/opt/java/openjdk", + "java.configuration.runtimes": [ + { + "name": "JavaSE-21", + "path": "/opt/java/openjdk", + "default": true + } + ], + "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xms100m -Xmx2G -Xlog:disable", + "java.server.launchMode": "Standard", + "java.project.importOnFirstTimeStartup": "automatic", + "java.configuration.updateBuildConfiguration": "automatic", + "java.import.maven.enabled": true, + "java.import.maven.offline.enabled": true, + "java.import.gradle.enabled": false, + "java.maven.downloadSources": false, + "java.eclipse.downloadSources": false, + "java.maven.updateSnapshots": false, + "java.configuration.detectJdksAtStart": false, + "java.showBuildStatusOnStart.enabled": "notification" +} diff --git a/images/p3-sandbox-ide-java/tests/image-contract.test.sh b/images/p3-sandbox-ide-java/tests/image-contract.test.sh new file mode 100755 index 0000000..d187163 --- /dev/null +++ b/images/p3-sandbox-ide-java/tests/image-contract.test.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -euo pipefail + +image=${IMAGE:-p3-sandbox-ide-java:dev} + +version=$(docker run --rm --entrypoint code-server "$image" --version) +case "$version" in + "4.22.0 "*" with Code 1.87.0") ;; + *) + printf 'unexpected code-server host: %s\n' "$version" >&2 + exit 1 + ;; +esac + +extensions=$(docker run --rm --entrypoint code-server "$image" \ + --list-extensions --show-versions) +printf '%s\n' "$extensions" | grep -Fx 'redhat.java@1.55.0' >/dev/null + +java_properties=$(docker run --rm --entrypoint java "$image" \ + -XshowSettings:properties -version 2>&1) +printf '%s\n' "$java_properties" | grep -F 'java.specification.version = 21' >/dev/null + +docker run --rm --entrypoint cat "$image" \ + /home/coder/.local/share/code-server/Machine/settings.json | + jq -e ' + .["files.watcherInclude"] == ["**/*", "../.ide-refresh-marker"] and + .["files.watcherExclude"] == {} and + .["terminal.integrated.profiles.linux"]["Sandbox VM"].path == + "/usr/local/bin/p3-ide-remote-shell" and + .["terminal.integrated.defaultProfile.linux"] == "Sandbox VM" + ' >/dev/null + +docker run --rm --entrypoint cat "$image" \ + /home/coder/.local/share/code-server/User/settings.json | + jq -e ' + .["extensions.autoCheckUpdates"] == false and + .["extensions.autoUpdate"] == false and + .["telemetry.telemetryLevel"] == "off" and + .["redhat.telemetry.enabled"] == false and + .["java.jdt.ls.java.home"] == "/opt/java/openjdk" and + .["java.configuration.runtimes"] == [{ + "name": "JavaSE-21", + "path": "/opt/java/openjdk", + "default": true + }] and + .["java.server.launchMode"] == "Standard" and + .["java.project.importOnFirstTimeStartup"] == "automatic" and + .["java.configuration.updateBuildConfiguration"] == "automatic" and + .["java.import.maven.enabled"] == true and + .["java.import.maven.offline.enabled"] == true and + .["java.import.gradle.enabled"] == false and + .["java.maven.downloadSources"] == false and + .["java.eclipse.downloadSources"] == false and + .["java.maven.updateSnapshots"] == false + ' >/dev/null + +maven_settings=$(docker run --rm --entrypoint cat "$image" /home/coder/.m2/settings.xml) +printf '%s\n' "$maven_settings" | + grep -F '/home/coder/shared/workspace/.m2/repository' >/dev/null +printf '%s\n' "$maven_settings" | grep -F 'true' >/dev/null From 11ca4c22b0ad0b73b1696140b3a9187fd01f6943 Mon Sep 17 00:00:00 2001 From: Jean-Luc Davern Date: Mon, 17 Aug 2026 16:50:53 -0500 Subject: [PATCH 3/5] refactor(images/p3-sandbox-sshfs-init): daemonized sshfs mount script - Removed `sshfs_debug` so `sshfs` no longer stayed in the foreground and blocked mount verification. - Deleted the unused file watcher, marker path, and its cleanup handling. BREAKING CHANGE: This commit introduces breaking changes Signed-off-by: Jean-Luc Davern --- images/p3-sandbox-sshfs-init/mount-sshfs.sh | 44 --------------------- 1 file changed, 44 deletions(-) diff --git a/images/p3-sandbox-sshfs-init/mount-sshfs.sh b/images/p3-sandbox-sshfs-init/mount-sshfs.sh index 257946a..5c190dc 100644 --- a/images/p3-sandbox-sshfs-init/mount-sshfs.sh +++ b/images/p3-sandbox-sshfs-init/mount-sshfs.sh @@ -126,7 +126,6 @@ sshfs_cmd=(sshfs -o gid=$SSHFS_GID -o ServerAliveInterval=15 -o ServerAliveCountMax=3 - -o sshfs_debug ) # Mount the remote filesystem using SSHFS @@ -166,50 +165,10 @@ fi echo "SUCCESS: Remote filesystem mounted successfully at $MOUNT_POINT" -# Start background file watcher to trigger IDE refresh on remote changes -start_file_watcher() { - local marker_file="$1/.ide-refresh-marker" - local watch_interval=${FILE_WATCH_INTERVAL:-2} - - ( - echo "Starting file watcher for $MOUNT_POINT (interval: ${watch_interval}s)" - local last_hash="" - - while true; do - if mount | grep -q "$MOUNT_POINT"; then - # Generate hash of directory structure and file timestamps - local current_hash - if current_hash=$(find "$MOUNT_POINT" -type f -exec stat -c '%n %Y %s' {} \; 2>/dev/null | sort | sha256sum | cut -d' ' -f1 2>/dev/null); then - if [ "$current_hash" != "$last_hash" ] && [ -n "$last_hash" ]; then - echo "File changes detected, triggering IDE refresh" - touch "$marker_file" 2>/dev/null || true - fi - last_hash="$current_hash" - fi - fi - sleep "$watch_interval" - done - ) & - - # Store PID for cleanup - echo $! > /tmp/file_watcher.pid - echo "File watcher started with PID $(cat /tmp/file_watcher.pid)" -} - # Enhanced cleanup handler cleanup() { echo "Received termination signal, cleaning up..." - # Kill file watcher if running - if [ -f /tmp/file_watcher.pid ]; then - local watcher_pid=$(cat /tmp/file_watcher.pid) - if kill -0 "$watcher_pid" 2>/dev/null; then - echo "Stopping file watcher (PID: $watcher_pid)" - kill "$watcher_pid" 2>/dev/null || true - fi - rm -f /tmp/file_watcher.pid - fi - # Unmount SSHFS retry_count=0 while [ $retry_count -lt 3 ]; do @@ -234,9 +193,6 @@ cleanup() { exit 0 } -# Start the file watcher -start_file_watcher "$(dirname "$MOUNT_POINT")" - # Create a monitoring loop to ensure mount stays active # Use shorter sleep intervals to improve signal responsiveness while true; do From bb3a15f390b97074d955564ecc7959813e4fa2fc Mon Sep 17 00:00:00 2001 From: Jean-Luc Davern Date: Fri, 21 Aug 2026 16:27:38 -0500 Subject: [PATCH 4/5] refactor(images): consolidated the Java IDE variant into shared targets - Moved the Java IDE variant into `images/p3-sandbox-ide` as a `java` build target. - Split dev and release workflows to build and publish Java-suffixed tags. - Updated CI scripts to build base and Java image variants separately. - Adjusted the Java contract test to validate the shared base image and new tag. - Filtered changed-image detection to skip deleted image roots. Signed-off-by: Jean-Luc Davern --- .github/workflows/dev-build.yml | 34 +++++++++++ .github/workflows/release-build.yml | 38 +++++++++++- images/p3-sandbox-ide-java/CHANGELOG.md | 1 - images/p3-sandbox-ide-java/Dockerfile | 29 --------- images/p3-sandbox-ide-java/README.md | 24 -------- images/p3-sandbox-ide/Dockerfile | 31 +++++++++- images/p3-sandbox-ide/README.md | 28 +++++++++ .../tests/java-image-contract.test.sh} | 17 +++++- .../variants/java}/maven-settings.xml | 0 .../variants/java}/settings.json | 0 scripts/ci/build-dev-image.sh | 60 +++++++++++++++---- scripts/ci/detect-changed-images.sh | 24 +++++++- 12 files changed, 212 insertions(+), 74 deletions(-) delete mode 100644 images/p3-sandbox-ide-java/CHANGELOG.md delete mode 100644 images/p3-sandbox-ide-java/Dockerfile delete mode 100644 images/p3-sandbox-ide-java/README.md rename images/{p3-sandbox-ide-java/tests/image-contract.test.sh => p3-sandbox-ide/tests/java-image-contract.test.sh} (81%) rename images/{p3-sandbox-ide-java => p3-sandbox-ide/variants/java}/maven-settings.xml (100%) rename images/{p3-sandbox-ide-java => p3-sandbox-ide/variants/java}/settings.json (100%) diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index 48d684a..1ebb0a6 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -107,11 +107,27 @@ jobs: org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} + - name: Generate Java variant metadata + id: java-meta + if: steps.extract.outputs.image-name == 'p3-sandbox-ide' + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ steps.extract.outputs.image-name }} + tags: | + type=ref,event=branch,prefix=dev-,suffix=-latest-java + type=sha,prefix=dev-{{branch}}-,suffix=-java + type=raw,value=dev-latest-java,enable={{is_default_branch}} + labels: | + org.opencontainers.image.title=${{ steps.extract.outputs.image-name }} + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + org.opencontainers.image.revision=${{ github.sha }} + - name: Build and push container image uses: docker/build-push-action@v5 with: context: ${{ matrix.image-path }} file: ${{ matrix.image-path }}/Dockerfile + target: ${{ steps.extract.outputs.image-name == 'p3-sandbox-ide' && 'base' || '' }} push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} @@ -120,6 +136,21 @@ jobs: cache-to: type=gha,mode=max provenance: false + - name: Build and push Java variant + if: steps.extract.outputs.image-name == 'p3-sandbox-ide' + uses: docker/build-push-action@v5 + with: + context: ${{ matrix.image-path }} + file: ${{ matrix.image-path }}/Dockerfile + target: java + push: true + tags: ${{ steps.java-meta.outputs.tags }} + labels: ${{ steps.java-meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + provenance: false + - name: Output build summary run: | echo "## 🚀 Dev Build Complete" >> $GITHUB_STEP_SUMMARY @@ -132,4 +163,7 @@ jobs: echo "### 📦 Published Tags:" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY echo "${{ steps.meta.outputs.tags }}" | tr ',' '\n' >> $GITHUB_STEP_SUMMARY + if [[ -n "${{ steps.java-meta.outputs.tags }}" ]]; then + echo "${{ steps.java-meta.outputs.tags }}" | tr ',' '\n' >> $GITHUB_STEP_SUMMARY + fi echo "\`\`\`" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index fa98aa6..4bb99c8 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -148,11 +148,28 @@ jobs: org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} org.opencontainers.image.revision=${{ github.sha }} + - name: Generate Java variant metadata + id: java-meta + if: needs.parse-tag.outputs.image-name == 'p3-sandbox-ide' + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ needs.parse-tag.outputs.image-name }} + tags: | + type=raw,value=${{ needs.parse-tag.outputs.version }}-java + type=raw,value=v${{ needs.parse-tag.outputs.major }}.${{ needs.parse-tag.outputs.minor }}-java + type=raw,value=v${{ needs.parse-tag.outputs.major }}-java + labels: | + org.opencontainers.image.title=${{ needs.parse-tag.outputs.image-name }} + org.opencontainers.image.version=${{ needs.parse-tag.outputs.version }}-java + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + org.opencontainers.image.revision=${{ github.sha }} + - name: Build and push container image uses: docker/build-push-action@v5 with: context: ${{ needs.parse-tag.outputs.image-path }} file: ${{ needs.parse-tag.outputs.image-path }}/Dockerfile + target: ${{ needs.parse-tag.outputs.image-name == 'p3-sandbox-ide' && 'base' || '' }} push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} @@ -161,8 +178,27 @@ jobs: cache-to: type=gha,mode=max provenance: false + - name: Build and push Java variant + if: needs.parse-tag.outputs.image-name == 'p3-sandbox-ide' + uses: docker/build-push-action@v5 + with: + context: ${{ needs.parse-tag.outputs.image-path }} + file: ${{ needs.parse-tag.outputs.image-path }}/Dockerfile + target: java + push: true + tags: ${{ steps.java-meta.outputs.tags }} + labels: ${{ steps.java-meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + provenance: false + - name: Output build summary run: | + DOCKER_TAGS="${{ steps.meta.outputs.tags }}" + if [[ -n "${{ steps.java-meta.outputs.tags }}" ]]; then + DOCKER_TAGS+=$'\n${{ steps.java-meta.outputs.tags }}' + fi devbox run -- make ci-build-summary \ IMAGE_NAME="${{ needs.parse-tag.outputs.image-name }}" \ IMAGE_PATH="${{ needs.parse-tag.outputs.image-path }}" \ @@ -170,5 +206,5 @@ jobs: GIT_TAG="${{ github.ref_name }}" \ GIT_SHA="${{ github.sha }}" \ IS_LATEST_MAJOR="${{ needs.check-latest-version.outputs.is-latest-major }}" \ - DOCKER_TAGS="${{ steps.meta.outputs.tags }}" \ + DOCKER_TAGS="$DOCKER_TAGS" \ DOCKER_LABELS="${{ steps.meta.outputs.labels }}" diff --git a/images/p3-sandbox-ide-java/CHANGELOG.md b/images/p3-sandbox-ide-java/CHANGELOG.md deleted file mode 100644 index 825c32f..0000000 --- a/images/p3-sandbox-ide-java/CHANGELOG.md +++ /dev/null @@ -1 +0,0 @@ -# Changelog diff --git a/images/p3-sandbox-ide-java/Dockerfile b/images/p3-sandbox-ide-java/Dockerfile deleted file mode 100644 index dcabe47..0000000 --- a/images/p3-sandbox-ide-java/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -# syntax=docker/dockerfile:1.7 - -FROM eclipse-temurin:21.0.8_9-jdk@sha256:7d1d666ddafac14da0ded6b4b076becf76cf88b31f9d7953a76555cc82f86511 AS java - -FROM ghcr.io/lf-certification/p3-sandbox-ide:v1.3.0@sha256:0cf8f45ca811cb45aa4d828b0ff95e5cf7bd9f8541256f1f7436382e542b8c95 - -USER root -COPY --from=java /opt/java/openjdk /opt/java/openjdk -ENV JAVA_HOME=/opt/java/openjdk -ENV PATH="${JAVA_HOME}/bin:${PATH}" - -# code-server 4.22.0 embeds VS Code 1.87.0. redhat.java 1.55.0 -# declares engines.vscode ^1.77.0, so the pinned host is compatible. -USER 1000:1000 -RUN curl --fail --location --silent --show-error \ - https://open-vsx.org/api/redhat/java/1.55.0/file/redhat.java-1.55.0.vsix \ - --output /tmp/redhat.java.vsix \ - && echo "011639c3ee347b9591895bfc77d8cf28f836c053d0a49ae4a83efe9dc473a603 /tmp/redhat.java.vsix" \ - | sha256sum --check --strict \ - && code-server --install-extension /tmp/redhat.java.vsix \ - && rm /tmp/redhat.java.vsix - -# The sandbox operator mounts the target user's home at shared/workspace, so -# jdt.ls resolves the prepared Maven repository directly from the VM. -COPY --chown=1000:1000 maven-settings.xml /home/coder/.m2/settings.xml - -# Keep the base image's terminal and watcher defaults at Machine scope. Java -# policy lives at User scope so code-server merges both settings layers. -COPY --chown=1000:1000 settings.json /home/coder/.local/share/code-server/User/settings.json diff --git a/images/p3-sandbox-ide-java/README.md b/images/p3-sandbox-ide-java/README.md deleted file mode 100644 index fc7f8d3..0000000 --- a/images/p3-sandbox-ide-java/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# P3 Sandbox Java IDE - -Java 21 variant of [`p3-sandbox-ide`](../p3-sandbox-ide/README.md) for sandbox workspaces that need Java language intelligence. - -## Included tooling - -- Eclipse Temurin JDK 21 -- Red Hat Java extension for code-server -- Maven project import and dependency-source indexing - -The image inherits the base IDE entrypoint, SSHFS workspace behavior, integrated terminal, and environment-variable contract. Sandbox blueprints select it through `ide.version: java-v1`; the sandbox operator maps that version to this image. - -## Offline contract - -Candidate sessions are not expected to download JDKs, extensions, Maven artifacts, plugins, or sources. The content build must prewarm the target VM user's Maven repository. The sandbox operator copies that prepared repository into writable pod-local storage mounted at `/home/coder/.m2/repository` before the IDE starts. - -Maven import runs offline in the IDE. Authoritative Maven builds and tests run through the integrated terminal on the target VM. - -## Local verification - -```bash -docker build --tag p3-sandbox-ide-java:dev images/p3-sandbox-ide-java -images/p3-sandbox-ide-java/tests/image-contract.test.sh -``` diff --git a/images/p3-sandbox-ide/Dockerfile b/images/p3-sandbox-ide/Dockerfile index 8d932ab..722ecee 100644 --- a/images/p3-sandbox-ide/Dockerfile +++ b/images/p3-sandbox-ide/Dockerfile @@ -1,11 +1,38 @@ -FROM codercom/code-server:4.132.0 +# syntax=docker/dockerfile:1.7 + +FROM codercom/code-server:4.132.0 AS base COPY --chown=1000:1000 settings.json /home/coder/.local/share/code-server/Machine/settings.json COPY code-server.yaml /etc/code-server.yaml COPY entrypoint.sh /usr/local/bin/entrypoint.sh COPY remote-shell.sh /usr/local/bin/p3-ide-remote-shell -# Expose code-server port EXPOSE 8080 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] + +FROM eclipse-temurin:21.0.8_9-jdk@sha256:7d1d666ddafac14da0ded6b4b076becf76cf88b31f9d7953a76555cc82f86511 AS java-runtime + +FROM base AS java + +USER root +COPY --from=java-runtime /opt/java/openjdk /opt/java/openjdk +ENV JAVA_HOME=/opt/java/openjdk +ENV PATH="${JAVA_HOME}/bin:${PATH}" + +USER 1000:1000 +RUN curl --fail --location --silent --show-error \ + https://open-vsx.org/api/redhat/java/1.55.0/file/redhat.java-1.55.0.vsix \ + --output /tmp/redhat.java.vsix \ + && echo "011639c3ee347b9591895bfc77d8cf28f836c053d0a49ae4a83efe9dc473a603 /tmp/redhat.java.vsix" \ + | sha256sum --check --strict \ + && code-server --install-extension /tmp/redhat.java.vsix \ + && rm /tmp/redhat.java.vsix + +# The sandbox operator mounts the target user's home at shared/workspace, so +# jdt.ls resolves the prepared Maven repository directly from the VM. +COPY --chown=1000:1000 variants/java/maven-settings.xml /home/coder/.m2/settings.xml + +# Keep the base image's terminal and watcher defaults at Machine scope. Java +# policy lives at User scope so code-server merges both settings layers. +COPY --chown=1000:1000 variants/java/settings.json /home/coder/.local/share/code-server/User/settings.json diff --git a/images/p3-sandbox-ide/README.md b/images/p3-sandbox-ide/README.md index 491d48b..4df77fa 100644 --- a/images/p3-sandbox-ide/README.md +++ b/images/p3-sandbox-ide/README.md @@ -54,6 +54,34 @@ docker run -p 8080:8080 \ p3-sandbox-ide ``` +## Java Variant + +The `java` target adds: + +- Eclipse Temurin JDK 21 +- Red Hat Java support for code-server +- Offline Maven project import and dependency-source indexing + +The Java image uses the same repository with a tag suffix: +`p3-sandbox-ide:v1.4.0-java`, `p3-sandbox-ide:v1.4-java`, or +`p3-sandbox-ide:v1-java`. Sandbox definitions select it with +`ide.version: v1-java`. + +Candidate sessions are not expected to download JDKs, extensions, Maven +artifacts, plugins, or sources. The content build must prewarm the target VM +user's Maven repository. The sandbox operator mounts that user's home at +`/home/coder/shared/workspace`, where the Java IDE reads `.m2/repository` +directly. Maven import runs offline in the IDE; authoritative builds and tests +run through the integrated terminal on the target VM. + +### Local Verification + +```bash +docker build --target base --tag p3-sandbox-ide:dev images/p3-sandbox-ide +docker build --target java --tag p3-sandbox-ide:dev-java images/p3-sandbox-ide +images/p3-sandbox-ide/tests/java-image-contract.test.sh +``` + ## Troubleshooting If the IDE fails to start: diff --git a/images/p3-sandbox-ide-java/tests/image-contract.test.sh b/images/p3-sandbox-ide/tests/java-image-contract.test.sh similarity index 81% rename from images/p3-sandbox-ide-java/tests/image-contract.test.sh rename to images/p3-sandbox-ide/tests/java-image-contract.test.sh index d187163..a35f530 100755 --- a/images/p3-sandbox-ide-java/tests/image-contract.test.sh +++ b/images/p3-sandbox-ide/tests/java-image-contract.test.sh @@ -1,11 +1,24 @@ #!/usr/bin/env bash set -euo pipefail -image=${IMAGE:-p3-sandbox-ide-java:dev} +image=${IMAGE:-p3-sandbox-ide:dev-java} +base_image=${BASE_IMAGE:-p3-sandbox-ide:dev} + +base_extensions=$(docker run --rm --entrypoint code-server "$base_image" \ + --list-extensions --show-versions) +if printf '%s\n' "$base_extensions" | grep -F 'redhat.java@' >/dev/null; then + printf 'base image unexpectedly contains redhat.java\n' >&2 + exit 1 +fi + +if docker run --rm --entrypoint test "$base_image" -x /opt/java/openjdk/bin/java; then + printf 'base image unexpectedly contains the Java runtime\n' >&2 + exit 1 +fi version=$(docker run --rm --entrypoint code-server "$image" --version) case "$version" in - "4.22.0 "*" with Code 1.87.0") ;; + "4.132.0 "*) ;; *) printf 'unexpected code-server host: %s\n' "$version" >&2 exit 1 diff --git a/images/p3-sandbox-ide-java/maven-settings.xml b/images/p3-sandbox-ide/variants/java/maven-settings.xml similarity index 100% rename from images/p3-sandbox-ide-java/maven-settings.xml rename to images/p3-sandbox-ide/variants/java/maven-settings.xml diff --git a/images/p3-sandbox-ide-java/settings.json b/images/p3-sandbox-ide/variants/java/settings.json similarity index 100% rename from images/p3-sandbox-ide-java/settings.json rename to images/p3-sandbox-ide/variants/java/settings.json diff --git a/scripts/ci/build-dev-image.sh b/scripts/ci/build-dev-image.sh index 8541cd6..bb2ffcc 100755 --- a/scripts/ci/build-dev-image.sh +++ b/scripts/ci/build-dev-image.sh @@ -82,12 +82,12 @@ echo " SHA: $SHORT_SHA" echo "" # Build the image with multiple tags -TAGS="--tag $DEV_TAG --tag $BRANCH_LATEST_TAG" +TAGS=(--tag "$DEV_TAG" --tag "$BRANCH_LATEST_TAG") # Add dev-latest tag only for main branch if [ "$BRANCH_NAME" = "main" ]; then DEV_LATEST_TAG="${FULL_IMAGE_NAME}:dev-latest" - TAGS="$TAGS --tag $DEV_LATEST_TAG" + TAGS+=(--tag "$DEV_LATEST_TAG") echo " Tags: dev-${BRANCH_NAME}-${SHORT_SHA}, dev-${BRANCH_NAME}-latest, dev-latest" else echo " Tags: dev-${BRANCH_NAME}-${SHORT_SHA}, dev-${BRANCH_NAME}-latest" @@ -102,20 +102,39 @@ if [ -n "$REPO_URL" ]; then REPO_URL=$(echo "$REPO_URL" | sed 's|git@github.com:|https://github.com/|' | sed 's|\.git$||') fi -# Build the image -docker build $TAGS \ - --label "org.opencontainers.image.source=${REPO_URL}" \ - --label "org.opencontainers.image.revision=$(git rev-parse HEAD)" \ - --label "org.opencontainers.image.created=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ - --label "org.opencontainers.image.description=Dev build of ${IMAGE_NAME} from ${BRANCH_NAME}" \ - --label "org.opencontainers.image.licenses=Proprietary" \ - --label "dev.lf-certification.image.path=$IMAGE_PATH" \ - --label "dev.lf-certification.build.branch=$BRANCH_NAME" \ - "$IMAGE_PATH" +LABELS=( + --label "org.opencontainers.image.source=${REPO_URL}" + --label "org.opencontainers.image.revision=$(git rev-parse HEAD)" + --label "org.opencontainers.image.created=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" + --label "org.opencontainers.image.description=Dev build of ${IMAGE_NAME} from ${BRANCH_NAME}" + --label "org.opencontainers.image.licenses=Proprietary" + --label "dev.lf-certification.image.path=$IMAGE_PATH" + --label "dev.lf-certification.build.branch=$BRANCH_NAME" +) + +TARGET=() +if [ "$IMAGE_NAME" = "p3-sandbox-ide" ]; then + TARGET=(--target base) +fi + +docker build "${TARGET[@]}" "${TAGS[@]}" "${LABELS[@]}" "$IMAGE_PATH" + +if [ "$IMAGE_NAME" = "p3-sandbox-ide" ]; then + JAVA_DEV_TAG="${DEV_TAG}-java" + JAVA_BRANCH_LATEST_TAG="${BRANCH_LATEST_TAG}-java" + JAVA_TAGS=(--tag "$JAVA_DEV_TAG" --tag "$JAVA_BRANCH_LATEST_TAG") + if [ "$BRANCH_NAME" = "main" ]; then + JAVA_DEV_LATEST_TAG="${DEV_LATEST_TAG}-java" + JAVA_TAGS+=(--tag "$JAVA_DEV_LATEST_TAG") + fi + + echo "" + echo "Building Java variant..." + docker build --target java "${JAVA_TAGS[@]}" "${LABELS[@]}" "$IMAGE_PATH" +fi echo "✅ Build complete!" -# Push if requested if [ "$PUSH_IMAGE" = true ]; then echo "" echo "Pushing to registry..." @@ -126,6 +145,14 @@ if [ "$PUSH_IMAGE" = true ]; then docker push "$DEV_LATEST_TAG" fi + if [ "$IMAGE_NAME" = "p3-sandbox-ide" ]; then + docker push "$JAVA_DEV_TAG" + docker push "$JAVA_BRANCH_LATEST_TAG" + if [ "$BRANCH_NAME" = "main" ]; then + docker push "$JAVA_DEV_LATEST_TAG" + fi + fi + echo "✅ Push complete!" echo "" echo "Published tags:" @@ -134,4 +161,11 @@ if [ "$PUSH_IMAGE" = true ]; then if [ "$BRANCH_NAME" = "main" ]; then echo " $DEV_LATEST_TAG" fi + if [ "$IMAGE_NAME" = "p3-sandbox-ide" ]; then + echo " $JAVA_DEV_TAG" + echo " $JAVA_BRANCH_LATEST_TAG" + if [ "$BRANCH_NAME" = "main" ]; then + echo " $JAVA_DEV_LATEST_TAG" + fi + fi fi diff --git a/scripts/ci/detect-changed-images.sh b/scripts/ci/detect-changed-images.sh index 468d419..e5c278a 100755 --- a/scripts/ci/detect-changed-images.sh +++ b/scripts/ci/detect-changed-images.sh @@ -51,8 +51,28 @@ if [ -z "$CHANGED_FILES" ]; then exit 0 fi -# Extract unique image directories (top-level only) -CHANGED_IMAGES=$(echo "$CHANGED_FILES" | sed 's|^images/\([^/]*\)/.*|images/\1|; s|^images/\([^/]*\)$|images/\1|' | grep "^images/" | sort -u) +# Extract unique image directories (top-level only), excluding image roots +# deleted by the change under inspection. +CHANGED_IMAGES=$( + echo "$CHANGED_FILES" | + sed 's|^images/\([^/]*\)/.*|images/\1|; s|^images/\([^/]*\)$|images/\1|' | + grep "^images/" | + sort -u | + while read -r image; do + if [ -f "$image/Dockerfile" ]; then + echo "$image" + fi + done +) + +if [ -z "$CHANGED_IMAGES" ]; then + if [ "$OUTPUT_FORMAT" = "json" ]; then + echo '{"has_changes": false, "changed_images": []}' + else + echo "No buildable image changes detected in images/" + fi + exit 0 +fi if [ "$OUTPUT_FORMAT" = "json" ]; then # Output JSON format for CI consumption From 5edfba73c02cfa516a81bb2d505d8ee351a66543 Mon Sep 17 00:00:00 2001 From: Jean-Luc Davern Date: Fri, 21 Aug 2026 17:28:55 -0500 Subject: [PATCH 5/5] chore(scripts/ci): fixed Bash 3.2 compatibility in dev image build - Replaced the optional `TARGET` array with a `BUILD_COMMAND` array to avoid Bash 3.2 issues. - Preserved the `--target base` behavior for `p3-sandbox-ide` builds. Signed-off-by: Jean-Luc Davern --- scripts/ci/build-dev-image.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/ci/build-dev-image.sh b/scripts/ci/build-dev-image.sh index bb2ffcc..96d15d2 100755 --- a/scripts/ci/build-dev-image.sh +++ b/scripts/ci/build-dev-image.sh @@ -112,12 +112,12 @@ LABELS=( --label "dev.lf-certification.build.branch=$BRANCH_NAME" ) -TARGET=() +BUILD_COMMAND=(docker build) if [ "$IMAGE_NAME" = "p3-sandbox-ide" ]; then - TARGET=(--target base) + BUILD_COMMAND+=(--target base) fi -docker build "${TARGET[@]}" "${TAGS[@]}" "${LABELS[@]}" "$IMAGE_PATH" +"${BUILD_COMMAND[@]}" "${TAGS[@]}" "${LABELS[@]}" "$IMAGE_PATH" if [ "$IMAGE_NAME" = "p3-sandbox-ide" ]; then JAVA_DEV_TAG="${DEV_TAG}-java"