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
37 changes: 17 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@ jobs:
persist-credentials: false
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
- name: Set up JDK 21 (Corretto)
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
- name: Set up Java 21 from mise
uses: jdx/mise-action@fc42bcc7ddbf51124c30f95a877a0c5168a3bfde # v5.2.1
with:
distribution: corretto
java-version: "21"
install_args: java
- name: Set up Gradle
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
- name: Build (quality gates + tests + coverage)
Expand All @@ -42,7 +41,7 @@ jobs:
if-no-files-found: ignore

cross-platform:
name: Build (${{ matrix.os }})
name: Test (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
Expand All @@ -53,15 +52,14 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up JDK 21 (Corretto)
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
- name: Set up Java 21 from mise
uses: jdx/mise-action@fc42bcc7ddbf51124c30f95a877a0c5168a3bfde # v5.2.1
with:
distribution: corretto
java-version: "21"
install_args: java
- name: Set up Gradle
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
- name: Build
run: ./gradlew clean build --console=plain
- name: Compile and test
run: ./gradlew clean test --console=plain

lint:
name: Lint & scan
Expand All @@ -72,21 +70,21 @@ jobs:
with:
persist-credentials: false
- name: Set up mise tools
uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0
uses: jdx/mise-action@fc42bcc7ddbf51124c30f95a877a0c5168a3bfde # v5.2.1
with:
experimental: true
- name: typos (spell check)
run: typos
- name: taplo (TOML)
run: taplo lint
- name: yamllint
run: uvx --from yamllint yamllint -c .yamllint .
run: yamllint -c .yamllint .
- name: editorconfig-checker
run: editorconfig-checker
- name: actionlint (workflows)
run: actionlint
- name: zizmor (workflow security)
run: uvx zizmor .github/workflows
run: zizmor .github/workflows
- name: hadolint (Dockerfiles)
run: hadolint Dockerfile Dockerfile.toolchain
- name: gitleaks (secret scan)
Expand All @@ -104,15 +102,14 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up JDK 21 (Corretto)
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
- name: Set up Java 21 from mise
uses: jdx/mise-action@fc42bcc7ddbf51124c30f95a877a0c5168a3bfde # v5.2.1
with:
distribution: corretto
java-version: "21"
install_args: java
- name: Set up Gradle
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
- name: Build for release target
run: ./gradlew clean build -PjavaRelease=${{ matrix.release }} --console=plain
- name: Compile for release target
run: ./gradlew compileJava -PjavaRelease=${{ matrix.release }} --console=plain

# The one status check branch protection requires,
# named the same in every P4suta repository.
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up JDK 21 (Corretto)
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
- name: Set up Java 21 from mise
uses: jdx/mise-action@fc42bcc7ddbf51124c30f95a877a0c5168a3bfde # v5.2.1
with:
distribution: corretto
java-version: "21"
install_args: java
- name: Initialize CodeQL
uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
with:
Expand Down
100 changes: 0 additions & 100 deletions .github/workflows/docker-publish.yml

This file was deleted.

1 change: 1 addition & 0 deletions .pr-workflow/contexts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schema":1,"receipts":[{"context":{"schema":1,"destination":{"repository":"P4suta/java-gradle-template","repository_id":1274153059,"owner_id":42543015,"required":true},"issue":40,"branch":"chore/refresh-template-tooling","head":"5f1f42c1a9182050935b8114deb265680036d256","tree":"92bbc3000c62ab25d96f0c7db56a08dfe1d80cb1","scopes":[".github/workflows/ci.yml",".github/workflows/codeql.yml",".github/workflows/docker-publish.yml","Dockerfile","Dockerfile.toolchain","gradle/libs.versions.toml","justfile","mise.toml","lefthook.yml","README.md","docs/development.md",".gradle/maintenance"],"initial_dirty":{},"adopted":false},"object":"addae433f35e89122c7e2d91a3aa0058f5f83230","signed_commit":"tree 92bbc3000c62ab25d96f0c7db56a08dfe1d80cb1\nparent 5f1f42c1a9182050935b8114deb265680036d256\nauthor Yasunobu <42543015+P4suta@users.noreply.github.com> 1792459977 +0900\ncommitter Yasunobu <42543015+P4suta@users.noreply.github.com> 1792459977 +0900\ngpgsig -----BEGIN SSH SIGNATURE-----\n U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAggYuFWiqTPADsoSIEpaH7qKaADz\n KmGLmP6IPbTLrOaDQAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5\n AAAAQJHB2E5dBB4F+9fqp5P+kViHN08qINVrHQX8FfIOquSHp2zukNBfX6dNpw/uoiYzyR\n tuMI01W6+Afht6KFGpyg4=\n -----END SSH SIGNATURE-----\n\n{\"schema\":1,\"destination\":{\"repository\":\"P4suta/java-gradle-template\",\"repository_id\":1274153059,\"owner_id\":42543015,\"required\":true},\"issue\":40,\"branch\":\"chore/refresh-template-tooling\",\"head\":\"5f1f42c1a9182050935b8114deb265680036d256\",\"tree\":\"92bbc3000c62ab25d96f0c7db56a08dfe1d80cb1\",\"scopes\":[\".github/workflows/ci.yml\",\".github/workflows/codeql.yml\",\".github/workflows/docker-publish.yml\",\"Dockerfile\",\"Dockerfile.toolchain\",\"gradle/libs.versions.toml\",\"justfile\",\"mise.toml\",\"lefthook.yml\",\"README.md\",\"docs/development.md\",\".gradle/maintenance\"],\"initial_dirty\":{},\"adopted\":false}"}]}
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
# Minimal runtime image for the App: build the jar, then run it on a distroless JRE.

