Skip to content

Improvements Based on First Round of Feedback #197

Improvements Based on First Round of Feedback

Improvements Based on First Round of Feedback #197

name: PR Commit Stage
# This workflow is triggered on pull requests to the main branch
on:
pull_request:
branches: [ main ]
jobs:
checkout_and_lint:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '14.17'
- name: Cache Node Modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
# Cache dependencies using a key for installers and dependencies
path: node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Install Dependencies
run: npm install
- name: Run Lint
run: npm run lint
test:
needs: checkout_and_lint
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '14.17'
- name: Cache Node Modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
# Cache dependencies using a key for installers and dependencies
path: node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: No-op Test Placeholder
run: echo "No tests implemented yet"
build-image:
needs: [ test ]
runs-on: ubuntu-latest
outputs:
imageURL: ${{ steps.generate_ghcr_tag.outputs.image }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: generate ghcr tag
id: generate_ghcr_tag
run: |
SHORT_SHA=${{ github.event.pull_request.head.sha }}
SHORT_SHA=${SHORT_SHA:0:7} # Using Bash substring extraction
echo "image=ghcr.io/${{ github.repository }}/nextjs-app:${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }}-$SHORT_SHA" >> $GITHUB_OUTPUT
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
build-args: |
MAPBOX_API_TOKEN=${{secrets.MAPBOX_API_TOKEN}}
platforms: linux/amd64
context: .
file: ./Dockerfile
push: true
tags: |
${{ steps.generate_ghcr_tag.outputs.image }}
- name: Comment PR with GHCR details
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
📦 New Docker Image has been pushed to Github Container Registry (GHCR)
Image: ${{ steps.generate_ghcr_tag.outputs.image }}
setup-copilot:
runs-on: ubuntu-latest
outputs:
copilot-path: ${{ steps.setup.outputs.copilot-path }}
steps:
- name: Create a temporary directory for downloading
id: setup
run: |
mkdir -p ${{ runner.temp }}/copilot-download
echo "copilot-path=${{ runner.temp }}/copilot-download/copilot" >> $GITHUB_OUTPUT
- name: Download Copilot CLI
run: |
curl -Lo ${{ steps.setup.outputs.copilot-path }} https://github.com/aws/copilot-cli/releases/latest/download/copilot-linux
- name: Make binary executable
run: chmod +x ${{ steps.setup.outputs.copilot-path }}
- name: Upload Copilot CLI to Artifacts
uses: actions/upload-artifact@v4
with:
name: copilot-cli
path: ${{ steps.setup.outputs.copilot-path }}
init-copilot:
runs-on: ubuntu-latest
needs: setup-copilot
steps:
- name: Download Copilot CLI from Artifacts
uses: actions/download-artifact@v4
with:
name: copilot-cli
path: /usr/local/bin
- name: Ensure Copilot is executable
run: chmod +x /usr/local/bin/copilot
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/ocean-watch-deployer
role-duration-seconds: 1800
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: Check for existing Copilot application
id: check_app
run: |
if copilot app ls | grep -qw "ocean-watch"; then
echo "App exists."
echo "app_exists=true" >> $GITHUB_ENV
else
echo "App does not exist."
echo "app_exists=false" >> $GITHUB_ENV
fi
- name: Notify User That A Bootstrap is Needed
if: env.app_exists == 'false'
run: |
echo "Manually run the `bootstrap-copilot-project` workflow first."
exit 1
shell: bash
create-copilot-environment:
needs: [ init-copilot ]
runs-on: ubuntu-latest
steps:
- name: Download Copilot CLI from Artifacts
uses: actions/download-artifact@v4
with:
name: copilot-cli
path: /usr/local/bin
- name: Ensure Copilot is executable
run: chmod +x /usr/local/bin/copilot
- name: Configure AWS Credentials
id: creds
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/ocean-watch-deployer
role-duration-seconds: 1800
output-credentials: true
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: Check for existing Copilot environment
id: check_environment
run: |
if copilot env show --name ${{ github.head_ref }} --json; then
echo "Environment exists."
echo "environment_exists=true" >> $GITHUB_ENV
else
echo "Environment does not exist."
echo "environment_exists=false" >> $GITHUB_ENV
fi
- name: Initialize Environment
id: init-environment
if: env.environment_exists == 'false'
run: |
if ! copilot env init --name ${{ github.head_ref }} --app ocean-watch --default-config --region ${{ secrets.AWS_REGION }} --aws-session-token ${{ steps.creds.outputs.aws-session-token }}; then
echo "Failed to initialize environment."
exit 1
fi
- name: Deploy Environment
run: |
if ! copilot env deploy --name ${{ github.head_ref }} --app ocean-watch; then
echo "Failed to deploy environment."
exit 1
fi
- name: Commit copilot environment configs
run: |
git config --global user.email "[email protected]"
git config --global user.name "GitHub Action"
git add -A # Adds all changes, including untracked files
git diff-index --quiet HEAD || git commit -m "AWS copilot environment configs generated by GitHub Actions Bot"
git push origin HEAD:${{ github.head_ref }} # Ensure you're pushing to the correct branch
deploy:
needs: [ build-image, create-copilot-environment ]
runs-on: ubuntu-latest
steps:
- name: Download Copilot CLI from Artifacts
uses: actions/download-artifact@v4
with:
name: copilot-cli
path: /usr/local/bin
- name: Ensure Copilot is executable
run: chmod +x /usr/local/bin/copilot
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/ocean-watch-deployer
role-duration-seconds: 1800
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: Handle AWS Secret --> GHCR Access
run: |
# Install jq if it's not available
sudo apt-get update && sudo apt-get install -y jq
SECRET_NAME="ocean-watch-env-${{ github.head_ref }}"
SECRET_VALUE='{"username":"${{ secrets.AWS_GHCR_ACCESS_TOKEN }}","password":"${{ secrets.AWS_GHCR_ACCESS_TOKEN }}"}'
TAGS='Key=project,Value=OceanWatch Key=program,Value=Oceans Key=application,Value=OceanWatch Key=copilot-application,Value=ocean-watch Key=copilot-environment,Value=${{ github.head_ref }}'
DESCRIPTION='Read-only access to github container registry'
# Check if the AWS Secret already exists
SECRET_INFO=$(aws secretsmanager describe-secret --secret-id $SECRET_NAME || echo "not_found")
if [[ "$SECRET_INFO" == "not_found" ]]; then
echo "Secret does not exist, creating..."
# Create the secret and extract the ARN
SECRET_OUTPUT=$(aws secretsmanager create-secret --name $SECRET_NAME --secret-string "$SECRET_VALUE" --tags $TAGS --description "$DESCRIPTION")
SECRET_ARN=$(echo $SECRET_OUTPUT | jq -r .ARN)
echo "Secret created with ARN: $SECRET_ARN"
else
echo "Secret already exists."
# Extract the ARN from existing secret info
SECRET_ARN=$(echo $SECRET_INFO | jq -r .ARN)
fi
# Store the ARN for use in subsequent steps
echo "SECRET_ARN=$SECRET_ARN" >> $GITHUB_ENV
- name: Check for existing Copilot service deployment
id: check_service_deployment
run: |
if copilot svc show --name nextjs-app --json; then
echo "Service exists."
echo "service_exists=true" >> $GITHUB_ENV
else
echo "Service does not exist."
echo "service_exists=false" >> $GITHUB_ENV
fi
- name: Initialize Service
id: init-service
if: env.service_exists == 'false'
run: |
export IMAGE_LOCATION="${{ needs.build-image.outputs.imageURL }}"
export GHCR_ACCESS="${{ env.SECRET_ARN }}"
if ! copilot svc init --app ocean-watch --name nextjs-app --svc-type "Load Balanced Web Service"; then
echo "Failed to initialize service."
exit 1
fi
- name: Deploy App to AWS using Copilot
run: |
export IMAGE_LOCATION="${{ needs.build-image.outputs.imageURL }}"
export GHCR_ACCESS="${{ env.SECRET_ARN }}"
if ! copilot svc deploy --force --name nextjs-app --env ${{ github.head_ref }}; then
echo "Failed to deploy service."
copilot svc delete --name nextjs-app --env ${{ github.head_ref }} --yes
exit 1
fi
- name: Show Service Information
id: service_info
run: |
ENV_NAME="${{ github.head_ref }}"
SERVICE_JSON=$(copilot svc show --app ocean-watch --name nextjs-app --json)
SERVICE_URL=$(echo "$SERVICE_JSON" | jq -r --arg ENV_NAME "$ENV_NAME" '.routes[] | select(.environment == $ENV_NAME) | .url')
if [[ -z "$SERVICE_URL" || "$SERVICE_URL" == "null" ]]; then
echo "No URL found for the specified environment: $ENV_NAME"
echo "$SERVICE_JSON"
exit 1
fi
echo "url=$SERVICE_URL" >> $GITHUB_OUTPUT
echo "Service URL: $SERVICE_URL"
- name: Comment PR with Service URL
if: github.event_name == 'pull_request'
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
🚀 Deployment successful!
🌐 Your service in environment `${{ github.head_ref }}` is now available at: ${{ steps.service_info.outputs.url }}
- name: Commit Copilot Service Configs
id: commit-changes
run: |
# Capture the original SHA for comparison
ORIG_SHA="${{ github.event.pull_request.head.sha }}"
git config --global user.email "[email protected]"
git config --global user.name "GitHub Action"
git add -A # Adds all changes, including untracked files
git diff-index --quiet HEAD || git commit -m "AWS copilot configs generated by GitHub Actions Bot"
git push origin HEAD:${{ github.head_ref }} # Ensure you're pushing to the correct branch
# Capture the current SHA after potential commit
CURRENT_SHA=$(git rev-parse HEAD)
# Compare SHAs to determine if a new commit was made
if [ "$ORIG_SHA" != "$CURRENT_SHA" ]; then
echo "AWS Copilot Configuration Changes Detected."
echo "config_changes_exist=true" >> $GITHUB_ENV
else
echo "No AWS Copilot Configuration Changes Detected."
echo "config_changes_exist=false" >> $GITHUB_ENV
fi
shell: bash
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Add Updated SHA to GHCR Image
if: env.config_changes_exist == 'true'
run: |
ORIG_SHORT_SHA=${{ github.event.pull_request.head.sha }}
ORIG_SHORT_SHA=${ORIG_SHORT_SHA:0:7} # Using Bash substring extraction
CURRENT_SHORT_SHA=$(git rev-parse HEAD)
CURRENT_SHORT_SHA=${CURRENT_SHORT_SHA:0:7} # Using Bash substring extraction
docker pull ghcr.io/${{ github.repository }}/nextjs-app:${{ github.event.pull_request.head.ref }}-$ORIG_SHORT_SHA
docker tag ghcr.io/${{ github.repository }}/nextjs-app:${{ github.event.pull_request.head.ref }}-$ORIG_SHORT_SHA ghcr.io/${{ github.repository }}/nextjs-app:${{ github.event.pull_request.head.ref }}-$CURRENT_SHORT_SHA
docker push --all-tags ghcr.io/${{ github.repository }}/nextjs-app
shell: bash