Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
140 changes: 68 additions & 72 deletions .github/workflows/deploy-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,33 @@ on:
workflow_call:

env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
DEPLOY_QUEUE_THREAD_ID: suwdx19o6mqj
REGISTRY_ALIAS: m8q5m4u3
REPOSITORY: mega
IMAGE_TAG_BASE: campsite-0.1.0-pre-release
RUBY_VERSION: 3.3.4
NODE_VERSION: 18.16.1
BUNDLER_VERSION: 2.3.14

permissions:
id-token: write
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
deploy-api:
# Build amd64 image in CI and push to ECR Public
build-push-amd64:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- cluster: gitmega-com
service: campsite-api-staging-service-njx3hjyx
- cluster: gitmono-com-mega-app
service: campsite-api-service
timeout-minutes: 30
steps:
# - name: GitHub App token
# id: gen-token
# uses: tibdex/github-app-token@v2.1.0
# with:
# app_id: 1157102
# private_key: ${{ secrets.DEPLOYBOT_TOKEN }}

- uses: actions/checkout@v4
# with:
# token: ${{ steps.gen-token.outputs.token }}

# - name: Set git info
# id: git-info
# run: .github/set-git-message
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Configure AWS Credentials
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
Expand All @@ -50,17 +43,62 @@ jobs:
with:
registry-type: public

- name: Build, tag, and push docker image to Amazon ECR Public
working-directory: api
- name: Build & push amd64 image
env:
REGISTRY: ${{ steps.login-ecr-public.outputs.registry }}
REGISTRY_ALIAS: m8q5m4u3
REPOSITORY: mega
IMAGE_TAG: campsite-0.1.0-pre-release
run: |
docker build -t $REGISTRY/$REGISTRY_ALIAS/$REPOSITORY:$IMAGE_TAG . --build-arg RUBY_VERSION=${{env.RUBY_VERSION}} --build-arg NODE_VERSION=${{env.NODE_VERSION}} --build-arg BUNDLER_VERSION=${{env.BUNDLER_VERSION}}
docker push $REGISTRY/$REGISTRY_ALIAS/$REPOSITORY:$IMAGE_TAG
set -euo pipefail
IMAGE_BASE="$REGISTRY/${{ env.REGISTRY_ALIAS }}/${{ env.REPOSITORY }}"
docker buildx build \
--platform linux/amd64 \
--provenance=false \
--sbom=false \
-t "$IMAGE_BASE:${{ env.IMAGE_TAG_BASE }}-amd64" \
--build-arg RUBY_VERSION=${{ env.RUBY_VERSION }} \
--build-arg NODE_VERSION=${{ env.NODE_VERSION }} \
--build-arg BUNDLER_VERSION=${{ env.BUNDLER_VERSION }} \
-f ./api/Dockerfile \
--push ./api

# Merge locally-built arm64 image (pushed separately) with CI-built amd64
manifest:
needs: build-push-amd64
runs-on: ubuntu-latest
steps:
- 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: us-east-1

- name: Login to Amazon ECR Public
id: login-ecr-public
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public

- name: Create & push manifest list
env:
REGISTRY: ${{ steps.login-ecr-public.outputs.registry }}
run: |
IMAGE_BASE="$REGISTRY/${{ env.REGISTRY_ALIAS }}/${{ env.REPOSITORY }}"
docker manifest create "$IMAGE_BASE:${{ env.IMAGE_TAG_BASE }}" \
"$IMAGE_BASE:${{ env.IMAGE_TAG_BASE }}-amd64" \
"$IMAGE_BASE:${{ env.IMAGE_TAG_BASE }}-arm64"
docker manifest push "$IMAGE_BASE:${{ env.IMAGE_TAG_BASE }}"

deploy:
needs: manifest
runs-on: ubuntu-latest
strategy:
matrix:
include:
- cluster: gitmega-com
service: campsite-api-staging-service-njx3hjyx
- cluster: gitmono-com-mega-app
service: campsite-api-service
steps:
- name: Force ECS redeploy
run: |
aws ecs update-service \
Expand All @@ -71,45 +109,3 @@ jobs:
AWS_REGION: ap-southeast-2
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

