Skip to content
Open
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
64 changes: 64 additions & 0 deletions .github/workflows/build-jvm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,46 @@ on:
description: "Additional Gradle arguments"
type: string
default: ""
test_postgres_image:
description: "PostgreSQL image for a service container on localhost:5432 (e.g. postgres:17). Empty = no database."
type: string
default: ""
test_postgres_db:
description: "Database created in the test PostgreSQL container"
type: string
default: "test"
test_postgres_user:
description: "User for the test PostgreSQL container"
type: string
default: "test"
test_postgres_password:
description: "Password for the test PostgreSQL container (throwaway CI database)"
type: string
default: "test"

jobs:
build:
name: Build JVM
runs-on: ubuntu-latest
# Optional database for tests that need a real PostgreSQL. GitHub skips a
# service whose image is an empty string, so this costs nothing when
# test_postgres_image is unset. No input is interpolated into `options`
# (docker flags); pg_isready needs no user, and 127.0.0.1 skips the
# socket-only server the image runs while it initialises.
services:
postgres:
image: ${{ inputs.test_postgres_image }}
env:
POSTGRES_DB: ${{ inputs.test_postgres_db }}
POSTGRES_USER: ${{ inputs.test_postgres_user }}
POSTGRES_PASSWORD: ${{ inputs.test_postgres_password }}
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -h 127.0.0.1"
--health-interval 5s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v6

Expand All @@ -46,6 +81,35 @@ jobs:
java-version: ${{ steps.java.outputs.version }}
cache: ${{ inputs.build_tool }}

# libpq-style variables, for tests that read their connection from the
# environment. Apps with hardcoded test settings can ignore them.
- name: Export test PostgreSQL connection
if: inputs.test_postgres_image != ''
env:
PG_DB: ${{ inputs.test_postgres_db }}
PG_USER: ${{ inputs.test_postgres_user }}
PG_PASSWORD: ${{ inputs.test_postgres_password }}
run: |
# Plain identifiers only, and no newline in the password: these
# values are written to $GITHUB_ENV, one variable per line.
for v in "$PG_DB" "$PG_USER"; do
if ! printf '%s' "$v" | grep -Eq '^[A-Za-z_][A-Za-z0-9_]{0,62}$'; then
echo "::error::test_postgres_db and test_postgres_user must be plain identifiers (letters, digits, _): '$v'"
exit 1
fi
done
if [ "$(printf '%s' "$PG_PASSWORD" | wc -l)" -ne 0 ]; then
echo "::error::test_postgres_password must not contain a newline"
exit 1
fi
{
echo "PGHOST=localhost"
echo "PGPORT=5432"
echo "PGDATABASE=$PG_DB"
echo "PGUSER=$PG_USER"
echo "PGPASSWORD=$PG_PASSWORD"
} >> "$GITHUB_ENV"

- name: Build and test (Maven)
if: inputs.build_tool == 'maven'
run: mvn --batch-mode verify ${{ inputs.maven_args }}
Expand Down
54 changes: 46 additions & 8 deletions .github/workflows/javabin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: Javabin CI
# name: Deploy
# on:
# push:
# branches: [main]
# branches: [main] # your default branch (main or master)
# pull_request:
# workflow_dispatch:
# permissions:
Expand All @@ -20,6 +20,10 @@ name: Javabin CI
# and platform scripts are read from this public repo with the caller's
# GITHUB_TOKEN. Onboarding prerequisites are listed in
# docs/reusable-workflows.md.
#
# Deploy jobs (docker-build, tf-apply, ecs-deploy, eb-deploy) run only for
# pushes and manual runs on the repo's default branch, whatever it is called
# (main, master, ...). Pull requests and other branches build, test and plan.

on:
workflow_call:
Expand All @@ -36,6 +40,30 @@ on:
description: "Java version override (default: read from the repo's version files)"
type: string
default: ""
maven_args:
description: "Extra arguments for `mvn verify` (e.g. -DskipITs)"
type: string
default: ""
gradle_args:
description: "Extra arguments for `gradle build`"
type: string
default: ""
test_postgres_image:
description: "Run a PostgreSQL service container on localhost:5432 during the JVM build (e.g. postgres:17). Empty = no database."
type: string
default: ""
test_postgres_db:
description: "Database created in the test PostgreSQL container"
type: string
default: "test"
test_postgres_user:
description: "User for the test PostgreSQL container"
type: string
default: "test"
test_postgres_password:
description: "Password for the test PostgreSQL container (throwaway CI database, not a secret)"
type: string
default: "test"

