Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
101 changes: 101 additions & 0 deletions .github/actions/docker-build-ecr/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Docker build and push to ECR (with ECR registry cache)
description: |
Build a Docker image and push to AWS ECR, using an ECR-stored layer cache
rather than the GHA cache backend. This is a temporary inline replacement
for `cardstack/gh-actions/.github/workflows/docker-ecr.yml` while we
iterate on which cache backend works best for boxel's large pnpm-fetch
layers (the GHA backend transfers them too slowly to be a net win).

Cache lives at `<ecr-repo>:buildcache` in the same ECR repository as the
image tags. ECR pulls inside the same AWS region are typically much faster
than the GHA cache service.

inputs:
repository:
required: true
description: ECR repository name (without registry prefix)
environment:
required: true
description: Deployment environment (staging or production)
dockerfile:
required: false
default: "Dockerfile"
description: Path to the Dockerfile
context:
required: false
default: "."
description: Docker build context
build-args:
required: false
default: ""
description: Build args passed to docker/build-push-action
platforms:
required: false
default: "linux/amd64"
description: Target platform

outputs:
image:
description: Final image tag (sha-tagged) suitable for ECS task-def update
value: ${{ steps.tags.outputs.tag_sha }}

runs:
using: composite
steps:
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0

- name: Set up AWS role for environment
shell: bash
env:
INPUT_ENVIRONMENT: ${{ inputs.environment }}
run: |
if [ "$INPUT_ENVIRONMENT" = "production" ]; then
echo "AWS_ROLE_ARN=arn:aws:iam::120317779495:role/github" >> "$GITHUB_ENV"
elif [ "$INPUT_ENVIRONMENT" = "staging" ]; then
echo "AWS_ROLE_ARN=arn:aws:iam::680542703984:role/github" >> "$GITHUB_ENV"
else
echo "unrecognized environment: $INPUT_ENVIRONMENT"
exit 1
fi

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
aws-region: us-east-1

- id: login-ecr
uses: aws-actions/amazon-ecr-login@fa648b43de3d4d023bcb3f89ed6940096949c419 # v2.1.5

- id: tags
shell: bash
env:
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
REPOSITORY: ${{ inputs.repository }}
ENVIRONMENT: ${{ inputs.environment }}
run: |
TAG_PREFIX="$REGISTRY/$REPOSITORY"
{
echo "tag_sha=${TAG_PREFIX}:${GITHUB_SHA::7}"
echo "tag_env=${TAG_PREFIX}:${ENVIRONMENT}"
echo "tag_latest=${TAG_PREFIX}:latest"
echo "tag_buildcache=${TAG_PREFIX}:buildcache"
} >> "$GITHUB_OUTPUT"

- name: Build and push
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
with:
context: ${{ inputs.context }}
file: ${{ inputs.dockerfile }}
push: true
# `image-manifest=true,oci-mediatypes=true` is required for the
# registry cache to round-trip through ECR — ECR rejects the
# default cache manifest format buildx uses for other registries.
cache-from: type=registry,ref=${{ steps.tags.outputs.tag_buildcache }}
cache-to: type=registry,ref=${{ steps.tags.outputs.tag_buildcache }},mode=max,image-manifest=true,oci-mediatypes=true
platforms: ${{ inputs.platforms }}
build-args: ${{ inputs.build-args }}
tags: |
${{ steps.tags.outputs.tag_latest }}
${{ steps.tags.outputs.tag_sha }}
${{ steps.tags.outputs.tag_env }}
163 changes: 113 additions & 50 deletions .github/workflows/manual-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,29 @@ jobs:
description: 'Deployment started',
});

