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
93 changes: 93 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: CI

on:
push:
branches: [ main, dev ]
pull_request:
branches: [ main ]

jobs:

# ── Linux ────────────────────────────────────────────────────
test-linux:
name: Test on Linux
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Make runner executable
run: chmod +x runner.sh

- name: Run HelloWorld
run: ./runner.sh examples/HelloWorld.java

- name: Run FizzBuzz
run: ./runner.sh examples/FizzBuzz.java

- name: Run auto-detect from examples/
run: |
mkdir -p /tmp/test-autodetect
cp examples/HelloWorld.java /tmp/test-autodetect/
cd /tmp/test-autodetect
bash "$GITHUB_WORKSPACE/runner.sh"

# ── macOS ────────────────────────────────────────────────────
test-macos:
name: Test on macOS
runs-on: macos-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Make runner executable
run: chmod +x runner.sh

- name: Run HelloWorld
run: ./runner.sh examples/HelloWorld.java

- name: Run FizzBuzz
run: ./runner.sh examples/FizzBuzz.java

# ── Windows ──────────────────────────────────────────────────
test-windows:
name: Test on Windows
runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Run HelloWorld
run: .\runner.bat examples\HelloWorld.java

- name: Run FizzBuzz
run: .\runner.bat examples\FizzBuzz.java

- name: Run auto-detect from examples\
run: |
New-Item -ItemType Directory -Force -Path "$env:TEMP\test-autodetect"
Copy-Item examples\HelloWorld.java "$env:TEMP\test-autodetect\"
Set-Location "$env:TEMP\test-autodetect"
& "$env:GITHUB_WORKSPACE\runner.bat"
shell: pwsh
6 changes: 6 additions & 0 deletions examples/HelloWorld.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
System.out.println("java-runner is working correctly.");
}
}
108 changes: 108 additions & 0 deletions runner.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
@echo off
REM ─────────────────────────────────────────────────────────────
REM java-runner - run Java exercises without an IDE (Windows)
REM Usage: runner.bat [File.java] [--watch]
REM ─────────────────────────────────────────────────────────────

setlocal EnableDelayedExpansion

set "FILE="
set "WATCH=false"

REM ── Parse arguments ───────────────────────────────────────────
for %%A in (%*) do (
if "%%A"=="--watch" set "WATCH=true"
if "%%~xA"==".java" set "FILE=%%A"
if "%%A"=="--help" goto :help
if "%%A"=="-h" goto :help
)

REM ── Auto-detect .java file ────────────────────────────────────
if "%FILE%"=="" (
set "count=0"
for %%F in (*.java) do (
set "FILE=%%F"
set /a count+=1
)

REM If none found in current dir, fall back to examples\
if "!count!"=="0" (
if exist examples\ (
echo [INFO] No .java in current dir, searching examples\
for %%F in (examples\*.java) do (
set "FILE=%%F"
set /a count+=1
)
)
)

if "!count!"=="0" (
echo [ERROR] No .java file found in current directory or examples\
exit /b 1
)
if !count! GTR 1 (
echo [WARNING] Multiple .java files found. Please specify one:
for %%F in (*.java) do echo %%F
if exist examples\ for %%F in (examples\*.java) do echo %%F
exit /b 1
)
)

REM ── Validate file exists ──────────────────────────────────────
if not exist "%FILE%" (
echo [ERROR] File not found: %FILE%
exit /b 1
)

REM ── Watch mode ────────────────────────────────────────────────
if "%WATCH%"=="true" (
echo [WATCH] Watch mode enabled - save the file to rerun
:watchloop
call :run
echo [WATCH] Waiting for changes... Press Ctrl+C to stop.
REM Poll every 2 seconds (Windows has no native inotify)
timeout /t 2 /nobreak >nul
goto :watchloop
)

call :run
exit /b

REM ── Help ──────────────────────────────────────────────────────
:help
echo Usage: runner.bat [File.java] [--watch]
echo File.java Java file to compile and run
echo --watch Recompile and rerun every 2 seconds
exit /b

REM ── Main run function ─────────────────────────────────────────
:run
for %%F in ("%FILE%") do set "CLASSNAME=%%~nF"
set "TMPDIR=%TEMP%\java-runner-%RANDOM%"
mkdir "%TMPDIR%" 2>nul

echo.
echo [....] Compiling %CLASSNAME%.java...

javac -d "%TMPDIR%" "%FILE%" 2>"%TMPDIR%\errors.txt"
if errorlevel 1 (
echo [FAIL] Compilation failed:
echo.
type "%TMPDIR%\errors.txt"
rmdir /s /q "%TMPDIR%"
exit /b 1
)

echo [ OK ] Compiled successfully
echo ───────────────────────── output ─────────────────────────

set "START_TIME=%TIME%"
java -cp "%TMPDIR%" "%CLASSNAME%"
set "END_TIME=%TIME%"

echo ───────────────────────────────────────────────────────────
echo [ OK ] Done
echo.