permissions:
id-token: write
Expand All @@ -59,6 +87,12 @@ jobs:
with:
build_tool: ${{ needs.detect.outputs.has_maven == 'true' && 'maven' || 'gradle' }}
java_version: ${{ inputs.java_version }}
maven_args: ${{ inputs.maven_args }}
gradle_args: ${{ inputs.gradle_args }}
test_postgres_image: ${{ inputs.test_postgres_image }}
test_postgres_db: ${{ inputs.test_postgres_db }}
test_postgres_user: ${{ inputs.test_postgres_user }}
test_postgres_password: ${{ inputs.test_postgres_password }}

build-ts:
needs: detect
Expand All @@ -69,13 +103,14 @@ jobs:
pnpm_version: ${{ inputs.pnpm_version }}

# --------------------------------------------------------------------------
# 3. Docker build + ECR push (main only, after builds pass)
# 3. Docker build + ECR push (default branch only, after builds pass)
# --------------------------------------------------------------------------
docker-build:
needs: [detect, build-jvm, build-ts, tf-plan, tf-apply]
if: |
always() &&
github.ref == 'refs/heads/main' &&
github.event_name != 'pull_request' &&
github.ref == format('refs/heads/{0}', github.event.repository.default_branch) &&
needs.detect.outputs.has_docker == 'true' &&
(needs.build-jvm.result == 'success' || needs.build-jvm.result == 'skipped') &&
(needs.build-ts.result == 'success' || needs.build-ts.result == 'skipped') &&
Expand Down Expand Up @@ -108,12 +143,13 @@ jobs:
team: ${{ needs.detect.outputs.app_team }}

# --------------------------------------------------------------------------
# 6. Terraform apply (main only, after plan + review — blocked on HIGH risk)
# 6. Terraform apply (default branch only, after plan + review — blocked on HIGH risk)
# --------------------------------------------------------------------------
tf-apply:
needs: [detect, tf-plan]
if: >-
github.ref == 'refs/heads/main' &&
github.event_name != 'pull_request' &&
github.ref == format('refs/heads/{0}', github.event.repository.default_branch) &&
needs.tf-plan.outputs.has_changes == 'true'
uses: javaBin/platform/.github/workflows/tf-apply.yml@main
with:
Expand All @@ -123,13 +159,14 @@ jobs:
team: ${{ needs.detect.outputs.app_team }}

