mirror of
https://github.com/Sea-Haven-Industries/.github.git
synced 2026-10-07 01:11:58 +00:00
Compare commits
3 commits
7e03d635fb
...
dbe7d25bc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbe7d25bc4 | ||
|
|
814b8d4ba5 | ||
|
|
937e4d8daa |
6 changed files with 404 additions and 5 deletions
84
.github/workflows/cd-cdk.yaml
vendored
84
.github/workflows/cd-cdk.yaml
vendored
|
|
@ -6,11 +6,19 @@ on:
|
|||
node-version:
|
||||
description: "Node.js version to use"
|
||||
type: string
|
||||
default: "22"
|
||||
default: "24"
|
||||
python-version:
|
||||
description: "Python version for Python CDK repos (leave empty for TypeScript CDK)"
|
||||
type: string
|
||||
default: ""
|
||||
dotnet-version:
|
||||
description: "Optional .NET SDK version for repos with .NET assets"
|
||||
type: string
|
||||
default: ""
|
||||
dotnet-publish-project:
|
||||
description: "Optional .NET project path to publish before CDK deploy"
|
||||
type: string
|
||||
default: ""
|
||||
region:
|
||||
description: "AWS region"
|
||||
type: string
|
||||
|
|
@ -23,6 +31,14 @@ on:
|
|||
description: "Enable QEMU for cross-platform Docker builds (arm64 on x86 runners)"
|
||||
type: boolean
|
||||
default: false
|
||||
stack-name:
|
||||
description: "CloudFormation stack name (for pre-flight checks)"
|
||||
type: string
|
||||
default: ""
|
||||
post-deploy-script:
|
||||
description: "Optional path to a script to run after CDK deploy (e.g. web build, S3 sync)"
|
||||
type: string
|
||||
default: ""
|
||||
secrets:
|
||||
deploy-role-arn:
|
||||
description: "OIDC deploy role ARN"
|
||||
|
|
@ -42,6 +58,15 @@ jobs:
|
|||
- uses: docker/setup-qemu-action@v3
|
||||
if: ${{ inputs.enable-qemu }}
|
||||
|
||||
- uses: actions/setup-dotnet@v5
|
||||
if: ${{ inputs.dotnet-version != '' }}
|
||||
with:
|
||||
dotnet-version: ${{ inputs.dotnet-version }}
|
||||
|
||||
- name: Publish .NET project
|
||||
if: ${{ inputs.dotnet-publish-project != '' }}
|
||||
run: dotnet publish ${{ inputs.dotnet-publish-project }} --configuration Release --runtime linux-arm64 --self-contained false --output $(dirname ${{ inputs.dotnet-publish-project }})/bin/Release/net8.0/linux-arm64/publish
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
|
|
@ -52,7 +77,7 @@ jobs:
|
|||
python-version: ${{ inputs.python-version }}
|
||||
|
||||
- name: Install Node dependencies
|
||||
if: ${{ inputs.python-version == '' }}
|
||||
working-directory: ${{ inputs.cdk-dir }}
|
||||
run: npm ci
|
||||
|
||||
- name: Install Python dependencies
|
||||
|
|
@ -67,6 +92,61 @@ jobs:
|
|||
role-to-assume: ${{ secrets.deploy-role-arn }}
|
||||
aws-region: ${{ inputs.region }}
|
||||
|
||||
- name: Pre-flight checks
|
||||
if: ${{ inputs.stack-name != '' }}
|
||||
run: |
|
||||
echo "Pre-flight: checking stack ${{ inputs.stack-name }}..."
|
||||
STATUS=$(aws cloudformation describe-stacks \
|
||||
--stack-name "${{ inputs.stack-name }}" \
|
||||
--query 'Stacks[0].StackStatus' --output text 2>/dev/null || echo "NOT_FOUND")
|
||||
case "$STATUS" in
|
||||
*ROLLBACK_COMPLETE|*FAILED)
|
||||
echo "::error::Stack ${{ inputs.stack-name }} is in $STATUS — manual intervention required."
|
||||
exit 1
|
||||
;;
|
||||
*IN_PROGRESS)
|
||||
echo "::error::Stack ${{ inputs.stack-name }} has an operation in progress ($STATUS) — wait for it to complete."
|
||||
exit 1
|
||||
;;
|
||||
NOT_FOUND)
|
||||
echo "Pre-flight: stack not found — will be created on first deploy."
|
||||
;;
|
||||
*)
|
||||
echo "Pre-flight: stack status is $STATUS — OK to deploy."
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: CDK deploy
|
||||
working-directory: ${{ inputs.cdk-dir }}
|
||||
run: npx -y cdk deploy --all --require-approval never
|
||||
|
||||
- name: Post-deploy script
|
||||
if: ${{ inputs.post-deploy-script != '' }}
|
||||
run: bash ${{ inputs.post-deploy-script }}
|
||||
|
||||
- name: Post-deploy health check
|
||||
if: ${{ inputs.stack-name != '' }}
|
||||
run: |
|
||||
echo "Health check: verifying stack ${{ inputs.stack-name }}..."
|
||||
|
||||
STATUS=$(aws cloudformation describe-stacks \
|
||||
--stack-name "${{ inputs.stack-name }}" \
|
||||
--query 'Stacks[0].StackStatus' --output text)
|
||||
if [[ "$STATUS" != *"COMPLETE" ]] || [[ "$STATUS" == *"ROLLBACK"* ]]; then
|
||||
echo "::error::Stack ${{ inputs.stack-name }} ended in $STATUS after deploy."
|
||||
exit 1
|
||||
fi
|
||||
echo "Stack status: $STATUS"
|
||||
|
||||
echo "Stack outputs:"
|
||||
aws cloudformation describe-stacks \
|
||||
--stack-name "${{ inputs.stack-name }}" \
|
||||
--query 'Stacks[0].Outputs[*].[OutputKey,OutputValue]' --output table
|
||||
|
||||
# Run project-specific health check if it exists
|
||||
if [[ -f scripts/health-check.sh ]]; then
|
||||
echo "Running project health check..."
|
||||
bash scripts/health-check.sh "${{ inputs.stack-name }}" "${{ inputs.region }}"
|
||||
fi
|
||||
|
||||
echo "Health check passed."
|
||||
|
|
|
|||
94
.github/workflows/cd-mobile-ios.yaml
vendored
Normal file
94
.github/workflows/cd-mobile-ios.yaml
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
name: CD — Mobile iOS (TestFlight)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
node-version:
|
||||
description: "Node.js version to use"
|
||||
type: string
|
||||
default: "24"
|
||||
ruby-version:
|
||||
description: "Ruby version for Fastlane"
|
||||
type: string
|
||||
default: "3.3"
|
||||
working-directory:
|
||||
description: "Directory containing the mobile project"
|
||||
type: string
|
||||
default: "."
|
||||
cache-dependency-path:
|
||||
description: "Path to package-lock.json for npm cache"
|
||||
type: string
|
||||
default: "package-lock.json"
|
||||
fastlane-lane:
|
||||
description: "Fastlane lane to run"
|
||||
type: string
|
||||
default: "ios beta"
|
||||
region:
|
||||
description: "AWS region (for match S3 storage)"
|
||||
type: string
|
||||
default: "us-east-1"
|
||||
timeout-minutes:
|
||||
description: "Job timeout in minutes"
|
||||
type: number
|
||||
default: 45
|
||||
secrets:
|
||||
deploy-role-arn:
|
||||
description: "OIDC deploy role ARN (for match S3 access)"
|
||||
required: true
|
||||
match-password:
|
||||
description: "Encryption passphrase for match certificates"
|
||||
required: true
|
||||
asc-key-id:
|
||||
description: "App Store Connect API key ID"
|
||||
required: true
|
||||
asc-issuer-id:
|
||||
description: "App Store Connect API issuer ID"
|
||||
required: true
|
||||
asc-key-content:
|
||||
description: "Base64-encoded App Store Connect API key (.p8)"
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy-ios:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: ${{ inputs.timeout-minutes }}
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ secrets.deploy-role-arn }}
|
||||
aws-region: ${{ inputs.region }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
cache: npm
|
||||
cache-dependency-path: ${{ inputs.cache-dependency-path }}
|
||||
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ inputs.ruby-version }}
|
||||
bundler-cache: true
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
|
||||
- name: Install JS dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install CocoaPods
|
||||
run: bundle exec pod install --project-directory=ios
|
||||
|
||||
- name: Build and upload
|
||||
run: bundle exec fastlane ${{ inputs.fastlane-lane }}
|
||||
env:
|
||||
MATCH_PASSWORD: ${{ secrets.match-password }}
|
||||
ASC_KEY_ID: ${{ secrets.asc-key-id }}
|
||||
ASC_ISSUER_ID: ${{ secrets.asc-issuer-id }}
|
||||
ASC_KEY_CONTENT: ${{ secrets.asc-key-content }}
|
||||
49
.github/workflows/cd-sam.yaml
vendored
49
.github/workflows/cd-sam.yaml
vendored
|
|
@ -53,6 +53,29 @@ jobs:
|
|||
role-to-assume: ${{ secrets.deploy-role-arn }}
|
||||
aws-region: ${{ inputs.region }}
|
||||
|
||||
- name: Pre-flight checks
|
||||
run: |
|
||||
echo "Pre-flight: checking stack ${{ inputs.stack-name }}..."
|
||||
STATUS=$(aws cloudformation describe-stacks \
|
||||
--stack-name "${{ inputs.stack-name }}" \
|
||||
--query 'Stacks[0].StackStatus' --output text 2>/dev/null || echo "NOT_FOUND")
|
||||
case "$STATUS" in
|
||||
*ROLLBACK_COMPLETE|*FAILED)
|
||||
echo "::error::Stack ${{ inputs.stack-name }} is in $STATUS — manual intervention required."
|
||||
exit 1
|
||||
;;
|
||||
*IN_PROGRESS)
|
||||
echo "::error::Stack ${{ inputs.stack-name }} has an operation in progress ($STATUS) — wait for it to complete."
|
||||
exit 1
|
||||
;;
|
||||
NOT_FOUND)
|
||||
echo "Pre-flight: stack not found — will be created on first deploy."
|
||||
;;
|
||||
*)
|
||||
echo "Pre-flight: stack status is $STATUS — OK to deploy."
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: SAM build
|
||||
run: sam build --template ${{ inputs.sam-template }}
|
||||
|
||||
|
|
@ -71,3 +94,29 @@ jobs:
|
|||
--no-fail-on-empty-changeset \
|
||||
--role-arn ${{ inputs.cfn-role-arn }} \
|
||||
$PARAMS
|
||||
|
||||
- name: Post-deploy health check
|
||||
run: |
|
||||
echo "Health check: verifying stack ${{ inputs.stack-name }}..."
|
||||
|
||||
STATUS=$(aws cloudformation describe-stacks \
|
||||
--stack-name "${{ inputs.stack-name }}" \
|
||||
--query 'Stacks[0].StackStatus' --output text)
|
||||
if [[ "$STATUS" != *"COMPLETE" ]] || [[ "$STATUS" == *"ROLLBACK"* ]]; then
|
||||
echo "::error::Stack ${{ inputs.stack-name }} ended in $STATUS after deploy."
|
||||
exit 1
|
||||
fi
|
||||
echo "Stack status: $STATUS"
|
||||
|
||||
echo "Stack outputs:"
|
||||
aws cloudformation describe-stacks \
|
||||
--stack-name "${{ inputs.stack-name }}" \
|
||||
--query 'Stacks[0].Outputs[*].[OutputKey,OutputValue]' --output table
|
||||
|
||||
# Run project-specific health check if it exists
|
||||
if [[ -f scripts/health-check.sh ]]; then
|
||||
echo "Running project health check..."
|
||||
bash scripts/health-check.sh "${{ inputs.stack-name }}" "${{ inputs.region }}"
|
||||
fi
|
||||
|
||||
echo "Health check passed."
|
||||
|
|
|
|||
45
.github/workflows/ci-dotnet.yaml
vendored
Normal file
45
.github/workflows/ci-dotnet.yaml
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
name: CI — .NET
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
dotnet-version:
|
||||
description: ".NET SDK version"
|
||||
type: string
|
||||
default: "8.0.x"
|
||||
working-directory:
|
||||
description: "Directory containing the solution/project"
|
||||
type: string
|
||||
default: "."
|
||||
solution:
|
||||
description: "Solution or project file to build"
|
||||
type: string
|
||||
default: "*.sln"
|
||||
run-tests:
|
||||
description: "Run dotnet test"
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: ${{ inputs.dotnet-version }}
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore ${{ inputs.solution }}
|
||||
|
||||
- name: Build
|
||||
run: dotnet build ${{ inputs.solution }} --no-restore --configuration Release
|
||||
|
||||
- name: Test
|
||||
if: ${{ inputs.run-tests }}
|
||||
run: dotnet test ${{ inputs.solution }} --no-build --configuration Release
|
||||
52
.github/workflows/ci-python-sam.yaml
vendored
52
.github/workflows/ci-python-sam.yaml
vendored
|
|
@ -34,7 +34,11 @@ on:
|
|||
node-version:
|
||||
description: "Node.js version for CDK CLI"
|
||||
type: string
|
||||
default: "22"
|
||||
default: "24"
|
||||
run-conventions-check:
|
||||
description: "Run lightweight conventions audit"
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
|
|
@ -81,6 +85,52 @@ jobs:
|
|||
working-directory: ${{ inputs.cdk-dir }}
|
||||
run: npx -y cdk synth --quiet
|
||||
|
||||
- name: Conventions check
|
||||
if: ${{ inputs.run-conventions-check }}
|
||||
run: |
|
||||
errors=0
|
||||
warn() { echo "::warning::$1"; }
|
||||
fail() { echo "::error::$1"; errors=$((errors + 1)); }
|
||||
|
||||
# README must exist
|
||||
if [[ ! -f README.md ]]; then
|
||||
fail "Missing README.md"
|
||||
fi
|
||||
|
||||
# .gitignore must cover .env
|
||||
if [[ -f .gitignore ]]; then
|
||||
if ! grep -qE '^\.env$|^\.env\b' .gitignore; then
|
||||
fail ".gitignore does not include .env"
|
||||
fi
|
||||
else
|
||||
fail "Missing .gitignore"
|
||||
fi
|
||||
|
||||
# SAM: check template for non-arm64 and missing log retention
|
||||
TEMPLATE="${{ inputs.sam-template }}"
|
||||
if [[ -f "$TEMPLATE" ]]; then
|
||||
if grep -qi 'x86_64' "$TEMPLATE" 2>/dev/null; then
|
||||
fail "SAM template: Lambda using x86_64 instead of arm64"
|
||||
fi
|
||||
if grep -qi 'AWS::Serverless::Function' "$TEMPLATE" 2>/dev/null; then
|
||||
if ! grep -qi 'RetentionInDays\|AWS::Logs::LogGroup' "$TEMPLATE" 2>/dev/null; then
|
||||
warn "SAM template: Lambda found but no explicit log retention"
|
||||
fi
|
||||
fi
|
||||
# Check for secrets in environment variables
|
||||
if grep -qiE '(API_KEY|SECRET|TOKEN|PASSWORD|WEBHOOK)' "$TEMPLATE" 2>/dev/null; then
|
||||
if grep -A5 'Environment:' "$TEMPLATE" | grep -qiE '(API_KEY|SECRET|TOKEN|PASSWORD|WEBHOOK)' 2>/dev/null; then
|
||||
fail "SAM template: possible secret in Lambda environment variables — use Secrets Manager"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $errors -gt 0 ]]; then
|
||||
echo "Conventions check failed with $errors error(s)."
|
||||
exit 1
|
||||
fi
|
||||
echo "Conventions check passed."
|
||||
|
||||
- name: Setup SAM CLI
|
||||
if: ${{ inputs.run-sam-validate }}
|
||||
uses: aws-actions/setup-sam@v2
|
||||
|
|
|
|||
85
.github/workflows/ci-typescript-cdk.yaml
vendored
85
.github/workflows/ci-typescript-cdk.yaml
vendored
|
|
@ -6,7 +6,23 @@ on:
|
|||
node-version:
|
||||
description: "Node.js version to use"
|
||||
type: string
|
||||
default: "22"
|
||||
default: "24"
|
||||
working-directory:
|
||||
description: "Directory to run npm/tsc/cdk commands from"
|
||||
type: string
|
||||
default: "."
|
||||
cache-dependency-path:
|
||||
description: "Path to package-lock.json for npm cache"
|
||||
type: string
|
||||
default: "package-lock.json"
|
||||
dotnet-version:
|
||||
description: "Optional .NET SDK version (set up before CDK synth for repos with .NET assets)"
|
||||
type: string
|
||||
default: ""
|
||||
dotnet-publish-project:
|
||||
description: "Optional .NET project path to publish before CDK synth"
|
||||
type: string
|
||||
default: ""
|
||||
run-typecheck:
|
||||
description: "Run tsc --noEmit"
|
||||
type: boolean
|
||||
|
|
@ -27,6 +43,10 @@ on:
|
|||
description: "Enable QEMU for cross-platform Docker builds (arm64 on x86 runners)"
|
||||
type: boolean
|
||||
default: false
|
||||
run-conventions-check:
|
||||
description: "Run lightweight conventions audit"
|
||||
type: boolean
|
||||
default: true
|
||||
run-sam-validate:
|
||||
description: "Run sam validate --lint (for Node.js SAM repos)"
|
||||
type: boolean
|
||||
|
|
@ -39,37 +59,98 @@ on:
|
|||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
if: ${{ inputs.enable-qemu }}
|
||||
|
||||
- uses: actions/setup-dotnet@v5
|
||||
if: ${{ inputs.dotnet-version != '' }}
|
||||
with:
|
||||
dotnet-version: ${{ inputs.dotnet-version }}
|
||||
|
||||
- name: Publish .NET project
|
||||
if: ${{ inputs.dotnet-publish-project != '' }}
|
||||
run: dotnet publish ${{ inputs.dotnet-publish-project }} --configuration Release --runtime linux-arm64 --self-contained
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
cache: npm
|
||||
cache-dependency-path: ${{ inputs.cache-dependency-path }}
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: npm ci
|
||||
|
||||
- name: Type check
|
||||
if: ${{ inputs.run-typecheck }}
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: npx tsc --noEmit
|
||||
|
||||
- name: Lint
|
||||
if: ${{ inputs.run-lint }}
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: npx eslint .
|
||||
|
||||
- name: Run tests
|
||||
if: ${{ inputs.run-tests }}
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: npx jest
|
||||
|
||||
- name: CDK synth
|
||||
if: ${{ inputs.run-cdk-synth }}
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: npx cdk synth --quiet
|
||||
|
||||
- name: Conventions check
|
||||
if: ${{ inputs.run-conventions-check }}
|
||||
run: |
|
||||
errors=0
|
||||
warn() { echo "::warning::$1"; }
|
||||
fail() { echo "::error::$1"; errors=$((errors + 1)); }
|
||||
|
||||
# README must exist
|
||||
if [[ ! -f README.md ]]; then
|
||||
fail "Missing README.md"
|
||||
fi
|
||||
|
||||
# .gitignore must cover .env
|
||||
if [[ -f .gitignore ]]; then
|
||||
if ! grep -qE '^\.env$|^\.env\b' .gitignore; then
|
||||
fail ".gitignore does not include .env"
|
||||
fi
|
||||
else
|
||||
fail "Missing .gitignore"
|
||||
fi
|
||||
|
||||
# CDK: check synth output for non-arm64 Lambdas and missing log retention
|
||||
if [[ -d cdk.out ]]; then
|
||||
for tmpl in cdk.out/*.template.json; do
|
||||
[[ -f "$tmpl" ]] || continue
|
||||
|
||||
# Check for x86_64 Lambdas
|
||||
if grep -q '"Architectures".*x86_64' "$tmpl" 2>/dev/null; then
|
||||
fail "$(basename "$tmpl"): Lambda using x86_64 instead of arm64"
|
||||
fi
|
||||
|
||||
# Check Lambdas exist but no log retention set
|
||||
if grep -q '"AWS::Lambda::Function"' "$tmpl" 2>/dev/null; then
|
||||
if ! grep -q '"AWS::Logs::LogGroup"' "$tmpl" 2>/dev/null; then
|
||||
warn "$(basename "$tmpl"): Lambda found but no explicit LogGroup with retention"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ $errors -gt 0 ]]; then
|
||||
echo "Conventions check failed with $errors error(s)."
|
||||
exit 1
|
||||
fi
|
||||
echo "Conventions check passed."
|
||||
|
||||
- name: Setup SAM CLI
|
||||
if: ${{ inputs.run-sam-validate }}
|
||||
uses: aws-actions/setup-sam@v2
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue