Skip to content

Commit 61a6aa8

Browse files
authored
chore(infra): consolidate node services into single Docker image (#8352)
1 parent a6b7bf3 commit 61a6aa8

29 files changed

Lines changed: 252 additions & 236 deletions

File tree

.claude/skills/build-docker-image/SKILL.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ Build and publish Docker images to GHCR (`ghcr.io/hyperlane-xyz/*`).
99

1010
## Workflows
1111

12-
| Workflow | Image(s) | Contents |
13-
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
14-
| `rust-docker.yml` | `hyperlane-agent` | Rust relayer, validator, scraper |
15-
| `monorepo-docker.yml` | `hyperlane-monorepo` | Full TS/Solidity monorepo |
16-
| `node-services-docker.yml` | `hyperlane-rebalancer`, `hyperlane-warp-monitor`, `hyperlane-key-funder`, `hyperlane-ts-relayer`, `hyperlane-offchain-lookup-server` | TypeScript node services |
12+
| Workflow | Image(s) | Contents |
13+
| -------------------------- | ------------------------- | -------------------------------------------------------------------------------- |
14+
| `rust-docker.yml` | `hyperlane-agent` | Rust relayer, validator, scraper |
15+
| `monorepo-docker.yml` | `hyperlane-monorepo` | Full TS/Solidity monorepo |
16+
| `node-services-docker.yml` | `hyperlane-node-services` | All TS node services (rebalancer, warp-monitor, ccip-server, keyfunder, relayer) |
1717

1818
## How to Trigger
1919

@@ -26,7 +26,7 @@ gh workflow run rust-docker.yml --ref <branch>
2626
# Monorepo image
2727
gh workflow run monorepo-docker.yml --ref <branch>
2828

29-
# Node services (all 5 built together)
29+
# Node services (single unified image)
3030
gh workflow run node-services-docker.yml --ref <branch>
3131

3232
# Include arm64 (multi-arch, slower)

.claude/skills/find-docker-image/SKILL.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@ Find existing Hyperlane Docker images on GHCR (`ghcr.io/hyperlane-xyz/*`).
99

1010
## Image Repositories
1111

12-
| Image | Contents |
13-
| ---------------------------------- | -------------------------------- |
14-
| `hyperlane-agent` | Rust relayer, validator, scraper |
15-
| `hyperlane-monorepo` | Full TS/Solidity monorepo |
16-
| `hyperlane-rebalancer` | Rebalancer node service |
17-
| `hyperlane-warp-monitor` | Warp monitor node service |
18-
| `hyperlane-key-funder` | Key funder node service |
19-
| `hyperlane-ts-relayer` | TypeScript relayer node service |
20-
| `hyperlane-offchain-lookup-server` | Offchain lookup server |
12+
| Image | Contents |
13+
| ------------------------- | -------------------------------------------------------------------------------- |
14+
| `hyperlane-agent` | Rust relayer, validator, scraper |
15+
| `hyperlane-monorepo` | Full TS/Solidity monorepo |
16+
| `hyperlane-node-services` | All TS node services (rebalancer, warp-monitor, ccip-server, keyfunder, relayer) |
17+
18+
The `hyperlane-node-services` image is a unified image. Set `SERVICE_NAME` env var at runtime to select which service to run.
2119

2220
## Tag Format
2321

.github/workflows/ghcr-cleanup.yml

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@ jobs:
2626
image-names: >-
2727
hyperlane-agent
2828
hyperlane-monorepo
29-
hyperlane-rebalancer
30-
hyperlane-warp-monitor
31-
hyperlane-key-funder
32-
hyperlane-ts-relayer
33-
hyperlane-offchain-lookup-server
29+
hyperlane-node-services
3430
tag-selection: untagged
3531
cut-off: 1w
3632
dry-run: ${{ github.event.inputs.dry-run || 'false' }}
@@ -43,11 +39,7 @@ jobs:
4339
image-names: >-
4440
hyperlane-agent
4541
hyperlane-monorepo
46-
hyperlane-rebalancer
47-
hyperlane-warp-monitor
48-
hyperlane-key-funder
49-
hyperlane-ts-relayer
50-
hyperlane-offchain-lookup-server
42+
hyperlane-node-services
5143
image-tags: "pr-*"
5244
tag-selection: tagged
5345
cut-off: 1w

.github/workflows/node-services-docker.yml

Lines changed: 52 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
name: Build and Push Node Services Images
1+
name: Build and Push Node Services Image
22

33
on:
44
push:
55
branches: [main]
66
paths:
7-
- 'typescript/Dockerfile.node-service'
8-
- 'typescript/docker-bake.hcl'
7+
- 'typescript/Dockerfile'
8+
- 'typescript/docker-entrypoint.sh'
99
- '.dockerignore'
1010
- '.github/workflows/node-services-docker.yml'
1111
- 'pnpm-lock.yaml'
1212
pull_request:
1313
paths:
14-
- 'typescript/Dockerfile.node-service'
15-
- 'typescript/docker-bake.hcl'
14+
- 'typescript/Dockerfile'
15+
- 'typescript/docker-entrypoint.sh'
1616
- '.dockerignore'
1717
- '.github/workflows/node-services-docker.yml'
1818
- 'pnpm-lock.yaml'
@@ -55,28 +55,19 @@ jobs:
5555
- name: Generate tag data
5656
id: taggen
5757
run: |
58-
set -euo pipefail
59-
TAG_SHA=$(echo '${{ github.event.pull_request.head.sha || github.sha }}' | cut -b 1-7)
60-
TAG_DATE=$(date +'%Y%m%d-%H%M%S')
61-
echo "TAG_SHA_DATE=${TAG_SHA}-${TAG_DATE}" >> $GITHUB_OUTPUT
62-
63-
SANITIZED_REF=$(echo '${{ github.ref_name }}' | tr '/' '-')
64-
if [ "${{ github.ref_type }}" = "tag" ]; then
65-
echo "TAG=${SANITIZED_REF}" >> $GITHUB_OUTPUT
66-
elif [ "${{ github.event_name }}" = "pull_request" ]; then
67-
echo "TAG=pr-${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
68-
else
69-
echo "TAG=${SANITIZED_REF}" >> $GITHUB_OUTPUT
70-
fi
58+
echo "TAG_DATE=$(date +'%Y%m%d-%H%M%S')" >> $GITHUB_OUTPUT
59+
echo "TAG_SHA=$(echo '${{ github.event.pull_request.head.sha || github.sha }}' | cut -b 1-7)" >> $GITHUB_OUTPUT
7160
72-
if [ "${{ github.event.inputs.include_arm64 }}" == "true" ]; then
73-
echo "PLATFORMS=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT
74-
else
75-
echo "PLATFORMS=linux/amd64" >> $GITHUB_OUTPUT
76-
fi
77-
78-
FOUNDRY_VERSION=$(cat solidity/.foundryrc)
79-
echo "FOUNDRY_VERSION=$FOUNDRY_VERSION" >> $GITHUB_OUTPUT
61+
- name: Docker meta
62+
id: meta
63+
uses: docker/metadata-action@v5
64+
with:
65+
images: |
66+
ghcr.io/hyperlane-xyz/hyperlane-node-services
67+
tags: |
68+
type=ref,event=branch
69+
type=ref,event=pr
70+
type=raw,value=${{ steps.taggen.outputs.TAG_SHA }}-${{ steps.taggen.outputs.TAG_DATE }}
8071
8172
- name: Set up Depot CLI
8273
uses: depot/setup-action@v1
@@ -88,53 +79,58 @@ jobs:
8879
username: ${{ github.actor }}
8980
password: ${{ secrets.GITHUB_TOKEN }}
9081

91-
- name: Build and push all images
82+
- name: Read Foundry version
83+
run: |
84+
FOUNDRY_VERSION=$(cat solidity/.foundryrc)
85+
echo "FOUNDRY_VERSION=$FOUNDRY_VERSION" >> $GITHUB_ENV
86+
87+
- name: Determine platforms
88+
id: determine-platforms
89+
run: |
90+
if [ "${{ github.event.inputs.include_arm64 }}" == "true" ]; then
91+
echo "platforms=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT
92+
else
93+
echo "platforms=linux/amd64" >> $GITHUB_OUTPUT
94+
fi
95+
96+
- name: Build and push
9297
id: build
93-
uses: depot/bake-action@v1
94-
env:
95-
TAG: ${{ steps.taggen.outputs.TAG }}
96-
TAG_SHA_DATE: ${{ steps.taggen.outputs.TAG_SHA_DATE }}
97-
PLATFORMS: ${{ steps.taggen.outputs.PLATFORMS }}
98-
FOUNDRY_VERSION: ${{ steps.taggen.outputs.FOUNDRY_VERSION }}
99-
SERVICE_VERSION: ${{ steps.taggen.outputs.TAG_SHA_DATE }}
98+
uses: depot/build-push-action@v1
10099
with:
101100
project: 057xstl4t6
102-
files: typescript/docker-bake.hcl
101+
context: ./
102+
file: ./typescript/Dockerfile
103103
push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
104104
provenance: false
105+
tags: ${{ steps.meta.outputs.tags }}
106+
labels: ${{ steps.meta.outputs.labels }}
107+
build-args: |
108+
FOUNDRY_VERSION=${{ env.FOUNDRY_VERSION }}
109+
SERVICE_VERSION=${{ steps.taggen.outputs.TAG_SHA }}-${{ steps.taggen.outputs.TAG_DATE }}
110+
platforms: ${{ steps.determine-platforms.outputs.platforms }}
105111

106112
- name: Generate image tags output
107113
id: image-tags
108114
if: always()
109115
run: |
110-
TAG="${{ steps.taggen.outputs.TAG }}"
111-
TAG_SHA_DATE="${{ steps.taggen.outputs.TAG_SHA_DATE }}"
116+
TAG_SHA_DATE="${{ steps.taggen.outputs.TAG_SHA }}-${{ steps.taggen.outputs.TAG_DATE }}"
112117
REGISTRY="ghcr.io/hyperlane-xyz"
118+
TAGS="${REGISTRY}/hyperlane-node-services:${TAG_SHA_DATE}"
113119
114-
TAGS=$(cat << EOF
115-
${REGISTRY}/hyperlane-rebalancer:${TAG_SHA_DATE}
116-
${REGISTRY}/hyperlane-warp-monitor:${TAG_SHA_DATE}
117-
${REGISTRY}/hyperlane-key-funder:${TAG_SHA_DATE}
118-
${REGISTRY}/hyperlane-ts-relayer:${TAG_SHA_DATE}
119-
${REGISTRY}/hyperlane-offchain-lookup-server:${TAG_SHA_DATE}
120-
EOF
121-
)
122120
echo "tags<<EOF" >> $GITHUB_OUTPUT
123121
echo "$TAGS" >> $GITHUB_OUTPUT
124122
echo "EOF" >> $GITHUB_OUTPUT
125123
126124
cat >> $GITHUB_STEP_SUMMARY << EOF
127-
## ⚙️ Node Service Docker Images
125+
## Node Services Docker Image
126+
127+
| Image | Tag |
128+
|-------|-----|
129+
| hyperlane-node-services | \`${TAG_SHA_DATE}\` |
128130
129-
| Service | Tag |
130-
|---------|-----|
131-
| ♻️ rebalancer | \`${TAG_SHA_DATE}\` |
132-
| 🕵️ warp-monitor | \`${TAG_SHA_DATE}\` |
133-
| 🔑 key-funder | \`${TAG_SHA_DATE}\` |
134-
| 🚀 ts-relayer | \`${TAG_SHA_DATE}\` |
135-
| 🔍 offchain-lookup-server | \`${TAG_SHA_DATE}\` |
131+
**Services included:** rebalancer, warp-monitor, ccip-server, keyfunder, relayer
136132
137-
**Full image paths:**
133+
**Full image path:**
138134
\`\`\`
139135
${TAGS}
140136
\`\`\`
@@ -145,8 +141,8 @@ jobs:
145141
uses: ./.github/actions/docker-image-comment
146142
with:
147143
comment_tag: typescript-docker-images
148-
image_name: Node Service Docker Images
149-
emoji: '⚙️'
144+
image_name: Node Services Docker Image
145+
emoji: ''
150146
image_tags: ${{ steps.image-tags.outputs.tags }}
151147
pr_number: ${{ github.event.pull_request.number }}
152148
github_token: ${{ steps.generate-token.outputs.token }}

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ jobs:
164164
with:
165165
base_sha: ${{ needs.set-base-sha.outputs.base_sha }}
166166
head_sha: ${{ needs.set-base-sha.outputs.current_sha }}
167-
path_pattern: '(rust/|\.github/|typescript/docker-bake\.hcl)'
167+
path_pattern: '(rust/|\.github/|typescript/Dockerfile|typescript/docker-entrypoint\.sh)'
168168
path_pattern_only: 'true'
169169

170170
- name: Categorize changes for CLI e2e and VM detection

docs/docker-image-policy.md

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,24 @@ GCR (`gcr.io/abacus-labs-dev`) is deprecated. A 30-day cleanup policy is applied
88

99
## Images
1010

11-
| Workflow | Image(s) | Contents |
12-
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
13-
| `rust-docker.yml` | `hyperlane-agent` | Rust relayer, validator, scraper |
14-
| `monorepo-docker.yml` | `hyperlane-monorepo` | Full TS/Solidity monorepo |
15-
| `node-services-docker.yml` | `hyperlane-rebalancer`, `hyperlane-warp-monitor`, `hyperlane-key-funder`, `hyperlane-ts-relayer`, `hyperlane-offchain-lookup-server` | TypeScript node services |
16-
| `simapp-docker.yml` | `hyperlane-cosmos-simapp` | Cosmos simapp (manual only) |
11+
| Workflow | Image(s) | Contents |
12+
| -------------------------- | ------------------------- | ---------------------------------------------------------------------------------------- |
13+
| `rust-docker.yml` | `hyperlane-agent` | Rust relayer, validator, scraper |
14+
| `monorepo-docker.yml` | `hyperlane-monorepo` | Full TS/Solidity monorepo |
15+
| `node-services-docker.yml` | `hyperlane-node-services` | All TypeScript node services (rebalancer, warp-monitor, ccip-server, keyfunder, relayer) |
16+
| `simapp-docker.yml` | `hyperlane-cosmos-simapp` | Cosmos simapp (manual only) |
17+
18+
### Node Services Image
19+
20+
The `hyperlane-node-services` image is a single unified image containing all TypeScript service bundles. At runtime, set the `SERVICE_NAME` environment variable to select which service to run:
21+
22+
| SERVICE_NAME | Service |
23+
| -------------- | -------------------------- |
24+
| `rebalancer` | Warp route rebalancer |
25+
| `warp-monitor` | Warp route balance monitor |
26+
| `ccip-server` | Offchain lookup server |
27+
| `keyfunder` | Agent key funder |
28+
| `relayer` | TypeScript relayer |
1729

1830
## Tagging
1931

@@ -41,11 +53,11 @@ Cleanup runs weekly (Sunday midnight UTC) via `.github/workflows/ghcr-cleanup.ym
4153

4254
Builds are **not** triggered automatically on every PR or merge to main. They only fire when Docker build infrastructure files change:
4355

44-
| Workflow | Trigger paths |
45-
| -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
46-
| `rust-docker.yml` | `rust/Dockerfile`, `rust/main/Cargo.lock`, `.dockerignore`, workflow file |
47-
| `monorepo-docker.yml` | `Dockerfile`, `docker-entrypoint.sh`, `pnpm-lock.yaml`, `.registryrc`, `.dockerignore`, workflow file |
48-
| `node-services-docker.yml` | `typescript/Dockerfile.node-service`, `typescript/docker-bake.hcl`, `pnpm-lock.yaml`, `.dockerignore`, workflow file |
56+
| Workflow | Trigger paths |
57+
| -------------------------- | ------------------------------------------------------------------------------------------------------------ |
58+
| `rust-docker.yml` | `rust/Dockerfile`, `rust/main/Cargo.lock`, `.dockerignore`, workflow file |
59+
| `monorepo-docker.yml` | `Dockerfile`, `docker-entrypoint.sh`, `pnpm-lock.yaml`, `.registryrc`, `.dockerignore`, workflow file |
60+
| `node-services-docker.yml` | `typescript/Dockerfile`, `typescript/docker-entrypoint.sh`, `pnpm-lock.yaml`, `.dockerignore`, workflow file |
4961

5062
Additionally:
5163

@@ -90,5 +102,5 @@ gh run list --workflow=rust-docker.yml --limit=1 --json url --jq '.[].url'
90102
| `typescript/infra/config/docker.ts` | Registry config, image names, deployed tags |
91103
| `.github/workflows/ghcr-cleanup.yml` | GHCR retention workflow |
92104
| `typescript/infra/src/utils/gcloud.ts` | `checkDockerTagExists()`, `warnIfPrTag()` |
93-
| `typescript/docker-bake.hcl` | Docker Bake config for node services |
105+
| `typescript/docker-entrypoint.sh` | Entrypoint script for service selection |
94106
| `rust/main/helm/hyperlane-agent/values.yaml` | Helm chart defaults |

0 commit comments

Comments
 (0)