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
35 changes: 35 additions & 0 deletions .cursor/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Cursor Cloud Agent base image for deweyjose/graphqlcodegen.
# Pins the toolchain agents need; does NOT COPY the repository (Cursor checks out
# the requested revision separately). Keep this lean — no IDEs, no credentials.
#
# Toolchain pins (must stay in sync with the repo / CI):
# - JDK 17 (java.version in plugin + example poms; actions/setup-java temurin 17)
# - Maven is NOT installed system-wide — agents always use ./mvnw, which downloads
# Apache Maven 3.9.6 (see .mvn/wrapper/maven-wrapper.properties).

FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive \
JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
LANG=C.UTF-8

# git + curl: required on fully custom images. python3: offline schemaUrl serving
# for the example harness (see AGENTS.md). unzip: Maven wrapper zip extraction.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
curl \
git \
openjdk-17-jdk-headless \
python3 \
unzip \
&& rm -rf /var/lib/apt/lists/* \
&& test -x "$JAVA_HOME/bin/java" \
&& "$JAVA_HOME/bin/java" -version 2>&1 | grep -q 'version "17'

# ubuntu:24.04 already has an `ubuntu` user; do not recreate it.
# Put JDK 17 first on PATH for login shells (bash -lc used by install/start).
ENV PATH="${JAVA_HOME}/bin:/usr/local/bin:/usr/bin:/bin"

USER ubuntu
WORKDIR /home/ubuntu
10 changes: 10 additions & 0 deletions .cursor/environment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "graphqlcodegen",
"build": {
"dockerfile": "Dockerfile",
"context": "."
},
"user": "ubuntu",
"install": "bash .cursor/install.sh",
"start": "bash .cursor/start.sh"
}
100 changes: 100 additions & 0 deletions .cursor/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env bash
# Cursor Cloud Agent install — runs once per environment build and is snapshotted.
# Prefetch the full reactor's Maven dependencies (plugin + examples) into ~/.m2 so
# agents boot without a cold download. Never bake credentials; never start servers.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"

HASH_DIR="${HOME}/.cache/graphqlcodegen-cloud-env"
HASH_FILE="${HASH_DIR}/pom.sha256"
mkdir -p "${HASH_DIR}"

log() { printf 'graphqlcodegen-cloud-env install: %s\n' "$*"; }

prefer_java_17_home() {
# Prefer the image/CI JDK when a newer system JDK is also installed.
local candidate="${JAVA_HOME:-}"
if [[ -z "${candidate}" || ! -x "${candidate}/bin/java" ]]; then
for candidate in \
/usr/lib/jvm/java-17-openjdk-amd64 \
/usr/lib/jvm/java-17-openjdk \
/usr/lib/jvm/temurin-17-jdk-amd64; do
if [[ -x "${candidate}/bin/java" ]]; then
export JAVA_HOME="${candidate}"
export PATH="${JAVA_HOME}/bin:${PATH}"
return 0
fi
done
elif [[ -x "${candidate}/bin/java" ]]; then
export PATH="${candidate}/bin:${PATH}"
fi
}

require_java_17() {
prefer_java_17_home
if ! command -v java >/dev/null 2>&1; then
echo "error: java not on PATH; expected JDK 17 from the environment image" >&2
exit 1
fi
local ver major
ver="$(java -version 2>&1 | head -n 1 || true)"
# Extract major from e.g. openjdk version "17.0.20.1" 2026-08-18
major="$(java -version 2>&1 | sed -n 's/.*version "\([0-9]*\).*/\1/p' | head -n 1)"
if [[ "${major}" != "17" ]]; then
echo "error: expected Java 17, got: ${ver}" >&2
echo "hint: JAVA_HOME=${JAVA_HOME:-<unset>} PATH=${PATH}" >&2
exit 1
fi
log "java OK (${ver})"
}

# Stable fingerprint of every reactor POM + the Maven wrapper pin. start.sh warns
# when an agent branch drifts from the snapshot that recorded this hash.
record_pom_hash() {
# LC_ALL=C keeps sort order stable across locales.
local fingerprint
fingerprint="$(
{
find . -name pom.xml -print
printf '%s\n' ./.mvn/wrapper/maven-wrapper.properties
} | LC_ALL=C sort -u | while IFS= read -r f; do
# Skip missing optional paths without failing the hash.
if [[ -f "${f}" ]]; then
# Strip leading ./ so sha256sum's path column (and thus the digest) is
# stable regardless of whether find emitted ./pom.xml or pom.xml.
sha256sum "${f#./}"
fi
done | sha256sum | awk '{print $1}'
)"
printf '%s\n' "${fingerprint}" >"${HASH_FILE}"
log "recorded pom hash ${fingerprint} -> ${HASH_FILE}"
}