rmdir /s /q "%TMPDIR%"
exit /b
145 changes: 145 additions & 0 deletions runner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────
# java-runner - run Java exercises without an IDE
# Usage: ./runner.sh [File.java] [--watch]
# ─────────────────────────────────────────────────────────────

set -euo pipefail

# ── Colors ────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
RESET='\033[0m'

# ── Detect OS ─────────────────────────────────────────────────
OS="$(uname -s)"
case "$OS" in
Linux*) PLATFORM="linux" ;;
Darwin*) PLATFORM="mac" ;;
*) PLATFORM="unknown" ;;
esac

# ── Arguments ─────────────────────────────────────────────────
FILE=""
WATCH=false

for arg in "$@"; do
case $arg in
--watch) WATCH=true ;;
*.java) FILE="$arg" ;;
--help|-h)
echo -e "Usage: ./runner.sh [File.java] [--watch]"
echo -e " File.java Java file to compile and run (optional if only one .java exists)"
echo -e " --watch Recompile and rerun on every file save"
exit 0
;;
esac
done

# ── Auto-detect .java file ────────────────────────────────────
if [[ -z "$FILE" ]]; then
mapfile -t found < <(find . -maxdepth 1 -name "*.java")

# If none in current dir, fall back to examples/
if [[ ${#found[@]} -eq 0 ]] && [[ -d "./examples" ]]; then
mapfile -t found < <(find ./examples -maxdepth 1 -name "*.java")
[[ ${#found[@]} -gt 0 ]] && echo -e "${YELLOW}⚠ No .java in current dir, searching examples/${RESET}"
fi

if [[ ${#found[@]} -eq 1 ]]; then
FILE="${found[0]}"
elif [[ ${#found[@]} -eq 0 ]]; then
echo -e "${RED}✗ No .java file found in current directory or examples/${RESET}"
exit 1
else
echo -e "${YELLOW}⚠ Multiple .java files found. Please specify one:${RESET}"
printf ' %s\n' "${found[@]}"
exit 1
fi
fi

# ── Validate file exists ──────────────────────────────────────
if [[ ! -f "$FILE" ]]; then
echo -e "${RED}✗ File not found: $FILE${RESET}"
exit 1
fi

# ── Main run function ─────────────────────────────────────────
run() {
local classname
classname=$(basename "$FILE" .java)
local tmpdir
tmpdir=$(mktemp -d)

echo -e ""
echo -e "${CYAN}▶ Compiling ${classname}.java...${RESET}"

# Compile - capture errors
if ! javac -d "$tmpdir" "$FILE" 2>"$tmpdir/errors.txt"; then
echo -e "${RED}✗ Compilation failed:${RESET}\n"
# Strip absolute path noise
sed "s|$PWD/||g" "$tmpdir/errors.txt"
rm -rf "$tmpdir"
return 1
fi

echo -e "${GREEN}✓ Compiled successfully${RESET}"
echo -e "${CYAN}───────────────────────── output ─────────────────────────${RESET}"

# Run and measure time
local start
start=$(date +%s%N 2>/dev/null || date +%s)
java -cp "$tmpdir" "$classname"
local end
end=$(date +%s%N 2>/dev/null || date +%s)

# Calculate elapsed (nanoseconds if available, else seconds)
local elapsed
if [[ ${#start} -gt 10 ]]; then
elapsed=$(( (end - start) / 1000000 ))
echo -e "${CYAN}───────────────────────────────────────────────────────────${RESET}"
echo -e "${GREEN}✓ Finished in ${elapsed}ms${RESET}\n"
else
elapsed=$(( end - start ))
echo -e "${CYAN}───────────────────────────────────────────────────────────${RESET}"
echo -e "${GREEN}✓ Finished in ${elapsed}s${RESET}\n"
fi

rm -rf "$tmpdir"
}

# ── Watch mode ────────────────────────────────────────────────
if $WATCH; then
echo -e "${YELLOW}👁 Watch mode enabled - save the file to rerun${RESET}"
run || true

if [[ "$PLATFORM" == "linux" ]]; then
if ! command -v inotifywait &>/dev/null; then
echo -e "${RED}✗ inotifywait not found. Install it with: sudo apt install inotify-tools${RESET}"
exit 1
fi
while inotifywait -q -e close_write "$FILE"; do
echo -e "\n${YELLOW}↻ Change detected...${RESET}"
run || true
done

elif [[ "$PLATFORM" == "mac" ]]; then
if ! command -v fswatch &>/dev/null; then
echo -e "${RED}✗ fswatch not found. Install it with: brew install fswatch${RESET}"
exit 1
fi
fswatch -o "$FILE" | while read -r; do
echo -e "\n${YELLOW}↻ Change detected...${RESET}"
run || true
done

else
echo -e "${RED}✗ Watch mode is not supported on this platform${RESET}"
exit 1
fi

else
run
fi
Loading