From 4fee567f2d26842048a68de2278047d6bfc83359 Mon Sep 17 00:00:00 2001 From: Eliot Lim Date: Fri, 3 Jul 2026 15:28:24 +0800 Subject: [PATCH] perf(ci): cache the release image locally during the crane copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ECR copy uses crane (registry-to-registry), so the image never lands on the runner's docker daemon. The static-assets step extracts apps/frontend/dist with `docker run`, so it then pulled the whole ~22-layer image itself — a ~12s extract became ~96s (run 28495610216, step 13). crane hadn't removed the pull, only moved it downstream. Pull the image in the background while crane copies to ECR (independent transfers of the same image), then tag it as the ECR ref locally. By the time the static-assets step runs the image is already on the daemon, so `docker run` does no pull and the extract drops back to ~12s. The pull is now hidden behind the ~37s crane copy instead of adding to the deploy's critical path. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/deploy-ecs.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy-ecs.yml b/.github/workflows/deploy-ecs.yml index 51b9e578b5..ea9522dc83 100644 --- a/.github/workflows/deploy-ecs.yml +++ b/.github/workflows/deploy-ecs.yml @@ -211,16 +211,27 @@ jobs: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} SRC_IMAGE: ${{ steps.check-release-image.outputs.image }} run: | - # Registry-to-registry copy: crane streams the manifest and blobs from - # GHCR straight to ECR without pulling the whole image onto the runner, - # which is far faster than docker pull + tag + push. crane authenticates - # from the ~/.docker/config.json entries the ECR and GHCR login steps - # above already wrote. + # This image is needed in two places: in ECR (for the ECS deploy) and + # on the local docker daemon (for the static-assets extract below). + # They are independent transfers of the same image, so do them at once. + # + # crane copies GHCR->ECR registry-to-registry without touching the + # local daemon, which beats docker pull + push for the ECR leg. But + # that leaves nothing on the daemon, so the later `docker run` to + # extract apps/frontend/dist would pull the whole image itself (~90s). + # So pull it in the background while crane runs, then tag it as the ECR + # ref the static-assets step expects — by then it is already local, so + # `docker run` does no pull. crane authenticates from the docker config + # the ECR and GHCR login steps above already wrote. CRANE_VERSION=0.21.7 curl -fsSL "https://github.com/google/go-containerregistry/releases/download/v${CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz" \ | tar -xzf - -C /usr/local/bin crane chmod +x /usr/local/bin/crane + docker pull "$SRC_IMAGE" & + pull_pid=$! crane copy "$SRC_IMAGE" "$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + wait "$pull_pid" + docker tag "$SRC_IMAGE" "$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" - name: Set up Docker Buildx if: steps.check-release-image.outputs.image == ''