Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 8 additions & 60 deletions .github/workflows/approve-override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,72 +31,20 @@ jobs:
approve:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: scripts

- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT_ID }}:role/javabin-ci-override-approver
aws-region: ${{ env.AWS_REGION }}

- name: Write override token
env:
OVERRIDE_REPO: ${{ inputs.repo }}
OVERRIDE_SHA: ${{ inputs.sha }}
OVERRIDE_REASON: ${{ inputs.reason }}
OVERRIDE_ACTOR: ${{ github.actor }}
run: |
PARAM_NAME="/javabin/platform-overrides/${OVERRIDE_REPO}/${OVERRIDE_SHA}"

python3 << 'PYEOF'
import json, os
value = json.dumps({
"approved_by": os.environ["OVERRIDE_ACTOR"],
"reason": os.environ["OVERRIDE_REASON"],
"approved_at": __import__('datetime').datetime.utcnow().isoformat() + "Z",
"run_id": os.environ.get("GITHUB_RUN_ID", ""),
})
with open("/tmp/override-value.txt", "w") as f:
f.write(value)
PYEOF
run: sh scripts/write-override-token.sh "${{ inputs.repo }}" "${{ inputs.sha }}" "${{ github.actor }}" "${{ inputs.reason }}"

aws ssm put-parameter \
--name "$PARAM_NAME" \
--type String \
--value "$(cat /tmp/override-value.txt)" \
--overwrite

echo "Override token written: ${PARAM_NAME}"

- name: Post to Slack
- name: Notify Slack
env:
OVERRIDE_REPO: ${{ inputs.repo }}
OVERRIDE_SHA: ${{ inputs.sha }}
OVERRIDE_REASON: ${{ inputs.reason }}
OVERRIDE_ACTOR: ${{ github.actor }}
GITHUB_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
export SLACK_WEBHOOK_URL=$(aws ssm get-parameter \
--name /javabin/slack/platform-override-alerts-webhook \
--with-decryption --query Parameter.Value --output text)

python3 << 'PYEOF'
import json, os, urllib.request
webhook_url = os.environ.get("SLACK_WEBHOOK_URL", "")
if not webhook_url:
print("No webhook URL, skipping Slack notification")
exit(0)
run_url = os.environ["GITHUB_RUN_URL"]
repo = os.environ["OVERRIDE_REPO"]
sha = os.environ["OVERRIDE_SHA"]
actor = os.environ["OVERRIDE_ACTOR"]
reason = os.environ["OVERRIDE_REASON"]
payload = json.dumps({
"blocks": [
{"type": "header", "text": {"type": "plain_text", "text": "Risk Override Approved", "emoji": True}},
{"type": "section", "text": {"type": "mrkdwn", "text": f"*Repo:* {repo}\n*SHA:* `{sha}`\n*By:* {actor}\n*Reason:* {reason}"}},
{"type": "section", "text": {"type": "mrkdwn", "text": f"<{run_url}|View Approval Run>"}},
],
"text": f"Risk override approved for {repo}"
}).encode()
req = urllib.request.Request(webhook_url, data=payload, headers={"Content-Type": "application/json"})
urllib.request.urlopen(req)
PYEOF
SSM_WEBHOOK_PARAM: /javabin/slack/platform-override-alerts-webhook
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"
31 changes: 3 additions & 28 deletions .github/workflows/commit-terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,7 @@ jobs:

- name: Check for app.yaml
id: check
run: |
if [ -f app.yaml ]; then
echo "has_yaml=true" >> "$GITHUB_OUTPUT"
else
echo "has_yaml=false" >> "$GITHUB_OUTPUT"
echo "No app.yaml — nothing to commit."
fi
run: echo "has_yaml=$(test -f app.yaml && echo true || echo false)" >> "$GITHUB_OUTPUT"

