Skip to content

Create Deployment

Create Deployment #423

Workflow file for this run

name: Create Deployment
on:
workflow_dispatch:
inputs:
force:
description: "Force deployment of all services, regardless of changes"
required: true
type: boolean
default: false
environment:
description: "Environment"
required: true
default: "production"
type: choice
options:
- production
jobs:
detect_changes:
name: Determine service updates
runs-on: ubuntu-latest
outputs:
migrations: ${{ steps.changes.outputs.migrations }}
api: ${{ steps.changes.outputs.api }}
client: ${{ steps.changes.outputs.client }}
build_label: ${{ steps.short_sha.outputs.sha }}
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- id: short_sha
run: |
echo `git rev-parse HEAD`
echo "::set-output name=sha::`git rev-parse --short HEAD`"
- uses: actions/github-script@v4
id: deployments
with:
script: |
const query = `query($owner: String!, $repo: String!, $env: [String!]) {
repository(owner: $owner, name: $repo) {
deployments(environments: $env, last: 100) {
nodes {
commitOid
state
}
}
}
}`;
const variables = {
owner: context.repo.owner,
repo: context.repo.repo,
env: ['production_client', 'production_server', 'production_db_migrations']
}
const data = await github.graphql(query, variables);
console.log(data);
const mostRecentActiveDeployment = data.repository.deployments.nodes.reverse().find(({state}) => state == 'ACTIVE');
if (mostRecentActiveDeployment) {
core.setOutput('active_ref', mostRecentActiveDeployment.commitOid);
console.log('base:', mostRecentActiveDeployment.commitOid);
}
console.log('ref:', '${{github.ref}}');
- uses: dorny/paths-filter@v2
name: get changed packages
id: changes
with:
base: ${{ steps.deployments.outputs.active_ref }}
filters: |
api:
- 'packages/api/**'
client:
- 'packages/client/**'
migrations:
- 'packages/api/migrations/committed/**'
infra:
- 'packages/infra/**'
unmanaged_packages:
- 'packages/!(api|client|infra)/**'
run_migrations:
name: Run DB Migrations
if: github.event.inputs.force == 'true' || needs.detect_changes.outputs.migrations == 'true'
concurrency: ${{github.event.inputs.environment }}_db_migrations
timeout-minutes: 5
runs-on: ubuntu-latest
environment:
name: ${{github.event.inputs.environment }}_db_migrations
url: https://api.seasket.ch/graphiql
needs:
- detect_changes
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Install unbuffer command
run: |
sudo apt-get install expect
# This remote command must succeed with the message "database migrations complete"
- name: Run migrations using ecs exec
run: |
unbuffer aws ecs execute-command --cluster ${{secrets.MAINTENANCE_STACK}} --task ${{secrets.MAINTENANCE_TASK}} --container Default --command "/bin/sh -l /home/migrate.sh ${{github.sha}}" --interactive | tee /dev/stderr | grep "database migrations complete"
deploy_server:
runs-on: ubuntu-latest
name: Deploy Server
if: always() && needs.run_migrations.result != 'failure' && (github.event.inputs.force == 'true' || needs.detect_changes.outputs.api == 'true')
concurrency: ${{github.event.inputs.environment }}_server
timeout-minutes: 25
environment:
name: ${{github.event.inputs.environment }}_server
url: https://api.seasket.ch/graphiql
needs:
- detect_changes
- run_migrations
steps:
- uses: actions/checkout@v2
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- uses: actions/setup-node@v3
with:
node-version: "18.6"
cache: "npm"
- name: Install lerna
run: |
npm install
- name: Install CDK
run: |
npm install -g aws-cdk typescript
- name: Install dependencies
run: |
npx lerna bootstrap --scope=infra
- name: Deploy server
working-directory: ./packages/infra
env:
AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CLIENT_SECRET }}
AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CLIENT_ID }}
BUILD: ${{ needs.detect_changes.outputs.build_label }}
COMMIT: ${{ needs.detect_changes.outputs.build_label }}
UNSPLASH_KEY: ${{ secrets.UNSPLASH_KEY }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
R2_FILE_UPLOADS_BUCKET: ${{ secrets.R2_FILE_UPLOADS_BUCKET }}
CLOUDFLARE_IMAGES_ACCOUNT: ${{ secrets.CLOUDFLARE_IMAGES_ACCOUNT }}
CLOUDFLARE_IMAGES_TOKEN: ${{ secrets.CLOUDFLARE_IMAGES_TOKEN }}
CLOUDFLARE_IMAGES_ACCOUNT_HASH: ${{ secrets.CLOUDFLARE_IMAGES_ACCOUNT_HASH }}
SCREENSHOTTER_FUNCTION_ARN: ${{ secrets.SCREENSHOTTER_FUNCTION_ARN }}
UPLOADS_BASE_URL: ${{ secrets.UPLOADS_BASE_URL }}
RESOURCES_REMOTE: ${{ secrets.RESOURCES_REMOTE }}
TILES_REMOTE: ${{ secrets.TILES_REMOTE }}
TILES_BASE_URL: ${{ secrets.TILES_BASE_URL }}
run: |
cdk deploy --require-approval never -e SeaSketchGraphQLServer
deploy_client:
name: Deploy Client
runs-on: ubuntu-latest
timeout-minutes: 15
if: github.event.inputs.force == 'true' || needs.detect_changes.outputs.client == 'true'
concurrency: ${{github.event.inputs.environment }}_client
environment:
name: ${{github.event.inputs.environment}}_client
url: https://www.seasketch.org/
needs:
- detect_changes
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: "18.6"
cache: "npm"
- name: Install lerna
run: |
npm install
- name: Install dependencies
run: |
npx lerna bootstrap --ci
- name: Build client
working-directory: ./packages/client
env:
SKIP_PREFLIGHT_CHECK: true
REACT_APP_AUTH0_CLIENT_ID: ${{ secrets.REACT_APP_AUTH0_CLIENT_ID }}
REACT_APP_AUTH0_DOMAIN: ${{ secrets.REACT_APP_AUTH0_DOMAIN }}
REACT_APP_AUTH0_SCOPE: "openid profile email permissions"
REACT_APP_AUTH0_AUDIENCE: https://api.seasketch.org
REACT_APP_MAPBOX_ACCESS_TOKEN: ${{ secrets.REACT_APP_MAPBOX_ACCESS_TOKEN }}
REACT_APP_GRAPHQL_ENDPOINT: ${{ secrets.REACT_APP_GRAPHQL_ENDPOINT }}
REACT_APP_BUILD: ${{ needs.detect_changes.outputs.build_label }}
REACT_APP_SENTRY_DSN: ${{ secrets.REACT_APP_SENTRY_DSN }}
REACT_APP_CLOUDFRONT_DOCS_DISTRO: ${{ secrets.REACT_APP_CLOUDFRONT_DOCS_DISTRO }}
REACT_APP_CLOUDFLARE_IMAGES_ENDPOINT: ${{ secrets.REACT_APP_CLOUDFLARE_IMAGES_ENDPOINT }}
run: |
CI=false npm run build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Update deployment
working-directory: ./packages/client
env:
S3_BUCKET: ${{ secrets.S3_CLIENT_BUCKET}}
run: |
ls -l
if [[ -z "$S3_BUCKET" ]]; then
echo "S3_BUCKET could not be found" 1>&2
exit 1
fi
echo "Uploading files to $S3_BUCKET..."
aws s3 sync build $S3_BUCKET \
--acl public-read \
--cache-control max-age=31536000 \
--exclude service-worker.js \
--exclude manifest.json \
--exclude index.html \
--delete
echo "Uploading manifest.json"
aws s3 cp build/manifest.json $S3_BUCKET/manifest.json \
--metadata-directive REPLACE \
--cache-control max-age=0,no-cache,no-store,must-revalidate \
--content-type application/json \
--acl public-read
echo "Uploading index.html"
aws s3 cp build/index.html $S3_BUCKET/index.html \
--metadata-directive REPLACE \
--cache-control max-age=0,no-cache,no-store,must-revalidate \
--content-type text/html \
--acl public-read
echo "Uploading service-worker.js"
aws s3 cp build/service-worker.js $S3_BUCKET/service-worker.js \
--metadata-directive REPLACE \
--cache-control max-age=300 \
--content-type text/javascript \
--acl public-read
aws s3 cp build/service-worker.js.map $S3_BUCKET/service-worker.js.map \
--metadata-directive REPLACE \
--cache-control max-age=300 \
--content-type text/javascript \
--acl public-read
- name: deploy to cloudflare pages
id: wrangler
working-directory: ./packages/client
env:
CLOUDFLARE_ACCOUNT_ID: ${{secrets.CLOUDFLARE_ACCOUNT_ID}}
CLOUDFLARE_API_TOKEN: ${{secrets.CLOUDFLARE_API_TOKEN}}
run: |
npx wrangler pages publish build --project-name=seasketch-next-client --commit-dirty=true --branch=main
- name: Create Sentry release
uses: getsentry/action-release@v1
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
with:
environment: production
version: ${{ needs.detect_changes.outputs.build_label }}
sourcemaps: packages/client/build/static/js/