Skip to content

Commit dc112b1

Browse files
authored
feat: add gha upgrade facet (#289)
1 parent d4b8b13 commit dc112b1

File tree

4 files changed

+200
-29
lines changed

4 files changed

+200
-29
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# This is a template workflow for upgrading PoCo facets.
2+
# Update the script path for each specific upgrade.
3+
name: Upgrade PoCo Facets
4+
5+
on:
6+
workflow_dispatch:
7+
inputs:
8+
network:
9+
description: 'Network'
10+
required: true
11+
type: choice
12+
options:
13+
- arbitrumSepolia
14+
- arbitrum
15+
default: 'arbitrumSepolia'
16+
dry_run:
17+
description: 'Dry Run (fork test only, no actual deployment)'
18+
required: false
19+
type: boolean
20+
default: true
21+
22+
jobs:
23+
# Build and test before upgrading.
24+
build-and-test:
25+
uses: ./.github/workflows/main.yml
26+
27+
upgrade:
28+
needs: build-and-test
29+
runs-on: ubuntu-latest
30+
environment: ${{ inputs.network }} # Use the selected environment
31+
permissions:
32+
contents: write # Required to commit artifacts.
33+
pull-requests: write # Required to create pull requests.
34+
env:
35+
# For commit action
36+
COMMIT_MESSAGE: 'chore: Save upgrade artifacts - ${{ inputs.network }} (runId:${{ github.run_id }})'
37+
GHA_BOT_NAME: 'GitHub Actions Bot'
38+
GHA_BOT_EMAIL: 'github-actions[bot]@users.noreply.github.com'
39+
steps:
40+
- name: Checkout
41+
uses: actions/checkout@v4
42+
43+
- name: Set up Nodejs
44+
uses: actions/setup-node@v4
45+
with:
46+
node-version: 20
47+
cache: 'npm' # Cache dependencies
48+
49+
- name: Install dependencies
50+
run: npm ci
51+
52+
- name: Build
53+
run: npm run build
54+
55+
- name: Run fork test (dry run)
56+
if: inputs.dry_run == true
57+
env:
58+
# Note: it is required to define both private key env variables when calling Hardhat.
59+
DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }}
60+
ADMIN_PRIVATE_KEY: ${{ secrets.ADMIN_PRIVATE_KEY }}
61+
run: |
62+
echo "Running upgrade in fork test mode (dry run)..."
63+
if [ "${{ inputs.network }}" == "arbitrumSepolia" ]; then
64+
export ARBITRUM_SEPOLIA_FORK=true
65+
elif [ "${{ inputs.network }}" == "arbitrum" ]; then
66+
export ARBITRUM_FORK=true
67+
fi
68+
npx hardhat run scripts/upgrades/apply-v6.1.0-upgrade.ts --network hardhat
69+
70+
- name: Execute upgrade on live network
71+
if: inputs.dry_run == false
72+
env:
73+
# Note: it is required to define both private key env variables when calling Hardhat.
74+
DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }}
75+
ADMIN_PRIVATE_KEY: ${{ secrets.ADMIN_PRIVATE_KEY }}
76+
RPC_URL: ${{ secrets.RPC_URL }}
77+
EXPLORER_API_KEY: ${{ secrets.EXPLORER_API_KEY }}
78+
run: |
79+
npx hardhat run scripts/upgrades/apply-v6.1.0-upgrade.ts --network ${{ inputs.network }}
80+
81+
- name: Push artifacts to the current branch
82+
if: inputs.dry_run == false && github.ref != 'refs/heads/main'
83+
uses: stefanzweifel/git-auto-commit-action@v5
84+
with:
85+
file_pattern: |
86+
deployments/${{ inputs.network }}/
87+
commit_message: ${{ env.COMMIT_MESSAGE }}
88+
commit_user_name: ${{ env.GHA_BOT_NAME }}
89+
commit_user_email: ${{ env.GHA_BOT_EMAIL }}
90+
commit_author: '${{ env.GHA_BOT_NAME }} <${{ env.GHA_BOT_EMAIL }}>'
91+
92+
# Since the `main` branch is protected, create a PR to push artifacts.
93+
- name: Push artifacts through a pull request
94+
if: inputs.dry_run == false && github.ref == 'refs/heads/main'
95+
uses: peter-evans/create-pull-request@v7
96+
with:
97+
add-paths: |
98+
deployments/${{ inputs.network }}/
99+
commit-message: ${{ env.COMMIT_MESSAGE }}
100+
committer: '${{ env.GHA_BOT_NAME }} <${{ env.GHA_BOT_EMAIL }}>'
101+
author: '${{ env.GHA_BOT_NAME }} <${{ env.GHA_BOT_EMAIL }}>'
102+
branch: chore/save-upgrade-artifacts-${{ inputs.network }}-${{ github.run_id }}
103+
title: ${{ env.COMMIT_MESSAGE }}
104+
body: 'PR created by "Create Pull Request" GitHub Action for upgrade artifacts.'
105+
draft: true