# - name: Setup Fly
# uses: superfly/flyctl-actions/setup-flyctl@master
# - name: Deploy API
# working-directory: api
# run: flyctl deploy --remote-only -e RELEASE_SHA=${{ github.sha }}

# - name: Bump version and push tag
# id: tag
# uses: campsite/github-tag-action@master
# with:
# github_token: ${{ steps.gen-token.outputs.token }}
# tag_prefix: api-
# default_bump: major
# fetch_all_tags: true

# - name: Notify success
# uses: campsite/campsite-github-action@v1.0.0
# with:
# api_key: ${{ secrets.CAMPBOT_API_KEY }}
# action_type: create_message
# thread_id: ${{ env.DEPLOY_QUEUE_THREAD_ID }}
# content: |
# **🛤️ API deployed**

# [${{ github.actor }}](https://github.com/${{ github.actor }}) deployed ${{ steps.git-info.outputs.escaped-git-message }}

# [${{ steps.git-info.outputs.git-sha }}](https://github.com/campsite/campsite/commit/${{ github.sha }}) · [Run logs](https://github.com/campsite/campsite/actions/runs/${{ github.run_id }})

# - name: Notify failure
# if: ${{ failure() }}
# uses: campsite/campsite-github-action@v1.0.0
# with:
# api_key: ${{ secrets.CAMPBOT_API_KEY }}
# action_type: create_message
# thread_id: ${{ env.DEPLOY_QUEUE_THREAD_ID }}
# content: |
# **🚨 API deploy failed!**

# [${{ github.actor }}](https://github.com/${{ github.actor }}) tried to deploy ${{ steps.git-info.outputs.escaped-git-message }}

# [${{ steps.git-info.outputs.git-sha }}](https://github.com/campsite/campsite/commit/${{ github.sha }}) · [Run logs](https://github.com/campsite/campsite/actions/runs/${{ github.run_id }})
1 change: 0 additions & 1 deletion api/config/environments/demo.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# frozen_string_literal: true

require "active_support/core_ext/integer/time"
require "debug/open_nonstop" unless Sidekiq.server?

Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
Expand Down
70 changes: 70 additions & 0 deletions script/demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# build-images-arm64-local.sh

Build and optionally push a **single-architecture** Docker image for the Campsite API.
The script automatically detects your host CPU and chooses the matching platform (`linux/amd64` or `linux/arm64`).
A multi-arch manifest is **not** created here – that happens later in the CI workflow.

## Features

* Automatic architecture detection (`uname -m`).
* Supports **build-only** (default) or **build & push** via `--push` flag.
* Adds an architecture suffix (`-amd64` or `-arm64`) to the provided tag.
* Pass-through build arguments for Ruby, Node and Bundler versions.

## Prerequisites

1. **Docker Buildx** enabled:
```bash
docker buildx create --use # once per machine
```
2. If you plan to push (`--push`), log in to the target registry, e.g. ECR Public:
```bash
aws ecr-public get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin public.ecr.aws
```

## Usage

```bash
./script/demo/build-images-arm64-local.sh [IMAGE_TAG] [--push]
```

* `IMAGE_TAG` (optional) – base tag without architecture suffix.
Defaults to `campsite-0.1.0-pre-release`.
* `--push` (optional) – push the image to the registry instead of only loading it locally.

### Examples

Build locally for the current architecture:
```bash
./script/demo/build-images-arm64-local.sh
```

Build with a custom tag:
```bash
./script/demo/build-images-arm64-local.sh my-feature-branch
```

Build and push:
```bash
./script/demo/build-images-arm64-local.sh release-1.2.3 --push
```

The resulting image names follow this pattern:
```
public.ecr.aws/m8q5m4u3/mega:<IMAGE_TAG>-<arch>
```
where `<arch>` is `amd64` or `arm64`.

