Repository navigation
feat(ci): deploy from the default branch and offer Postgres to JVM tests - #154
Merged
Merged
Conversation
Terraform Plan✅ No changes — infrastructure is up to date. Plan output |
This was referenced Oct 24, 2026
Terraform Plan✅ No changes — infrastructure is up to date. Plan output |
Deploy jobs in javabin.yml were gated on refs/heads/main, so repos whose default branch is master (javaBin/moresleep) could never deploy. Gate on github.event.repository.default_branch instead, for pushes and manual runs. build-jvm gains an optional PostgreSQL service container (test_postgres_image/_db/_user/_password), passed through javabin.yml together with maven_args/gradle_args, for test suites that need a real database. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…es inputs Compare github.ref with refs/heads/<default branch> so a tag named like the default branch never deploys. Stop interpolating test_postgres_* into docker service options, and reject non-identifier db/user values before they reach GITHUB_ENV. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Alexanderamiri
force-pushed
the
feat/moresleep-ci
branch
from
October 26, 2026 14:20
b885055 to
445ae61
Compare
Terraform Plan🚧 Changes detected — Plan: 0 to add, 1 to change, 0 to destroy. Plan outputLLM ReviewRisk: 🟢 LOW Plan makes one in-place update to a Cognito Google identity provider, changing only the authorize_url to add an hd=java.no domain hint and prompt=select_account parameter; no creates or destroys.
|
Alexanderamiri
added a commit
that referenced
this pull request
Oct 26, 2026
## Why
Today app.yaml can only **create** resources. `javaBin/moresleep`
(sleepingpill.javazone.no) is moving onto the platform with no
architectural change, so it has to keep two existing things:
- RDS `dbmoresleep` (Postgres 17, public endpoint, serves the program
API since 2021). Not in any Terraform state.
- The hand-made Route53 A record `sleepingpill.javazone.no`.
`routing.host` would fail on apply because the record already exists.
moresleep also reads `DBHOST` and `DBPORT` separately, but the RDS
modules only expose `endpoint` (`host:port`).
## What
app.yaml (validated in `scripts/generate-modules.py`):
```yaml
routing:
host: sleepingpill.javazone.no
priority: 120
adopt_dns: true # take over the existing record
resources:
databases:
- name: main
engine: postgres
adopt: dbmoresleep # import, never create
host_env: DBHOST # new, postgres only (created or adopted)
port_env: DBPORT
deletion_protection: true # default true
backup_retention_period: 14 # optional; unset = keep the live value
```
- **`databases[].adopt`** generates `module "rds_<name>"` from the new
**`terraform/modules/service-rds-adopted`**, plus:
```hcl
import {
to = module.rds_main.aws_db_instance.this
id = "dbmoresleep"
}
```
- The module manages only tags, `deletion_protection`, an optional
`backup_retention_period`, and the Terraform-only final-snapshot
settings: `skip_final_snapshot=false`, a final snapshot id,
`delete_automated_backups=false`.
- `instance_class` (required by the provider) is read from the live
instance. Every other argument is in `ignore_changes`, and
`prevent_destroy` is set.
- `instance_class`, `allocated_storage`, `engine_version` and `multi_az`
are rejected together with `adopt`. `adopt` is rejected for dynamodb or
an invalid identifier.
- No task-role policy is added, because the app keeps its own password
auth.
- Once the resource is in state, Terraform skips the import block, so it
stays in the generated file.
- **`host_env` / `port_env`** map to `address` / `port` outputs.
`service-rds` gains an `address` output, and the adopted module's `port`
is a string.
- **`routing.adopt_dns: true`** passes `allow_dns_overwrite = true` to
`service-routing`, which sets `allow_overwrite` on the record, so
Route53 UPSERTs the existing record instead of failing on CREATE. The
value is `null` when unset, so existing apps' records and generated
files do not change.
- I chose this over an `import` block on purpose. moresleep runs in
parallel on `moresleep.javazone.no` first and then changes `host`. At
that point the routing record address is already in state, so Terraform
would skip an import and the replacement CREATE would fail.
`allow_overwrite` covers both the fresh case and the host-change case.
- **Each adoption is approved in a platform PR (review fix).** `adopt`
and `adopt_dns` only validate when the identifier or host is listed for
the app's team in `ADOPTIONS` in `scripts/registry.py`. This PR lists
`moresleep: rds dbmoresleep, dns sleepingpill.javazone.no`. Why: Route53
records in the shared zones have no team ABAC, and `DenyXTeam` only
fires on resources tagged with another team. Without the allowlist, any
team could take over another app's hostname (`host: submit.javazone.no`
+ `adopt_dns: true`), or adopt, tag away and later delete an untagged
legacy database with one app.yaml line. The generator always runs from
platform `main`, so a team can't edit the list itself.
- The adopted instance's final snapshot is
`<identifier>-final-<random_id hex>` (stable per state), so a later
delete/re-adopt cycle can't collide with an old snapshot. The module
pins `hashicorp/aws >= 5.40, < 6.0` because `dedicated_log_volume` is in
`ignore_changes`.
- Docs: `app-yaml-reference.md` (an "Adopting an existing PostgreSQL
instance" section, `adopt_dns`, `host_env`/`port_env`),
`reusable-modules.md`, `terraform/modules/README.md` and the module
table in `CLAUDE.md`.
- `scripts/registry.py`: `MODULE_SOURCES` gains `service-rds-adopted`.
## How verified
- `python3 -m unittest discover -s scripts/tests`: 59 tests OK, 16 of
them new in `AdoptTest` (3 cover the approval list: another team's
database, another team adopting `dbmoresleep`, an unapproved host). The
existing fmt test now also runs on the moresleep fixture.
`terraform/lambda-src/tests`: 64 OK.
- `terraform fmt -check -recursive terraform/modules` is clean.
`terraform validate` passes for `service-rds-adopted`, `service-routing`
and `service-rds` (`init -backend=false`).
- **A local, read-only `terraform plan` against the real account**
(local backend, admin profile, never applied):
- Minimal root with the module plus the import block:
`Plan: 1 to import, 0 to add, 1 to change, 0 to destroy` (before the
review fix; the `random_id` suffix now adds `1 to add`). The only diffs
are `deletion_protection false -> true`, `tags_all` (+5 default tags),
and the Terraform-only `skip_final_snapshot`,
`final_snapshot_identifier` and `delete_automated_backups`. Engine,
class, storage, security group, publicity, PI and monitoring are
untouched.
- The generated moresleep phase-1 root (`moresleep.javazone.no`, all
modules, local module sources):
`Plan: 1 to import, 22 to add, 1 to change, 0 to destroy`. It stops only
at `reading IAM Role (javabin-ecs-exec-moresleep)`, which is created
when the `moresleep` team is registered (javaBin/registry#40).
- This PR's own Terraform Plan / Unit Tests checks are below.
## Follow-ups
- Companion PR #154: deploy from the default branch
(moresleep uses `master`) and Postgres for JVM tests.
- Security workstream: `dbmoresleep` sits in the default VPC with the
default SG open to `0.0.0.0/0`. This PR deliberately leaves the SG
alone.
- An adopted instance is only reachable if its network allows the
platform NAT. This is documented, not automated. The docs now say to
allow only the NAT address (plus legacy clients) on a public instance's
security group, never `0.0.0.0/0`. All traffic to such an instance goes
through the NAT and pays data processing.
- Tagging the remaining untagged legacy resources (so `DenyXTeam`
protects them) is still a security-workstream item. The approval list
closes the app.yaml path, not hand-written Terraform.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Onboarding
javaBin/moresleep(sleepingpill) without architectural changes hits two pipeline gaps:javabin.ymlwere gated ongithub.ref == 'refs/heads/main'. moresleep's default branch ismaster, so it would build and plan but never apply or deploy.build-jvmrunsmvn verifywith no database. moresleep's test suite needs a real PostgreSQL onlocalhost:5432, which is common for JVM apps.What
javabin.yml: thedocker-build,tf-apply,ecs-deployandeb-deploygates are nowgithub.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch). The full ref is compared (review fix), so a tag named like the default branch (refs/tags/master) never deploys; the broker/apply-gate doesn't check the app ref, so this gate is the only branch control. Repos onmainbehave exactly as before.tf-plan.yml: the HIGH-risk SlackNOTIFYflag uses the same default-branch test.build-jvm.yml: an optionalservices.postgrescontainer. Its image comes from the newtest_postgres_imageinput; GitHub skips a service whose image is an empty string (actions/runner#822), so nothing changes for existing callers. Its health check ispg_isready -h 127.0.0.1, with no input interpolated into the dockeroptionsstring (review fix; 127.0.0.1 also skips the socket-only server the image runs while initialising). The export step fails early unlesstest_postgres_db/test_postgres_userare plain identifiers and the password has no newline, because the values are written to$GITHUB_ENV. The build step getsPGHOST/PGPORT/PGDATABASE/PGUSER/PGPASSWORD.javabin.ymlpasses new inputs through:test_postgres_image,test_postgres_db,test_postgres_user,test_postgres_password(each defaults totest), and the existingmaven_args/gradle_argsthatbuild-jvmalready accepted but the orchestrator never passed on.docs/reusable-workflows.md(inputs, default-branch rule, a Postgres example) and one line indocs/app-yaml-reference.md.Not changed: OIDC trust and the broker/apply-gate still pin the platform workflow refs to
javaBin/platform@refs/heads/main. They already accept any app repo branch (sub = repo:javaBin/*:*), so nothing in IAM depends on the app's branch name.How verified
actionlintonjavabin.yml,build-jvm.ymlandtf-plan.yml: clean.github.event.repository.default_branchis in the payload forpushandworkflow_dispatch, which are the only events that can reach the deploy jobs (pull requests are excluded explicitly).javabin.yml@mainwithtest_postgres_image: postgres:17.Follow-ups
feat(app-yaml): adopt an existing RDS instance and Route53 record(independent; no shared files).🤖 Generated with Claude Code