deploy/0_deploy.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers';
55
import { ZeroAddress, ZeroHash } from 'ethers';
66
import { deployments, ethers } from 'hardhat';
77
import { FacetCut, FacetCutAction } from 'hardhat-deploy/dist/types';
8+
import { tryVerify } from '../scripts/verify';
89
import {
910
AppRegistry__factory,
1011
DatasetRegistry__factory,
@@ -232,15 +233,7 @@ export default async function deploy() {
232233
ownerAddress,
233234
);
234235
// Verify contracts if not on a development network.
235-
if (
236-
!['hardhat', 'localhost', 'external-hardhat', 'dev-native', 'dev-token'].includes(
237-
network.name,
238-
)
239-
) {
240-
console.log('Waiting for block explorer to index the contracts...');
241-
await new Promise((resolve) => setTimeout(resolve, 60000));
242-
await import('../scripts/verify').then((module) => module.default());
243-
}
236+
await tryVerify();
244237
}
245238

246239
async function getOrDeployRlc(

scripts/upgrades/deploy-and-update-some-facet.ts renamed to scripts/upgrades/apply-v6.1.0-upgrade.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { FactoryDeployer } from '../../utils/FactoryDeployer';
1616
import config from '../../utils/config';
1717
import { getDeployerAndOwnerSigners } from '../../utils/deploy-tools';
1818
import { linkContractToProxy } from '../../utils/proxy-tools';
19+
import { tryVerify } from '../verify';
1920
import { printFunctions } from './upgrade-helper';
2021

2122
(async () => {
@@ -160,4 +161,16 @@ import { printFunctions } from './upgrade-helper';
160161
console.log('\nUpgrade completed successfully!');
161162
console.log(`New IexecPocoAccessorsFacet deployed at: ${iexecPocoAccessorsFacet}`);
162163
console.log(`New IexecPoco1Facet deployed at: ${newIexecPoco1Facet}`);
164+
await tryVerify([
165+
{
166+
name: 'IexecPocoAccessorsFacet',
167+
address: iexecPocoAccessorsFacet,
168+
constructorArguments: [],
169+
},
170+
{
171+
name: 'IexecPoco1Facet',
172+
address: newIexecPoco1Facet,
173+
constructorArguments: [],
174+
},
175+
]);
163176
})();

scripts/verify.ts

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,103 @@ import fs from 'fs';
55
import hre, { deployments } from 'hardhat';
66
import path from 'path';
77

8-
async function verify() {
9-
const jsonExtension = '.json';
10-
const contractNames = fs
11-
.readdirSync(path.resolve(__dirname, `../deployments/${hre.network.name}`))
12-
.filter((file) => file.endsWith(jsonExtension))
13-
.map((filePath) => filePath.replace(jsonExtension, ''));
8+
export interface ContractToVerify {
9+
name: string;
10+
address: string;
11+
constructorArguments?: any[];
12+
}
13+
14+
/**
15+
* Verifies contracts on block explorer (e.g., Etherscan, Arbiscan).
16+
* Can verify either specific contracts or all contracts from deployments directory.
17+
*
18+
* @param contracts - Optional array of specific contracts to verify. If not provided,
19+
* will verify all contracts from the deployments/{network} directory.
20+
*/
21+
async function verify(contracts?: ContractToVerify[]): Promise<void> {
22+
const skippedNetworks: string[] = [
23+
'hardhat',
24+
'localhost',
25+
'external-hardhat',
26+
'dev-native',
27+
'dev-token',
28+
];
29+
if (skippedNetworks.includes(hre.network.name)) {
30+
console.log(`\nSkipping verification on development network: ${hre.network.name}`);
31+
return;
32+
}
1433

15-
console.log(`Contracts to verify: ${contractNames}`);
34+
const contractsToVerify =
35+
contracts && contracts.length > 0 ? contracts : await getContractsFromDeployments();
1636

17-
for (const contractName of contractNames) {
37+
console.log('\n=== Verifying contracts on block explorer ===');
38+
console.log(`Contracts to verify: ${contractsToVerify.map((c) => c.name).join(', ')}`);
39+
console.log('Waiting for block explorer to index the contracts...');
40+
await new Promise((resolve) => setTimeout(resolve, 60000));
41+
42+
for (const contract of contractsToVerify) {
1843
try {
19-
console.log(`Verifying ${contractName}..`);
20-
const deployment = await deployments.get(contractName);
21-
const address = deployment.address;
22-
const constructorArguments = deployment.args || [];
2344
await hre.run('verify:verify', {
24-
address,
25-
constructorArguments,
45+
address: contract.address,
46+
constructorArguments: contract.constructorArguments || [],
2647
});
27-
28-
console.log(`${contractName} verified successfully`);
48+
console.log(`${contract.name} verified successfully`);
2949
} catch (error: any) {
30-
console.error(`Error verifying ${contractName}:`, error.message || error);
50+
console.error(`Error verifying ${contract.name}:`, error.message || error);
3151
if (
3252
typeof error.message === 'string' &&
3353
error.message.includes('has') &&
3454
error.message.includes('parameters but') &&
3555
error.message.includes('arguments were provided')
3656
) {
3757
console.error(
38-
`${contractName} requires constructor arguments. Please add them to the deployment artifact.`,
58+
`${contract.name} requires constructor arguments. Please add them to the deployment artifact.`,
3959
);
4060
}
4161
}
4262
}
63+
console.log('\nVerification completed!');
64+
}
65+
66+
/**
67+
* Attempts to verify contracts without throwing errors.
68+
* This is useful when verification is optional and should not break the deployment process.
69+
*
70+
* @param contracts - Optional array of specific contracts to verify.
71+
*/
72+
export async function tryVerify(contracts?: ContractToVerify[]): Promise<void> {
73+
try {
74+
await verify(contracts);
75+
} catch (error) {
76+
console.error('Verification failed, but continuing with deployment:', error);
77+
}
78+
}
79+
80+
/**
81+
* Gets contracts to verify from deployments directory.
82+
*/
83+
async function getContractsFromDeployments(): Promise<ContractToVerify[]> {
84+
const jsonExtension = '.json';
85+
const contractNames = fs
86+
.readdirSync(path.resolve(__dirname, `../deployments/${hre.network.name}`))
87+
.filter((file) => file.endsWith(jsonExtension))
88+
.map((filePath) => filePath.replace(jsonExtension, ''));
89+
90+
if (contractNames.length === 0) {
91+
console.log(`\nNo contracts to verify on network: ${hre.network.name}`);
92+
return [];
93+
}
94+
95+
const contracts: ContractToVerify[] = [];
96+
for (const contractName of contractNames) {
97+
const deployment = await deployments.get(contractName);
98+
contracts.push({
99+
name: contractName,
100+
address: deployment.address,
101+
constructorArguments: deployment.args || [],
102+
});
103+
}
104+
return contracts;
43105
}
44106

45107
if (require.main === module) {
@@ -50,5 +112,3 @@ if (require.main === module) {
50112
process.exit(1);
51113
});
52114
}
53-
54-
export default verify;

0 commit comments

Comments
 (0)