Skip to content

Commit

Permalink
Move the contracts and app deployments into the same workflow (#107)
Browse files Browse the repository at this point in the history
- Move the contracts and app deployment into the same workflow (makes it easier to share
  data and execute sequentially).
- Write a “deployment context” file whenever ./deploy gets executed successfully.
- Add a script that converts this file into the env vars needed by Next.js.
- Update the workflows wording to make it more concise & consistent.
  • Loading branch information
bpierre authored Apr 11, 2024
1 parent 17ea02e commit 927eb48
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
name: test contracts
name: Contracts Tests

# This workflow:
# - Runs the Foundry tests
# - Checks for console imports in the contracts
# - Runs the Hardhat tests
# - Generates a coverage report (currently disabled)

defaults:
run:
Expand Down Expand Up @@ -45,43 +51,8 @@ jobs:
forge test -vvv
id: test

deploy:
if: false
name: Deploy contracts to Liquity v2 Testnet
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install pnpm
uses: pnpm/action-setup@v3.0.0
with:
version: 8

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
cache-dependency-path: 'contracts/pnpm-lock.yaml'

- name: Install dependencies
run: pnpm install

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Run deployment tool
run: ./deploy liquity-testnet --verify
env:
DEPLOYER: ${{ secrets.DEPLOYER }}

console-logs:
name: Check we didn’t forget to remove console imports
name: Console imports check
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -215,4 +186,3 @@ jobs:
base-path: ./contracts/
path-to-lcov: ./contracts/lcov_merged.info
debug: true

101 changes: 101 additions & 0 deletions .github/workflows/testnet-deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Testnet Deployment

# This workflow:
# - Deploys the contracts to the Liquity v2 Testnet (currently disabled)
# - Deploys the app to Vercel (only for the main branch, not PRs)

on:
push:
branches: [main]
paths:
- ".github/workflows/deploy-testnet.yml"
- "contracts/**"
- "frontend/**"
pull_request:
paths:
- ".github/workflows/deploy-testnet.yml"
- "contracts/**"

env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

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

jobs:
deploy-contracts:
name: Deploy contracts
if: false # Disable contracts deployment for now
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install pnpm
uses: pnpm/action-setup@v3.0.0
with:
version: 8

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
cache-dependency-path: 'contracts/pnpm-lock.yaml'

- name: Install dependencies
run: pnpm install

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Run deployment tool
working-directory: ./contracts
run: ./deploy liquity-testnet --verify
env:
DEPLOYER: ${{ secrets.DEPLOYER }}

- name: Upload deployment context
uses: actions/upload-artifact@v4
with:
name: deployment-context
path: ./contracts/deployment-context-latest.json

deploy-app:
name: Deploy app
runs-on: ubuntu-latest
if: ${{ github.event_name != 'pull_request' }}
steps:
- name: Git checkout
uses: actions/checkout@v4

- name: Install pnpm
uses: pnpm/action-setup@v3.0.0
with:
version: 8

- name: Install Vercel CLI
run: pnpm install --global vercel@canary

- name: Download deployment context
if: false # Disable contracts deployment for now
uses: actions/download-artifact@v4
with:
name: deployment-context

- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}

- name: Build Project Artifacts
run: |
test -f ./deployment-context-latest.json && cat ./deployment-context-latest.json | pn tsx contracts/utils/deployment-artifacts-to-next-env.ts > ./frontend/.env.local
vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
- name: Deploy Project Artifacts to Vercel
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
25 changes: 0 additions & 25 deletions .github/workflows/vercel-deployment-main.yml

This file was deleted.

3 changes: 2 additions & 1 deletion contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ node_modules
coverage/
coverage.json
mochaOutput.json
testMatrix.json
testMatrix.json
/deployment-context-latest.json
1 change: 1 addition & 0 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"tsx": "^4.7.1",
"typechain": "^8.1.0",
"typescript": ">=4.5.0",
"zod": "^3.22.4",
"zx": "^7.2.3"
}
}
7 changes: 7 additions & 0 deletions contracts/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion contracts/utils/deploy-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ Deploying Liquity contracts with the following settings:
`broadcast/DeployLiquity2.s.sol/${options.chainId}/run-latest.json`,
);

// write env file
await fs.writeJson("deployment-context-latest.json", {
options,
deployedContracts: Object.fromEntries(deployedContracts),
});

