Skip to content

Commit 73029bd

Browse files
Extract inline scripts, add registry OIDC role, clean up (#13)
## Summary - Extract all inline bash/Python from workflows into `scripts/` - Replace Python with shell (curl/jq/aws CLI) everywhere except `review-plan.py` (Bedrock SDK) - Scripts self-resolve SSM webhooks — no more SSM fetches in workflow YAML - Add `javabin-ci-registry` OIDC role so registry invokes team-provisioner Lambda directly - Delete `provision-app.yml` (registry no longer dispatches to platform) - Delete stale `repos/` directory - Plan-review only alerts Slack on HIGH risk with override link ## New scripts | Script | Replaces | |--------|----------| | `notify-slack.sh` | `notify-slack.py`, `notify-block.py`, 4 inline Python blocks | | `check-risk-gate.sh` | inline risk+override logic in tf-apply | | `check-risk-block.sh` | inline risk block logic in platform-ci | | `ecs-deploy.sh` | inline ECS deploy in ecs-deploy.yml | | `update-task-def.sh` | `update-task-def.py` | | `write-override-token.sh` | `create-override-token.py` | | `provision-teams.sh` | `provision-teams.py` | | `drift-check.sh`, `verify-plan.sh`, `run-plan.sh`, `upload-plan.sh` | inline blocks in platform-ci/tf-plan | ## Test plan - [ ] Merge and verify platform CI passes - [ ] Push a team change to registry → verify provision.yml invokes Lambda via new OIDC role
1 parent 178a8e9 commit 73029bd

39 files changed

Lines changed: 584 additions & 947 deletions

.github/workflows/approve-override.yml

Lines changed: 8 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -31,72 +31,20 @@ jobs:
3131
approve:
3232
runs-on: ubuntu-latest
3333
steps:
34+
- uses: actions/checkout@v4
35+
with:
36+
sparse-checkout: scripts
37+
3438
- name: Configure AWS credentials via OIDC
3539
uses: aws-actions/configure-aws-credentials@v4
3640
with:
3741
role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/javabin-ci-override-approver
3842
aws-region: ${{ env.AWS_REGION }}
3943

4044
- name: Write override token
41-
env:
42-
OVERRIDE_REPO: ${{ inputs.repo }}
43-
OVERRIDE_SHA: ${{ inputs.sha }}
44-
OVERRIDE_REASON: ${{ inputs.reason }}
45-
OVERRIDE_ACTOR: ${{ github.actor }}
46-
run: |
47-
PARAM_NAME="/javabin/platform-overrides/${OVERRIDE_REPO}/${OVERRIDE_SHA}"
48-
49-
python3 << 'PYEOF'
50-
import json, os
51-
value = json.dumps({
52-
"approved_by": os.environ["OVERRIDE_ACTOR"],
53-
"reason": os.environ["OVERRIDE_REASON"],
54-
"approved_at": __import__('datetime').datetime.utcnow().isoformat() + "Z",
55-
"run_id": os.environ.get("GITHUB_RUN_ID", ""),
56-
})
57-
with open("/tmp/override-value.txt", "w") as f:
58-
f.write(value)
59-
PYEOF
45+
run: sh scripts/write-override-token.sh "${{ inputs.repo }}" "${{ inputs.sha }}" "${{ github.actor }}" "${{ inputs.reason }}"
6046

61-
aws ssm put-parameter \
62-
--name "$PARAM_NAME" \
63-
--type String \
64-
--value "$(cat /tmp/override-value.txt)" \
65-
--overwrite
66-
67-
echo "Override token written: ${PARAM_NAME}"
68-
69-
- name: Post to Slack
47+
- name: Notify Slack
7048
env:
71-
OVERRIDE_REPO: ${{ inputs.repo }}
72-
OVERRIDE_SHA: ${{ inputs.sha }}
73-
OVERRIDE_REASON: ${{ inputs.reason }}
74-
OVERRIDE_ACTOR: ${{ github.actor }}
75-
GITHUB_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
76-
run: |
77-
export SLACK_WEBHOOK_URL=$(aws ssm get-parameter \
78-
--name /javabin/slack/platform-override-alerts-webhook \
79-
--with-decryption --query Parameter.Value --output text)
80-
81-
python3 << 'PYEOF'
82-
import json, os, urllib.request
83-
webhook_url = os.environ.get("SLACK_WEBHOOK_URL", "")
84-
if not webhook_url:
85-
print("No webhook URL, skipping Slack notification")
86-
exit(0)
87-
run_url = os.environ["GITHUB_RUN_URL"]
88-
repo = os.environ["OVERRIDE_REPO"]
89-
sha = os.environ["OVERRIDE_SHA"]
90-
actor = os.environ["OVERRIDE_ACTOR"]
91-
reason = os.environ["OVERRIDE_REASON"]
92-
payload = json.dumps({
93-
"blocks": [
94-
{"type": "header", "text": {"type": "plain_text", "text": "Risk Override Approved", "emoji": True}},
95-
{"type": "section", "text": {"type": "mrkdwn", "text": f"*Repo:* {repo}\n*SHA:* `{sha}`\n*By:* {actor}\n*Reason:* {reason}"}},
96-
{"type": "section", "text": {"type": "mrkdwn", "text": f"<{run_url}|View Approval Run>"}},
97-
],
98-
"text": f"Risk override approved for {repo}"
99-
}).encode()
100-
req = urllib.request.Request(webhook_url, data=payload, headers={"Content-Type": "application/json"})
101-
urllib.request.urlopen(req)
102-
PYEOF
49+
SSM_WEBHOOK_PARAM: /javabin/slack/platform-override-alerts-webhook
50+
run: sh scripts/notify-slack.sh "Risk Override Approved" "*Repo:* ${{ inputs.repo }}\n*SHA:* \`${{ inputs.sha }}\`\n*By:* ${{ github.actor }}\n*Reason:* ${{ inputs.reason }}" "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" "View Approval Run"

.github/workflows/commit-terraform.yml

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,7 @@ jobs:
3333

3434
- name: Check for app.yaml
3535
id: check
36-
run: |
37-
if [ -f app.yaml ]; then
38-
echo "has_yaml=true" >> "$GITHUB_OUTPUT"
39-
else
40-
echo "has_yaml=false" >> "$GITHUB_OUTPUT"
41-
echo "No app.yaml — nothing to commit."
42-
fi
36+
run: echo "has_yaml=$(test -f app.yaml && echo true || echo false)" >> "$GITHUB_OUTPUT"
4337

4438
- uses: hashicorp/setup-terraform@v3
4539
if: steps.check.outputs.has_yaml == 'true'
@@ -73,27 +67,8 @@ jobs:
7367
AWS_ACCOUNT_ID: ${{ inputs.aws_account_id }}
7468
AWS_REGION: ${{ inputs.aws_region }}
7569
TF_ROOT: ${{ inputs.tf_root }}
76-
run: |
77-
chmod +x .platform/scripts/generate-terraform.sh
78-
.platform/scripts/generate-terraform.sh
70+
run: sh .platform/scripts/generate-terraform.sh
7971

8072
- name: Commit and push generated files
8173
if: steps.check.outputs.has_yaml == 'true'
82-
run: |
83-
git config user.name "javabin-platform[bot]"
84-
git config user.email "platform@javazone.no"
85-
86-
git add "${{ inputs.tf_root }}/"
87-
88-
if git diff --cached --quiet; then
89-
echo "Generated Terraform files are already up to date."
90-
exit 0
91-
fi
92-
93-
git commit -m "[skip ci] Update generated Terraform from app.yaml
94-
95-
Auto-generated by platform CI after successful apply.
96-
Source: app.yaml → generate-terraform.sh"
97-
98-
git push origin HEAD:${{ github.ref_name }}
99-
echo "Committed generated Terraform files."
74+
run: sh .platform/scripts/commit-generated-tf.sh "${{ inputs.tf_root }}" "${{ github.ref_name }}"

.github/workflows/detect.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,8 @@ jobs:
5353
echo "has_pnpm=$(test -f pnpm-lock.yaml && echo true || echo false)" >> "$GITHUB_OUTPUT"
5454
echo "has_eb=$(test -d .elasticbeanstalk && echo true || echo false)" >> "$GITHUB_OUTPUT"
5555
echo "has_cdk=$(test -f cdk.json && echo true || echo false)" >> "$GITHUB_OUTPUT"
56-
5756
if [ -f app.yaml ]; then
58-
APP_NAME=$(grep -m1 '^name:' app.yaml | awk '{print $2}' | tr -d '"'"'" || echo "")
59-
echo "app_name=${APP_NAME}" >> "$GITHUB_OUTPUT"
57+
echo "app_name=$(grep -m1 '^name:' app.yaml | awk '{print $2}' | tr -d '\"'"'")" >> "$GITHUB_OUTPUT"
6058
else
6159
echo "app_name=" >> "$GITHUB_OUTPUT"
6260
fi

.github/workflows/docker-build.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,4 @@ jobs:
8585

8686
- name: Set output
8787
id: push
88-
run: |
89-
echo "image_uri=${{ steps.tags.outputs.repo }}:${{ steps.tags.outputs.primary_tag }}" >> "$GITHUB_OUTPUT"
88+
run: echo "image_uri=${{ steps.tags.outputs.repo }}:${{ steps.tags.outputs.primary_tag }}" >> "$GITHUB_OUTPUT"

.github/workflows/ecs-deploy.yml

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -32,56 +32,35 @@ jobs:
3232
deploy:
3333
runs-on: ubuntu-latest
3434
steps:
35+
- name: Generate GitHub App token
36+
id: app-token
37+
uses: actions/create-github-app-token@v1
38+
with:
39+
app-id: ${{ secrets.PLATFORM_APP_ID }}
40+
private-key: ${{ secrets.PLATFORM_APP_PRIVATE_KEY }}
41+
owner: javaBin
42+
43+
- name: Checkout platform scripts
44+
uses: actions/checkout@v4
45+
with:
46+
repository: javaBin/platform
47+
token: ${{ steps.app-token.outputs.token }}
48+
ref: main
49+
sparse-checkout: scripts
50+
path: .platform
51+
3552
- name: Configure AWS credentials via OIDC
3653
uses: aws-actions/configure-aws-credentials@v4
3754
with:
3855
role-to-assume: arn:aws:iam::${{ inputs.aws_account_id }}:role/javabin-ci-deploy-${{ github.event.repository.name }}
3956
aws-region: ${{ inputs.aws_region }}
4057

41-
- name: Update ECS service
58+
- name: Deploy to ECS
4259
env:
4360
SERVICE: ${{ inputs.service_name || github.event.repository.name }}
4461
CLUSTER: ${{ inputs.cluster_name }}
4562
IMAGE_TAG: ${{ inputs.image_tag }}
4663
ECR_REPO: ${{ github.event.repository.name }}
4764
ACCOUNT_ID: ${{ inputs.aws_account_id }}
4865
REGION: ${{ inputs.aws_region }}
49-
run: |
50-
export ECR_URI="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${ECR_REPO}:${IMAGE_TAG}"
51-
52-
# Get current task definition from the service
53-
TASK_DEF_ARN=$(aws ecs describe-services \
54-
--cluster "$CLUSTER" --services "$SERVICE" \
55-
--query 'services[0].taskDefinition' --output text)
56-
57-
# Fetch and update the task definition
58-
aws ecs describe-task-definition --task-definition "$TASK_DEF_ARN" \
59-
--query 'taskDefinition' > task-def.json
60-
61-
python3 << 'PYEOF'
62-
import json, os
63-
with open('task-def.json') as f:
64-
td = json.load(f)
65-
td['containerDefinitions'][0]['image'] = os.environ['ECR_URI']
66-
for key in ['taskDefinitionArn', 'revision', 'status', 'requiresAttributes',
67-
'compatibilities', 'registeredAt', 'registeredBy', 'deregisteredAt']:
68-
td.pop(key, None)
69-
with open('task-def-new.json', 'w') as f:
70-
json.dump(td, f)
71-
PYEOF
72-
73-
# Register new task definition
74-
NEW_ARN=$(aws ecs register-task-definition \
75-
--cli-input-json file://task-def-new.json \
76-
--query 'taskDefinition.taskDefinitionArn' --output text)
77-
echo "New task definition: $NEW_ARN"
78-
79-
# Update the service
80-
aws ecs update-service \
81-
--cluster "$CLUSTER" --service "$SERVICE" \
82-
--task-definition "$NEW_ARN" > /dev/null
83-
84-
# Wait for deployment to stabilize
85-
echo "Waiting for service to stabilize..."
86-
aws ecs wait services-stable --cluster "$CLUSTER" --services "$SERVICE"
87-
echo "Deployment complete."
66+
run: sh .platform/scripts/ecs-deploy.sh

.github/workflows/plan-review.yml

Lines changed: 6 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
repository: javaBin/platform
4747
token: ${{ steps.app-token.outputs.token }}
4848
ref: main
49-
sparse-checkout: scripts/review-plan.py
49+
sparse-checkout: scripts
5050
path: platform
5151

5252
- name: Configure AWS credentials via OIDC
@@ -56,24 +56,13 @@ jobs:
5656
aws-region: ${{ inputs.aws_region }}
5757

5858
- name: Download plan text from S3
59-
run: |
60-
PREFIX=$(dirname "${{ inputs.plan_key }}")
61-
aws s3 cp "s3://${PLAN_BUCKET}/${PREFIX}/plan-output.txt" plan-output.txt
59+
run: aws s3 cp "s3://${PLAN_BUCKET}/$(dirname "${{ inputs.plan_key }}")/plan-output.txt" plan-output.txt
6260

6361
- name: Run LLM review
6462
id: review
6563
env:
6664
REVIEW_RESULT_PATH: review-result.json
67-
run: |
68-
python3 platform/scripts/review-plan.py plan-output.txt 2>&1 | tee review-output.txt || true
69-
70-
if [ -f review-result.json ]; then
71-
RISK=$(python3 -c "import json; print(json.load(open('review-result.json')).get('risk', 'FAILED'))")
72-
else
73-
RISK="FAILED"
74-
fi
75-
echo "risk_level=${RISK}" >> "$GITHUB_OUTPUT"
76-
echo "LLM review risk: ${RISK}"
65+
run: sh platform/scripts/extract-review-risk.sh platform/scripts/review-plan.py plan-output.txt
7766

7867
- name: Post review to PR
7968
if: github.event_name == 'pull_request'
@@ -110,40 +99,6 @@ jobs:
11099
body: body
111100
});
112101
113-
- name: Post to Slack on direct push
114-
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
115-
env:
116-
GITHUB_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
117-
run: |
118-
export SLACK_WEBHOOK_URL=$(aws ssm get-parameter \
119-
--name /javabin/slack/platform-resource-alerts-webhook \
120-
--with-decryption --query Parameter.Value --output text)
121-
122-
python3 << 'PYEOF'
123-
import json, os, urllib.request
124-
webhook_url = os.environ.get("SLACK_WEBHOOK_URL", "")
125-
if not webhook_url:
126-
print("No webhook URL, skipping Slack notification")
127-
exit(0)
128-
run_url = os.environ["GITHUB_RUN_URL"]
129-
repo = os.environ.get("GITHUB_REPOSITORY", "unknown")
130-
try:
131-
with open("review-result.json") as f:
132-
result = json.load(f)
133-
risk = result.get("risk", "UNKNOWN")
134-
summary = result.get("summary", "No summary")
135-
except Exception:
136-
risk = "FAILED"
137-
summary = "Review failed"
138-
emoji = {"LOW": "\U0001F7E2", "MEDIUM": "\U0001F7E1", "HIGH": "\U0001F534"}.get(risk, "\u26AA")
139-
payload = json.dumps({
140-
"blocks": [
141-
{"type": "header", "text": {"type": "plain_text", "text": f"{emoji} Plan Review: {risk}", "emoji": True}},
142-
{"type": "section", "text": {"type": "mrkdwn", "text": f"*Repo:* {repo}\n*Summary:* {summary}"}},
143-
{"type": "section", "text": {"type": "mrkdwn", "text": f"<{run_url}|View Workflow Run>"}},
144-
],
145-
"text": f"Plan review: {risk} for {repo}"
146-
}).encode()
147-
req = urllib.request.Request(webhook_url, data=payload, headers={"Content-Type": "application/json"})
148-
urllib.request.urlopen(req)
149-
PYEOF
102+
- name: Alert Slack on HIGH risk
103+
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.review.outputs.risk_level == 'HIGH'
104+
run: sh platform/scripts/notify-high-risk.sh /javabin/slack/platform-override-alerts-webhook "https://github.com/javaBin/platform/actions/workflows/approve-override.yml"

0 commit comments

Comments
 (0)