Skip to content

Bump SQL server JDK from Temurin 20 (EOL) to 21 LTS#1290

Merged
mbouaziz merged 1 commit into
SkipLabs:mainfrom
mbouaziz:temurin-21-lts
Jul 8, 2026
Merged

Bump SQL server JDK from Temurin 20 (EOL) to 21 LTS#1290
mbouaziz merged 1 commit into
SkipLabs:mainfrom
mbouaziz:temurin-21-lts

Conversation

@mbouaziz

@mbouaziz mbouaziz commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Temurin/OpenJDK 20 is a non-LTS release that stopped receiving security updates in 2023. This moves the SKDB SQL-server Java toolchain to Temurin 21 LTS, a natural follow-on to the base-image modernization in #1261.

Changes (all version-string bumps, no logic changes):

  • eclipse-temurin:20:21 — server core and dev images
  • temurin-20-jdktemurin-21-jdk (Adoptium apt) + JAVA_HOME in sql/Dockerfile
  • Gradle toolchain languageVersion 2021core and pg modules
  • CodeQL workflow's Kotlin-analysis JDK 2021

The Gradle wrapper stays at 8.1.1 — it runs fine on JDK 21 (verified), so no wrapper bump is needed.

Note on the SDKMAN sentinel

The .sdkman/candidates/java/20.0.2-tem marker in sql/Dockerfile and sql/ts/tests/service_common.mk is intentionally left at 20.0.2-tem. It is a build-time sentinel that lets the Makefile skip its SDKMAN install (the real JDK comes from apt), and it must match the already-published image that the skdb-wasm CI job runs in. A comment now documents this so future JDK bumps don't touch it. (An earlier revision bumped it and turned skdb-wasm red — sdk: command not found — because the published image still had the old sentinel dir.)

Test plan

Verified locally (Docker, amd64):

  • server-core image builds on eclipse-temurin:21 — Kotlin compiles, jars, check passes with wrapper 8.1.1
  • dev image skgw stage builds on eclipse-temurin:21
  • temurin-21-jdk installs from the Adoptium noble apt repo; JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64 resolves; java -version21.0.11 LTS
  • CI green

@mbouaziz
mbouaziz force-pushed the temurin-21-lts branch 2 times, most recently from c225166 to c88a363 Compare July 8, 2026 15:35
Temurin/OpenJDK 20 is a non-LTS release and no longer receives security
updates. Move the SKDB SQL server Java toolchain to Temurin 21 LTS:

- eclipse-temurin:20 → :21 in the server core and dev images
- temurin-20-jdk → temurin-21-jdk (Adoptium apt) + JAVA_HOME in sql/Dockerfile
- Gradle toolchain languageVersion 20 → 21 in the core and pg modules
- CodeQL workflow's Kotlin analysis JDK 20 → 21

The Gradle wrapper (8.1.1) runs fine on JDK 21; verified locally by
building the server-core and dev images and confirming temurin-21-jdk
installs from the Adoptium noble repo at the expected JAVA_HOME path.

The .sdkman sentinel version in sql/Dockerfile and sql/ts/tests/
service_common.mk is intentionally left at 20.0.2-tem: it is a build-time
marker that lets the Makefile skip its SDKMAN install (the real JDK comes
from apt), and it must match the already-published image, which the
skdb-wasm CI job runs against. A comment now records this.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mbouaziz
mbouaziz merged commit 6faf821 into SkipLabs:main Jul 8, 2026
5 checks passed
@mbouaziz
mbouaziz deleted the temurin-21-lts branch July 8, 2026 16:04
@mbouaziz

mbouaziz commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

JDK 20 → 21 breaking-change check: no impact

Short answer: no, we're not affected. Details of what I checked:

Scope. The only JVM code in scope is the Kotlin SQL server (sql/server/{core,dev,pg}) — 11 .kt files, 0 .java. The SKDB engine itself is Skiplang (compiled by skargo, JDK-independent), so it's untouched by the JDK version. Build and run both happen on eclipse-temurin:21, and the jars aren't distributed to run on older JVMs, so there's no bytecode target-level concern either.

