diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc30a5a..7ec28a2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## How to Suggest Changes -- Open an issue describing the proposed change and why it's needed +- Create a Jira ticket in `DEV`, `PLAT`, or `SEC`, based on the change's scope - Or submit a PR with edits to the relevant markdown file ## Style Guidelines diff --git a/README.md b/README.md index df28ebf..e214539 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Engineering conventions and best practices for Sea Haven Industries. - [Naming Conventions](naming-conventions.md) -- kebab-case everywhere, no exceptions - [Development Environment](dev-environment.md) -- workstation directory layout, pyenv, Node, launchd/TCC - [Issue Tracking](issue-tracking.md) -- Jira projects, ticket description template, ticket hygiene -- [Git Workflow](git-workflow.md) -- feature branches, incremental commits, deploy-then-merge +- [Git Workflow](git-workflow.md) -- feature branches, incremental commits, CI-on-merge - [Commit Messages](commit-messages.md) -- Conventional Commits, type(scope) format, explain "why" - [Pull Requests](pull-requests.md) -- scope, title, description format, merge strategy - [Code Review](code-review.md) -- what to look for, giving feedback, turnaround expectations @@ -24,7 +24,7 @@ Engineering conventions and best practices for Sea Haven Industries. - [Secrets and Configuration](secrets-and-config.md) -- Secrets Manager vs SSM Parameter Store - [CI/CD Pipelines](cicd.md) -- every deployable repo gets a pipeline, no manual deploys - [Bedrock](bedrock.md) -- cross-region inference profiles, alias pinning, KB Docker requirement -- [Git Hooks](hooks/) -- recommended pre-push and pre-commit hooks +- [Git Hooks](hooks/) -- shim for the global security pre-push hook - [Scripts](scripts/) -- repo provisioning, automation tooling - [CDK Constructs](constructs/) -- shared VPN EC2 instance construct and other reusable patterns diff --git a/cdk-project-layout.md b/cdk-project-layout.md index 5153a6a..422b003 100644 --- a/cdk-project-layout.md +++ b/cdk-project-layout.md @@ -173,7 +173,7 @@ on: branches: [main] jobs: ci: - uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@ # main + uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@ # vX.Y.Z with: node-version: "24" @@ -184,14 +184,14 @@ on: branches: [main] jobs: deploy: - uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@ # main + uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@ # vX.Y.Z with: node-version: "24" secrets: deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }} ``` -Always pass `node-version: "24"` explicitly. Reusable workflow refs are pinned to a full 40-character commit SHA of the central `.github` repo with a trailing `# main` comment, never to a branch or tag; see [Workflow Ref Pinning](cicd.md#workflow-ref-pinning). Pin to the current tip of that repo's `main` when adding a caller by hand and let Dependabot advance it. See [cicd.md](cicd.md) for the full pipeline convention. +Always pass `node-version: "24"` explicitly. Reusable workflow refs are pinned to a full 40-character commit SHA of the central `.github` repo with a trailing `# vX.Y.Z` comment (the release tag the SHA corresponds to), never a branch or tag ref; see [Workflow Ref Pinning](cicd.md#workflow-ref-pinning). Pin to the SHA for the latest release when adding a caller by hand and let Dependabot advance it. See [cicd.md](cicd.md) for the full pipeline convention. ## Bedrock Agents diff --git a/cicd.md b/cicd.md index c9c6020..6ec065d 100644 --- a/cicd.md +++ b/cicd.md @@ -27,7 +27,7 @@ on: branches: [main] jobs: ci: - uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@ # main + uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@ # vX.Y.Z with: node-version: "24" @@ -38,7 +38,7 @@ on: branches: [main] jobs: deploy: - uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@ # main + uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@ # vX.Y.Z with: node-version: "24" secrets: @@ -55,7 +55,7 @@ on: branches: [main] jobs: ci: - uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@ # main + uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@ # vX.Y.Z # .github/workflows/deploy.yaml name: Deploy @@ -64,7 +64,7 @@ on: branches: [main] jobs: deploy: - uses: Sea-Haven-Industries/.github/.github/workflows/cd-sam.yaml@ # main + uses: Sea-Haven-Industries/.github/.github/workflows/cd-sam.yaml@ # vX.Y.Z with: stack-name: "your-stack-name" cfn-role-arn: "arn:aws:iam:::role/github-cfn-execution-role" @@ -151,24 +151,28 @@ cancel-in-progress: true ## Naming - All workflow files: kebab-case -- Reusable workflow references: pinned to a full 40-character commit SHA with a `# main` comment — never a branch or tag ref +- Reusable workflow references: pinned to a full 40-character commit SHA — never a branch or tag ref ## Workflow Ref Pinning -Reusable workflow references are pinned to a full commit SHA of the central `.github` repo, with a trailing `# main` comment: +Reusable workflow references are pinned to a full commit SHA of the central `.github` repo. The trailing comment names the release tag when the central repo is release-backed, or `main` when no release exists: ```yaml +# Release-backed (standard — central .github repo cuts releases): +uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@ # vX.Y.Z + +# No release yet (use main only when the central repo has no release tags): uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@ # main ``` -Branch refs are mutable: a compromised or bad commit on the central repo would flow instantly into every consumer's CI and deploy path. A SHA pin turns that same change into a reviewable Dependabot PR instead. The comment tells Dependabot (and readers) which ref the pin tracks. +Branch refs are mutable: a compromised or bad commit on the central repo would flow instantly into every consumer's CI and deploy path. A SHA pin turns that same change into a reviewable Dependabot PR instead. The `# vX.Y.Z` comment lets Dependabot embed changelog information and lets readers identify which release the pin corresponds to. Per the pinning principle, pins are for reproducibility, not for freezing time. Two prerequisites keep them moving: 1. Every repo's `dependabot.yml` must include the `github-actions` ecosystem (weekly), so pin-advance PRs are opened automatically. 2. Dependabot must be granted access to the internal `.github` 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 — consumers stop receiving central workflow fixes with no visible signal beyond the failed Dependabot run. -When adding a caller workflow by hand, pin to the current tip of the central repo's `main` (`gh api /repos/Sea-Haven-Industries/.github/commits/main --jq .sha`) and let Dependabot advance it from there. +When adding a caller workflow by hand, pin to the commit SHA corresponding to the latest release of the central repo (`gh api /repos/Sea-Haven-Industries/.github/commits/vX.Y.Z --jq .sha`) and annotate with `# vX.Y.Z`. The commits endpoint resolves both lightweight and annotated tags to the underlying commit. Let Dependabot advance the pin from there. Use `gh api /repos/Sea-Haven-Industries/.github/commits/main --jq .sha` with a `# main` comment only when the central repo has no release tags. ## When to Add a Pipeline @@ -193,7 +197,7 @@ permissions: issues: write jobs: label: - uses: Sea-Haven-Industries/.github/.github/workflows/callable-labeler.yaml@ # main + uses: Sea-Haven-Industries/.github/.github/workflows/callable-labeler.yaml@ # vX.Y.Z ``` - The trigger is plain `pull_request`, not `pull_request_target`: private repos take no fork PRs, so the lower-privilege event is sufficient and avoids the pwn-request surface. Because `pull_request` runs the workflow from the merge commit, the Labeler check appears on the PR that first adds the caller — an absent or failed check means a missing permission, not expected behaviour. diff --git a/code-review.md b/code-review.md index 576c71c..d549ed7 100644 --- a/code-review.md +++ b/code-review.md @@ -36,17 +36,27 @@ Code review exists to catch defects, share knowledge, and maintain consistency. ## Deferred Findings -When a reviewer identifies a finding that won't be addressed in the current PR, the PR author must create a GitHub issue for it before the PR merges. No exceptions -- if it's worth commenting on, it's worth tracking. +When a reviewer identifies a finding that won't be addressed in the current PR, the PR author must file a Jira ticket in the appropriate project (`DEV`, `PLAT`, or `SEC`) before the PR merges. No exceptions — if it's worth commenting on, it's worth tracking. + +**Exception:** Repos with GitHub Issues enabled (contractor intake repos such as `shoc-backend` and `shoc-frontend-new`, and open-source fork repos) may use a GitHub issue instead. ### Requirements -- The GitHub issue must reference the PR number and link to the specific review comment. -- The PR author must reply to the review comment with a link to the created issue, acknowledging the deferral. +- The Jira ticket (or GitHub issue, where applicable) must reference the PR number and link to the specific review comment. +- The PR author must reply to the review comment with a link to the created ticket, acknowledging the deferral. - This applies to all severity levels: bugs, nits, refactors, missing tests, documentation gaps. ### Why -Deferred findings handled informally (retro notes, mental to-do lists, "we'll get to it") fall through the cracks. An issue in the backlog is the minimum bar for accountability. +Deferred findings handled informally (retro notes, mental to-do lists, "we'll get to it") fall through the cracks. A ticket in the Jira backlog is the minimum bar for accountability. + +## Security Review Gates + +Two separate gates apply to security-sensitive changes: + +**Cross-family review (`cross_review.py`):** Required when the change touches IAM roles, IAM policies, or resource permission boundaries. Run the stateless GPT cross-reviewer via `cross_review.py` in the `security-review` repo. It produces findings-to-verify, not a gospel verdict. After two rounds without convergence, stop and disposition the remainder with Adam. Lambda handler signatures are not a cross-review trigger. + +**Security review:** Required when the change touches sensitive authentication paths, secrets handling, IaC/IAM definitions, payment flows, or surfaces that accept untrusted input. These surfaces warrant a structured security review pass in addition to standard code review. ## Turnaround diff --git a/commit-messages.md b/commit-messages.md index 89ced6e..331278c 100644 --- a/commit-messages.md +++ b/commit-messages.md @@ -19,7 +19,7 @@ Examples: ``` feat(parser): add retry logic for transient upstream failures fix(auth): correct null check in session handler -docs: document the deploy-then-merge workflow +docs: document the CI-on-merge workflow chore(deps): bump aws-cdk-lib to 2.150.0 ``` @@ -119,10 +119,10 @@ Refs: PROJ-123, #123 Use the `Refs:` trailer to point at the work this commit relates to: -- **Jira key** (`PROJ-123`) when the work tracks a Jira issue. The GitHub for Jira app reads the key and threads the commit into the issue's development panel. See [git-workflow.md](git-workflow.md#linking-to-jira). -- **GitHub issue** (`#123`) when the work tracks a GitHub issue in the same repo. +- **Jira key** (`DEV-123`, `PLAT-7`, `SEC-4`) when the work tracks a Jira issue. The GitHub for Jira app reads the key and threads the commit into the issue's development panel. See [git-workflow.md](git-workflow.md#linking-to-jira). +- **GitHub issue** (`#123`) for repos where GitHub Issues are enabled (contractor and fork repos only). -List both when both apply: `Refs: PROJ-123, #123`. The key only needs to appear once in the branch, PR title, or any commit for the link to form, but including it in the trailer keeps the reference attached to the individual change. +The Jira key is required in the PR title suffix `(KEY-123)`. Including it in the `Refs:` trailer of each commit is recommended but not required — the link forms as long as the key appears in the PR title. ## Example diff --git a/dev-environment.md b/dev-environment.md index 8a80486..db175b5 100644 --- a/dev-environment.md +++ b/dev-environment.md @@ -36,7 +36,7 @@ When troubleshooting Python issues, check that pyenv is active before anything e - Local: Node 24 / npm 11 (generates `lockfileVersion: 3`) - Lambda: `nodejs24.x`, set explicitly in IaC. `nodejs22.x` is legacy only and never `nodejs26.x`; see [AWS Infrastructure](aws-infrastructure.md#node-runtime) for the canonical rule -- Reusable workflows: always pass `node-version: "24"` (the default is 22 / npm 10, which can fail `npm ci` on npm 11 lockfiles) +- Reusable workflows: always pass `node-version: "24"` explicitly rather than relying on a default that may change ## macOS launchd and the TCC Sandbox diff --git a/git-workflow.md b/git-workflow.md index 053bb3b..759c47b 100644 --- a/git-workflow.md +++ b/git-workflow.md @@ -8,19 +8,19 @@ | Prefix | Use when | |---|---| -| `feature/-` | Adding new functionality or enhancing existing features | -| `bug/-` | Fixing a non-urgent defect found during development or testing | -| `hotfix/-` | Fixing a production issue that needs immediate attention | -| `chore/-` | Routine maintenance, cleanup, dependency bumps, or tooling | -| `docs/-` | Documentation-only changes | -| `refactor/-` | Restructuring code without changing behavior | -| `release/-` | Preparing a release (version bump, tag, changelog) | +| `feature/` | Adding new functionality or enhancing existing features | +| `fix/` | Fixing a non-urgent defect found during development or testing | +| `hotfix/` | Fixing a production issue that needs immediate attention | +| `chore/` | Routine maintenance, cleanup, dependency bumps, or tooling | +| `docs/` | Documentation-only changes | +| `refactor/` | Restructuring code without changing behavior | +| `release/` | Preparing a release (version bump, tag, changelog) | -The prefixes mirror the commit types in [commit-messages.md](commit-messages.md#types), so the branch and its commits speak the same vocabulary. +The prefixes mirror the commit types in [commit-messages.md](commit-messages.md#types), so the branch and its commits speak the same vocabulary. Use kebab-case for the description (e.g. `feature/add-receipt-parser`). -**bug vs hotfix:** Use `bug/` for defects caught before they affect production (failing tests, broken dev flows, issues found in review). Use `hotfix/` only when production is impacted and the fix needs to bypass normal review cadence. +**fix vs hotfix:** Use `fix/` for defects caught before they affect production (failing tests, broken dev flows, issues found in review). Use `hotfix/` only when production is impacted and the fix needs to bypass normal review cadence. -**Jira key:** When the work tracks a Jira issue, put the key after the prefix: `feature/PROJ-123-add-receipt-parser` (substitute the real project key, e.g. the infra or software-development project). This is what wires the branch, commits, and PR into the issue's development panel. See [Linking to Jira](#linking-to-jira) below. Work with no Jira issue (one-off scripts, trivial fixes) omits the key and uses a plain description. +**Jira key:** The Jira key does not go in the branch name. It goes in the PR title as a suffix (e.g. `feat(parser): add receipt parser (DEV-123)`). See [pull-requests.md](pull-requests.md#title) and [Linking to Jira](#linking-to-jira) below. ## Commits @@ -32,13 +32,12 @@ The prefixes mirror the commit types in [commit-messages.md](commit-messages.md# Work is tracked in Jira (`seahaven.atlassian.net`). Jira is the source of truth for *work*; GitHub is the source of truth for *code*. We do not duplicate issues between the two — we link them. -The org-level **GitHub for Jira** app is already installed across all repos. It detects the Jira issue key (e.g. `PROJ-123`) wherever it appears and surfaces the branch, commits, PR, and deployment status in that issue's development panel automatically. To make that happen, mention the key in at least one of: +The org-level **GitHub for Jira** app is already installed across all repos. It detects the Jira issue key (e.g. `DEV-123`) wherever it appears and surfaces the branch, commits, PR, and deployment status in that issue's development panel automatically. To make that happen: -- the branch name (`feature/PROJ-123-add-receipt-parser`) -- the PR title (`[PROJ-123] Add receipt parser`) -- a commit message (the `Refs:` trailer, see [commit-messages.md](commit-messages.md)) +- **Required:** include the key at the end of the PR title in parentheses — `feat(parser): add receipt parser (DEV-123)`. See [pull-requests.md](pull-requests.md#title) for the full format. +- **Recommended:** include the key in the `Refs:` trailer of each commit (see [commit-messages.md](commit-messages.md#referencing-issues)). -Mentioning it in the branch name covers all three at once, so that is the minimum bar. +Branch names do not carry Jira keys. The required linkage point is the PR title. ### Smart Commit commands @@ -118,18 +117,9 @@ Start at `v0.1.0` for new projects. Move to `v1.0.0` when the interface is stabl ## Git Hooks -Recommended hooks live in [`hooks/`](hooks/) — copy them into `.git/hooks/` when setting up a project. +The global security `pre-push` hook at `~/.config/git/hooks/pre-push` is the only pre-push hook in active use. It runs the workstation security scanner before every push and is the deterministic gate for catching secrets and high-severity findings locally. Linting (ruff, tsc/eslint) runs in CI, not in pre-push. -| Hook | Purpose | -|---|---| -| `pre-push` | Runs `npm ci` to catch lock file drift before it breaks CI | - -Install for a Node.js project: - -```bash -cp ~/Documents/repositories/seahaven/engineering-handbook/hooks/pre-push .git/hooks/pre-push -chmod +x .git/hooks/pre-push -``` +The [`hooks/`](hooks/) directory ships a `pre-push` shim for use by repos that set their own `core.hooksPath`. That shim re-execs the global hook rather than replacing it. It is not an npm-ci runner and is not a general-purpose pre-push template. ### `core.hooksPath` shadows, it does not merge @@ -140,8 +130,11 @@ A repo that sets its own `core.hooksPath` must ship a `pre-push` that re-execs t ```bash #!/usr/bin/env bash GLOBAL="${SH_GLOBAL_HOOKS:-$HOME/.config/git/hooks}/pre-push" -[ -x "$GLOBAL" ] && exec "$GLOBAL" "$@" # passes args and stdin, preserves the exit code -exit 0 # nothing to delegate to +if [ ! -x "$GLOBAL" ]; then + echo "pre-push: global security hook is missing or not executable: $GLOBAL" >&2 + exit 1 +fi +exec "$GLOBAL" "$@" # passes args and stdin, preserves the exit code ``` Two consequences: diff --git a/github-standards.md b/github-standards.md index 5509283..5e636d1 100644 --- a/github-standards.md +++ b/github-standards.md @@ -118,6 +118,10 @@ updates: - No force push to `main` - No branch deletion for `main` +## Agents and Automation + +Agents and automation (CI bots, Cursor agents, scripts) do not push directly to `main` unless Adam has explicitly directed it for a specific action. The default path for any automated change is a branch and a PR, same as human-authored work. + ## README Badges Every repo's README carries a small badge block immediately under the H1. Use **static** badges only — dynamic badges (for example shields.io `last-commit` or `open-issues`) query the public GitHub API and render broken on private repos. @@ -136,6 +140,10 @@ Every repo gets a set of lowercase, hyphenated topics so the org is filterable b Set them with `gh repo edit --add-topic a,b,c`. Adding topics is part of new-repo provisioning, not a follow-up. +## Required CI Status Check + +The org ruleset requires the status check named **`ci / ci`** — the job name `ci` under the workflow named `CI`. This is what the reusable `ci-typescript-cdk.yaml` and `ci-python-sam.yaml` callers emit. If the check name in the ruleset does not match what CI actually emits, merges will be blocked by a phantom required check. Verify after any change to CI job names. + ## Repo Hygiene - Delete feature branches after merge diff --git a/hooks/pre-push b/hooks/pre-push index 01021c6..86adc93 100755 --- a/hooks/pre-push +++ b/hooks/pre-push @@ -1,19 +1,18 @@ #!/usr/bin/env bash -# Pre-push hook: verify npm ci passes before pushing Node.js projects. -# Catches lock file drift that would break CI. +# pre-push shim for repos that set core.hooksPath. # -# Install: cp hooks/pre-push .git/hooks/pre-push && chmod +x .git/hooks/pre-push +# Setting core.hooksPath in a repo replaces the machine-global hooks directory +# entirely. Without this shim the workstation security pre-push gate is silently +# disabled. This file re-execs the global hook so the gate stays active. +# +# Install: used automatically when a repo's core.hooksPath points to a tracked +# hooks directory containing this file. -set -euo pipefail - -if [[ ! -f package-lock.json ]]; then - exit 0 -fi - -echo "pre-push: running npm ci..." -if ! npm ci --ignore-scripts --no-audit --no-fund 2>/dev/null; then - echo "pre-push: npm ci failed — lock file may be out of sync." - echo "Run: rm -rf node_modules package-lock.json && npm install" +GLOBAL="${SH_GLOBAL_HOOKS:-$HOME/.config/git/hooks}/pre-push" +if [ ! -x "$GLOBAL" ]; then + echo "pre-push: global security hook is missing or not executable: $GLOBAL" >&2 + echo "Repair the security-review hook installation before pushing." >&2 exit 1 fi -echo "pre-push: npm ci passed." + +exec "$GLOBAL" "$@" diff --git a/issue-tracking.md b/issue-tracking.md index 72ce41f..0e283d3 100644 --- a/issue-tracking.md +++ b/issue-tracking.md @@ -12,6 +12,8 @@ Work is tracked in Jira (`seahaven.atlassian.net`). This page covers how tickets Boards use the columns **To Do / In Progress / Blocked / Done**. SEC adds **Risk Accepted** for findings that are acknowledged and deliberately not remediated; a ticket moved there must say who accepted the risk and why. +`INFRA` is a closed-ticket archive (zero open tickets). `SCRUM` and `SUP` are idle legacy projects. Do not create new work in any of these three projects. + ## Ticket Description Template Every ticket uses the same four-section description. The template is set as the default description on each project's issue types, and each project pins a `TEMPLATE` ticket (issue 1 in the project, e.g. `PLAT-1`) as the reference copy. Fill in all four sections; a section that genuinely has nothing in it should say so explicitly rather than be deleted. @@ -76,6 +78,6 @@ Descriptions go stale: the affected system was renamed, half the scope was done An epic must not sit open and empty after all its children are done. Either close it with an evidence comment like any other ticket, or add the children that represent the remaining work. An open epic is a claim that work remains; keep the claim true. -## Keys in Branches, Commits, and PRs +## Keys in PRs and Commits -The Jira key goes in the branch name, the PR title, and the commit `Refs:` trailer, which threads the code into the issue's development panel. The mechanics are covered in [git-workflow.md](git-workflow.md#linking-to-jira) and [commit-messages.md](commit-messages.md#referencing-issues). +The Jira key goes in the PR title as a suffix in parentheses — `feat(scope): description (DEV-123)` — and in the commit `Refs:` trailer. Branch names do not carry Jira keys. The PR-title suffix is required; the `Refs:` trailer is recommended. The mechanics are covered in [git-workflow.md](git-workflow.md#linking-to-jira) and [commit-messages.md](commit-messages.md#referencing-issues). diff --git a/pull-requests.md b/pull-requests.md index f4d1244..8915659 100644 --- a/pull-requests.md +++ b/pull-requests.md @@ -12,19 +12,23 @@ Each PR should represent a single logical change. If you find yourself writing " ## Title -- Keep it under 70 characters -- Use the Conventional Commit format, same as commit messages (`type(scope): description`) +- Use the Conventional Commit format with a required Jira key suffix: `type(scope): description (KEY-123)` +- `scope` is optional; omit it when the change is broad or no single scope applies +- Active keys are `DEV`, `PLAT`, and `SEC` +- Keep the full title under 72 characters - Describe the change, not the ticket | Good | Bad | |---|---| -| `feat: add retry logic for transient upstream failures` | `JIRA-123` | -| `fix: correct null check in auth handler` | `Bug fix` | -| `build: update Lambda runtime to Python 3.12` | `Updates` | +| `feat(parser): add retry logic for transient upstream failures (DEV-42)` | `JIRA-123` | +| `fix(auth): correct null check in session handler (PLAT-7)` | `Bug fix` | +| `build: update Lambda runtime to Python 3.12 (DEV-88)` | `Updates` | + +**Exemptions:** Dependabot PRs and permission-controlled emergency reverts are the only PRs that may omit the Jira key suffix. ## Description -Use a structured format: +Use a structured format with exactly these four sections in this order: ```markdown ## Summary @@ -37,16 +41,18 @@ How you verified it works — steps taken, commands run, screenshots if UI. What tests were added, updated, or run. If no automated tests, explain manual testing. ## Notes -Anything reviewers should know — migration steps, deploy order, follow-ups, breaking changes. Omit this section if empty. +Anything reviewers should know — migration steps, deploy order, follow-ups, breaking changes. Use `None.` when this section has nothing to say; do not omit it. ``` The summary should explain **why** the change is needed, not just restate the diff. Reviewers can read the code; they need context. +State verifiable facts about the code. Do not cite the engineering handbook, and do not include AI attribution footers. + ## When to Open a PR -- Before deploying to production (see [deploy-then-merge](git-workflow.md) workflow) - When the work is ready for review, not as a draft for parking incomplete work - After verifying locally that the change works as expected +- The deploy happens after the PR merges to `main`; see [Deploying](git-workflow.md#deploying) ## Merging diff --git a/scripts/provision-repo.sh b/scripts/provision-repo.sh index 220c213..c20bea3 100755 --- a/scripts/provision-repo.sh +++ b/scripts/provision-repo.sh @@ -3,7 +3,10 @@ # Usage: ./provision-repo.sh [sam|cdk] # # Creates: GitHub repo, OIDC deploy role, repo secret, security features, -# CI/CD workflow stubs, and pre-push hook. +# and CI/CD workflow stubs. +# +# Workflow callers are generated from the latest released version of the +# Sea-Haven-Industries/.github repository and pinned to its full commit SHA. set -euo pipefail @@ -20,11 +23,25 @@ if [[ ! "$REPO_NAME" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]]; then exit 1 fi +WORKFLOW_VERSION=$(gh api "repos/${ORG}/.github/tags?per_page=100" \ + --jq '[.[] | select(.name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))][0].name // empty') +if [[ -z "$WORKFLOW_VERSION" ]]; then + echo "Error: no released Sea-Haven-Industries/.github vX.Y.Z tag found." + exit 1 +fi + +WORKFLOW_SHA=$(gh api "repos/${ORG}/.github/commits/${WORKFLOW_VERSION}" --jq .sha) +if [[ ! "$WORKFLOW_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo "Error: ${WORKFLOW_VERSION} did not resolve to a full commit SHA." + exit 1 +fi + echo "=== Provisioning ${ORG}/${REPO_NAME} (${STACK_TYPE}) ===" +echo "Workflow release: ${WORKFLOW_VERSION} (${WORKFLOW_SHA})" # 1. Create GitHub repo echo "" -echo "[1/6] Creating GitHub repo..." +echo "[1/5] Creating GitHub repo..." if gh repo view "${ORG}/${REPO_NAME}" &>/dev/null; then echo " Repo already exists — skipping." else @@ -37,7 +54,7 @@ fi # 2. Create OIDC deploy role echo "" -echo "[2/6] Creating IAM deploy role: ${ROLE_NAME}..." +echo "[2/5] Creating IAM deploy role: ${ROLE_NAME}..." TRUST_POLICY=$(cat </dev/null || true gh api "repos/${ORG}/${REPO_NAME}" -X PATCH \ -f security_and_analysis.dependabot_security_updates.status=enabled \ @@ -142,7 +159,7 @@ echo " Dependabot alerts, security updates, and secret scanning enabled." # 5. Create CI/CD workflow stubs echo "" -echo "[5/6] Creating CI/CD workflow files..." +echo "[5/5] Creating CI/CD workflow files..." REPO_DIR="${HOME}/Documents/repositories/${REPO_NAME}" if [[ ! -d "${REPO_DIR}" ]]; then @@ -152,74 +169,58 @@ else mkdir -p "${REPO_DIR}/.github/workflows" if [[ "$STACK_TYPE" == "cdk" ]]; then - cat > "${REPO_DIR}/.github/workflows/ci.yaml" <<'CIEOF' + cat > "${REPO_DIR}/.github/workflows/ci.yaml" < "${REPO_DIR}/.github/workflows/deploy.yaml" <<'CDEOF' + cat > "${REPO_DIR}/.github/workflows/deploy.yaml" < "${REPO_DIR}/.github/workflows/ci.yaml" <<'CIEOF' + cat > "${REPO_DIR}/.github/workflows/ci.yaml" < "${REPO_DIR}/.github/workflows/deploy.yaml" <<'CDEOF' + cat > "${REPO_DIR}/.github/workflows/deploy.yaml" <