engineering-handbook/scripts/provision-repo.sh
Adam Moussa 4b5d39fb91
Add CDK version policy, update Node 24 and GitHub Actions CI/CD (#6)
* Update CDK version policy, Node 24 runtime, and GitHub Actions CI/CD

- Pin blessed aws-cdk-lib version (2.253.1) with upgrade procedure
- Update Lambda runtime default from Node 22 to Node 24
- Rewrite CI/CD page to reflect GitHub Actions reusable workflows
  (was still referencing CodePipeline/CodeBuild)

* Add pre-push hook for npm ci validation

Catches lock file drift locally before it breaks CI. Includes
install instructions in git-workflow.md.

* Add repo provisioning script

Automates the new-repo checklist: GitHub repo creation, OIDC deploy
role, repo secret, security features, CI/CD workflow stubs, and
pre-push hook installation. Supports both SAM and CDK stack types.

* Add shared VpnEc2Instance CDK construct

Reference construct for the VPN-accessible EC2 pattern used by
file-share and forgejo. Includes VPC/subnet lookup, SG, IAM role,
encrypted EBS, and DLM snapshots. Copy into lib/constructs/.

* Add post-deploy health check template

Template script for project-specific health checks. Copy to
scripts/health-check.sh — CD workflows run it automatically.
2026-05-14 18:39:13 -04:00

232 lines
6.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# Provision a new Sea Haven Industries repo with all required infrastructure.
# Usage: ./provision-repo.sh <repo-name> [sam|cdk]
#
# Creates: GitHub repo, OIDC deploy role, repo secret, security features,
# CI/CD workflow stubs, and pre-push hook.
set -euo pipefail
REPO_NAME="${1:?Usage: provision-repo.sh <repo-name> [sam|cdk]}"
STACK_TYPE="${2:-sam}"
ORG="Sea-Haven-Industries"
ACCOUNT_ID="328440206208"
REGION="us-east-1"
OIDC_PROVIDER="arn:aws:iam::${ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com"
ROLE_NAME="githubdeploy-${REPO_NAME}"
if [[ ! "$REPO_NAME" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]]; then
echo "Error: repo name must be kebab-case (lowercase, hyphens only, no leading/trailing hyphens)"
exit 1
fi
echo "=== Provisioning ${ORG}/${REPO_NAME} (${STACK_TYPE}) ==="
# 1. Create GitHub repo
echo ""
echo "[1/6] Creating GitHub repo..."
if gh repo view "${ORG}/${REPO_NAME}" &>/dev/null; then
echo " Repo already exists — skipping."
else
gh repo create "${ORG}/${REPO_NAME}" \
--private \
--description "${REPO_NAME} — Sea Haven Industries" \
--clone=false
echo " Created ${ORG}/${REPO_NAME}"
fi
# 2. Create OIDC deploy role
echo ""
echo "[2/6] Creating IAM deploy role: ${ROLE_NAME}..."
TRUST_POLICY=$(cat <<POLICY
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Federated": "${OIDC_PROVIDER}"},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:${ORG}/${REPO_NAME}:ref:refs/heads/main"
}
}
}]
}
POLICY
)
if aws iam get-role --role-name "${ROLE_NAME}" &>/dev/null; then
echo " Role already exists — skipping."
else
aws iam create-role \
--role-name "${ROLE_NAME}" \
--assume-role-policy-document "${TRUST_POLICY}" \
--tags "Key=Project,Value=${REPO_NAME}" "Key=ManagedBy,Value=provision-script" \
--query 'Role.Arn' --output text
if [[ "$STACK_TYPE" == "cdk" ]]; then
DEPLOY_POLICY=$(cat <<DPOLICY
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::${ACCOUNT_ID}:role/cdk-hnb659fds-*"
}]
}
DPOLICY
)
else
DEPLOY_POLICY=$(cat <<DPOLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::${ACCOUNT_ID}:role/cdk-hnb659fds-*"
},
{
"Effect": "Allow",
"Action": "cloudformation:*",
"Resource": "arn:aws:cloudformation:${REGION}:${ACCOUNT_ID}:stack/${REPO_NAME}/*"
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::${ACCOUNT_ID}:role/${REPO_NAME}-*"
},
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::aws-sam-cli-managed-default-samclisourcebucket-*",
"arn:aws:s3:::aws-sam-cli-managed-default-samclisourcebucket-*/*"
]
}
]
}
DPOLICY
)
fi
aws iam put-role-policy \
--role-name "${ROLE_NAME}" \
--policy-name "deploy" \
--policy-document "${DEPLOY_POLICY}"
echo " Created role with deploy policy."
fi
ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}"
# 3. Set repo secret
echo ""
echo "[3/6] Setting AWS_DEPLOY_ROLE_ARN secret..."
gh secret set AWS_DEPLOY_ROLE_ARN \
--repo "${ORG}/${REPO_NAME}" \
--body "${ROLE_ARN}"
echo " Secret set."
# 4. Enable security features
echo ""
echo "[4/6] Enabling security features..."
gh api "repos/${ORG}/${REPO_NAME}/vulnerability-alerts" -X PUT 2>/dev/null || true
gh api "repos/${ORG}/${REPO_NAME}" -X PATCH \
-f security_and_analysis.dependabot_security_updates.status=enabled \
-f security_and_analysis.secret_scanning.status=enabled \
--silent 2>/dev/null || true
echo " Dependabot alerts, security updates, and secret scanning enabled."
# 5. Create CI/CD workflow stubs
echo ""
echo "[5/6] Creating CI/CD workflow files..."
REPO_DIR="${HOME}/Documents/repositories/${REPO_NAME}"
if [[ ! -d "${REPO_DIR}" ]]; then
echo " Repo not cloned locally — skipping workflow file creation."
echo " Clone it and re-run, or create .github/workflows/ manually."
else
mkdir -p "${REPO_DIR}/.github/workflows"
if [[ "$STACK_TYPE" == "cdk" ]]; then
cat > "${REPO_DIR}/.github/workflows/ci.yaml" <<'CIEOF'
name: CI
on:
pull_request:
branches: [main]
jobs:
ci:
uses: Sea-Haven-Industries/.github/.github/workflows/ci-typescript-cdk.yaml@main
with:
node-version: "24"
CIEOF
cat > "${REPO_DIR}/.github/workflows/deploy.yaml" <<'CDEOF'
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@main
with:
node-version: "24"
secrets:
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
CDEOF
else
cat > "${REPO_DIR}/.github/workflows/ci.yaml" <<'CIEOF'
name: CI
on:
pull_request:
branches: [main]
jobs:
ci:
uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-sam.yaml@main
CIEOF
cat > "${REPO_DIR}/.github/workflows/deploy.yaml" <<'CDEOF'
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
uses: Sea-Haven-Industries/.github/.github/workflows/cd-sam.yaml@main
with:
stack-name: "REPO_PLACEHOLDER"
secrets:
cfn-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
CDEOF
sed -i '' "s/REPO_PLACEHOLDER/${REPO_NAME}/" "${REPO_DIR}/.github/workflows/deploy.yaml"
fi
echo " Created ci.yaml and deploy.yaml"
fi
# 6. Install pre-push hook
echo ""
echo "[6/6] Installing pre-push hook..."
if [[ -d "${REPO_DIR}/.git" ]]; then
HOOK_SRC="${HOME}/Documents/repositories/engineering-handbook/hooks/pre-push"
if [[ -f "$HOOK_SRC" ]]; then
cp "$HOOK_SRC" "${REPO_DIR}/.git/hooks/pre-push"
chmod +x "${REPO_DIR}/.git/hooks/pre-push"
echo " Installed pre-push hook."
else
echo " Hook source not found — skipping."
fi
else
echo " No local .git — skipping."
fi
echo ""
echo "=== Provisioning complete ==="
echo ""
echo "Remaining manual steps:"
echo " 1. Create any Secrets Manager secrets needed (${REPO_NAME}/secret-name)"
echo " 2. Commit and push the workflow files"
echo " 3. Verify CI passes on first PR"
echo " 4. Create a project memory entry"