Known 20 → 21 source-incompatibilities — all checked, none present:

  • SequencedCollection (JEP 431) — the headline Kotlin-on-21 gotcha: JDK 21 added removeFirst()/removeLast()/getFirst()/getLast()/addFirst()/addLast() to List/Deque, which can clash with Kotlin stdlib's existing removeFirst/removeLast extensions. Grep for any of these on collections: no usages.
  • Removed / deprecated-for-removal APIs (Thread.stop/suspend/resume, SecurityManager, finalize(), Runtime.exec(String)): none used.

Toolchain. Kotlin 2.1.20 fully supports JDK 21. 20 → 21 is a single feature release whose headline changes (virtual threads, pattern matching, sequenced collections) are all additive.

Empirical. The server core and dev images compile cleanly on JDK 21 (the only warning is a pre-existing, JDK-independent Kotlin/Java interop note in Mux.kt about a property named uri shadowing a Java field), and all CI is green including skdb and skdb-wasm.

🤖 Generated with Claude Code

mbouaziz added a commit that referenced this pull request Jul 16, 2026
The skiplabs/* images CI runs on were published by a maintainer running
bin/release_docker_ci_images.sh locally, with their own Docker Hub credentials.
Nothing else ever rebuilt them, which is why skiplang-bin-builder sat a year
stale carrying 3 critical / 68 high CVEs (#1338), and why skdb-base silently
stopped building after the Ubuntu 24.04 bump until someone happened to rebuild
it by hand (#1290) -- CI runs against the published images, so it cannot notice.

Build and push the bake "ci" group from Actions instead, on three triggers:
workflow_dispatch, push to main filtered to the paths that affect the images,
and weekly. The path filter is what fixes the #1338 class of rot, where the
Dockerfile pin was bumped and the republish forgotten. The weekly run is what
picks up base image security updates, since with no layer cache every run
re-resolves FROM and re-runs apt.

Deliberately not triggered on skiplang/**: the compiler changes constantly, and
republishing the CI images on every compiler commit is a policy change well
beyond fixing the rot. Images can still lag main between publishes, exactly as
they do today.

Native runners rather than QEMU: arm64 hosted runners are free on public repos,
and emulating a self-hosting compiler bootstrap is both ~4x slower and a risk
worth not taking. Each runner builds its own platform and pushes by digest, then
a merge job creates the multi-arch tags. Nothing rewrites a :latest tag until
every platform of every image has pushed successfully.

Verified locally against a registry on buildx v0.35.0, since the interesting
parts cannot be exercised without pushing: a target that is both published and
consumed as a named context (skdb-base builds FROM skip) keeps its own
push-by-digest output; each per-platform push is an index carrying its
attestation manifest inline; and imagetools create preserves both platforms'
attestations into the merged tag.

The buildx pin is deliberate. setup-buildx-action has no default version, and
imagetools create silently dropped attestations before v0.30.0 -- a failure
whose only symptom is the registry quietly lacking provenance.
mbouaziz added a commit that referenced this pull request Jul 16, 2026
Closes #1348.

## Problem

The `skiplabs/*` images CI runs on were published by a maintainer
running `bin/release_docker_ci_images.sh` locally, with their own Docker
Hub credentials. Nothing else ever rebuilt them, and it shows:

- `skiplang-bin-builder` sat ~a year stale carrying **3 critical / 68
high** CVEs (#1338), because the only thing that ever published it was a
human remembering to.
- `skdb-base` silently stopped building after the Ubuntu 24.04 bump and
no one noticed until a manual rebuild happened to surface it (#1290) —
CI runs *against* the published images, so it structurally cannot catch
this.
- `skiplang:latest` and `skip:latest` on Docker Hub are currently a week
apart despite being stages of the *same* Dockerfile, so CI jobs run
against inconsistent compiler builds.

## What this does

Builds and pushes the images from GitHub Actions instead, on three
triggers:

- **`workflow_dispatch`** — on demand
- **`push` to `main`**, path-filtered to the files the images are built
from — this is what fixes the #1338 class of rot (pin bumped, republish
forgotten)
- **weekly `schedule`** — picks up base-image security updates, since
with no layer cache every run re-resolves `FROM` and re-runs `apt`

### Scope: four toolchain images

Publishes the new **`ci` group** in `docker-bake.hcl`: `skiplang`,
`skiplang-bin-builder`, `skip`, `skdb-base`. These are toolchain images
— their final stages hold compiled binaries and apt packages but no repo
source, so any green `main` commit can rebuild them and republishing is
not a release.

`skdb` and `skdb-dev-server` are deliberately excluded: they bake in
source and their tags carry release semantics. (`skiplabs/skdb` has in
fact never existed on Docker Hub, despite being in bake's `default`
group.)

### Source of truth

The `ci` group is authoritative. The workflow derives its per-image tag
overrides from `bake --print ci` at runtime rather than listing them,
`release_docker_ci_images.sh` now takes its list from the same group (so
the manual path and the automated one can't drift, and a manual publish
finally includes `skiplang-bin-builder`), and a new
`bin/check_ci_images.sh` asserts every image CircleCI pulls is in the
group — replacing the old `grep .circleci/base.yml` derivation, which
structurally could not express `skiplang-bin-builder`.

### Runner strategy

Native `ubuntu-24.04` + `ubuntu-24.04-arm` in parallel (arm64 hosted
runners are free on public repos), each pushing by digest, then a merge
job creates the multi-arch tags. QEMU was rejected: ~4× slower and it
would emulate a self-hosting compiler bootstrap. **Nothing rewrites a
`:latest` tag until every platform of every image has pushed
successfully**, and the merge validates all images before publishing
any.

Provenance (`mode=max`) + SBOM attestations are emitted via
bake-action's `provenance`/`sbom` inputs, matching what
`bin/docker_build.sh` already does for the manual path (#1350), so
images published either way carry the same attestations.

## Verification

The parts that can't be exercised without pushing were verified locally
against a registry on buildx v0.35.0:

- a target that is both published **and** consumed as a named context
(`skdb-base` builds `FROM skip`) keeps its own push-by-digest output —
settled at solve time, not just `bake --print`;
- each per-platform push is an OCI index carrying its attestation
manifest inline;
- `imagetools create` preserves **both** platforms' attestations into
the merged tag (verified: merged index = 2 platform manifests + 2
attestation manifests).

`actionlint` (1.7.7) and `shellcheck` are clean on everything here.

## Notes for the reviewer

- **Requires org secrets `DOCKERHUB_USERNAME` / `DOCKERHUB_TOKEN`**
(scoped push token) before the first run — the repo has none today. The
first run should be a watched `workflow_dispatch`, not the weekly
schedule.
- **Stacked on the actionlint fix** (first commit): CI currently
installs actionlint **1.7.6** — the `v1.7.7` in the URL only selects the
downloader script, which defaults to 1.7.6 — and 1.7.6 rejects `runs-on:
ubuntu-24.04-arm`. That commit stands on its own but this workflow needs
it to lint.
- **Conscious tradeoff:** the workflow does not run CI against the
freshly built images before overwriting `:latest`. That would roughly
double the pipeline; the digest/attestation guards catch push-level
failures but not "builds fine, breaks CI". `cancel-in-progress: false`
and a watched first run are the mitigations.
- Actions are pinned to major tags (`@v4`/`@v7`), matching repo
convention (no dependabot). SHA-pinning would harden this first
credential-holding workflow but the actions would then never update —
happy to switch if preferred.
- Expect `unknown/unknown` rows on Docker Hub for these images — that's
how BuildKit stores attestation manifests, not a bug.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants