Skip to content

Commit 9d2822b

Browse files
committed
feat: add ci/cd
1 parent 7b7efaf commit 9d2822b

35 files changed

Lines changed: 1633 additions & 80 deletions

.environment/mainnet/config.example.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44
"chainId": 1
55
},
66
"database": {
7-
"url": "postgresql://user:password@host-leader:5432/dashtec_mainnet?schema=public",
8-
"replicaUrl": "postgresql://user:password@host-replica:5432/dashtec_mainnet?schema=public"
7+
"url": "postgresql://dashtec:dashtec@postgres-mainnet:5432/dashtec",
8+
"replicaUrl": "postgresql://dashtec:dashtec@postgres-mainnet:5432/dashtec"
99
},
1010
"redis": {
11-
"url": "redis://localhost:6379"
11+
"url": "redis://:dashtec@redis-mainnet:6379"
1212
},
1313
"rpc": {
14-
"ethereumUrls": "https://mainnet.infura.io/v3/YOUR_INFURA_KEY"
14+
"ethereumUrls": "<<SECRET: injected from MAINNET_ETHEREUM_RPC_URL>>"
1515
},
1616
"sentinel": {
17-
"proxyUrl": "http://your-aztec-node:8080"
17+
"proxyUrl": "<<SECRET: injected from MAINNET_SENTINEL_PROXY_URL>>"
1818
},
1919
"contracts": {
2020
"rollupAddress": "0x603bb2c05D474794ea97805e8De69bCcFb3bCA12",
@@ -59,7 +59,7 @@
5959
"startBlock": 20000000
6060
},
6161
"app": {
62-
"url": "https://mainnet.dashtec.io",
62+
"url": "https://dashtec.xyz",
6363
"port": 3000,
6464
"ethereumExplorerUrl": "https://etherscan.io",
6565
"aztecScanUrl": "https://aztecscan.xyz",
@@ -70,18 +70,18 @@
7070
},
7171
"auth": {
7272
"discord": {
73-
"clientId": "",
74-
"clientSecret": ""
73+
"clientId": "<<set via DISCORD_CLIENT_ID (variable)>>",
74+
"clientSecret": "<<SECRET: injected from DISCORD_CLIENT_SECRET>>"
7575
},
7676
"x": {
77-
"clientId": "",
78-
"clientSecret": ""
77+
"clientId": "<<set via X_CLIENT_ID (variable)>>",
78+
"clientSecret": "<<SECRET: injected from X_CLIENT_SECRET>>"
7979
},
80-
"sessionPassword": ""
80+
"sessionPassword": "<<SECRET: injected from MAINNET_SESSION_PASSWORD>>"
8181
}
8282
},
8383
"logging": {
8484
"level": "info"
8585
},
8686
"nodeEnv": "production"
87-
}
87+
}

.environment/mainnet/config.json

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"network": {
3+
"type": "mainnet",
4+
"chainId": 1
5+
},
6+
"database": {
7+
"url": "postgresql://dashtec:dashtec@postgres-mainnet:5432/dashtec",
8+
"replicaUrl": "postgresql://dashtec:dashtec@postgres-mainnet:5432/dashtec"
9+
},
10+
"redis": {
11+
"url": "redis://:dashtec@redis-mainnet:6379"
12+
},
13+
"rpc": {
14+
"ethereumUrls": ""
15+
},
16+
"sentinel": {
17+
"proxyUrl": ""
18+
},
19+
"contracts": {
20+
"rollupAddress": "0x603bb2c05D474794ea97805e8De69bCcFb3bCA12",
21+
"governanceAddress": "0x1102471Eb3378FEE427121c9EfcEa452E4B6B75e",
22+
"governanceProposerAddress": "0x06Ef1DcF87E419C48B94a331B252819FADbD63ef",
23+
"slashingProposerAddress": "0x7a318c3DaA9f21f8fc8238c65755eB0394Fbf189",
24+
"slashFactoryAddress": "0x",
25+
"gseAddress": "0xa92ecFD0E70c9cd5E5cd76c50Af0F7Da93567a4f",
26+
"stakingRegistryAddress": "0x042dF8f42790d6943F41C25C2132400fd727f452",
27+
"registryAddress": "0x0000000000000000000000000000000000000000"
28+
},
29+
"collectors": {
30+
"validatorStats": {
31+
"pollIntervalMs": 300000,
32+
"maxPastEpochs": 10,
33+
"batchSize": 10
34+
},
35+
"validatorList": {
36+
"pollIntervalMs": 60000,
37+
"batchSize": 50
38+
},
39+
"epochIntegrity": {
40+
"pollIntervalMs": 600000,
41+
"batchSize": 20,
42+
"epochsToCheck": 100
43+
},
44+
"validatorMigration": {
45+
"enabled": false,
46+
"pollIntervalMs": 3600000,
47+
"batchSize": 50,
48+
"sourceDbUrl": ""
49+
},
50+
"epochAggregates": {
51+
"pollIntervalMs": 300000,
52+
"batchSize": 10,
53+
"epochsToRepair": 50
54+
}
55+
},
56+
"ponder": {
57+
"port": 42069,
58+
"maxHealthcheckDuration": 240,
59+
"startBlock": 20000000
60+
},
61+
"app": {
62+
"url": "https://dashtec.xyz",
63+
"port": 3000,
64+
"ethereumExplorerUrl": "https://etherscan.io",
65+
"aztecScanUrl": "https://aztecscan.xyz",
66+
"rateLimitingEnabled": true,
67+
"domains": {
68+
"mainnet": "dashtec.xyz",
69+
"sepolia": "testnet.dashtec.xyz"
70+
},
71+
"auth": {
72+
"discord": {
73+
"clientId": "",
74+
"clientSecret": ""
75+
},
76+
"x": {
77+
"clientId": "",
78+
"clientSecret": ""
79+
},
80+
"sessionPassword": ""
81+
}
82+
},
83+
"logging": {
84+
"level": "info"
85+
},
86+
"nodeEnv": "production"
87+
}

.environment/testnet/config.example.json

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44
"chainId": 11155111
55
},
66
"database": {
7-
"url": "postgresql://user:password@host-leader:5432/dashtec_testnet?schema=public",
8-
"replicaUrl": "postgresql://user:password@host-replica:5432/dashtec_testnet?schema=public"
7+
"url": "postgresql://dashtec:dashtec@postgres-testnet:5432/dashtec",
8+
"replicaUrl": "postgresql://dashtec:dashtec@postgres-testnet:5432/dashtec"
99
},
1010
"redis": {
11-
"url": "redis://localhost:6379"
11+
"url": "redis://:dashtec@redis-testnet:6379"
1212
},
1313
"rpc": {
14-
"ethereumUrls": "https://sepolia.infura.io/v3/YOUR_INFURA_KEY"
14+
"ethereumUrls": "<<SECRET: injected from TESTNET_ETHEREUM_RPC_URL>>"
1515
},
1616
"sentinel": {
17-
"proxyUrl": "http://your-aztec-node:8080"
17+
"proxyUrl": "<<SECRET: injected from TESTNET_SENTINEL_PROXY_URL>>"
1818
},
1919
"contracts": {
2020
"rollupAddress": "0xebd99ff0ff6677205509ae73f93d0ca52ac85d67",
@@ -59,7 +59,7 @@
5959
"startBlock": 9500000
6060
},
6161
"app": {
62-
"url": "https://testnet.dashtec.io",
62+
"url": "https://testnet.dashtec.xyz",
6363
"port": 3000,
6464
"ethereumExplorerUrl": "https://sepolia.etherscan.io",
6565
"aztecScanUrl": "https://sepolia.aztecscan.xyz",
@@ -70,18 +70,18 @@
7070
},
7171
"auth": {
7272
"discord": {
73-
"clientId": "",
74-
"clientSecret": ""
73+
"clientId": "<<set via DISCORD_CLIENT_ID (variable)>>",
74+
"clientSecret": "<<SECRET: injected from DISCORD_CLIENT_SECRET>>"
7575
},
7676
"x": {
77-
"clientId": "",
78-
"clientSecret": ""
77+
"clientId": "<<set via X_CLIENT_ID (variable)>>",
78+
"clientSecret": "<<SECRET: injected from X_CLIENT_SECRET>>"
7979
},
80-
"sessionPassword": ""
80+
"sessionPassword": "<<SECRET: injected from TESTNET_SESSION_PASSWORD>>"
8181
}
8282
},
8383
"logging": {
84-
"level": "debug"
84+
"level": "info"
8585
},
86-
"nodeEnv": "development"
87-
}
86+
"nodeEnv": "production"
87+
}

.environment/testnet/config.json

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"network": {
3+
"type": "sepolia",
4+
"chainId": 11155111
5+
},
6+
"database": {
7+
"url": "postgresql://dashtec:dashtec@postgres-testnet:5432/dashtec",
8+
"replicaUrl": "postgresql://dashtec:dashtec@postgres-testnet:5432/dashtec"
9+
},
10+
"redis": {
11+
"url": "redis://:dashtec@redis-testnet:6379"
12+
},
13+
"rpc": {
14+
"ethereumUrls": ""
15+
},
16+
"sentinel": {
17+
"proxyUrl": ""
18+
},
19+
"contracts": {
20+
"rollupAddress": "0xebd99ff0ff6677205509ae73f93d0ca52ac85d67",
21+
"governanceAddress": "0x1b5dad737609a548543ce32a71377900b5fd5584",
22+
"governanceProposerAddress": "0xef1b0d8aaca52f70323cbb91b0103bce75e9c533",
23+
"slashingProposerAddress": "0x1d78c05369d4fd947b90De0B7727737c19636d4D",
24+
"slashFactoryAddress": "0xd71673ec4b5d5b468cd48da855a66df0a02cdf3b",
25+
"gseAddress": "0xfb243b9112bb65785a4a8edaf32529accf003614",
26+
"stakingRegistryAddress": "0xa65f52BD0889BbE7DF0cE14709865e3E3bDDbEEa",
27+
"registryAddress": "0x0000000000000000000000000000000000000000"
28+
},
29+
"collectors": {
30+
"validatorStats": {
31+
"pollIntervalMs": 300000,
32+
"maxPastEpochs": 10,
33+
"batchSize": 10
34+
},
35+
"validatorList": {
36+
"pollIntervalMs": 60000,
37+
"batchSize": 50
38+
},
39+
"epochIntegrity": {
40+
"pollIntervalMs": 600000,
41+
"batchSize": 20,
42+
"epochsToCheck": 100
43+
},
44+
"validatorMigration": {
45+
"enabled": false,
46+
"pollIntervalMs": 3600000,
47+
"batchSize": 50,
48+
"sourceDbUrl": ""
49+
},
50+
"epochAggregates": {
51+
"pollIntervalMs": 300000,
52+
"batchSize": 10,
53+
"epochsToRepair": 50
54+
}
55+
},
56+
"ponder": {
57+
"port": 42069,
58+
"maxHealthcheckDuration": 240,
59+
"startBlock": 9500000
60+
},
61+
"app": {
62+
"url": "https://testnet.dashtec.xyz",
63+
"port": 3000,
64+
"ethereumExplorerUrl": "https://sepolia.etherscan.io",
65+
"aztecScanUrl": "https://sepolia.aztecscan.xyz",
66+
"rateLimitingEnabled": false,
67+
"domains": {
68+
"mainnet": "dashtec.xyz",
69+
"sepolia": "testnet.dashtec.xyz"
70+
},
71+
"auth": {
72+
"discord": {
73+
"clientId": "",
74+
"clientSecret": ""
75+
},
76+
"x": {
77+
"clientId": "",
78+
"clientSecret": ""
79+
},
80+
"sessionPassword": ""
81+
}
82+
},
83+
"logging": {
84+
"level": "info"
85+
},
86+
"nodeEnv": "production"
87+
}

.github/workflows/deploy.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Deploy
2+
3+
# Deploys the docker-compose stack to the Hetzner host via Ansible. The host has
4+
# no public SSH, so the runner joins the tailnet (Tailscale) and reaches it over
5+
# Tailscale SSH. Secrets come from GitHub Actions Secrets (no Ansible Vault).
6+
7+
on:
8+
push:
9+
branches: [main]
10+
workflow_dispatch:
11+
12+
# Never let two deploys race on the same host.
13+
concurrency:
14+
group: deploy-production
15+
cancel-in-progress: false
16+
17+
jobs:
18+
deploy:
19+
runs-on: ubuntu-latest
20+
environment: production
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
25+
- name: Connect to Tailscale
26+
uses: tailscale/github-action@v3
27+
with:
28+
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
29+
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
30+
# The tailnet ACL must allow `tag:ci` to SSH to the server's tag as
31+
# root with `action: accept` (not `check`, which needs interactivity).
32+
tags: tag:ci
33+
34+
- name: Install Ansible
35+
run: pipx install --include-deps ansible
36+
37+
- name: Write inventory
38+
run: |
39+
cat > ansible/inventory.ini <<EOF
40+
[dashtec]
41+
${{ vars.DEPLOY_HOST || 'dashtec-host' }} ansible_user=root
42+
[dashtec:vars]
43+
ansible_python_interpreter=/usr/bin/python3
44+
EOF
45+
46+
- name: Run Ansible
47+
working-directory: ansible
48+
run: ansible-playbook site.yml
49+
env:
50+
ANSIBLE_HOST_KEY_CHECKING: "False"
51+
# Per-network secrets injected into the committed config.json.
52+
MAINNET_ETHEREUM_RPC_URL: ${{ secrets.MAINNET_ETHEREUM_RPC_URL }}
53+
MAINNET_SENTINEL_PROXY_URL: ${{ secrets.MAINNET_SENTINEL_PROXY_URL }}
54+
MAINNET_SESSION_PASSWORD: ${{ secrets.MAINNET_SESSION_PASSWORD }}
55+
TESTNET_ETHEREUM_RPC_URL: ${{ secrets.TESTNET_ETHEREUM_RPC_URL }}
56+
TESTNET_SENTINEL_PROXY_URL: ${{ secrets.TESTNET_SENTINEL_PROXY_URL }}
57+
TESTNET_SESSION_PASSWORD: ${{ secrets.TESTNET_SESSION_PASSWORD }}
58+
# OAuth, shared across networks (one app each). IDs are public
59+
# (variables); secrets are sensitive.
60+
DISCORD_CLIENT_ID: ${{ vars.DISCORD_CLIENT_ID }}
61+
DISCORD_CLIENT_SECRET: ${{ secrets.DISCORD_CLIENT_SECRET }}
62+
X_CLIENT_ID: ${{ vars.X_CLIENT_ID }}
63+
X_CLIENT_SECRET: ${{ secrets.X_CLIENT_SECRET }}

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ generated
1414
.env.local
1515
.env*.local
1616

17-
# Environment configuration (contains sensitive data)
18-
.environment/*/config.json
17+
# Environment configuration: config.json is COMMITTED with non-secret values
18+
# only. Secrets (RPC/sentinel URLs, OAuth secrets, session password) are injected
19+
# at deploy time by Ansible — keep them OUT of the committed file.
1920

2021
# Prisma
2122
*.db

ansible/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Generated by Terraform / CI
2+
inventory.ini
3+
4+
*.retry

0 commit comments

Comments
 (0)