Skip to content

Commit 2bb2a16

Browse files
ericglaucoderabbitai[bot]CoveMB
authored
Add git commit hash in output comments for Community Contracts (#627)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Cove Marquis-Bortoli <[email protected]>
1 parent 69e04f2 commit 2bb2a16

14 files changed

+302
-96
lines changed

.changeset/famous-parts-pump.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@openzeppelin/wizard': patch
3+
---
4+
5+
Add compatible git commit in comments when importing OpenZeppelin Community Contracts

packages/core/solidity/openzeppelin-contracts.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
export interface OpenZeppelinContracts {
2+
/**
3+
* Version of `@openzeppelin/contracts` and `@openzeppelin/contracts-upgradeable`
4+
*/
25
version: string;
6+
/**
7+
* Map of source file path to source code.
8+
*/
39
sources: Record<string, string>;
10+
/**
11+
* Map of source file path to the list of all source file paths it depends on (including transitive dependencies).
12+
*/
413
dependencies: Record<string, string[]>;
514
}
615

packages/core/solidity/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"update-env": "rm ./src/environments/hardhat/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat && rm ./src/environments/hardhat/upgradeable/package-lock.json && npm install --package-lock-only --prefix ./src/environments/hardhat/upgradeable"
2323
},
2424
"devDependencies": {
25-
"@openzeppelin/community-contracts": "https://github.com/OpenZeppelin/openzeppelin-community-contracts",
25+
"@openzeppelin/community-contracts": "git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#de17c8e",
2626
"@openzeppelin/contracts": "^5.4.0",
2727
"@openzeppelin/contracts-upgradeable": "^5.4.0",
2828
"@types/node": "^20.0.0",

packages/core/solidity/src/account.test.ts.md

Lines changed: 77 additions & 77 deletions
Large diffs are not rendered by default.
76 Bytes
Binary file not shown.

packages/core/solidity/src/print.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import SOLIDITY_VERSION from './solidity-version.json';
1717
import { inferTranspiled } from './infer-transpiled';
1818
import { compatibleContractsSemver } from './utils/version';
1919
import { stringifyUnicodeSafe } from './utils/sanitize';
20+
import { importsCommunityContracts } from './utils/imports-libraries';
21+
import { getCommunityContractsGitCommit } from './utils/community-contracts-git-commit';
2022

2123
export function printContract(contract: Contract, opts?: Options): string {
2224
const helpers = withHelpers(contract, opts);
@@ -29,7 +31,7 @@ export function printContract(contract: Contract, opts?: Options): string {
2931
...spaceBetween(
3032
[
3133
`// SPDX-License-Identifier: ${contract.license}`,
32-
`// Compatible with OpenZeppelin Contracts ${compatibleContractsSemver}`,
34+
printCompatibleLibraryVersions(contract),
3335
`pragma solidity ^${SOLIDITY_VERSION};`,
3436
],
3537

@@ -54,6 +56,19 @@ export function printContract(contract: Contract, opts?: Options): string {
5456
);
5557
}
5658

59+
function printCompatibleLibraryVersions(contract: Contract): string {
60+
let result = `// Compatible with OpenZeppelin Contracts ${compatibleContractsSemver}`;
61+
if (importsCommunityContracts(contract)) {
62+
try {
63+
const commit = getCommunityContractsGitCommit();
64+
result += ` and Community Contracts commit ${commit}`;
65+
} catch (e) {
66+
console.error(e);
67+
}
68+
}
69+
return result;
70+
}
71+
5772
function printInheritance(contract: Contract, { transformName }: Helpers): [] | [string] {
5873
if (contract.parents.length > 0) {
5974
return ['is ' + contract.parents.map(p => transformName(p.contract)).join(', ')];

packages/core/solidity/src/stablecoin.test.ts.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ Generated by [AVA](https://avajs.dev).
327327
> Snapshot 1
328328
329329
`// SPDX-License-Identifier: MIT␊
330-
// Compatible with OpenZeppelin Contracts ^5.4.0␊
330+
// Compatible with OpenZeppelin Contracts ^5.4.0 and Community Contracts commit de17c8e
331331
pragma solidity ^0.8.27;␊
332332
333333
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
@@ -362,7 +362,7 @@ Generated by [AVA](https://avajs.dev).
362362
> Snapshot 1
363363
364364
`// SPDX-License-Identifier: MIT␊
365-
// Compatible with OpenZeppelin Contracts ^5.4.0␊
365+
// Compatible with OpenZeppelin Contracts ^5.4.0 and Community Contracts commit de17c8e
366366
pragma solidity ^0.8.27;␊
367367
368368
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
@@ -408,7 +408,7 @@ Generated by [AVA](https://avajs.dev).
408408
> Snapshot 1
409409
410410
`// SPDX-License-Identifier: MIT␊
411-
// Compatible with OpenZeppelin Contracts ^5.4.0␊
411+
// Compatible with OpenZeppelin Contracts ^5.4.0 and Community Contracts commit de17c8e
412412
pragma solidity ^0.8.27;␊
413413
414414
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";␊
@@ -588,7 +588,7 @@ Generated by [AVA](https://avajs.dev).
588588
> Snapshot 1
589589
590590
`// SPDX-License-Identifier: MIT␊
591-
// Compatible with OpenZeppelin Contracts ^5.4.0␊
591+
// Compatible with OpenZeppelin Contracts ^5.4.0 and Community Contracts commit de17c8e
592592
pragma solidity ^0.8.27;␊
593593
594594
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";␊
Binary file not shown.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import test from 'ava';
2+
3+
import { extractGitCommitHash } from './community-contracts-git-commit';
4+
5+
// Valid cases
6+
7+
test('extractGitCommitHash - lowercase 40-char hash', t => {
8+
const hash = extractGitCommitHash(
9+
'@openzeppelin/community-contracts',
10+
'git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#0123456789abcdef0123456789abcdef01234567',
11+
);
12+
t.is(hash, '0123456789abcdef0123456789abcdef01234567');
13+
});
14+
15+
test('extractGitCommitHash - uppercase 7-char hash returns lowercase', t => {
16+
const hash = extractGitCommitHash(
17+
'@openzeppelin/community-contracts',
18+
'git+ssh://[email protected]/OpenZeppelin/openzeppelin-community-contracts.git#ABCDEF1',
19+
);
20+
t.is(hash, 'abcdef1');
21+
});
22+
23+
// Invalid format cases
24+
25+
test('extractGitCommitHash - missing git+ prefix', t => {
26+
const err = t.throws(() =>
27+
extractGitCommitHash(
28+
'@openzeppelin/community-contracts',
29+
'https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#abcdef1',
30+
),
31+
);
32+
t.true(
33+
err instanceof Error &&
34+
err.message.includes(
35+
'Expected package dependency for @openzeppelin/community-contracts in format git+<url>#<commit-hash>,',
36+
),
37+
);
38+
});
39+
40+
test('extractGitCommitHash - missing #', t => {
41+
const err = t.throws(() =>
42+
extractGitCommitHash(
43+
'@openzeppelin/community-contracts',
44+
'git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git',
45+
),
46+
);
47+
t.true(
48+
err instanceof Error &&
49+
err.message.includes(
50+
'Expected package dependency for @openzeppelin/community-contracts in format git+<url>#<commit-hash>,',
51+
),
52+
);
53+
});
54+
55+
test('extractGitCommitHash - multiple # parts', t => {
56+
const err = t.throws(() =>
57+
extractGitCommitHash(
58+
'@openzeppelin/community-contracts',
59+
'git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#abcdef1#extra',
60+
),
61+
);
62+
t.true(
63+
err instanceof Error &&
64+
err.message.includes(
65+
'Expected package dependency for @openzeppelin/community-contracts in format git+<url>#<commit-hash>,',
66+
),
67+
);
68+
});
69+
70+
// Invalid hash content cases
71+
72+
test('extractGitCommitHash - too short hash', t => {
73+
const err = t.throws(() =>
74+
extractGitCommitHash(
75+
'@openzeppelin/community-contracts',
76+
'git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#abcde',
77+
),
78+
);
79+
t.true(
80+
err instanceof Error &&
81+
err.message.includes(
82+
'Expected git commit hash for package dependency @openzeppelin/community-contracts to have between 7 and 40 hex chars',
83+
),
84+
);
85+
});
86+
87+
test('extractGitCommitHash - too long hash', t => {
88+
const err = t.throws(() =>
89+
extractGitCommitHash(
90+
'@openzeppelin/community-contracts',
91+
'git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#0123456789abcdef0123456789abcdef012345678',
92+
),
93+
);
94+
t.true(
95+
err instanceof Error &&
96+
err.message.includes(
97+
'Expected git commit hash for package dependency @openzeppelin/community-contracts to have between 7 and 40 hex chars',
98+
),
99+
);
100+
});
101+
102+
test('extractGitCommitHash - non-hex characters', t => {
103+
const err = t.throws(() =>
104+
extractGitCommitHash(
105+
'@openzeppelin/community-contracts',
106+
'git+https://github.com/OpenZeppelin/openzeppelin-community-contracts.git#abcdefg',
107+
),
108+
);
109+
t.true(
110+
err instanceof Error &&
111+
err.message.includes(
112+
'Expected git commit hash for package dependency @openzeppelin/community-contracts to have between 7 and 40 hex chars',
113+
),
114+
);
115+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { devDependencies } from '../../package.json';
2+
3+
/**
4+
* @returns The git commit hash of the @openzeppelin/community-contracts package dependency.
5+
* @throws Error if the @openzeppelin/community-contracts package dependency is not found in devDependencies.
6+
*/
7+
export function getCommunityContractsGitCommit(): string {
8+
const communityContractsVersion = devDependencies['@openzeppelin/community-contracts'];
9+
if (!communityContractsVersion) {
10+
throw new Error('@openzeppelin/community-contracts not found in devDependencies');
11+
}
12+
return extractGitCommitHash('@openzeppelin/community-contracts', communityContractsVersion);
13+
}
14+
15+
/**
16+
* Extracts the git commit hash from a package dependency version string.
17+
* The expected format is `git+<url>#<commit-hash>`.
18+
*
19+
* @param dependencyName The name of the package dependency.
20+
* @param dependencyVersion The version string of the package dependency.
21+
* @returns The git commit hash, normalized to lowercase.
22+
* @throws Error if the version string or commit hash is not in the expected format.
23+
*/
24+
export function extractGitCommitHash(dependencyName: string, dependencyVersion: string): string {
25+
const split = dependencyVersion.split('#');
26+
if (!dependencyVersion.startsWith('git+') || split.length !== 2) {
27+
throw new Error(
28+
`Expected package dependency for ${dependencyName} in format git+<url>#<commit-hash>, but got ${dependencyVersion}`,
29+
);
30+
}
31+
const hash = split[1]!;
32+
if (!/^[a-fA-F0-9]{7,40}$/.test(hash)) {
33+
throw new Error(
34+
`Expected git commit hash for package dependency ${dependencyName} to have between 7 and 40 hex chars, but got ${hash}`,
35+
);
36+
}
37+
return hash.toLowerCase();
38+
}

0 commit comments

Comments
 (0)