- uses: hashicorp/setup-terraform@v3
if: steps.check.outputs.has_yaml == 'true'
Expand Down Expand Up @@ -73,27 +67,8 @@ jobs:
AWS_ACCOUNT_ID: ${{ inputs.aws_account_id }}
AWS_REGION: ${{ inputs.aws_region }}
TF_ROOT: ${{ inputs.tf_root }}
run: |
chmod +x .platform/scripts/generate-terraform.sh
.platform/scripts/generate-terraform.sh
run: sh .platform/scripts/generate-terraform.sh

- name: Commit and push generated files
if: steps.check.outputs.has_yaml == 'true'
run: |
git config user.name "javabin-platform[bot]"
git config user.email "platform@javazone.no"

git add "${{ inputs.tf_root }}/"

if git diff --cached --quiet; then
echo "Generated Terraform files are already up to date."
exit 0
fi

git commit -m "[skip ci] Update generated Terraform from app.yaml

Auto-generated by platform CI after successful apply.
Source: app.yaml → generate-terraform.sh"

git push origin HEAD:${{ github.ref_name }}
echo "Committed generated Terraform files."
run: sh .platform/scripts/commit-generated-tf.sh "${{ inputs.tf_root }}" "${{ github.ref_name }}"
4 changes: 1 addition & 3 deletions .github/workflows/detect.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ jobs:
echo "has_pnpm=$(test -f pnpm-lock.yaml && echo true || echo false)" >> "$GITHUB_OUTPUT"
echo "has_eb=$(test -d .elasticbeanstalk && echo true || echo false)" >> "$GITHUB_OUTPUT"
echo "has_cdk=$(test -f cdk.json && echo true || echo false)" >> "$GITHUB_OUTPUT"

if [ -f app.yaml ]; then
APP_NAME=$(grep -m1 '^name:' app.yaml | awk '{print $2}' | tr -d '"'"'" || echo "")
echo "app_name=${APP_NAME}" >> "$GITHUB_OUTPUT"
echo "app_name=$(grep -m1 '^name:' app.yaml | awk '{print $2}' | tr -d '\"'"'")" >> "$GITHUB_OUTPUT"
else
echo "app_name=" >> "$GITHUB_OUTPUT"
fi
3 changes: 1 addition & 2 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,4 @@ jobs:

- name: Set output
id: push
run: |
echo "image_uri=${{ steps.tags.outputs.repo }}:${{ steps.tags.outputs.primary_tag }}" >> "$GITHUB_OUTPUT"
run: echo "image_uri=${{ steps.tags.outputs.repo }}:${{ steps.tags.outputs.primary_tag }}" >> "$GITHUB_OUTPUT"
59 changes: 19 additions & 40 deletions .github/workflows/ecs-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,56 +32,35 @@ jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.PLATFORM_APP_ID }}
private-key: ${{ secrets.PLATFORM_APP_PRIVATE_KEY }}
owner: javaBin

- name: Checkout platform scripts
uses: actions/checkout@v4
with:
repository: javaBin/platform
token: ${{ steps.app-token.outputs.token }}
ref: main
sparse-checkout: scripts
path: .platform

- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ inputs.aws_account_id }}:role/javabin-ci-deploy-${{ github.event.repository.name }}
aws-region: ${{ inputs.aws_region }}

- name: Update ECS service
- name: Deploy to ECS
env:
SERVICE: ${{ inputs.service_name || github.event.repository.name }}
CLUSTER: ${{ inputs.cluster_name }}
IMAGE_TAG: ${{ inputs.image_tag }}
ECR_REPO: ${{ github.event.repository.name }}
ACCOUNT_ID: ${{ inputs.aws_account_id }}
REGION: ${{ inputs.aws_region }}
run: |
export ECR_URI="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${ECR_REPO}:${IMAGE_TAG}"

# Get current task definition from the service
TASK_DEF_ARN=$(aws ecs describe-services \
--cluster "$CLUSTER" --services "$SERVICE" \
--query 'services[0].taskDefinition' --output text)

# Fetch and update the task definition
aws ecs describe-task-definition --task-definition "$TASK_DEF_ARN" \
--query 'taskDefinition' > task-def.json