# Temporarily inlined (CS-11143): the reusable docker-ecr workflow in
# cardstack/gh-actions caches via `type=gha`, which is a net loss for our
# large pnpm-fetch layers — observed cache transfer ~230s vs ~30s to just
# rerun the fetch. We call a local composite action instead that uses an
# ECR registry cache (same AWS region as the build runner, much faster).
# Once we're confident in the approach we can fold it back into the shared
# action.
build-ai-bot:
name: Build ai-bot Docker image
uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main
secrets: inherit
with:
repository: "boxel-ai-bot-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/ai-bot/Dockerfile"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
outputs:
image: ${{ steps.build.outputs.image }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: build
uses: ./.github/actions/docker-build-ecr
with:
repository: "boxel-ai-bot-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/ai-bot/Dockerfile"

deploy-ai-bot:
needs: [build-ai-bot, post-migrate-db]
Expand All @@ -97,12 +112,20 @@ jobs:

build-bot-runner:
name: Build bot-runner Docker image
uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main
secrets: inherit
with:
repository: "boxel-bot-runner-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/bot-runner/Dockerfile"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
outputs:
image: ${{ steps.build.outputs.image }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: build
uses: ./.github/actions/docker-build-ecr
with:
repository: "boxel-bot-runner-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/bot-runner/Dockerfile"

deploy-bot-runner:
needs: [build-bot-runner, post-migrate-db]
Expand Down Expand Up @@ -142,56 +165,96 @@ jobs:

build-realm-server:
name: Build realm-server Docker image
uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main
secrets: inherit
with:
repository: "boxel-realm-server-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/realm-server/realm-server.Dockerfile"
build-args: |
"realm_server_script=start:${{ inputs.environment }}"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
outputs:
image: ${{ steps.build.outputs.image }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: build
uses: ./.github/actions/docker-build-ecr
with:
repository: "boxel-realm-server-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/realm-server/realm-server.Dockerfile"
build-args: |
"realm_server_script=start:${{ inputs.environment }}"

build-prerender-manager:
name: Build prerender manager Docker image
uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main
secrets: inherit
with:
repository: "boxel-prerender-manager-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/realm-server/prerender-manager.Dockerfile"
build-args: |
"prerender_manager_script=start:prerender-manager"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
outputs:
image: ${{ steps.build.outputs.image }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: build
uses: ./.github/actions/docker-build-ecr
with:
repository: "boxel-prerender-manager-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/realm-server/prerender-manager.Dockerfile"
build-args: |
"prerender_manager_script=start:prerender-manager"

build-prerender:
name: Build prerender Docker image
uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main
secrets: inherit
with:
repository: "boxel-prerender-server-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/realm-server/prerender.Dockerfile"
build-args: |
"prerender_script=start:prerender-${{ inputs.environment }}"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
outputs:
image: ${{ steps.build.outputs.image }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: build
uses: ./.github/actions/docker-build-ecr
with:
repository: "boxel-prerender-server-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/realm-server/prerender.Dockerfile"
build-args: |
"prerender_script=start:prerender-${{ inputs.environment }}"

build-worker:
name: Build worker Docker image
uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main
secrets: inherit
with:
repository: "boxel-worker-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/realm-server/worker.Dockerfile"
build-args: |
"worker_script=start:worker-${{ inputs.environment }}"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
outputs:
image: ${{ steps.build.outputs.image }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: build
uses: ./.github/actions/docker-build-ecr
with:
repository: "boxel-worker-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/realm-server/worker.Dockerfile"
build-args: |
"worker_script=start:worker-${{ inputs.environment }}"

build-pg-migration:
name: Build pg-migration Docker image
uses: cardstack/gh-actions/.github/workflows/docker-ecr.yml@main
secrets: inherit
with:
repository: "boxel-pg-migration-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/postgres/Dockerfile"
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
outputs:
image: ${{ steps.build.outputs.image }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: build
uses: ./.github/actions/docker-build-ecr
with:
repository: "boxel-pg-migration-${{ inputs.environment }}"
environment: ${{ inputs.environment }}
dockerfile: "packages/postgres/Dockerfile"

migrate-db:
# use "deploy-host" and "build-realm-server" as deps so we can run
Expand Down
11 changes: 10 additions & 1 deletion packages/postgres/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,18 @@
RUN apt-get update && apt-get install -y postgresql
RUN npm install -g pnpm@${PNPM_VERSION}
WORKDIR /boxel

# Cache-friendly dependency fetch: this layer only re-runs when the lockfile
# (or patches it references) changes, not on every source edit. `pnpm fetch`
# populates the global pnpm store in $HOME from the lockfile alone, so the
# subsequent `pnpm install --offline` doesn't need the registry.
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
COPY patches/ ./patches
RUN CI=1 pnpm fetch

COPY . .
RUN pnpm install --frozen-lockfile
RUN CI=1 pnpm install -r --offline

WORKDIR /boxel/packages/postgres

CMD ./node_modules/.bin/ts-node --transpileOnly ./scripts/fix-migration-names.ts && ./node_modules/.bin/node-pg-migrate --check-order false --migrations-table migrations up && sleep infinity

Check warning on line 23 in packages/postgres/Dockerfile

View workflow job for this annotation

GitHub Actions / Build pg-migration Docker image

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals More info: https://docs.docker.com/go/dockerfile/rule/json-args-recommended/
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an existing issue, but CS-11154 tracks it.

13 changes: 7 additions & 6 deletions packages/realm-server/prerender-manager.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
RUN apt-get update && apt-get install -y ca-certificates curl unzip jq
RUN npm install -g pnpm@11.0.9

COPY pnpm-lock.yaml ./

# Cache-friendly dependency fetch: this layer only re-runs when the lockfile
# (or patches it references) changes, not on every source edit. `pnpm fetch`
# populates the global pnpm store in $HOME from the lockfile alone, so the
# subsequent `pnpm install --offline` doesn't need the registry.
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
COPY patches/ ./patches
COPY vendor/ ./vendor

ADD . ./

RUN CI=1 pnpm fetch

COPY . ./
RUN CI=1 pnpm install -r --offline

EXPOSE 4222
Expand All @@ -24,4 +25,4 @@
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD curl --fail --silent --show-error --max-time 5 --output /dev/null http://localhost:4222/ || exit 1

CMD pnpm --filter "./packages/realm-server" $prerender_manager_script

Check warning on line 28 in packages/realm-server/prerender-manager.Dockerfile

View workflow job for this annotation

GitHub Actions / Build prerender manager Docker image

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals More info: https://docs.docker.com/go/dockerfile/rule/json-args-recommended/
13 changes: 7 additions & 6 deletions packages/realm-server/prerender.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,15 @@

RUN mkdir -p /home/pptruser/Downloads "${PUPPETEER_CACHE_DIR}"

COPY pnpm-lock.yaml ./

# Cache-friendly dependency fetch: this layer only re-runs when the lockfile
# (or patches it references) changes, not on every source edit. `pnpm fetch`
# populates the global pnpm store in $HOME from the lockfile alone, so the
# subsequent `pnpm install --offline` doesn't need the registry.
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
COPY patches/ ./patches
COPY vendor/ ./vendor

ADD . ./

RUN CI=1 pnpm fetch

COPY . ./
RUN CI=1 pnpm install -r --offline
RUN chown -R pptruser:pptruser /home/pptruser /realm-server

Expand All @@ -78,4 +79,4 @@
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
CMD curl --fail --silent --show-error --max-time 5 --output /dev/null http://localhost:4221/ || exit 1

CMD pnpm --filter "./packages/realm-server" $prerender_script

Check warning on line 82 in packages/realm-server/prerender.Dockerfile

View workflow job for this annotation

GitHub Actions / Build prerender Docker image

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals More info: https://docs.docker.com/go/dockerfile/rule/json-args-recommended/
13 changes: 7 additions & 6 deletions packages/realm-server/realm-server.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@
RUN apt-get update && apt-get install -y ca-certificates curl unzip postgresql jq rsync git
RUN npm install -g pnpm@11.0.9

COPY pnpm-lock.yaml ./

# Cache-friendly dependency fetch: this layer only re-runs when the lockfile
# (or patches it references) changes, not on every source edit. `pnpm fetch`
# populates the global pnpm store in $HOME from the lockfile alone, so the
# subsequent `pnpm install --offline` doesn't need the registry.
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
COPY patches/ ./patches
COPY vendor/ ./vendor

ADD . ./

RUN CI=1 pnpm fetch

COPY . ./
RUN CI=1 pnpm install -r --offline

EXPOSE 3000

CMD pnpm --filter "./packages/realm-server" $realm_server_script

Check warning on line 25 in packages/realm-server/realm-server.Dockerfile

View workflow job for this annotation

GitHub Actions / Build realm-server Docker image

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals More info: https://docs.docker.com/go/dockerfile/rule/json-args-recommended/
Loading
Loading