## How It Works

1. Parses CLI flags and determines whether to push.
2. Detects host architecture and sets `--platform` and suffix.
3. Invokes `docker buildx build` with exactly **one** platform, ensuring a single-architecture image.
4. If `--push` is provided, the image is pushed; otherwise it is simply loaded into the local Docker daemon.

## Notes

* This script must be run from the repository root (or adjust the `api` build context path).
* Multi-architecture support is handled in GitHub Actions where the single-arch images are combined into a manifest list.

98 changes: 98 additions & 0 deletions script/demo/build-images-arm64-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env bash
# Build a single-architecture Docker image for Campsite locally.
# The script auto-detects host CPU (arm64 vs amd64) and builds the matching
# architecture image. Multi-arch manifest is created later in CI.
#
# Usage:
# ./script/demo/build-images-arm64-local.sh [IMAGE_TAG] [--push]
# --push Also push to remote registry; if omitted, image is only built and loaded locally.
#
# Requirements:
# - Docker with buildx enabled ("docker buildx create --use")
# - If using --push you must already be logged into the destination registry.

set -euo pipefail

REGISTRY_ALIAS="m8q5m4u3" # Public ECR alias
REGISTRY="public.ecr.aws/${REGISTRY_ALIAS}"
REPOSITORY="mega"

# ----------------------------------------
# Parse CLI arguments
# ----------------------------------------
DO_PUSH=false
POSITIONAL=()
for arg in "$@"; do
case "$arg" in
--push)
DO_PUSH=true
;;
*)
POSITIONAL+=("$arg")
;;
esac
done
set -- "${POSITIONAL[@]}"

# Image tag via first positional arg, or env, or default
IMAGE_TAG_BASE="${IMAGE_TAG:-${1:-campsite-0.1.0-pre-release}}"

# Detect host architecture and map to Docker platform / suffix
ARCH_NATIVE="$(uname -m)"
case "$ARCH_NATIVE" in
arm64|aarch64)
PLATFORM="linux/arm64"
ARCH_SUFFIX="arm64"
;;
x86_64|amd64)
PLATFORM="linux/amd64"
ARCH_SUFFIX="amd64"
;;
*)
echo "Unsupported architecture: $ARCH_NATIVE" >&2
exit 1
;;
esac

# Build-time args (override via env vars if needed)
RUBY_VERSION="${RUBY_VERSION:-3.3.4}"
NODE_VERSION="${NODE_VERSION:-18.16.1}"
BUNDLER_VERSION="${BUNDLER_VERSION:-2.3.14}"

IMAGE_TAG="${IMAGE_TAG_BASE}-${ARCH_SUFFIX}"
IMAGE_NAME="${REGISTRY}/${REPOSITORY}:${IMAGE_TAG}"

echo "Building ${IMAGE_NAME} (${PLATFORM}) …"

# Ensure buildx is available
if ! docker buildx version >/dev/null 2>&1; then
echo "docker buildx not available; please install Docker 19.03+ and enable buildx." >&2
exit 1
fi

# Build image (always --load to ensure single-arch manifest)
if ! docker buildx build \
--platform "$PLATFORM" \
--load \
-t "$IMAGE_NAME" \
api \
--build-arg RUBY_VERSION="$RUBY_VERSION" \
--build-arg NODE_VERSION="$NODE_VERSION" \
--build-arg BUNDLER_VERSION="$BUNDLER_VERSION"; then
echo "docker buildx build failed" >&2
exit 1
fi

echo "Image built and loaded locally: $IMAGE_NAME"

# Optionally push to registry
if $DO_PUSH; then
echo "Pushing $IMAGE_NAME to registry …"
if ! docker push "$IMAGE_NAME"; then
echo "docker push failed" >&2
exit 1
fi
echo "Image pushed successfully: $IMAGE_NAME"
else
echo "Push flag not set; skipping docker push."
fi