Skip to content

Commit c281814

Browse files
authored
Add a release workflow for Hyperbridge's indexer package (#763)
1 parent 7bc6606 commit c281814

File tree

4 files changed

+206
-26
lines changed

4 files changed

+206
-26
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
name: "Release @hyperbridge/indexer"
2+
3+
on:
4+
push:
5+
tags:
6+
- "indexer-v*"
7+
8+
jobs:
9+
build-and-release:
10+
runs-on: ubuntu-latest
11+
defaults:
12+
run:
13+
working-directory: sdk
14+
permissions:
15+
contents: write
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Setup Node.js
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: "22"
26+
27+
- name: Setup pnpm
28+
uses: pnpm/action-setup@v2
29+
with:
30+
version: "7"
31+
32+
- name: Install dependencies
33+
run: pnpm install
34+
35+
- name: Build indexer dependencies
36+
run: pnpm --filter="@hyperbridge/indexer^..." build
37+
38+
- name: Build indexer
39+
run: pnpm --filter="@hyperbridge/indexer" build:release
40+
env:
41+
ENV: mainnet
42+
43+
- name: Get version from tag
44+
id: version
45+
run: |
46+
TAG=${GITHUB_REF#refs/tags/indexer-}
47+
echo "version=${TAG}" >> $GITHUB_OUTPUT
48+
49+
- name: Assemble release package
50+
run: |
51+
VERSION="${{ steps.version.outputs.version }}"
52+
RELEASE_DIR="${GITHUB_WORKSPACE}/hyperbridge-indexer-${VERSION}"
53+
mkdir -p "${RELEASE_DIR}/src"
54+
55+
# Copy built output
56+
cp -r packages/indexer/dist "${RELEASE_DIR}/dist"
57+
58+
# Copy configs (JSONs, ABIs, schema, generated yamls, config loader)
59+
cp -r packages/indexer/src/configs "${RELEASE_DIR}/src/configs"
60+
61+
# Copy generation scripts and templates
62+
cp -r packages/indexer/scripts "${RELEASE_DIR}/scripts"
63+
64+
# Create release package.json with deployment scripts
65+
cat > "${RELEASE_DIR}/package.json" <<PKGJSON
66+
{
67+
"name": "@hyperbridge/indexer",
68+
"version": "${VERSION}",
69+
"description": "Hyperbridge indexer release package with deployment config generators",
70+
"main": "dist/index.js",
71+
"type": "module",
72+
"engines": {
73+
"node": ">=22"
74+
},
75+
"scripts": {
76+
"codegen:yamls": "tsx scripts/generate-chain-yamls.ts",
77+
"codegen:compose": "tsx scripts/generate-compose.ts",
78+
"codegen": "npm run codegen:yamls && npm run codegen:compose",
79+
"start": "./scripts/up.sh ./docker/\$ENV",
80+
"down": "./scripts/down.sh ./docker/\$ENV"
81+
},
82+
"dependencies": {
83+
"dotenv": "^16.4.7",
84+
"handlebars": "^4.7.8",
85+
"rpc-websocket-client": "^1.1.4",
86+
"tsx": "^4.19.4",
87+
"viem": "^2.23.5",
88+
"zod": "^3.25.32"
89+
}
90+
}
91+
PKGJSON
92+
93+
# Patch env-file paths for standalone package
94+
# (monorepo uses ../../.env.$ENV, release package uses ./.env.$ENV)
95+
sed -i 's|../../.env.\$ENV|./.env.\$ENV|g' "${RELEASE_DIR}/scripts/up.sh"
96+
sed -i 's|../../.env.\$ENV|./.env.\$ENV|g' "${RELEASE_DIR}/scripts/down.sh"
97+
sed -i 's|../../.env.|./.env.|g' "${RELEASE_DIR}/src/configs/index.ts"
98+
99+
# Create the tarball
100+
cd "${GITHUB_WORKSPACE}"
101+
tar -czf "hyperbridge-indexer-${VERSION}.tar.gz" "hyperbridge-indexer-${VERSION}"
102+
103+
- name: Get previous tag
104+
id: previous-tag
105+
run: |
106+
CURRENT_TAG=${GITHUB_REF#refs/tags/}
107+
PREVIOUS_TAG=$(git tag -l "indexer-v*" --sort=-version:refname | grep -v "^${CURRENT_TAG}$" | head -n1)
108+
echo "previous_tag=${PREVIOUS_TAG}" >> $GITHUB_OUTPUT
109+
echo "current_tag=${CURRENT_TAG}" >> $GITHUB_OUTPUT
110+
111+
- name: Generate changelog
112+
id: changelog
113+
run: |
114+
CURRENT_TAG=${{ steps.previous-tag.outputs.current_tag }}
115+
PREVIOUS_TAG=${{ steps.previous-tag.outputs.previous_tag }}
116+
VERSION=${{ steps.version.outputs.version }}
117+
118+
if [ -z "$PREVIOUS_TAG" ]; then
119+
COMMIT_RANGE="HEAD"
120+
else
121+
COMMIT_RANGE="${PREVIOUS_TAG}..${CURRENT_TAG}"
122+
fi
123+
124+
CHANGELOG=$(git log --pretty=format:"* %s (%h)" $COMMIT_RANGE --reverse | grep -i "indexer" || true)
125+
126+
echo "changelog<<EOF" >> $GITHUB_OUTPUT
127+
echo "## What's Changed" >> $GITHUB_OUTPUT
128+
echo "" >> $GITHUB_OUTPUT
129+
echo "$CHANGELOG" >> $GITHUB_OUTPUT
130+
echo "" >> $GITHUB_OUTPUT
131+
echo "## Usage" >> $GITHUB_OUTPUT
132+
echo "" >> $GITHUB_OUTPUT
133+
echo '```bash' >> $GITHUB_OUTPUT
134+
echo "tar -xzf hyperbridge-indexer-${VERSION}.tar.gz" >> $GITHUB_OUTPUT
135+
echo "cd hyperbridge-indexer-${VERSION}" >> $GITHUB_OUTPUT
136+
echo "npm install" >> $GITHUB_OUTPUT
137+
echo "" >> $GITHUB_OUTPUT
138+
echo "# Set environment and RPC endpoints" >> $GITHUB_OUTPUT
139+
echo "export ENV=mainnet" >> $GITHUB_OUTPUT
140+
echo "# Create .env.mainnet with chain RPC endpoints" >> $GITHUB_OUTPUT
141+
echo "" >> $GITHUB_OUTPUT
142+
echo "# Generate chain yamls and docker compose files" >> $GITHUB_OUTPUT
143+
echo "npm run codegen" >> $GITHUB_OUTPUT
144+
echo '```' >> $GITHUB_OUTPUT
145+
echo "EOF" >> $GITHUB_OUTPUT
146+
147+
- name: Create GitHub Release
148+
uses: softprops/action-gh-release@v2
149+
with:
150+
name: "@hyperbridge/indexer ${{ steps.version.outputs.version }}"
151+
body: ${{ steps.changelog.outputs.changelog }}
152+
files: hyperbridge-indexer-${{ steps.version.outputs.version }}.tar.gz
153+
env:
154+
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}

sdk/packages/indexer/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"start:local": "ENV=local npm run build && docker-compose -f docker/docker-compose.local.yml --env-file ../../.env.local up --force-recreate --remove-orphans && docker-compose -f docker/docker-compose.local.yml --env-file ../../.env.local rm -fsv",
4646
"start:nexus-ci": "ENV=nexus-ci npm run build && docker-compose -f docker/docker-compose.nexus-ci.yml --env-file ../../.env.nexus-ci up --force-recreate --remove-orphans && docker-compose -f docker/docker-compose.nexus-ci.yml --env-file ../../.env.nexus-ci rm -fsv",
4747
"build": "npm run codegen:yamls && npm run codegen:l2-chains && npm run codegen:subql && ./node_modules/.bin/subql build",
48-
"migrate": "tsx scripts/migrate-entity-data.ts RequestV2 ResponseV2 GetRequestV2 AssetTeleportedV2 TokenGatewayAssetTeleportedV2 RelayerV2 RelayerStatsPerChainV2 --drop-source"
48+
"build:release": "tsx scripts/generate-chain-yamls.ts --skip-rpc && npm run codegen:l2-chains && npm run codegen:subql && ./node_modules/.bin/subql build"
4949
},
5050
"dependencies": {
5151
"@ethersproject/abi": "^5.7.0",

sdk/packages/indexer/scripts/generate-chain-yamls.ts

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ import Handlebars from "handlebars"
77
import { RpcWebSocketClient } from "rpc-websocket-client"
88
import { Hex, hexToNumber } from "viem"
99

10-
import { type Configuration, getEnv, getValidChains } from "../src/configs"
10+
import { type Configuration, getConfigs, getEnv, getValidChains } from "../src/configs"
1111

12+
const skipRpc = process.argv.includes("--skip-rpc")
1213
const root = process.cwd()
1314
const currentEnv = getEnv()
14-
const validChains = getValidChains()
15+
const validChains = skipRpc
16+
? new Map<string, Configuration>(Object.entries(getConfigs()))
17+
: getValidChains()
1518

1619
const __filename = fileURLToPath(import.meta.url)
1720
const __dirname = path.dirname(__filename)
@@ -60,13 +63,18 @@ const generateSubstrateYaml = async (chain: string, config: Configuration) => {
6063
const chainTypesConfig = getChainTypesPath(chain)
6164
const endpoints = generateEndpoints(chain)
6265

63-
// Expect comma-separated endpoints in env var
64-
const rpcUrl = process.env[chain.replace(/-/g, "_").toUpperCase()]?.split(",")[0]
65-
const rpc = new RpcWebSocketClient()
66-
await rpc.connect(rpcUrl as string)
67-
const header = (await rpc.call("chain_getHeader", [])) as { number: Hex }
68-
const blockNumber =
69-
currentEnv === "local" || currentEnv === "nexus-ci" ? hexToNumber(header.number) : config.startBlock
66+
let blockNumber: number
67+
if (skipRpc) {
68+
blockNumber = config.startBlock
69+
} else {
70+
// Expect comma-separated endpoints in env var
71+
const rpcUrl = process.env[chain.replace(/-/g, "_").toUpperCase()]?.split(",")[0]
72+
const rpc = new RpcWebSocketClient()
73+
await rpc.connect(rpcUrl as string)
74+
const header = (await rpc.call("chain_getHeader", [])) as { number: Hex }
75+
blockNumber =
76+
currentEnv === "local" || currentEnv === "nexus-ci" ? hexToNumber(header.number) : config.startBlock
77+
}
7078

7179
// Check if this is a Hyperbridge chain (stateMachineId is KUSAMA-4009 or POLKADOT-3367)
7280
const isHyperbridgeChain = ["KUSAMA-4009", "POLKADOT-3367"].includes(config.stateMachineId)
@@ -121,22 +129,27 @@ const generateSubstrateYaml = async (chain: string, config: Configuration) => {
121129
const generateEvmYaml = async (chain: string, config: Configuration) => {
122130
const endpoints = generateEndpoints(chain)
123131

124-
// Expect comma-separated endpoints in env var
125-
const rpcUrl = process.env[chain.replace(/-/g, "_").toUpperCase()]?.split(",")[0]
126-
const response = await fetch(rpcUrl as string, {
127-
method: "POST",
128-
headers: {
129-
accept: "application/json",
130-
"content-type": "application/json",
131-
},
132-
body: JSON.stringify({
133-
id: 1,
134-
jsonrpc: "2.0",
135-
method: "eth_blockNumber",
136-
}),
137-
})
138-
const data = await response.json()
139-
const blockNumber = currentEnv === "local" ? hexToNumber(data.result) : config.startBlock
132+
let blockNumber: number
133+
if (skipRpc) {
134+
blockNumber = config.startBlock
135+
} else {
136+
// Expect comma-separated endpoints in env var
137+
const rpcUrl = process.env[chain.replace(/-/g, "_").toUpperCase()]?.split(",")[0]
138+
const response = await fetch(rpcUrl as string, {
139+
method: "POST",
140+
headers: {
141+
accept: "application/json",
142+
"content-type": "application/json",
143+
},
144+
body: JSON.stringify({
145+
id: 1,
146+
jsonrpc: "2.0",
147+
method: "eth_blockNumber",
148+
}),
149+
})
150+
const data = await response.json()
151+
blockNumber = currentEnv === "local" ? hexToNumber(data.result) : config.startBlock
152+
}
140153

141154
const templateData = {
142155
name: chain,

sdk/packages/indexer/src/configs/config-testnet.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,18 @@
145145
"tokenGateway": "0x1c1e5be83df4a54c7a2230c337e4a3e8b7354b1c",
146146
"intentGatewayV2": "0xbB6BFF5Cf62cf5a091C29D88FAe88b3010F100D1"
147147
}
148+
},
149+
"pharos-atlantic": {
150+
"type": "evm",
151+
"chainId": "688689",
152+
"startBlock": 18128677,
153+
"stateMachineId": "EVM-688689",
154+
"contracts": {
155+
"ethereumHost": "0xED54E9b64043c389173316B6351Bd25491060eA8",
156+
"handlerV1": "0x8B37747BBF8c8485026B9Dc2f8Fb177096EF574f",
157+
"erc6160ext20": "0xeaB7572c5506978C9A4Ff0A0BA5A1291e327B0B8",
158+
"tokenGateway": "0x451bDd8273839AD0Ec7F4Fa798E8B3DABb223fD8",
159+
"intentGatewayV2": "0xb8039832c6c9266F928d038eA49A8a169300C670"
160+
}
148161
}
149162
}

0 commit comments

Comments
 (0)