python3 << 'PYEOF'
import json, os
with open('task-def.json') as f:
td = json.load(f)
td['containerDefinitions'][0]['image'] = os.environ['ECR_URI']
for key in ['taskDefinitionArn', 'revision', 'status', 'requiresAttributes',
'compatibilities', 'registeredAt', 'registeredBy', 'deregisteredAt']:
td.pop(key, None)
with open('task-def-new.json', 'w') as f:
json.dump(td, f)
PYEOF

# Register new task definition
NEW_ARN=$(aws ecs register-task-definition \
--cli-input-json file://task-def-new.json \
--query 'taskDefinition.taskDefinitionArn' --output text)
echo "New task definition: $NEW_ARN"

# Update the service
aws ecs update-service \
--cluster "$CLUSTER" --service "$SERVICE" \
--task-definition "$NEW_ARN" > /dev/null

# Wait for deployment to stabilize
echo "Waiting for service to stabilize..."
aws ecs wait services-stable --cluster "$CLUSTER" --services "$SERVICE"
echo "Deployment complete."
run: sh .platform/scripts/ecs-deploy.sh
57 changes: 6 additions & 51 deletions .github/workflows/plan-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
repository: javaBin/platform
token: ${{ steps.app-token.outputs.token }}
ref: main
sparse-checkout: scripts/review-plan.py
sparse-checkout: scripts
path: platform

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

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

- name: Run LLM review
id: review
env:
REVIEW_RESULT_PATH: review-result.json
run: |
python3 platform/scripts/review-plan.py plan-output.txt 2>&1 | tee review-output.txt || true

if [ -f review-result.json ]; then
RISK=$(python3 -c "import json; print(json.load(open('review-result.json')).get('risk', 'FAILED'))")
else
RISK="FAILED"
fi
echo "risk_level=${RISK}" >> "$GITHUB_OUTPUT"
echo "LLM review risk: ${RISK}"
run: sh platform/scripts/extract-review-risk.sh platform/scripts/review-plan.py plan-output.txt

- name: Post review to PR
if: github.event_name == 'pull_request'
Expand Down Expand Up @@ -110,40 +99,6 @@ jobs:
body: body
});

- name: Post to Slack on direct push
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
env:
GITHUB_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
export SLACK_WEBHOOK_URL=$(aws ssm get-parameter \
--name /javabin/slack/platform-resource-alerts-webhook \
--with-decryption --query Parameter.Value --output text)

python3 << 'PYEOF'
import json, os, urllib.request
webhook_url = os.environ.get("SLACK_WEBHOOK_URL", "")
if not webhook_url:
print("No webhook URL, skipping Slack notification")
exit(0)
run_url = os.environ["GITHUB_RUN_URL"]
repo = os.environ.get("GITHUB_REPOSITORY", "unknown")
try:
with open("review-result.json") as f:
result = json.load(f)
risk = result.get("risk", "UNKNOWN")
summary = result.get("summary", "No summary")
except Exception:
risk = "FAILED"
summary = "Review failed"
emoji = {"LOW": "\U0001F7E2", "MEDIUM": "\U0001F7E1", "HIGH": "\U0001F534"}.get(risk, "\u26AA")
payload = json.dumps({
"blocks": [
{"type": "header", "text": {"type": "plain_text", "text": f"{emoji} Plan Review: {risk}", "emoji": True}},
{"type": "section", "text": {"type": "mrkdwn", "text": f"*Repo:* {repo}\n*Summary:* {summary}"}},
{"type": "section", "text": {"type": "mrkdwn", "text": f"<{run_url}|View Workflow Run>"}},
],
"text": f"Plan review: {risk} for {repo}"
}).encode()
req = urllib.request.Request(webhook_url, data=payload, headers={"Content-Type": "application/json"})
urllib.request.urlopen(req)
PYEOF
- name: Alert Slack on HIGH risk
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.review.outputs.risk_level == 'HIGH'
run: sh platform/scripts/notify-high-risk.sh /javabin/slack/platform-override-alerts-webhook "https://github.com/javaBin/platform/actions/workflows/approve-override.yml"
Loading