// format deployed contracts
const longestContractName = Math.max(
...deployedContracts.map(([name]) => name.length),
Expand Down Expand Up @@ -241,7 +247,7 @@ async function parseArgs() {
rpcUrl: argv["rpc-url"],
verify: argBoolean("verify"),
verifier: argv["verifier"],
verifierUrl: argv["verifier-url"]
verifierUrl: argv["verifier-url"],
};

const [networkPreset] = argv._;
Expand Down
129 changes: 129 additions & 0 deletions contracts/utils/deployment-artifacts-to-app-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { z } from "zod";
import { argv, echo, stdin } from "zx";

const HELP = `
converts the deployment artifacts created by ./deploy into environment
variables to be used by the Next.js app located in frontend/.
Usage:
./deployment-artifacts-to-app-env.ts < deployment-latest.json
Options:
--help, -h Show this help message.
`;

const ZAddress = z.string().regex(/^0x[0-9a-fA-F]{40}$/);

const ZDeploymentContext = z.object({
options: z.object({
chainId: z.number(),
deployer: z.string(), // can be an address or a private key
help: z.boolean(),
openDemoTroves: z.boolean(),
rpcUrl: z.string(),
verify: z.boolean(),
verifier: z.string(),
}),
deployedContracts: z.record(ZAddress),
});

type DeploymentContext = z.infer<typeof ZDeploymentContext>;

const NULL_ADDRESS = `0x${"0".repeat(40)}`;

export async function main() {
if ("help" in argv || "h" in argv) {
echo`${HELP}`;
process.exit(0);
}

const deploymentContext = parseDeploymentContext(await stdin());

console.log(
objectToEnvironmentVariables(
deploymentContextToAppEnvVariables(deploymentContext),
),
);
}

function objectToEnvironmentVariables(object: Record<string, unknown>) {
return Object.entries(object)
.map(([key, value]) => `${key}=${value}`)
.sort()
.join("\n");
}

function deploymentContextToAppEnvVariables({ deployedContracts, options }: DeploymentContext) {
const appEnvVariables: Record<string, string> = {
NEXT_PUBLIC_CHAIN_ID: String(options.chainId),
};

for (const [contractName, address] of Object.entries(deployedContracts)) {
const envVarName = contractNameToAppEnvVariable(contractName);
if (envVarName) {
appEnvVariables[envVarName] = address;
}
}

appEnvVariables.NEXT_PUBLIC_CONTRACT_FUNCTION_CALLER = NULL_ADDRESS;
appEnvVariables.NEXT_PUBLIC_CONTRACT_HINT_HELPERS = NULL_ADDRESS;

return appEnvVariables;
}

function contractNameToAppEnvVariable(contractName: string) {
switch (contractName) {
case "ActivePool":
return "NEXT_PUBLIC_CONTRACT_ACTIVE_POOL";
case "BoldToken":
return "NEXT_PUBLIC_CONTRACT_BOLD_TOKEN";
case "BorrowerOperations":
return "NEXT_PUBLIC_CONTRACT_BORROWER_OPERATIONS";
case "CollSurplusPool":
return "NEXT_PUBLIC_CONTRACT_COLL_SURPLUS_POOL";
case "DefaultPool":
return "NEXT_PUBLIC_CONTRACT_DEFAULT_POOL";
case "GasPool":
return "NEXT_PUBLIC_CONTRACT_GAS_POOL";
case "PriceFeedTestnet":
return "NEXT_PUBLIC_CONTRACT_PRICE_FEED_TESTNET";
case "SortedTroves":
return "NEXT_PUBLIC_CONTRACT_SORTED_TROVES";
case "StabilityPool":
return "NEXT_PUBLIC_CONTRACT_STABILITY_POOL";
case "TroveManager":
return "NEXT_PUBLIC_CONTRACT_TROVE_MANAGER";
}
return null;
}

function parseDeploymentContext(content: string) {
if (!content.trim()) {
console.error("\nNo deployment context provided.\n");
process.exit(1);
}

let json: unknown;
try {
json = JSON.parse(content);
} catch (error) {
console.error("\nInvalid format provided.\n");
process.exit(1);
}

const context = ZDeploymentContext.safeParse(json);
if (!context.success) {
console.error("\nInvalid deployment context provided.\n");
console.error(
context.error.errors.map((error) => {
return `${error.path.join(".")}: ${error.message} (${error.code})`;
}).join("\n"),
);
console.error("");
process.exit(1);
}

return context.data;
}

main();
Loading

0 comments on commit 927eb48

Please sign in to comment.