mirror of
https://github.com/Sea-Haven-Industries/engineering-handbook.git
synced 2026-09-30 08:03:16 +00:00
Some checks failed
ci / ci / ci (push) Has been cancelled
Make HCP Terraform plus GitHub Actions content CD the default for new workloads, and keep SAM/CDK documented as the remaining path.
270 lines
7.9 KiB
Bash
Executable file
270 lines
7.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Provision a remaining SAM or CDK repo. Do not use this for new app workloads.
|
|
# New deployables use HCP Terraform (see hcp-terraform.md). A Terraform
|
|
# provisioner (workspaces, org-baseline roles, GitHub Environments) is a
|
|
# later PLAT ticket.
|
|
#
|
|
# Usage: ./provision-repo.sh <repo-name> [sam|cdk]
|
|
#
|
|
# Creates: GitHub repo, OIDC deploy role, repo secret, security features,
|
|
# and CI/CD workflow stubs.
|
|
#
|
|
# Workflow callers are generated from the latest released version of the
|
|
# Sea-Haven-Industries/.github repository, or main when no release exists,
|
|
# and pinned to its full commit SHA.
|
|
|
|
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 [[ "${STACK_TYPE}" == "terraform" || "${STACK_TYPE}" == "hcp" ]]; then
|
|
echo "Error: this script does not provision HCP Terraform repos." >&2
|
|
echo "New app workloads follow hcp-terraform.md. Do not create githubdeploy-* here." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${STACK_TYPE}" != "sam" && "${STACK_TYPE}" != "cdk" ]]; then
|
|
echo "Error: stack type must be sam or cdk (remaining path only). Got: ${STACK_TYPE}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
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
|
|
|
|
# GitHub returns tags by recency; compare semantic versions explicitly.
|
|
WORKFLOW_TAGS=$(gh api --paginate "repos/${ORG}/.github/tags?per_page=100" \
|
|
--jq '.[] | select(.name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$")) | .name')
|
|
WORKFLOW_VERSION=""
|
|
WORKFLOW_MAJOR=-1
|
|
WORKFLOW_MINOR=-1
|
|
WORKFLOW_PATCH=-1
|
|
while IFS= read -r tag; do
|
|
[[ -z "$tag" ]] && continue
|
|
|
|
if [[ "$tag" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
|
major=$((10#${BASH_REMATCH[1]}))
|
|
minor=$((10#${BASH_REMATCH[2]}))
|
|
patch=$((10#${BASH_REMATCH[3]}))
|
|
|
|
if (( major > WORKFLOW_MAJOR || \
|
|
(major == WORKFLOW_MAJOR && minor > WORKFLOW_MINOR) || \
|
|
(major == WORKFLOW_MAJOR && minor == WORKFLOW_MINOR && patch > WORKFLOW_PATCH) )); then
|
|
WORKFLOW_VERSION="$tag"
|
|
WORKFLOW_MAJOR="$major"
|
|
WORKFLOW_MINOR="$minor"
|
|
WORKFLOW_PATCH="$patch"
|
|
fi
|
|
fi
|
|
done <<< "$WORKFLOW_TAGS"
|
|
if [[ -z "$WORKFLOW_VERSION" ]]; then
|
|
WORKFLOW_VERSION="main"
|
|
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/5] 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/5] 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/5] 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/5] 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/5] 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@${WORKFLOW_SHA} # ${WORKFLOW_VERSION}
|
|
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@${WORKFLOW_SHA} # ${WORKFLOW_VERSION}
|
|
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@${WORKFLOW_SHA} # ${WORKFLOW_VERSION}
|
|
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@${WORKFLOW_SHA} # ${WORKFLOW_VERSION}
|
|
with:
|
|
stack-name: "${REPO_NAME}"
|
|
cfn-role-arn: "arn:aws:iam::${ACCOUNT_ID}:role/github-cfn-execution-role"
|
|
secrets:
|
|
deploy-role-arn: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
|
CDEOF
|
|
fi
|
|
echo " Created ci.yaml and deploy.yaml pinned to ${WORKFLOW_VERSION}"
|
|
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 the workflow files on a conventional branch"
|
|
echo " 3. Open a PR and verify the ci / ci check passes"
|