From e4ba85bdc527c0764b996f5a9da142039a6336e6 Mon Sep 17 00:00:00 2001 From: Rain Ramm Date: Thu, 9 Apr 2026 21:10:16 +0300 Subject: [PATCH 1/6] Add devcontainer configuration and README Set up a devcontainer with Java 17 (Temurin), GitHub CLI, and Gradle cache warm-up to provide a consistent, reproducible development environment for all contributors. Closes #4 Co-Authored-By: Claude Opus 4.6 (1M context) --- .devcontainer/devcontainer.json | 8 ++++++ CLAUDE.md | 4 +++ README.md | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 README.md diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..3ae521c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "name": "Montonio Java SDK", + "image": "mcr.microsoft.com/devcontainers/java:17", + "features": { + "ghcr.io/devcontainers/features/github-cli:1": {} + }, + "postCreateCommand": "echo 'alias yolo=\"claude --dangerously-skip-permissions\"' >> ~/.bashrc && ./gradlew dependencies --no-daemon" +} diff --git a/CLAUDE.md b/CLAUDE.md index 4607748..8382e33 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,6 +19,10 @@ Requires Java 17 (managed via `.sdkmanrc`). Uses Gradle 9.4.1 with Groovy DSL. ./gradlew test --tests 'com.example.MyTest.myMethod' # single test ``` +## Devcontainer + +A devcontainer configuration is provided in `.devcontainer/devcontainer.json`. It includes Java 17 (Temurin), Gradle (via wrapper), and GitHub CLI. Open the project in any devcontainer-compatible tool (VS Code, IntelliJ, GitHub Codespaces, Claude Code) to get a ready-to-use environment. + ## Gradle Structure Build is split across files following BitWeb conventions: diff --git a/README.md b/README.md new file mode 100644 index 0000000..df4a75a --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# Montonio Java SDK + +A type-safe Java client for the [Montonio](https://montonio.com) payment gateway REST API (V2 + Stargate). Covers payment order lifecycle, payment method discovery, and JWT webhook/return validation. + +## Requirements + +- Java 17+ +- Gradle 9.4.1 (included via wrapper) + +## Getting Started + +### Using a Devcontainer (recommended) + +The project includes a [devcontainer](.devcontainer/devcontainer.json) configuration with Java 17, Gradle, and GitHub CLI pre-installed. Open the project in any devcontainer-compatible tool: + +- **VS Code** — install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension, then _Reopen in Container_ +- **IntelliJ IDEA** — open the project and follow the [Dev Containers integration](https://www.jetbrains.com/help/idea/connect-to-devcontainer.html) guide +- **GitHub Codespaces** — click _Code > Codespaces > New codespace_ on the repository page +- **CLI** — using the [Dev Container CLI](https://github.com/devcontainers/cli): + ```bash + devcontainer up --workspace-folder . + devcontainer exec --workspace-folder . bash + ``` + +### Local Setup + +Install Java 17 via [SDKMAN](https://sdkman.io/): + +```bash +sdk env install +``` + +## Build & Test + +```bash +./gradlew build # full build +./gradlew test # all tests +./gradlew unitTest # unit tests only +./gradlew integrationTest # integration tests only +./gradlew testAndReport # all tests + JaCoCo coverage reports +``` + +## License + +[MIT](LICENSE) From 2aefcfdd088a7300c3ebfe864a9f154a5be78959 Mon Sep 17 00:00:00 2001 From: Rain Ramm Date: Thu, 9 Apr 2026 21:16:12 +0300 Subject: [PATCH 2/6] Clarify Gradle wrapper usage in README Address CodeRabbit review: Gradle is provided via the wrapper, not pre-installed as a system binary. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df4a75a..87d21b9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A type-safe Java client for the [Montonio](https://montonio.com) payment gateway ### Using a Devcontainer (recommended) -The project includes a [devcontainer](.devcontainer/devcontainer.json) configuration with Java 17, Gradle, and GitHub CLI pre-installed. Open the project in any devcontainer-compatible tool: +The project includes a [devcontainer](.devcontainer/devcontainer.json) configuration with Java 17, Gradle via the wrapper (`./gradlew`), and GitHub CLI. Open the project in any devcontainer-compatible tool: - **VS Code** — install the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension, then _Reopen in Container_ - **IntelliJ IDEA** — open the project and follow the [Dev Containers integration](https://www.jetbrains.com/help/idea/connect-to-devcontainer.html) guide From b5a356402858ab0cf2d1cdf4ae40107f6a3c633f Mon Sep 17 00:00:00 2001 From: Rain Ramm Date: Thu, 9 Apr 2026 21:22:39 +0300 Subject: [PATCH 3/6] Add Node.js feature and install Claude Code CLI in devcontainer The Java base image lacks Node.js/npm. Add the Node.js devcontainer feature so Claude Code CLI can be installed for the yolo alias. Co-Authored-By: Claude Opus 4.6 (1M context) --- .devcontainer/devcontainer.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 3ae521c..4162ef7 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,8 @@ "name": "Montonio Java SDK", "image": "mcr.microsoft.com/devcontainers/java:17", "features": { - "ghcr.io/devcontainers/features/github-cli:1": {} + "ghcr.io/devcontainers/features/github-cli:1": {}, + "ghcr.io/devcontainers/features/node:1": {} }, - "postCreateCommand": "echo 'alias yolo=\"claude --dangerously-skip-permissions\"' >> ~/.bashrc && ./gradlew dependencies --no-daemon" + "postCreateCommand": "npm install -g @anthropic-ai/claude-code && echo 'alias yolo=\"claude --dangerously-skip-permissions\"' >> ~/.bashrc && ./gradlew dependencies --no-daemon" } From 6544a1bbcd0eb665d26a4b95700f8d0dd7ae32e0 Mon Sep 17 00:00:00 2001 From: Rain Ramm Date: Thu, 9 Apr 2026 21:57:44 +0300 Subject: [PATCH 4/6] Add GitHub CLI auto-auth, setup script, and token read protection - Extract postCreateCommand into .devcontainer/setup.sh for clarity - Add gh-auth.sh for GitHub CLI authentication via gitignored token file - Add Claude Code hook to block Read/Edit access to the token file - Mount named volume for Claude settings persistence Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/hooks/block-token-read.sh | 14 ++++++++++++++ .claude/settings.json | 15 +++++++++++++++ .devcontainer/devcontainer.json | 6 +++++- .devcontainer/gh-auth.sh | 28 ++++++++++++++++++++++++++++ .devcontainer/setup.sh | 16 ++++++++++++++++ .gitignore | 1 + 6 files changed, 79 insertions(+), 1 deletion(-) create mode 100755 .claude/hooks/block-token-read.sh create mode 100644 .claude/settings.json create mode 100755 .devcontainer/gh-auth.sh create mode 100755 .devcontainer/setup.sh diff --git a/.claude/hooks/block-token-read.sh b/.claude/hooks/block-token-read.sh new file mode 100755 index 0000000..b8afc6a --- /dev/null +++ b/.claude/hooks/block-token-read.sh @@ -0,0 +1,14 @@ +#!/bin/bash +FILE_PATH=$(jq -r '.tool_input.file_path // ""' < /dev/stdin) + +if [[ "$FILE_PATH" == *".devcontainer/github-token"* ]]; then + jq -n '{ + hookSpecificOutput: { + hookEventName: "PreToolUse", + permissionDecision: "deny", + permissionDecisionReason: "Access to .devcontainer/github-token is blocked — it contains secrets" + } + }' +else + exit 0 +fi diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..c18e018 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-token-read.sh" + } + ] + } + ] + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4162ef7..e9a5bc9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,5 +5,9 @@ "ghcr.io/devcontainers/features/github-cli:1": {}, "ghcr.io/devcontainers/features/node:1": {} }, - "postCreateCommand": "npm install -g @anthropic-ai/claude-code && echo 'alias yolo=\"claude --dangerously-skip-permissions\"' >> ~/.bashrc && ./gradlew dependencies --no-daemon" + "mounts": [ + "source=montonio-sdk-claude-settings,target=/home/vscode/.claude,type=volume" + ], + "postCreateCommand": "bash .devcontainer/setup.sh", + "postStartCommand": "bash .devcontainer/gh-auth.sh" } diff --git a/.devcontainer/gh-auth.sh b/.devcontainer/gh-auth.sh new file mode 100755 index 0000000..485e077 --- /dev/null +++ b/.devcontainer/gh-auth.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Authenticate GitHub CLI using a personal access token stored in +# .devcontainer/github-token. Runs on every container start so that +# gh and git credentials stay configured across restarts. + +set -euo pipefail + +TOKEN_FILE="$(dirname "$0")/github-token" + +if [ ! -f "$TOKEN_FILE" ]; then + echo "No GitHub token found. To enable gh CLI auth:" + echo " 1. Create a file at .devcontainer/github-token" + echo " 2. Paste a GitHub PAT with the scopes you need (e.g. repo, read:org)" + echo " 3. Rebuild or restart the container" + echo " (This file is already in .gitignore and will not be committed.)" + exit 0 +fi + +TOKEN=$(cat "$TOKEN_FILE" | tr -d '[:space:]') + +if [ -z "$TOKEN" ]; then + echo "WARNING: .devcontainer/github-token exists but is empty. Skipping gh auth." + exit 0 +fi + +echo "$TOKEN" | gh auth login --with-token +gh auth setup-git +echo "GitHub CLI authenticated and git credentials configured." diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100755 index 0000000..c6795e8 --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "==> Fixing volume permissions..." +sudo chown -R vscode:vscode /home/vscode/.claude + +echo "==> Installing Claude Code CLI..." +npm install -g @anthropic-ai/claude-code + +echo "==> Configuring shell aliases..." +echo 'alias yolo="claude --dangerously-skip-permissions"' >> ~/.bashrc + +echo "==> Pre-warming Gradle dependency cache..." +./gradlew dependencies --no-daemon + +echo "==> Setup complete!" diff --git a/.gitignore b/.gitignore index 05d7676..d4e4f36 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /build /.gradle .DS_Store +.devcontainer/github-token From f48013512402f5bfe230e69c1d940d7e965bb72a Mon Sep 17 00:00:00 2001 From: Rain Ramm Date: Thu, 9 Apr 2026 22:23:52 +0300 Subject: [PATCH 5/6] Set CLAUDE_CONFIG_DIR to persist auth across container rebuilds Point Claude Code config directory at the mounted volume so all settings and credentials survive container rebuilds. Co-Authored-By: Claude Opus 4.6 (1M context) --- .devcontainer/devcontainer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e9a5bc9..6ac91d6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,6 +5,9 @@ "ghcr.io/devcontainers/features/github-cli:1": {}, "ghcr.io/devcontainers/features/node:1": {} }, + "containerEnv": { + "CLAUDE_CONFIG_DIR": "/home/vscode/.claude" + }, "mounts": [ "source=montonio-sdk-claude-settings,target=/home/vscode/.claude,type=volume" ], From e78bd654eedf4c9b74759336b438c8b0662c850d Mon Sep 17 00:00:00 2001 From: Rain Ramm Date: Thu, 9 Apr 2026 22:27:13 +0300 Subject: [PATCH 6/6] Harden gh-auth script and make alias append idempotent - Enforce 600 permissions on token file before reading - Skip auth if already authenticated, make failures non-fatal - Guard alias append to prevent duplicates in .bashrc Co-Authored-By: Claude Opus 4.6 (1M context) --- .devcontainer/gh-auth.sh | 19 +++++++++++++++++-- .devcontainer/setup.sh | 3 ++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.devcontainer/gh-auth.sh b/.devcontainer/gh-auth.sh index 485e077..086acc6 100755 --- a/.devcontainer/gh-auth.sh +++ b/.devcontainer/gh-auth.sh @@ -16,6 +16,12 @@ if [ ! -f "$TOKEN_FILE" ]; then exit 0 fi +TOKEN_PERMS="$(stat -c '%a' "$TOKEN_FILE" 2>/dev/null || stat -f '%Lp' "$TOKEN_FILE")" +if [ "$TOKEN_PERMS" != "600" ]; then + echo "WARNING: $TOKEN_FILE permissions are $TOKEN_PERMS; fixing to 600." + chmod 600 "$TOKEN_FILE" +fi + TOKEN=$(cat "$TOKEN_FILE" | tr -d '[:space:]') if [ -z "$TOKEN" ]; then @@ -23,6 +29,15 @@ if [ -z "$TOKEN" ]; then exit 0 fi -echo "$TOKEN" | gh auth login --with-token -gh auth setup-git +if gh auth status >/dev/null 2>&1; then + echo "GitHub CLI already authenticated." + exit 0 +fi + +if ! printf '%s\n' "$TOKEN" | gh auth login --with-token; then + echo "WARNING: GitHub CLI authentication failed. Continuing without gh auth." + exit 0 +fi + +gh auth setup-git || echo "WARNING: Failed to configure git credential helper via gh." echo "GitHub CLI authenticated and git credentials configured." diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh index c6795e8..5711e3b 100755 --- a/.devcontainer/setup.sh +++ b/.devcontainer/setup.sh @@ -8,7 +8,8 @@ echo "==> Installing Claude Code CLI..." npm install -g @anthropic-ai/claude-code echo "==> Configuring shell aliases..." -echo 'alias yolo="claude --dangerously-skip-permissions"' >> ~/.bashrc +grep -qxF 'alias yolo="claude --dangerously-skip-permissions"' ~/.bashrc || \ + echo 'alias yolo="claude --dangerously-skip-permissions"' >> ~/.bashrc echo "==> Pre-warming Gradle dependency cache..." ./gradlew dependencies --no-daemon