Skip to content

Commit

Permalink
Merge pull request #482 from JoinColony/feature/extensions-versions
Browse files Browse the repository at this point in the history
Introduce Extension Versioning
  • Loading branch information
rdig committed Jul 5, 2021
2 parents e2f1e1c + 6643f1d commit 100a098
Show file tree
Hide file tree
Showing 92 changed files with 83,212 additions and 6,736 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,18 +154,14 @@ Done 🎊

### To upgrade to a new colonyNetwork version

1) Add the version to `constants.ts` in `ColonyVersion`
2) Change the `CurrentVersion` variable to the one you just added
1) Add the version to `versions.ts` in `ColonyVersion` as well as the network git tag to `releaseMaps`
3) Add the git tag to `src/constants.ts` release map
4) _Optional:_ If you are tracking a development branch instead of a static tag or commit, make sure to pull the latest changes, otherwise the contracts generated will be exactly the same as your last ones -- _this is a step often forgotten when using a dev version_
5) If needed: add new contracts that need clients to the `contractsToBuild` array in `scripts/build-contracts.ts`
6) Run
```shell
DISABLE_DOCKER=true npm run build-contracts -- -V=X
npm run build-contracts
```

where `X` is the version number you just added (the incremental integer of the `ColonyVersion` enum).

This will create a new folder: `src/contracts/X` containing all the type definitions you'll need to implement the new colony client.

7) Update the following lines in `ColonyNetworkClient.ts` to reflect the new version:
Expand All @@ -178,6 +174,18 @@ import { IColonyNetwork } from '../contracts/X/IColonyNetwork';
8) Update all the other contract imports in the non-colony clients, even if they haven't been upgraded (just in case). Then make adjustments to the clients to reflect the contract changes (typescript will tell you, where to make changes). Also add necessary helper functions (e.g. `withProofs` functions) for newly added methods. The newly added methods and their required roles can be found in [this file](https://github.com/JoinColony/colonyNetwork/blob/develop/contracts/colony/ColonyAuthority.sol) (and by diffing the generated interface files).