# --------------------------------------------------------------------------
# 7. ECS deploy (main only, after docker build + TF apply)
# 7. ECS deploy (default branch only, after docker build + TF apply)
# --------------------------------------------------------------------------
ecs-deploy:
needs: [detect, docker-build, tf-apply]
if: |
always() &&
github.ref == 'refs/heads/main' &&
github.event_name != 'pull_request' &&
github.ref == format('refs/heads/{0}', github.event.repository.default_branch) &&
needs.docker-build.result == 'success' &&
(needs.tf-apply.result == 'success' || needs.tf-apply.result == 'skipped')
uses: javaBin/platform/.github/workflows/ecs-deploy.yml@main
Expand All @@ -147,7 +184,8 @@ jobs:
needs: [detect, build-jvm, build-ts]
if: |
always() &&
github.ref == 'refs/heads/main' &&
github.event_name != 'pull_request' &&
github.ref == format('refs/heads/{0}', github.event.repository.default_branch) &&
needs.detect.outputs.has_eb == 'true' &&
(needs.build-jvm.result == 'success' || needs.build-jvm.result == 'skipped') &&
(needs.build-ts.result == 'success' || needs.build-ts.result == 'skipped')
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tf-plan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ jobs:
id: review
if: steps.plan.outputs.has_changes == 'true'
env:
NOTIFY: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
NOTIFY: ${{ github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
run: sh .platform/scripts/invoke-plan-review.sh "${{ steps.upload.outputs.plan_key }}"

- name: Post plan + review to PR
Expand Down
2 changes: 1 addition & 1 deletion docs/app-yaml-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ These fields were documented earlier but are not implemented. Using them fails `

1. Developer creates/updates `app.yaml`
2. The `expand` job runs `scripts/generate-modules.py`, which validates `app.yaml` and writes `terraform/backend.tf`, `providers.tf`, `main.tf`, `outputs.tf` (module blocks sourced from `javaBin/platform//terraform/modules/*`), then commits them to the branch
3. `tf-plan` plans the committed files → LLM review → `tf-apply` on main
3. `tf-plan` plans the committed files → LLM review → `tf-apply` on the default branch (main or master)
4. `docker-build` pushes `{team}-{name}` to ECR, `ecs-deploy` rolls the service

Generated files carry a `# GENERATED FROM app.yaml` marker and are overwritten on every run. Developers can add their own `.tf` files alongside them.
Expand Down
34 changes: 32 additions & 2 deletions docs/reusable-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Create `.github/workflows/deploy.yml`:
name: Deploy
on:
push:
branches: [main]
branches: [main] # your default branch: main or master
pull_request:
workflow_dispatch:

Expand Down Expand Up @@ -44,7 +44,13 @@ detect ──┬─ expand ──── tf-plan ── tf-apply ──┐
```

- **PRs:** detect → build → expand (commits generated TF to the PR branch) → tf-plan (plan + LLM review) → PR comment
- **Main:** detect → build → expand → tf-plan → tf-apply → docker-build → ecs-deploy
- **Default branch** (push or manual run): detect → build → expand → tf-plan → tf-apply → docker-build → ecs-deploy

Deploy jobs (`tf-apply`, `docker-build`, `ecs-deploy`, `eb-deploy`) run only when
`github.ref` is `refs/heads/<default branch>` (`github.event.repository.default_branch`) and the
event is not a pull request. The full ref is compared, so a tag that happens to be named like the
default branch never deploys. Repos whose default branch is `master` deploy from `master`; there is no
need to rename it. Pushes to other branches build and plan but never apply.

## Workflow Reference

Expand All @@ -57,6 +63,28 @@ Unified entrypoint. Calls all other workflows via the nested reusable workflow p
| `node_version` | read from `.nvmrc` / `.node-version` / `.tool-versions` / `mise.toml`, else 22 |
| `pnpm_version` | `packageManager` in `package.json` |
| `java_version` | read from `.java-version` / `.tool-versions` / `mise.toml`, else 21 |
| `maven_args` / `gradle_args` | (empty) — extra arguments for `mvn verify` / `gradle build` |
| `test_postgres_image` | (empty) — set to e.g. `postgres:17` to run a PostgreSQL service container during the JVM build |
| `test_postgres_db` / `test_postgres_user` / `test_postgres_password` | `test` / `test` / `test` |

#### Tests that need PostgreSQL

Set `test_postgres_image` and match database, user and password to what your tests expect. The
container listens on `localhost:5432` and is healthy (`pg_isready`) before the build starts. The
build step also gets `PGHOST`, `PGPORT`, `PGDATABASE`, `PGUSER` and `PGPASSWORD`. The password is
for a throwaway CI container, not a secret. Database and user must be plain identifiers (letters,
digits, `_`), and the build fails early otherwise.

```yaml
jobs:
javabin:
uses: javaBin/platform/.github/workflows/javabin.yml@main
with:
test_postgres_image: postgres:17
test_postgres_db: myappunit
test_postgres_user: localdevuser
test_postgres_password: localdevuser
```

### detect.yml

Expand All @@ -73,6 +101,8 @@ Maven (`mvn verify`) or Gradle (`./gradlew build`) build + test. Uploads test re
| `build_tool` | `maven` (javabin.yml passes `gradle` when there is no `pom.xml`) |
| `java_version` | resolved from version files, else 21 |
| `maven_args` / `gradle_args` | (empty) |
| `test_postgres_image` | (empty) = no database; else a PostgreSQL service container on `localhost:5432` |
| `test_postgres_db` / `test_postgres_user` / `test_postgres_password` | `test` |

### build-ts.yml

Expand Down
Loading