prefetch_maven() {
if [[ ! -x ./mvnw ]]; then
echo "error: ./mvnw missing; always use the repo wrapper (Maven 3.9.6)" >&2
exit 1
fi

# First invocation downloads the wrapper's pinned Maven distribution (3.9.6).
log "warming Maven wrapper (./mvnw -v)"
./mvnw -v

# Populate ~/.m2 for the whole reactor (plugin + examples). go-offline pulls
# dependency + plugin artifacts; resolve-plugins covers plugin-extension gaps
# go-offline sometimes misses on multi-module reactors.
log "prefetching reactor dependencies (dependency:go-offline)"
./mvnw -B -ntp dependency:go-offline

log "resolving plugin artifacts (dependency:resolve-plugins)"
./mvnw -B -ntp dependency:resolve-plugins

log "Maven local repository warm at ${HOME}/.m2/repository"
}

require_java_17
record_pom_hash
prefetch_maven
log "done"
46 changes: 46 additions & 0 deletions .cursor/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
# Cursor Cloud Agent start — runs detached on every agent boot.
# This repo is a Maven plugin + example harness: no long-running services to
# launch. Only warn when the snapshotted pom fingerprint looks stale.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"

HASH_DIR="${HOME}/.cache/graphqlcodegen-cloud-env"
HASH_FILE="${HASH_DIR}/pom.sha256"

log() { printf 'graphqlcodegen-cloud-env start: %s\n' "$*"; }

current_pom_hash() {
{
find . -name pom.xml -print
printf '%s\n' ./.mvn/wrapper/maven-wrapper.properties
} | LC_ALL=C sort -u | while IFS= read -r f; do
if [[ -f "${f}" ]]; then
# Strip leading ./ so sha256sum's path column (and thus the digest) is
# stable regardless of whether find emitted ./pom.xml or pom.xml.
sha256sum "${f#./}"
fi
done | sha256sum | awk '{print $1}'
}

if [[ -f "${HASH_FILE}" ]]; then
expected="$(tr -d '[:space:]' <"${HASH_FILE}")"
actual="$(current_pom_hash)"
if [[ "${expected}" != "${actual}" ]]; then
log "WARNING: pom/wrapper fingerprint changed since the environment snapshot."
log " snapshot=${expected}"
log " current =${actual}"
log " Dependency versions may have drifted; re-run: bash .cursor/install.sh"
else
log "pom hash matches snapshot (${actual})"
fi
else
log "WARNING: no pom hash at ${HASH_FILE} (install did not record one)."
log " Run: bash .cursor/install.sh"
fi

# Intentionally empty of services — agents start Maven themselves via ./mvnw.
log "no runtime services to start"
exit 0
27 changes: 26 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ encodes the architecture, the conventions CI enforces, and the gotchas that are

## Doc map

- **This file** — repo-wide: reactor layout, build/test commands, CI, release.
- **This file** — repo-wide: reactor layout, build/test commands, CI, release, Cloud Agent env.
- [`graphqlcodegen-maven-plugin/AGENTS.md`](graphqlcodegen-maven-plugin/AGENTS.md) — the plugin's
code: request flow, services, the option-wiring checklist, module-level gotchas.
- [`examples/graphqlcodegen-example/AGENTS.md`](examples/graphqlcodegen-example/AGENTS.md) — the
vendored example harness: what each module demonstrates, how to extend it.
- [`README.md`](README.md) — the **consumer-facing option reference**. Its documented types and
defaults must match the `@Parameter` declarations in `Codegen.java`; keep it in sync whenever
options change.
- [`.cursor/`](.cursor/) — Cursor Cloud Agent environment (Dockerfile + install/start scripts).

## What this project is

Expand All @@ -30,6 +31,30 @@ and clients from `.graphqls` schemas during the build.
to the `generate-sources` phase.
- Consumers configure it entirely through Maven `<configuration>` parameters.

## Cloud Agent environment

Cursor Cloud Agents boot from a prebuilt environment snapshot so they skip cold JDK/Maven
downloads. Configuration lives under [`.cursor/`](.cursor/):

| File | Role |
| --- | --- |
| [`environment.json`](.cursor/environment.json) | Wires the Dockerfile + `install` / `start` scripts |
| [`Dockerfile`](.cursor/Dockerfile) | Lean Ubuntu 24.04 image with **JDK 17** only (no system Maven) |
| [`install.sh`](.cursor/install.sh) | One-shot dependency prefetch (`./mvnw dependency:go-offline` + `resolve-plugins`) and pom hash |
| [`start.sh`](.cursor/start.sh) | Minimal boot: warn if the snapshotted pom/wrapper hash is stale; no services |

**Pins (keep in sync when bumping toolchains):**

- **JDK 17** — `java.version` in the plugin and example poms; CI uses Temurin 17 /
`openjdk-17` in the image.
- **Maven 3.9.6** via `./mvnw` — see `.mvn/wrapper/maven-wrapper.properties`
(`wrapperVersion=3.3.4`). Never install or invoke a system `mvn`.

`install` is snapshotted; `start` runs on every agent boot and must stay cheap. Do not put
credentials in these files. After changing poms or the wrapper on a branch that diverges from
the snapshot, re-run `bash .cursor/install.sh` (or ignore the start warning and let Maven
refresh what changed).

## Build, test, format

Always use the Maven wrapper (`./mvnw`), never a system `mvn` — it pins the build version.
Expand Down
Loading