### To add new extension contract versions:
1. Add the new version and corresponding git tag for one or more extesions inside `versions.ts`
2. Run `npm run build-contracts` _-- this will build the network contracts for the extensions using `typechain`_
3. Run `npm run build-clients` _-- this will build basic clients and addon files for your new extension versions_
4. If you need extra methods added to your client _(helpers like `withProofs`)_, add them inside the `Addon` file that you'll find in the client's folder _(don't forget to also add the estimate method)_

Eg:
```js
'/src/clients/Extensions/OneTxPayment/1/OneTxPaymentClient.ts' // the OneTxPayment extension client
'/src/clients/Extensions/OneTxPayment/1/OneTxPaymentClientAddons.ts' // the OneTxPayment extension client addons
```

## License

GPL-3.0
36 changes: 32 additions & 4 deletions package-lock.json

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

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@colony/colony-js",
"version": "3.1.2",
"version": "v4.0.0",
"main": "lib/index.js",
"module": "lib-esm/index.js",
"license": "GPL-3.0-only",
Expand All @@ -14,7 +14,9 @@
"compile-esm": "tsc -p tsconfig.build.json -m es6 --outDir lib-esm",
"copy-declarations": "copyfiles -u 1 src/contracts/**/*.d.ts lib && copyfiles -u 1 src/contracts/**/*.d.ts lib-esm",
"build-docs": "typedoc --out docs src/index.ts",
"build-contracts": "ts-node -P scripts/tsconfig-scripts.json scripts/build-contracts.ts",
"build-contracts": "DISABLE_DOCKER=true npm run build-contracts:docker",
"build-contracts:docker": "ts-node -P scripts/tsconfig-scripts.json scripts/build-contracts.ts",
"build-clients": "ts-node -P scripts/tsconfig-scripts.json scripts/build-clients.ts",
"prepublishOnly": "npm test && npm run build"
},
"devDependencies": {
Expand All @@ -26,6 +28,7 @@
"@types/yargs": "^15.0.4",
"@typescript-eslint/eslint-plugin": "^2.24.0",
"@typescript-eslint/parser": "^2.24.0",
"camelcase": "^6.2.0",
"copyfiles": "^2.2.0",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.1.0",
Expand Down
89 changes: 89 additions & 0 deletions scripts/build-clients.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* - Get clients and versions
* - Check if they exist
* - Generate client for version
*/
import { resolve as resolvePath } from 'path';
import { existsSync, mkdirSync, writeFileSync } from 'fs';

import { Extension } from '../src/clients/Extensions/colonyContractExtensions';
import {
CurrentCoinMachineVersion,
CurrentOneTxPaymentVersion,
CurrentVotingReputationVersion,
} from '../src/versions';
import getClientTemplate from './client-templates/extension-client-generator';
import getClientAddonTemplate from './client-templates/extension-client-addons-generator';

const extensionContracts = [
Extension.OneTxPayment,
Extension.CoinMachine,
Extension.VotingReputation,
];

const currentExtensionsVersions = {
[Extension.OneTxPayment]: CurrentOneTxPaymentVersion,
[Extension.CoinMachine]: CurrentCoinMachineVersion,
[Extension.VotingReputation]: CurrentVotingReputationVersion,
};

const getExtensionVersionedPath = (
extensionName: Extension,
extensionVersion: number,
): string =>
resolvePath(
__dirname,
'../src/clients/Extensions',
extensionName,
String(extensionVersion),
);

const checkClientVersionExistance = (
extensionName: Extension,
extensionVersion: number,
): boolean => {
const extensionPath = getExtensionVersionedPath(
extensionName,
extensionVersion,
);
return existsSync(`${extensionPath}/${extensionName}Client.ts`);
};

const generateExtensionClient = async (
extensionName: Extension,
extensionVersion: number,
): Promise<void> => {
const extensionPath = getExtensionVersionedPath(
extensionName,
extensionVersion,
);
const client = getClientTemplate(extensionName, extensionVersion);
const addons = getClientAddonTemplate(extensionName, extensionVersion);

mkdirSync(extensionPath, { recursive: true });

writeFileSync(`${extensionPath}/${extensionName}Client.ts`, client, {
encoding: 'utf8',
});
writeFileSync(`${extensionPath}/${extensionName}ClientAddons.ts`, addons, {
encoding: 'utf8',
});
};

const orchestrateBuild = async (): Promise<void> => {
await Promise.all(
extensionContracts.map(async (extensionContract) => {
const extensionVersion = currentExtensionsVersions[extensionContract];
const clientExists = checkClientVersionExistance(
extensionContract,
extensionVersion,
);

if (!clientExists) {
await generateExtensionClient(extensionContract, extensionVersion);
}
}),
);
};

orchestrateBuild();
114 changes: 81 additions & 33 deletions scripts/build-contracts.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { resolve as resolvePath } from 'path';
import { copyFileSync } from 'fs';
import { promisify } from 'util';

import { options } from 'yargs';
import * as camelcase from 'camelcase';
import * as execute from 'execa';
import * as rimraf from 'rimraf';

import { ColonyVersion, CurrentVersion, releaseMap } from '../src/constants';
import {
CurrentColonyVersion,
releaseMap,
CurrentCoinMachineVersion,
CurrentOneTxPaymentVersion,
CurrentVotingReputationVersion,
} from '../src/versions';
import { Extension } from '../src/clients/Extensions/colonyContractExtensions';

/*
* State Vars
*/
let currentNetworkTag: string;

const rimrafPromise = promisify(rimraf);

Expand All @@ -19,31 +30,38 @@ const vendorTokenDir = resolvePath(__dirname, '../vendor/tokens');

const contractsToBuild = ['IColony', 'IColonyNetwork', 'TokenLocking'];

const extensionContracts = ['OneTxPayment', 'CoinMachine'];
const extensionContracts = [
Extension.OneTxPayment,
Extension.CoinMachine,
Extension.VotingReputation,
];

const currentExtensionsVersions = {
[Extension.OneTxPayment]: CurrentOneTxPaymentVersion,
[Extension.CoinMachine]: CurrentCoinMachineVersion,
[Extension.VotingReputation]: CurrentVotingReputationVersion,
};

const tokenContracts = [
// ERC20 tokens
'Token',
'TokenERC20',
'TokenSAI',
];

const args = options({
V: { type: 'number', alias: 'contract-version', demandOption: true },
}).argv;

const version = args.V as ColonyVersion;
const version = CurrentColonyVersion;
const outRoot = resolvePath(__dirname, '../src/contracts');
const outDir = `${outRoot}/${version}`;
const colonyContractsOutDir = `${outRoot}/colony/${version}`;
const deployDir = `${outRoot}/deploy`;

if (!releaseMap[version]) {
throw new Error(`Version ${version} of colonyNetwork doesn't seem to exist`);
}
const provisionNetworkVendor = async (tag: string): Promise<void> => {
if (!tag) {
throw new Error(
`Version ${version} of colonyNetwork doesn't seem to exist`,
);
}

const buildContracts = async (): Promise<void> => {
console.info(`Checking out network tag ${releaseMap[version]}`);
const git = execute('git', ['checkout', releaseMap[version]], {
console.info(`Checking out network tag ${tag}`);
const git = execute('git', ['checkout', tag], {
cwd: networkDir,
});
if (git.stdout) git.stdout.pipe(process.stdout);
Expand All @@ -68,23 +86,19 @@ const buildContracts = async (): Promise<void> => {
});
if (truffle.stdout) truffle.stdout.pipe(process.stdout);
await truffle;
};

if (version === CurrentVersion) {
// Copy contract json files of latest version for deployment purposes
copyFileSync(`${tokenBuildDir}/Token.json`, `${deployDir}/Token.json`);
copyFileSync(
`${tokenBuildDir}/TokenAuthority.json`,
`${deployDir}/TokenAuthority.json`,
);

/*
* Add extensions contracts to be built
*/
contractsToBuild.push(...extensionContracts);
const buildColonyContracts = async (): Promise<void> => {
// @ts-ignore
const releaseTag: string = releaseMap.colony[version];

// Just build token contracts for the latest version
contractsToBuild.push(...tokenContracts);
if (currentNetworkTag !== releaseTag) {
await provisionNetworkVendor(releaseTag);
currentNetworkTag = releaseTag;
}

// Just build token contracts for the latest version
contractsToBuild.push(...tokenContracts);
const contractGlobs = `{${contractsToBuild
.map((c) => `${c}.json`)
.join(',')}}`;
Expand All @@ -93,11 +107,45 @@ const buildContracts = async (): Promise<void> => {
'--target',
'ethers-v4',
'--outDir',
outDir,
colonyContractsOutDir,
`{${networkDir}/{${relativeBuildDir},${relativeTokenDir}}/${contractGlobs},${vendorTokenDir}/${contractGlobs}}`,
]);
if (typechain.stdout) typechain.stdout.pipe(process.stdout);
await typechain;
};

buildContracts();
const buildExtensionContracts = async (): Promise<void> => {
await Promise.all(
extensionContracts.map(async (extensionContract) => {
const extensionVersion = currentExtensionsVersions[extensionContract];
const releaseTag: string =
// @ts-ignore
releaseMap.extension[camelcase(extensionContract)][extensionVersion];

if (currentNetworkTag !== releaseTag) {
await provisionNetworkVendor(releaseTag);
currentNetworkTag = releaseTag;
}

const extensionOutDir = `${outRoot}/extensions/${camelcase(
extensionContract,
)}/${currentExtensionsVersions[extensionContract]}`;
const typechain = execute('typechain', [
'--target',
'ethers-v4',
'--outDir',
extensionOutDir,
`${networkDir}/${relativeBuildDir}/${extensionContract}.json`,
]);
if (typechain.stdout) typechain.stdout.pipe(process.stdout);
await typechain;
}),
);
};

const orchestrateBuild = async (): Promise<void> => {
await buildColonyContracts();
await buildExtensionContracts();
};

orchestrateBuild();
Loading

0 comments on commit 100a098

Please sign in to comment.