mirror of
https://github.com/Sea-Haven-Industries/.github.git
synced 2026-09-30 23:23:11 +00:00
467 lines
27 KiB
Markdown
467 lines
27 KiB
Markdown
# .github
|
|
|
|
Organization-level GitHub configuration for Sea Haven Industries.
|
|
|
|
## Git and PR conventions
|
|
|
|
### Branch naming
|
|
|
|
`feature/`, `fix/`, `hotfix/`, `chore/`, `docs/`, `refactor/`, `release/` + kebab-case description. Branch names do not contain Jira keys.
|
|
|
|
### Commit format
|
|
|
|
`type(scope): description` — lowercase, imperative, no trailing period, header ≤ 72 chars. Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`, `release`. Breaking change: `feat!:` + `BREAKING CHANGE:` footer.
|
|
|
|
### PR title
|
|
|
|
`type(scope): description (DEV-123)` — maximum 120 characters, including the Jira suffix. Put the Jira key at the end in parentheses. A missing key is a warning, not a failure. Active projects: **DEV** (product), **PLAT** (platform), **SEC** (security). INFRA is a closed archive.
|
|
|
|
### PR body
|
|
|
|
Exactly four headings in order: `## Summary`, `## Validation`, `## Tests`, `## Notes`. Use `None.` under Notes if empty.
|
|
|
|
### Deploy path
|
|
|
|
The two sanctioned deploy paths are merge to `main` triggering the pipeline and `workflow_dispatch` on that same pipeline. No manual workstation deploys to production.
|
|
|
|
### Merge queue
|
|
|
|
CI callers keep a `merge_group` trigger so native GitHub merge queues still run portions. Mergify YAML is not used. Do not put portion job names (`frontend / static`, `unit (1)`, …) in a ruleset.
|
|
|
|
### Required checks
|
|
|
|
Two org rulesets. A repo is on exactly one of them:
|
|
|
|
- **main branch protection** requires `ci / ci` for unconverted remaining-lane repos.
|
|
- **CI complete** requires `ci-complete` for converted HCP callers. It targets no repos until a cutover includes the repo and excludes it from the old ruleset in the same window.
|
|
|
|
The formatter GitHub App is not on the main-branch bypass list.
|
|
|
|
## What's in here
|
|
|
|
### Renovate preset
|
|
|
|
`default.json` is the file loaded by `local>Sea-Haven-Industries/.github`. It is intentionally empty of policy. Org Renovate rules live in `Sea-Haven-Industries/renovate-config` as `org-inherited-config.json`.
|
|
|
|
### Reusable Workflows
|
|
|
|
**`.github/workflows/ci-python-sam.yaml`** — Reusable CI workflow for Python / SAM repos. Runs `ruff check` + `ruff format --check`, optional `pytest`, and optional `sam validate --lint`. Also usable for Python CDK repos by disabling SAM validate.
|
|
|
|
**`.github/workflows/ci-typescript-cdk.yaml`** — Reusable CI workflow for TypeScript / CDK repos. Runs `npm ci` + optional `tsc --noEmit`, optional ESLint, optional Jest, and optional `cdk synth`. Also supports Node.js SAM repos via an optional `sam validate` step.
|
|
|
|
**`.github/workflows/ci-typescript-frontend.yaml`** — Sequential reusable CI for remaining-lane bundled TypeScript front-end apps that still emit `ci / ci`. HCP app repos should call `ci-frontend.yaml` plus a caller-owned `ci-complete` aggregator instead.
|
|
|
|
**`.github/workflows/ci-frontend.yaml`** — Parallel HCP frontend CI: `guard`, `static`, `build`, `unit` (optional shards), `browser-smoke`. The caller owns `ci-complete`. Do not put those portion names in a ruleset.
|
|
|
|
**`.github/workflows/ci-terraform.yaml`** — Terraform `fmt -check`, `init -backend=false`, and `validate`. Default version `1.16.0`.
|
|
|
|
**`.github/workflows/ci-autofix.yaml`** — Pull-request-only formatter. Mints a GitHub App token (`AUTOFMT_APP_ID`, `AUTOFMT_APP_PRIVATE_KEY`), runs the requested presets and any optional write commands, and pushes `style: apply formatter` only when the tree is dirty. Presets are `prettier` (`npm run format`), `eslint` (`npx eslint . --fix`, opt-in), `ruff` (`ruff format .` and `ruff check --fix .`), and `terraform` (`terraform fmt -recursive` in `terraform-working-directory`, default `terraform`). Output `committed` lets the caller skip portions on SHA_old. Does not `--no-verify` and does not push to `main`.
|
|
|
|
**`.github/workflows/cd-hcp-fargate.yaml`** — HCP Fargate image CD. Checkout at `ref` (empty means `github.sha`), OIDC, SSM cluster/service/family/ecr/container/api-url, docker build+push tagged `$sha` and `$environment`, patch `GIT_SHA`, RegisterTaskDefinition + UpdateService + services-stable, poll health SHA. `environment` is a `with:` input. The reusable job owns `environment:`, concurrency, OIDC, and `vars.DEPLOY_ROLE_ARN`.
|
|
|
|
**`.github/workflows/cd-hcp-spa.yaml`** — HCP SPA CD. Checkout at `ref`, Node 24, `npm ci` + `npm run build`, origin-path guard, hashed s3 sync then `index.html` last then prune, invalidate, verify. Build env comes from the GitHub Environment (`vars.VITE_*`, `secrets.SENTRY_AUTH_TOKEN`).
|
|
|
|
**`.github/workflows/cd-hcp-static.yaml`** — HCP static-site CD for unfingerprinted builds such as Eleventy. Checkout at `ref`, Node 24, `npm ci --ignore-scripts` + `npm run build`, one-day cache on assets, `no-cache` on HTML/XML/text, prune, invalidate, verify the served index hash. Reads `/<prefix>/bucket` and `/<prefix>/distribution-id`. Do not use this for a hashed SPA.
|
|
|
|
**`.github/workflows/cd-sam.yaml`** — Reusable CD workflow for SAM repos. Runs `sam build` + `sam deploy` with OIDC credentials and a CloudFormation execution role. Triggers via `workflow_call` from per-repo `deploy.yaml` on push to `main`.
|
|
|
|
**`.github/workflows/cd-cdk.yaml`** — Reusable CD workflow for CDK repos (TypeScript and Python). Runs `cdk deploy --all` with OIDC credentials. Supports optional Python setup for Python CDK repos and QEMU emulation for cross-platform Docker builds.
|
|
|
|
**`.github/workflows/ci-python-app.yaml`** — Reusable CI for non-SAM Python apps (ruff check + format and conventions; no pytest, no SAM validate). Pytest stays a caller-owned job.
|
|
|
|
**`.github/workflows/ci-dotnet.yaml`** — Reusable CI for .NET solutions (`dotnet build`, optional `dotnet test`; SDK version and solution path as inputs).
|
|
|
|
**`.github/workflows/ci-static.yaml`** — Reusable CI for static sites (e.g. Eleventy builds for seahaven-site).
|
|
|
|
**`.github/workflows/ci-mobile-ios.yaml`** — Reusable CI for React Native iOS apps: dependency install, typecheck, optional lint and unit tests, and an unsigned compile (nothing uploaded). Emits the aggregated `ci / ci` status context.
|
|
|
|
**`.github/workflows/cd-mobile-ios.yaml`** — Reusable CD for iOS apps via Fastlane to TestFlight (Node + Ruby setup inputs).
|
|
|
|
**`.github/workflows/cd-dotnet-eb.yaml`** — Reusable CD for .NET apps on AWS Elastic Beanstalk. Publishes the project, packages a bundle, uploads it, creates an application version, and updates an **existing** environment with OIDC credentials — it never creates an environment. Serialised per environment via a `concurrency` group, and the post-deploy check fails the job if EB rolls the deploy back. The caller owns branch-to-environment mapping.
|
|
|
|
**`.github/workflows/callable-labeler.yaml`** — Org-wide PR auto-labeler. Label rules live inline here (single source of truth) — consumer repos need only a thin caller with `contents: read`, `pull-requests: write`, and `issues: write`; no per-repo labeler.yml.
|
|
|
|
**`.github/workflows/callable-dependency-review.yaml`** — Dependency review on PRs, failing on high severity. Requires Dependency Graph.
|
|
|
|
**`.github/workflows/release.yaml`** — Reusable release workflow: creates an annotated git tag at a commit and publishes a GitHub Release pointing at it. The version is an input (not read from a manifest).
|
|
|
|
**`.github/workflows/release-on-merge.yaml`** — Repo automation (not callable): cuts a tag and GitHub Release for **this** repo whenever a merge to `main` changes a reusable workflow, so Dependabot has a release to advance consumer SHA pins to (see the pinning policy below).
|
|
|
|
**`.github/workflows/labeler.yaml`** — This repo's own thin caller of `callable-labeler.yaml`, so the labeler runs on `.github`'s own PRs.
|
|
|
|
**`.github/workflows/ci.yaml`** — Self-CI for this repo: actionlint (checksum-verified install) over all workflow files, emitting the required `ci / ci` status context. Its shellcheck integration is enabled, so `run:` bodies are shell-linted too; the two deploy steps that rely on intentional word-splitting (`sam deploy … $PARAMS`, `cdk deploy $STACKS`) carry a per-line, commented `# shellcheck disable=SC2086` rather than being quoted or globally exempted.
|
|
|
|
### Workflow templates (`workflow-templates/`)
|
|
|
|
Starter workflows offered on the org's **Actions → New workflow** page: `cdk-deploy`, `ci-dotnet`, `ci-hcp`, `ci-mobile-ios`, `ci-node`, `ci-python`, `ci-python-app`, `ci-static`, `ci-terraform`, `ci-typescript-frontend`, `dependency-review`, `dotnet-eb-deploy`, `hcp-fargate-deploy`, `hcp-spa-deploy`, `labeler`, `mobile-ios-deploy`, `release`, `sam-deploy`, `triage`. Each is a thin caller of the corresponding reusable workflow above (`triage` is standalone; `ci-hcp` is the converted-repo caller with autofix, frontend, terraform, and `ci-complete`). Every template has a paired `properties.json` (name, description, icon, `filePatterns` for auto-suggestion). Replace any `REPLACE-ME` placeholders before enabling. Templates are not scanned by Dependabot, so refresh their pinned SHAs opportunistically when editing one.
|
|
|
|
### Ref pinning policy
|
|
|
|
All workflow refs across the org are pinned to full commit SHAs:
|
|
|
|
- **Org reusable workflows** are referenced at a **full commit SHA** of this repo with a trailing comment naming the ref or release the pin tracks:
|
|
|
|
```yaml
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@<full-commit-sha> # v1.0.3
|
|
```
|
|
|
|
Branch refs are mutable: a bad commit on this repo would flow instantly into every consumer's CI and deploy path, while a SHA pin turns the same change into a reviewable Dependabot PR. Two prerequisites keep pins advancing instead of freezing: every consumer repo's `dependabot.yml` must include the `github-actions` ecosystem (weekly), and Dependabot must be granted access to this repo at the org level (Org Settings → Advanced Security → Global settings → "Grant Dependabot access to repositories"); without the grant, update jobs fail with `git_dependencies_not_reachable` and pins freeze silently. `release-on-merge.yaml` tags this repo on every reusable-workflow change so Dependabot has releases to diff against. When adding a caller by hand, pin to the latest release commit (`gh api /repos/Sea-Haven-Industries/.github/commits/vX.Y.Z --jq .sha`), annotate it with `# vX.Y.Z`, and let Dependabot advance it from there.
|
|
- **Third-party and first-party actions** (`actions/checkout`, `actions/setup-python`, `actions/labeler`, …) — a subset are already SHA-pinned (e.g. `actions/labeler`, `aws-actions/*`, `docker/setup-qemu-action`, `ruby/setup-ruby`); the remainder (`actions/checkout`, `actions/setup-node`, `actions/setup-python`, `actions/setup-dotnet`, `actions/dependency-review-action`) currently use floating major-version tags. Full SHA pinning for this group is deferred (PLAT backlog); Dependabot will keep SHA and comment current once pins are set.
|
|
- **Binary installs are checksum-verified** (actionlint in `ci.yaml`).
|
|
|
|
### AWS deploy roles & IAM (`oidc-deploy-roles.yaml`)
|
|
|
|
**`oidc-deploy-roles.yaml`** is a **bootstrap CloudFormation stack** (`github-oidc-deploy-roles`, us-east-1, account 328440206208) that owns the IAM the CI/CD workflows assume. It contains:
|
|
|
|
- The GitHub Actions **OIDC provider** (conditional — already exists in the account).
|
|
- One **OIDC deploy role per repo** (`githubdeploy-<repo>`), assumed by that repo's `deploy.yaml` via OIDC and passed in as `AWS_DEPLOY_ROLE_ARN`. CDK repos use these to assume the `cdk-hnb659fds-*` bootstrap roles; SAM repos use these to run `sam deploy`.
|
|
- The shared **SAM CloudFormation execution role** `github-cfn-execution-role` (`SamCfnExecutionRole`) — passed as `cfn-role-arn` by every SAM `deploy.yaml` (see §3). CloudFormation assumes it to provision the SAM stacks' resources.
|
|
- The **`seahaven-lambda-execution-boundary`** managed policy.
|
|
- The **`seahaven-cfn-exec-iam-management`** managed policy (`SamCfnIamManagementPolicy`), attached to `github-cfn-execution-role`. It holds that role's boundary-gated IAM statements plus the Deny backstops that keep the permissions boundary from being detached, rewritten, or applied to the deploy substrate's own roles. It lives in a managed policy rather than inline because the role's inline policies sit at 10,006 of IAM's hard 10,240-byte per-role limit; attached managed policies have a separate 6,144-byte budget.
|
|
|
|
> **Constraint for future maintainers.** `github-cfn-execution-role` is explicitly denied from mutating the deploy substrate's own principals — itself, any `githubdeploy-*` role, and any `seahaven-*` managed policy. Those are owned by this stack and deployed manually with administrator credentials, so nothing legitimate needs that path. If you ever add automation that manages one of them, it must not run through `github-cfn-execution-role` or it will fail with `AccessDenied`.
|
|
|
|
> **Where the exec role's IAM statements live.** All of them are in the attached `seahaven-cfn-exec-iam-management` managed policy — there is no inline copy. The role's inline policies were previously at 10,006 of the 10,240-byte limit, leaving no room to add anything; consolidating into the managed policy brought that to **8,261 bytes (1,979 free)**. If you need to add a permission to this role, prefer the managed policy: the inline budget is the scarce one.
|
|
|
|
> ⚠️ **This stack has no CD pipeline — it is deployed manually.** (It defines the very roles the pipelines use, so it can't deploy itself.)
|
|
|
|
```bash
|
|
# Review IAM changes FIRST (IAM changes also require the cross-family review per the handbook):
|
|
aws cloudformation deploy \
|
|
--region us-east-1 \
|
|
--stack-name github-oidc-deploy-roles \
|
|
--template-file oidc-deploy-roles.yaml \
|
|
--capabilities CAPABILITY_NAMED_IAM \
|
|
--s3-bucket cdk-hnb659fds-assets-328440206208-us-east-1 \
|
|
--no-execute-changeset
|
|
# inspect the printed change-set, then drop --no-execute-changeset to apply.
|
|
```
|
|
|
|
`--s3-bucket` is **required** — the template is larger than the 51,200-byte inline limit.
|
|
|
|
#### `github-cfn-execution-role` is scoped (no `*FullAccess`)
|
|
|
|
The execution role carries **no blanket `*FullAccess`/`IAMFullAccess`** — only per-service inline policies. Its `iam:CreateRole` / `iam:AttachRolePolicy` / `iam:PutRolePolicy` are conditioned on `iam:PermissionsBoundary == seahaven-lambda-execution-boundary`, so it can only create roles that carry the boundary (it cannot mint an unconstrained admin role). **Adding a new AWS service to a SAM stack means adding that service's provisioning actions to this role**, or the deploy fails.
|
|
|
|
#### `seahaven-lambda-execution-boundary` is the Lambda runtime ceiling
|
|
|
|
Every SAM-created Lambda execution role gets this boundary attached — SAM stacks set it on `Globals.Function`:
|
|
|
|
```yaml
|
|
Globals:
|
|
Function:
|
|
PermissionsBoundary: arn:aws:iam::328440206208:policy/seahaven-lambda-execution-boundary
|
|
```
|
|
|
|
A function's effective permissions are the **intersection** of its own role policy and this boundary. **A new runtime permission must also be added to the boundary, or it is silently denied at runtime** (the deploy still succeeds — the failure only shows when the function runs). SAM does **not** support a custom `Path` on auto-generated function roles, so the boundary *condition* (not a role path) is the escalation guard.
|
|
|
|
#### Order of operations when changing the exec role or boundary
|
|
|
|
1. Deploy the boundary change first.
|
|
2. Redeploy the SAM stacks so their roles pick it up (while the exec role still permits it).
|
|
3. *Then* tighten the exec role.
|
|
|
|
Wrong order breaks every SAM deploy. CDK repos are unaffected — they deploy via `cdk-hnb659fds-*` roles, not this execution role.
|
|
|
|
This ordering rule is about changing the **boundary** or the conditions that gate it. It does not apply to changes that only add permissions to the exec role.
|
|
|
|
#### Permissions boundaries can no longer be removed by CloudFormation
|
|
|
|
`github-cfn-execution-role` is explicitly denied `iam:DeleteRolePermissionsBoundary`. It can *set* the boundary (that is what SAM needs) but never remove one. Two consequences worth knowing before debugging a stuck stack:
|
|
|
|
- **Removing `PermissionsBoundary` from an existing role fails by design.** CloudFormation issues `DeleteRolePermissionsBoundary` for that edit, gets `AccessDenied`, and the stack update rolls back. Removing the boundary from a SAM function is a security regression, so failing loudly is intended.
|
|
- **Rollback of an update that *adds* a boundary to an existing role would also fail**, landing the stack in `UPDATE_ROLLBACK_FAILED`. This is currently unreachable — all 26 IAM roles across the five SAM stacks already carry the boundary (verified 2026-07-27), so no update can add one. It becomes reachable again only if a role is created without the boundary and given one later.
|
|
|
|
Recovery from `UPDATE_ROLLBACK_FAILED` is an administrator action, not a pipeline retry: clear the wedged stack with `aws cloudformation continue-update-rollback --stack-name <stack> --resources-to-skip <RoleLogicalId>`, or replace the role by renaming its logical id. A completed update rollback lands in `UPDATE_ROLLBACK_COMPLETE`, which is stable and can accept a corrective update; `cd-sam` blocks only first-create `ROLLBACK_COMPLETE` and failed or in-progress states.
|
|
|
|
## Setup
|
|
|
|
### 1. Org-level secrets
|
|
|
|
Managed under **Organization Settings > Secrets and variables > Actions**. Each is set to **selected repositories** visibility — grant it to a repo before a workflow there can read it.
|
|
|
|
| Secret | Value | Consumed by |
|
|
|--------|-------|-------------|
|
|
| `ANTHROPIC_API_KEY` | Anthropic API key | `reviewer-eval.yml` in `open-swe` |
|
|
| `AUTOFMT_APP_ID` | Formatter GitHub App id | `ci-autofix.yaml` |
|
|
| `AUTOFMT_APP_PRIVATE_KEY` | Formatter GitHub App private key | `ci-autofix.yaml` |
|
|
|
|
The remaining-lane CI and CD workflows below need no org secret — CD authenticates to AWS via OIDC using the per-repo `AWS_DEPLOY_ROLE_ARN` secret (see §3). HCP CD uses `vars.DEPLOY_ROLE_ARN` on the GitHub Environment after OIDC. Adam installs the formatter App (contents: write, metadata: read; not a main-branch ruleset bypass) and grants the two autofmt secrets before the first converted repo runs autofix.
|
|
|
|
### 2. Add CI to a repo
|
|
|
|
Create `.github/workflows/ci.yaml` in the target repo. Examples:
|
|
|
|
**Python SAM repo** (e.g., afterhours-shift-manager, expense-approval-bot):
|
|
|
|
```yaml
|
|
name: CI
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
ci:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
```
|
|
|
|
**TypeScript CDK repo** (e.g., seahaven-door-unlock-api, seahaven-slack-bot):
|
|
|
|
```yaml
|
|
name: CI
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
ci:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
```
|
|
|
|
**Node.js SAM repo** (e.g., payments-dashboard):
|
|
|
|
```yaml
|
|
name: CI
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
ci:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
with:
|
|
run-typecheck: false
|
|
run-cdk-synth: false
|
|
run-sam-validate: true
|
|
```
|
|
|
|
**Mixed stack** (e.g., exec-aide — TypeScript CDK + Python Lambdas):
|
|
|
|
```yaml
|
|
name: CI
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
python:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
with:
|
|
source-dirs: "src"
|
|
run-sam-validate: false
|
|
typescript:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
```
|
|
|
|
**HCP app repo** (converted callers; required check is `ci-complete`):
|
|
|
|
```yaml
|
|
name: CI
|
|
on:
|
|
pull_request:
|
|
branches: [main, hotfix/**, release/**]
|
|
merge_group:
|
|
push:
|
|
branches: [hotfix/**, release/**]
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
autofix:
|
|
if: github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/ci-autofix.yaml@<full-commit-sha> # vX.Y.Z
|
|
permissions: { contents: write }
|
|
secrets: inherit
|
|
with:
|
|
presets: prettier,terraform
|
|
|
|
frontend:
|
|
needs: autofix
|
|
if: always() && !cancelled() && (needs.autofix.result == 'skipped' || needs.autofix.outputs.committed != 'true')
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/ci-frontend.yaml@<full-commit-sha> # vX.Y.Z
|
|
with:
|
|
node-version: "24"
|
|
unit-shards: 4
|
|
run-e2e: true
|
|
|
|
terraform:
|
|
needs: autofix
|
|
if: always() && !cancelled() && (needs.autofix.result == 'skipped' || needs.autofix.outputs.committed != 'true')
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/ci-terraform.yaml@<full-commit-sha> # vX.Y.Z
|
|
with:
|
|
terraform-version: "1.16.0"
|
|
|
|
ci-complete:
|
|
name: ci-complete
|
|
needs: [autofix, frontend, terraform]
|
|
if: always() && !cancelled() && (needs.autofix.result == 'skipped' || needs.autofix.outputs.committed != 'true')
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- name: Require portions
|
|
env:
|
|
FRONTEND: ${{ needs.frontend.result }}
|
|
TERRAFORM: ${{ needs.terraform.result }}
|
|
run: |
|
|
set -euo pipefail
|
|
test "${FRONTEND}" = success
|
|
test "${TERRAFORM}" = success
|
|
```
|
|
|
|
Python HCP callers pass `presets: ruff,terraform`. The `eslint` preset is opt-in and runs `npx eslint . --fix`. Do not pass `npm run lint -- --fix`: several apps chain Redocly into `lint`. Enable `eslint` only when that repo's CI lint step is ESLint itself and Prettier owns formatting. Optional `format-command`, `lint-fix-command`, and `extra-command` still run after the presets. Flip org ruleset membership in the same window as this merge: include on `CI complete`, exclude from `main branch protection`. Never require both `ci / ci` and `ci-complete`. Do not edit native GitHub merge-queue rulesets.
|
|
|
|
### 3. Add CD to a repo
|
|
|
|
**HCP Fargate** (one caller job per GitHub Environment; `environment` is a `with:` input):
|
|
|
|
```yaml
|
|
name: Deploy API
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths-ignore: [terraform/**, docs/**, "*.md"]
|
|
release:
|
|
types: [published]
|
|
workflow_dispatch:
|
|
inputs:
|
|
environment: { type: choice, options: [dev, prod] }
|
|
ref: { type: string, default: "" }
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
deploy-dev:
|
|
name: Deploy API to dev
|
|
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.environment == 'dev')
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/cd-hcp-fargate.yaml@<full-commit-sha> # vX.Y.Z
|
|
permissions: { contents: read, id-token: write }
|
|
secrets: inherit
|
|
with:
|
|
environment: dev
|
|
ref: ${{ inputs.ref }}
|
|
ssm-prefix: /meal-order-manager/deploy
|
|
docker-platform: linux/amd64
|
|
extra-task-env: '{"SENTRY_DSN_PARAM":"/meal-order-manager/sentry-dsn"}'
|
|
|
|
deploy-prod:
|
|
name: Deploy API to prod
|
|
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.environment == 'prod')
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/cd-hcp-fargate.yaml@<full-commit-sha> # vX.Y.Z
|
|
permissions: { contents: read, id-token: write }
|
|
secrets: inherit
|
|
with:
|
|
environment: prod
|
|
ref: ${{ github.event.release.tag_name || inputs.ref }}
|
|
ssm-prefix: /meal-order-manager/deploy
|
|
docker-platform: linux/amd64
|
|
ship-gate: true
|
|
extra-task-env: '{"SENTRY_DSN_PARAM":"/meal-order-manager/sentry-dsn"}'
|
|
```
|
|
|
|
SPA callers use `cd-hcp-spa.yaml` the same way. Pass `required-vite-vars` for Environment `VITE_*` keys that must be set before `npm run build`. Add `deploy-staging` only where that Environment exists. `DEPLOY_ROLE_ARN` is a GitHub Environment variable, not a repo secret. SHA-pinned org reusables change `job_workflow_ref` to `Sea-Haven-Industries/.github/.github/workflows/cd-hcp-fargate.yaml@<sha>` (and spa). Keep `workflow_ref` on the thin caller at `refs/heads/main` and `refs/tags/v*`. `sub` stays `repo:.../<app>:environment:<env>`. Adding a reusable is a cross-family IAM change.
|
|
|
|
Create `.github/workflows/deploy.yaml` in remaining SAM/CDK repos. Requires `AWS_DEPLOY_ROLE_ARN` repo secret.
|
|
|
|
**SAM repo** (e.g., remaining SAM stacks):
|
|
|
|
```yaml
|
|
name: Deploy
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
deploy:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/cd-sam.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
with:
|
|
stack-name: afterhours-shift-manager
|
|
cfn-role-arn: arn:aws:iam::328440206208:role/github-cfn-execution-role
|
|
secrets:
|
|
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
```
|
|
|
|
> The `cfn-role-arn` (`github-cfn-execution-role`) is scoped and boundary-gated — adding a new AWS service or a new Lambda runtime permission to a SAM stack may require updating that role and/or `seahaven-lambda-execution-boundary` first. See [AWS deploy roles & IAM](#aws-deploy-roles--iam-oidc-deploy-rolesyaml).
|
|
|
|
**TypeScript CDK repo** (e.g., seahaven-door-unlock-api):
|
|
|
|
```yaml
|
|
name: Deploy
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
deploy:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
secrets:
|
|
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
```
|
|
|
|
**Python CDK repo** (e.g., po-ingest):
|
|
|
|
```yaml
|
|
name: Deploy
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
deploy:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
with:
|
|
python-version: "3.12"
|
|
cdk-dir: cdk
|
|
secrets:
|
|
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
```
|
|
|
|
**CDK repo with arm64 Docker builds** (e.g., exec-aide):
|
|
|
|
```yaml
|
|
name: Deploy
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
deploy:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
with:
|
|
enable-qemu: true
|
|
secrets:
|
|
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
```
|
|
|
|
**.NET app on Elastic Beanstalk** (e.g., shoc-backend):
|
|
|
|
```yaml
|
|
name: Deploy
|
|
on:
|
|
push:
|
|
branches: [dev]
|
|
|
|
jobs:
|
|
deploy:
|
|
uses: Sea-Haven-Industries/.github/.github/workflows/cd-dotnet-eb.yaml@81cf168170f356d1423d7736f7ce93fd6611ad53 # v1.0.4
|
|
with:
|
|
project: Api.SeaHavenIndustries/Api.SeaHavenIndustries.csproj
|
|
eb-application: shoc-backend
|
|
eb-environment: shoc-backend-dev
|
|
procfile-command: dotnet Api.SeaHavenIndustries.dll
|
|
secrets:
|
|
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
```
|
|
|
|
> The environment must already exist — this workflow deploys a new application version to it and never creates one. Branch-to-environment mapping belongs in the caller: add one job per branch (e.g. `dev` → `…-dev`, `main` → `…-staging`) rather than parameterising the reusable by branch. `procfile-command` generates the Procfile the Amazon Linux .NET platform needs; omit it only if the repo commits its own Procfile into the publish output. Deploys are serialised per environment, and the job fails if Elastic Beanstalk rolls the version back.
|
|
|
|
Enable optional steps as repos adopt them:
|
|
|
|
| Input | Default | Turn on when... |
|
|
|-------|---------|-----------------|
|
|
| `run-tests` | `false` | Repo has `pytest` tests or Jest tests |
|
|
| `run-lint` | `false` | Repo has an ESLint config |
|
|
| `run-typecheck` | `true` | Repo has `tsconfig.json` |
|
|
| `run-cdk-synth` | `true` | Repo is CDK-based |
|
|
| `run-sam-validate` | `true` (Python) / `false` (TS) | Repo has a SAM template |
|