name: deploy on: push: branches: - main workflow_dispatch: inputs: ttl: description: "TTL for deploy operation (1h | 4h | 24h)" required: false default: 1h jobs: deploy: runs-on: - toolzoo-host env: BACKSTAGE_STACK_NAME: toolzoo-backstage AWS_REGION: us-east-1 APP_STACK_PREFIX: temp-stack steps: - name: Checkout repository uses: actions/checkout@v4 - name: Derive deployment settings shell: bash run: | set -euo pipefail repo_full="${GITHUB_REPOSITORY}" owner_raw="${repo_full%%/*}" repo_raw="${repo_full##*/}" repo_slug="$(printf '%s' "$repo_raw" | tr '[:upper:]' '[:lower:]')" service_slug="${repo_slug}" stack_name="${APP_STACK_PREFIX}-${repo_slug}" account_id="$(aws sts get-caller-identity --query Account --output text)" image_tag="${GITHUB_SHA:-manual}" short_tag="${image_tag:0:12}" registry="${account_id}.dkr.ecr.${AWS_REGION}.amazonaws.com" ecr_repo="temp/${repo_slug}" image_uri="${registry}/${ecr_repo}:${short_tag}" techdocs_bucket="$(aws cloudformation describe-stacks --stack-name "$BACKSTAGE_STACK_NAME" --query "Stacks[0].Outputs[?OutputKey=='TechDocsBucketName'].OutputValue" --output text)" ttl_input="${{ github.event.inputs.ttl }}" case "${ttl_input:-}" in ""|"1h") ttl_hours="1" ;; "4h") ttl_hours="4" ;; "24h") ttl_hours="24" ;; *) ttl_hours="1" ;; esac expires_at="$(date -u -d "+${ttl_hours} hour" +%Y-%m-%dT%H:%M:%SZ)" { echo "TOOLZOO_OWNER=${owner_raw}" echo "TOOLZOO_REPO=${repo_raw}" echo "TOOLZOO_STACK_NAME=${stack_name}" echo "TOOLZOO_SERVICE_NAME=${service_slug}" echo "TOOLZOO_ECR_REPO=${ecr_repo}" echo "TOOLZOO_IMAGE_URI=${image_uri}" echo "TOOLZOO_TECHDOCS_BUCKET=${techdocs_bucket}" echo "TOOLZOO_EXPIRES_AT=${expires_at}" } >> "$GITHUB_ENV" - name: Ensure ECR repository exists shell: bash run: | set -euo pipefail aws ecr describe-repositories --repository-names "$TOOLZOO_ECR_REPO" >/dev/null 2>&1 || \ aws ecr create-repository \ --repository-name "$TOOLZOO_ECR_REPO" \ --image-scanning-configuration scanOnPush=true >/dev/null - name: Log in to ECR shell: bash run: | set -euo pipefail aws ecr get-login-password --region "$AWS_REGION" | \ docker login --username AWS --password-stdin "${TOOLZOO_IMAGE_URI%/*}" - name: Build and push container image shell: bash run: | set -euo pipefail docker build -t "$TOOLZOO_IMAGE_URI" . docker push "$TOOLZOO_IMAGE_URI" - name: Deploy ECS service stack shell: bash run: | set -euo pipefail aws cloudformation deploy \ --stack-name "$TOOLZOO_STACK_NAME" \ --template-file infra/cloudformation/service.yaml \ --capabilities CAPABILITY_NAMED_IAM \ --parameter-overrides \ EnvironmentName="$TOOLZOO_STACK_NAME" \ ContainerImage="$TOOLZOO_IMAGE_URI" \ ServiceName="$TOOLZOO_SERVICE_NAME" \ --tags \ Owner="$TOOLZOO_OWNER" \ Repository="$TOOLZOO_REPO" \ ExpiresAt="$TOOLZOO_EXPIRES_AT" \ ManagedBy="gitea-actions" - name: Write Service URL to catalog metadata shell: bash run: | set -euo pipefail service_url="$(aws cloudformation describe-stacks --stack-name "$TOOLZOO_STACK_NAME" --query "Stacks[0].Outputs[?OutputKey=='ServiceUrl'].OutputValue" --output text)" if [[ -z "$service_url" || "$service_url" == "None" ]]; then echo "ServiceUrl output not found for stack $TOOLZOO_STACK_NAME" exit 1 fi printf '%s\n' \ ' # BEGIN TOOLZOO MANAGED LINK' \ " - url: \"$service_url\"" \ ' title: Service URL' \ ' icon: web' \ ' # END TOOLZOO MANAGED LINK' \ > /tmp/toolzoo-managed-link-item.yaml awk ' BEGIN { in_block=0 } /^ # BEGIN TOOLZOO MANAGED LINK$/ { in_block=1; next } /^ # END TOOLZOO MANAGED LINK$/ { in_block=0; next } in_block==1 { next } { print } ' catalog-info.yaml > /tmp/catalog-info.yaml.base if grep -q '^ links:$' /tmp/catalog-info.yaml.base; then awk ' BEGIN { inserted=0 } { print } /^ links:$/ && inserted==0 { while ((getline l < "/tmp/toolzoo-managed-link-item.yaml") > 0) { print l } close("/tmp/toolzoo-managed-link-item.yaml") inserted=1 } ' /tmp/catalog-info.yaml.base > /tmp/catalog-info.yaml.updated else awk ' BEGIN { inserted=0 } /^ labels:$/ && inserted==0 { print " links:" while ((getline l < "/tmp/toolzoo-managed-link-item.yaml") > 0) { print l } close("/tmp/toolzoo-managed-link-item.yaml") inserted=1 } /^ annotations:$/ && inserted==0 { print " links:" while ((getline l < "/tmp/toolzoo-managed-link-item.yaml") > 0) { print l } close("/tmp/toolzoo-managed-link-item.yaml") inserted=1 } /^spec:$/ && inserted==0 { print " links:" while ((getline l < "/tmp/toolzoo-managed-link-item.yaml") > 0) { print l } close("/tmp/toolzoo-managed-link-item.yaml") inserted=1 } { print } ' /tmp/catalog-info.yaml.base > /tmp/catalog-info.yaml.updated fi mv /tmp/catalog-info.yaml.updated catalog-info.yaml - name: Commit catalog metadata update shell: bash run: | set -euo pipefail if git diff --quiet -- catalog-info.yaml; then echo "No catalog-info.yaml changes to commit." exit 0 fi git config user.name "toolzoo-bot" git config user.email "toolzoo-bot@local" git add catalog-info.yaml git commit -m "Update catalog service URL from deployment output" git push origin HEAD:main - name: Publish TechDocs to S3 shell: bash run: | set -euo pipefail python3 -m venv .techdocs-venv . .techdocs-venv/bin/activate python -m pip install --upgrade pip python -m pip install mkdocs-techdocs-core npx -y @techdocs/cli generate --no-docker npx -y @techdocs/cli publish \ --publisher-type awsS3 \ --storage-name "$TOOLZOO_TECHDOCS_BUCKET" \ --entity "default/Component/${TOOLZOO_REPO}" - name: Emit deployment summary shell: bash run: | set -euo pipefail : "${GITHUB_STEP_SUMMARY:=/tmp/toolzoo-step-summary}" service_url="$(aws cloudformation describe-stacks --stack-name "$TOOLZOO_STACK_NAME" --query "Stacks[0].Outputs[?OutputKey=='ServiceUrl'].OutputValue" --output text)" { echo "Deployment stack: $TOOLZOO_STACK_NAME" echo "Image: $TOOLZOO_IMAGE_URI" echo "TechDocs bucket: $TOOLZOO_TECHDOCS_BUCKET" echo "ExpiresAt (UTC): $TOOLZOO_EXPIRES_AT" echo "Service URL: $service_url" } | tee -a "$GITHUB_STEP_SUMMARY"