# ---- build: produce the runnable jar ----
FROM gradle:8.14.5-jdk21 AS build
FROM gradle:9.8.1-jdk21@sha256:5ec5f726f14851a11a1bac6e6a5720e41062c33357053e7717eb0d189a6f1db9 AS build
WORKDIR /app
COPY . .
RUN gradle --no-daemon --console=plain clean jar

# ---- runtime: distroless JRE ----
FROM gcr.io/distroless/java21-debian12:nonroot AS runtime
FROM gcr.io/distroless/java21-debian12:nonroot@sha256:7e37784d94dccbf5ccb195c73b295f5ad00cd266512dfbac12eb9c3c28f8077d AS runtime
WORKDIR /app
COPY --from=build /app/build/libs/*.jar /app/app.jar
# Pipe input on stdin: docker run -i --rm <image> < input.txt
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile.toolchain
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# syntax=docker/dockerfile:1
# Dev/CI toolchain image: JDK 21 + Gradle + lint tools, pinned via the same mise.toml.
# Example: docker run --rm -v "$PWD":/work ghcr.io/p4suta/java-gradle-template:toolchain ./gradlew build
# Local development image with the project's mise tools.
# Build: docker build -f Dockerfile.toolchain -t java-gradle-toolchain .

FROM debian:bookworm-slim
FROM debian:bookworm-slim@sha256:7c7b2c966bc9ee8cedfeef67e0e279108992c77681fa595db4a9d65c06ccc587

# Fail on errors in piped commands (hadolint DL4006).
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
Expand Down
108 changes: 15 additions & 93 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,104 +1,26 @@
# Coding-training template
# Java coding challenge template

A template for stdin → stdout coding challenges (algorithm / simulation). The scaffolding —
build, quality gates, differential test, dev tooling — is fixed; per challenge you write only the
solution. The initial stub echoes input, so a fresh clone is green immediately.
A Java 21 and Gradle starter for programming challenges that read stdin and write stdout.
The initial solution echoes its input.
Replace it alongside the independent reference, input generator, and sample cases.

## Quick start
## Start

```sh
mise trust && mise install # one-time: JDK 21 + just/lefthook/typos/... (downloads once)
just setup # install git hooks, warm the Gradle + dependency caches
just dev # fast loop: re-run tests on change
mise trust && mise install
just setup
just build
```

`mise install` + `just setup` download the JDK, Gradle, and dependencies once (a few hundred MB);
after that, builds are incremental (seconds). No Docker required — append `USE_DOCKER=1` to any
recipe to run inside `gradle:8.14.1-jdk21` instead.

## Start a new challenge

1. Create a repo from this template ("Use this template") and clone it.
2. (Optional) rename the `track` package — see the checklist below. It works as-is.
3. Implement:
- `src/main/java/track/App.java` — `run(BufferedReader)`, the submitted solution.
- `src/test/java/track/ReferenceSystem.java` — an independent reference.
- `src/test/java/track/QueryGenerator.java` — input generator (bias toward boundaries).
- `src/test/resources/samples/` — `NN_name.in` / `NN_name.out` pairs (auto-discovered).
4. Run `just build` until green.

## Target Java version (per submission)

Judges differ in Java version, so the solution's compile target is a one-knob switch:
`just build` runs the tests, static analysis, and coverage checks.
The solution can target Java 8 through 21 while the build tools and tests use Java 21.

```sh
just release 8 # compile src/main against Java 8 (range 8..21; default 21)
./gradlew build -PjavaRelease=8 # or ad hoc, without persisting
just release 8
just run-sample src/test/resources/samples/00_sample_00.in
```

The toolchain, tests, CI, and Docker all stay on JDK 21 — only `src/main`'s bytecode/API level
changes (via `--release`), so newer-API usage is caught at build time. CI builds every target
(8 / 11 / 17 / 21) to keep the switch honest.

## Commands

| Command | What |
|---|---|
| `just setup` | Install hooks, warm caches (one-time) |
| `just dev` | Re-run tests on change (fast loop) |
| `just build` | All quality gates + tests + coverage |
| `just release <N>` | Set the solution's Java compile target (8..21) |
| `just test` | Tests only (sweep 2000) |
| `just sweep 30000` | Differential sweep with a given count |
| `just coverage` | Coverage report (HTML) |
| `just lint` | All linters + build over the whole tree |
| `just run-sample <file>` | Pipe a sample to stdin |
| `just clean` | Remove build outputs |
| `just init` | Docker cache volume (only with `USE_DOCKER=1`) |

`./gradlew clean build` works directly too.

## Quality gates

`just build` fails if any of these fail:

| Layer | Tool | Catches |
|---|---|---|
| Compile-time analysis | Error Prone + NullAway | null dereferences, known bug patterns |
| Style | Checkstyle (0 warnings) | unused imports, missing braces, ... |
| Bytecode analysis | SpotBugs (effort MAX) | bug patterns |
| Design | ArchUnit | I/O confined to the entry point, ... |
| Differential test | JUnit5 + reference + generator | App vs reference over random inputs |
| Coverage | JaCoCo | under-tested code (fails below threshold) |

Coverage thresholds are at the top of `build.gradle` (`lineCoverageMin` / `branchCoverageMin`);
raise them per challenge after checking `just coverage`.

## Dev tooling

`mise install` provides tools wired into git hooks by lefthook: typos, taplo, yamllint,
shellcheck, editorconfig-checker, gitleaks, actionlint, hadolint.

- pre-commit: lint staged files + `./gradlew build`.
- pre-push: re-run over the whole tree.

Run it all manually with `just lint`.

## CI & images

- `.github/workflows/ci.yml` — build + test on Linux/Windows/macOS, plus the lint suite.
- `.github/workflows/codeql.yml` — CodeQL analysis.
- `.github/workflows/docker-publish.yml` — on push to `main`, publishes two GHCR images:
`:latest` (distroless runtime) and `:toolchain` (JDK + Gradle + lint tools).

## Rename checklist (only to change `track`)

- `settings.gradle` `rootProject.name`
- `build.gradle` `group`, `application.mainClass`, `NullAway:AnnotatedPackages`
- `src/main/java/track/`, `src/test/java/track/` directories and `package track;` declarations
- `justfile` `src/main/java/track/App.java` path
- `ArchitectureTest.java` `@AnalyzeClasses(packages = "track")` and the `App` name prefix

## License
See the [development guide](docs/development.md) for challenge setup, commands, Docker usage, and renaming the package.
See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution workflow.

Apache-2.0. See `LICENSE`.
Licensed under [Apache-2.0](LICENSE).
Loading
Loading