diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b8aa81..a538078 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,28 +1,109 @@ name: CI on: - push: - branches: [ '*' ] pull_request: - branches: [ master ] + types: + - opened + - synchronize + - reopened + branches: + - 'main' -jobs: - build: +permissions: + contents: read - runs-on: ubuntu-latest +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true +jobs: + unit-tests: + name: Unit tests (Node ${{ matrix.node }}) + runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - node-version: [16.x] - + node: [18, 20] steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} - - name: Install dependencies + node-version: ${{ matrix.node }} + cache: npm + + - name: Install deps (clean) run: npm ci - - run: npm run build --if-present - # - run: npm run lint - - run: npm test + + - name: Build + run: | + if npm run | grep -q " build"; then + npm run build + else + echo "No build script; skipping." + fi + + - name: Test + run: npm test -- --ci --reporters=default --reporters=jest-junit + env: + CI: true + + cli-smoke: + name: CLI smoke test (Ubuntu) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node (LTS) + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install deps (clean) + run: | + npm ci + npm run build + + - name: Generate minimal config via CLI + working-directory: /home/runner/work/quorum-genesis-tool/quorum-genesis-tool/ + run: | + npx quorum-genesis-tool \ + --consensus clique \ + --validators 4 \ + --members 0 \ + --bootnodes 0 \ + --chainID 7680 \ + --blockperiod 5 \ + --epochLength 30000 \ + --requestTimeout 20 \ + --difficulty 0x1 \ + --gasLimit 0xe4e1c0 \ + --coinbase 0x0000000000000000000000000000000000000000 \ + --quickstartDevAccounts \ + --accountPassword ${ACCOUNT_PASSWORD} \ + --maxCodeSize 2.147483647e+09 \ + --alloc '{"4e59b44847b379578588920cA78FbF26c0B4956C":{"balance":"1","code":"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3","storage":{}},"b3f1656D5b2A9Cf82826D0f39866125616428213":{"balance":"1000000000000000000000000"}}' \ + --outputPath ./test-output + + - name: Verify expected artifacts exist + shell: bash + run: | + set -e + ls -R ./test-output + if [ -f ./test-output/*/besu/genesis.json ] || [ -f ./test-output/*/goQuorum/genesis.json ]; then + echo "Genesis artifacts present." + else + echo "Expected genesis file not found." >&2 + exit 1 + fi + + - name: Upload generated artifacts + uses: actions/upload-artifact@v4 + with: + name: quorum-genesis-artifacts + path: test-output/ + if-no-files-found: error diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yaml similarity index 97% rename from .github/workflows/codeql-analysis.yml rename to .github/workflows/codeql-analysis.yaml index 7e64b18..ed67922 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yaml @@ -13,9 +13,9 @@ name: "CodeQL" on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: analyze: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..349c752 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,228 @@ +name: Release (GitHub Packages on main) + +on: + push: + branches: + - 'main' + workflow_dispatch: + +permissions: + contents: read + packages: write # required to publish to GitHub Packages + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +jobs: + test: + name: Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: { fetch-depth: 0 } + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install + # Provide token in case any deps live on GH Packages + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: npm ci + + - name: Build + run: npm run build --if-present + + - name: Test + run: npm test --if-present + env: + CI: true + + publish-npm-package: + name: Publish to GitHub Packages (npm) + needs: test + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + env: + OWNER: ${{ github.repository_owner }} + steps: + - uses: actions/checkout@v4 + with: { fetch-depth: 0 } + + - name: Compute lowercase owner + id: owner + run: echo "lc=$(echo '${{ env.OWNER }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" + + - name: Verify package scope matches @ + id: scopecheck + shell: bash + run: | + OWNER_LC='${{ steps.owner.outputs.lc }}' + NAME=$(node -p "require('./package.json').name") + NAME_LC=$(node -p "require('./package.json').name.toLowerCase()") + echo "Package name: $NAME" + if [[ "$NAME_LC" != "@${OWNER_LC}/"* ]]; then + echo "::error::package.json name must be scoped to @${OWNER_LC} (e.g. \"@${OWNER_LC}/quorum-genesis-tool\")." + exit 1 + fi + echo "name=$NAME" >> "$GITHUB_OUTPUT" + + - name: Ensure CLI entry exists after build + shell: bash + run: | + BIN=$(node -e "const p=require('./package.json'); const b=p.bin; if(typeof b==='string'){console.log(b)} else if(b&&typeof b==='object'){console.log(Object.values(b)[0])} else {console.log('')}") + echo "bin path from package.json: $BIN" + if [ -z "$BIN" ]; then + echo "::error::No \"bin\" entry in package.json. Set it to your CLI entry (e.g. \"build/index.js\")." + exit 1 + fi + # Build before checking files + npm ci + npm run build --if-present + if [ ! -f "$BIN" ]; then + echo "::error::The bin file \"$BIN\" does not exist after build. Make sure it points to your built CLI (e.g. \"build/index.js\")." + exit 1 + fi + + - name: Show packed contents (sanity check) + run: | + npm pack --dry-run + echo "↑ Verify the files to be published look correct." + + # Configure npm to publish to GitHub Packages (keeps npmjs.org as default earlier) + - uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://npm.pkg.github.com + + - name: Read version + id: ver + run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT" + + - name: Skip if this version already exists on GitHub Packages + id: check + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + NAME='${{ steps.scopecheck.outputs.name }}' + VERSION='${{ steps.ver.outputs.version }}' + echo "Checking $NAME@$VERSION on GitHub Packages…" + if npm view "$NAME@$VERSION" version --registry=https://npm.pkg.github.com >/dev/null 2>&1; then + echo "already=true" >> "$GITHUB_OUTPUT" + echo "Version $VERSION already present. Skipping publish." + else + echo "already=false" >> "$GITHUB_OUTPUT" + fi + + - name: Publish to GitHub Packages + if: steps.check.outputs.already == 'false' + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Use --ignore-scripts to avoid recursion from your "publish" lifecycle script + npm publish --ignore-scripts + echo "✅ Published ${{ steps.scopecheck.outputs.name }}@${{ steps.ver.outputs.version }}" + + - name: Outcome + run: | + if [ "${{ steps.check.outputs.already }}" = "false" ]; then + echo "✅ Release complete." + else + echo "ℹ️ Nothing to publish." + fi + + publish-docker-image: + name: Publish Docker Repository (Docker image) + needs: publish-npm-package + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + + permissions: + contents: read + packages: read # pull from GitHub Packages during docker build + id-token: write # for AWS OIDC + + env: + AWS_REGION: us-east-1 # <--- change if needed + ECR_REPOSITORY: quantnetwork/quorum-k8s-hooks # <--- your ECR repo name + PACKAGE_SCOPE: "@quantnetwork" + PACKAGE_NAME: "@quantnetwork/quorum-genesis-tool" + + steps: + - uses: actions/checkout@v4 + with: { fetch-depth: 0 } + + - name: Read package version + id: ver + run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT" + + - name: Verify package exists on GitHub Packages + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + V='${{ steps.ver.outputs.version }}' + echo "Checking ${{ env.PACKAGE_NAME }}@$V..." + if ! npm view "${{ env.PACKAGE_NAME }}@$V" version --registry=https://npm.pkg.github.com >/dev/null 2>&1; then + echo "::error::${{ env.PACKAGE_NAME }}@$V not found on GitHub Packages. Did the publish step run?" + exit 1 + fi + + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + aws-region: ${{ env.AWS_REGION }} + + - name: Ensure ECR repository exists + run: | + set -euo pipefail + : "${ECR_REPOSITORY:?ECR_REPOSITORY not set}" + + if aws ecr describe-repositories --repository-names "$ECR_REPOSITORY" >/dev/null 2>&1; then + echo "ECR repository '$ECR_REPOSITORY' already exists." + else + echo "Creating ECR repository '$ECR_REPOSITORY'." + aws ecr create-repository --repository-name "$ECR_REPOSITORY" >/dev/null + echo "Created ECR repository '$ECR_REPOSITORY'." + fi + + - name: Login to Amazon ECR + id: ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Set image tags + id: tags + run: | + REG="${{ steps.ecr.outputs.registry }}" + IMG_VERSION="$REG/${{ env.ECR_REPOSITORY }}:${{ steps.ver.outputs.version }}" + IMG_LATEST="$REG/${{ env.ECR_REPOSITORY }}:latest" + echo "version_tag=$IMG_VERSION" >> "$GITHUB_OUTPUT" + echo "latest_tag=$IMG_LATEST" >> "$GITHUB_OUTPUT" + echo "Will push: $IMG_VERSION and $IMG_LATEST" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + platforms: linux/amd64 + tags: | + ${{ steps.tags.outputs.version_tag }} + ${{ steps.tags.outputs.latest_tag }} + build-args: | + SCOPE=${{ env.PACKAGE_SCOPE }} + PACKAGE=${{ env.PACKAGE_NAME }} + VERSION=${{ steps.ver.outputs.version }} + secrets: | + "npm_token=${{ secrets.GITHUB_TOKEN }}" + diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..4af0054 --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +registry=https://registry.npmjs.org/ +@quantnetwork:registry=https://npm.pkg.github.com +//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN} +always-auth=true \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4ba78dc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +# Use an official Node image (no NodeSource curl|bash) +FROM node:22-bookworm-slim + +ENV DEBIAN_FRONTEND=noninteractive + +# Optional: match your previous machine-id + kube dir +RUN set -eux; \ + mkdir -p /root/.kube; \ + echo "fd97de6b91a121428112c52e5fe04a15" > /etc/machine-id + +# System deps you had (trimmed obsolete ones) +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + pkg-config ca-certificates gnupg lsb-release jq libc6-dev make curl wget \ + vim dnsutils unzip libsodium-dev; \ + rm -rf /var/lib/apt/lists/* + +# kubectl (same approach you used) +RUN set -eux; \ + KUBECTL_VERSION="$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)"; \ + curl -s "https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" \ + -o /usr/local/bin/kubectl; \ + chmod a+x /usr/local/bin/kubectl + +# --- Install your package from GitHub Packages securely --- +# These are set by your GitHub Actions build step: +# build-arg SCOPE=@quantnetwork +# build-arg PACKAGE=@quantnetwork/quorum-genesis-tool +# build-arg VERSION= +ARG SCOPE=@quantnetwork +ARG PACKAGE=@quantnetwork/quorum-genesis-tool +ARG VERSION + +# Pass the GitHub token as a BuildKit secret named "npm_token" +# (workflow does: secrets: "npm_token=${{ secrets.GITHUB_TOKEN }}") +RUN --mount=type=secret,id=npm_token \ + set -eux; \ + TOKEN="$(cat /run/secrets/npm_token)"; \ + printf "%s:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=%s\nalways-auth=true\n" "$SCOPE" "$TOKEN" > /tmp/.npmrc; \ + NPM_CONFIG_USERCONFIG=/tmp/.npmrc npm i -g "${PACKAGE}@${VERSION}"; \ + rm -f /tmp/.npmrc + +# AWS CLI v2 (your original steps) +RUN set -eux; \ + curl -fsSLo awscliv2.zip "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"; \ + unzip awscliv2.zip; \ + ./aws/install; \ + rm -rf /var/lib/apt/lists/* aws awscliv2.zip + +# Optional: basic smoke check (don’t fail build if it just prints help) +RUN quorum-genesis-tool --help || true + +# If this image is meant to run the CLI directly: +ENTRYPOINT ["quorum-genesis-tool"] diff --git a/index.js b/index.js index dd69af5..375d043 100755 --- a/index.js +++ b/index.js @@ -1,22 +1,25 @@ #!/usr/bin/env node -const main = require("./build").main; - -if (require.main === module) { - // note: main returns a Promise, but we don't need to do anything - // special with it, so we use the void operator to indicate to eslint that - // we left this dangling intentionally... - try { - void main(); - } catch (err) { - if (err && err.stack && process.argv.length >= 3 && process.argv[2] === "--stackTraceOnError") { - console.error(`Fatal error: ${err.stack}`); - } else if (err && err.message) { - console.error(`Fatal error: ${err.message}`); - } else if (err) { - console.error(`Fatal error: ${err}`); - } else { - console.error(`Fatal error: unknown`); - } - process.exit(1); +(async () => { + try { + const { main } = require('./build'); // ensure build exports main + await Promise.resolve(main()); + } catch (err) { + const showStack = process.argv.includes('--stackTraceOnError'); + if (showStack && err && err.stack) { + console.error(`Fatal error: ${err.stack}`); + } else if (err && err.message) { + console.error(`Fatal error: ${err.message}`); + } else { + console.error(`Fatal error: ${err}`); } -} \ No newline at end of file + process.exit(1); + } +})(); + +// Extra safety: catch unhandled promise rejections that occur outside the try/catch +process.on('unhandledRejection', (err) => { + const showStack = process.argv.includes('--stackTraceOnError'); + const msg = err && (showStack && err.stack ? err.stack : err.message || String(err)); + console.error(`Fatal error (unhandledRejection): ${msg}`); + process.exit(1); +}); \ No newline at end of file diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 4717cb9..51ecb3a 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,11 +1,11 @@ { - "name": "quorum-genesis-tool", + "name": "@quantnetwork/quorum-genesis-tool", "version": "0.2.20", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "quorum-genesis-tool", + "name": "@quantnetwork/quorum-genesis-tool", "version": "0.2.20", "license": "Apache-2.0", "dependencies": { @@ -43,6 +43,7 @@ "eslint-plugin-prefer-arrow": "^1.2.1", "husky": "^9.1.1", "jest": "^29.7.0", + "jest-junit": "^16.0.0", "lint-staged": "^15.2.7", "ts-jest": "^29.2.3", "typescript": "<=5.6.0" @@ -4415,6 +4416,21 @@ "fsevents": "^2.3.2" } }, + "node_modules/jest-junit": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz", + "integrity": "sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "strip-ansi": "^6.0.1", + "uuid": "^8.3.2", + "xml": "^1.0.1" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/jest-leak-detector": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", @@ -5427,6 +5443,18 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6962,6 +6990,12 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -10275,6 +10309,18 @@ "walker": "^1.0.8" } }, + "jest-junit": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz", + "integrity": "sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "strip-ansi": "^6.0.1", + "uuid": "^8.3.2", + "xml": "^1.0.1" + } + }, "jest-leak-detector": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", @@ -11006,6 +11052,12 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -12088,6 +12140,12 @@ "signal-exit": "^3.0.7" } }, + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 3bbe4de..c0bbb40 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "quorum-genesis-tool", + "name": "@quantnetwork/quorum-genesis-tool", "version": "0.2.20", "description": "A utility that lets developers create genesis files and node (&account) keys from ConsenSys!", "main": "build/index.js", "types": "build/typescript/index.d.ts", - "repository": "git@github.com:ConsenSys/quorum-genesis-tool.git", + "repository": "git@github.com:quantnetwork/quorum-genesis-tool.git", "contributors": [ "Joshua Fernandes <3722503+joshuafernandes@users.noreply.github.com>", "Eric Lin <38420555+Ezzahhh@users.noreply.github.com>" @@ -22,14 +22,14 @@ "bin": "./index.js", "scripts": { "clean": "rm -rf build output", - "build": "tsc", + "build": "tsc && node scripts/copying-template.js", "postbuild": "cp -R src/templates build", "test": "jest --verbose", "lint": "eslint './src/**'", "lintAndFix": "eslint --fix './src/**'", "prepare": "tsc && eslint --fix './src/**'", "start": "node build/index.js", - "publish": "npm run clean && npm run build && npm run test && npm publish --tag=latest" + "release:local": "npm run clean && npm run build && npm run test && npm publish --ignore-scripts --tag=latest" }, "dependencies": { "argon2": "^0.40", @@ -63,6 +63,7 @@ "eslint-plugin-prefer-arrow": "^1.2.1", "husky": "^9.1.1", "jest": "^29.7.0", + "jest-junit": "^16.0.0", "lint-staged": "^15.2.7", "ts-jest": "^29.2.3", "typescript": "<=5.6.0" @@ -73,5 +74,9 @@ "eslint-plugin-import": "import/export syntax", "eslint-config-prettier": "Turns off all rules that are unnecessary or might conflict with Prettier", "lint-staged": "Run linters against staged git files and don't let stuff slip into your code base!" + }, + "publishConfig": { + "registry": "https://npm.pkg.github.com" } + } diff --git a/scripts/copying-template.js b/scripts/copying-template.js new file mode 100644 index 0000000..36f578a --- /dev/null +++ b/scripts/copying-template.js @@ -0,0 +1,23 @@ +// CommonJS version (works on Node 18) +const fs = require("node:fs"); +const fsp = require("node:fs/promises"); +const path = require("node:path"); + +const SRC = path.join(__dirname, "..", "src", "templates"); +const DEST = path.join(__dirname, "..", "build", "templates"); + +(async () => { + try { + if (!fs.existsSync(SRC)) { + console.error(`Missing ${SRC}`); + process.exit(1); + } + await fsp.mkdir(path.join(__dirname, "..", "build"), { recursive: true }); + // Node 18+: fsp.cp is available + await fsp.cp(SRC, DEST, { recursive: true }); + console.log(`Copied ${SRC} -> ${DEST}`); + } catch (err) { + console.error(err); + process.exit(1); + } +})(); diff --git a/src/index.ts b/src/index.ts index 0f7b341..df8bb1a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -151,7 +151,7 @@ export async function main(): Promise { type: "string", demandOption: true, default: "{}", - describe: "JSON format of the prefunded accounts {'address': { balance: '100000'}}", + describe: "JSON format of the prefunded accounts {'address': { 'balance': '100000', 'code': '60808060405...', 'storage': { '0x0000000000000000000000000000000000000000000000000000000000000001': '0x000040' }}}", }, noOutputTimestamp: { type: "boolean", diff --git a/src/lib/genesisGenerate.ts b/src/lib/genesisGenerate.ts index 062d775..dfc57ad 100644 --- a/src/lib/genesisGenerate.ts +++ b/src/lib/genesisGenerate.ts @@ -92,6 +92,8 @@ export function createBesuGenesis( Object.entries(prefundedAccounts).forEach(([key, value]) => { besu.alloc[key] = { balance: value.balance, + code: value.code, + storage: value.storage, }; }); } @@ -165,6 +167,8 @@ export function createGoQuorumGenesis( Object.entries(prefundedAccounts).forEach(([key, value]) => { goquorum.alloc[key] = { balance: value.balance, + code: value.code, + storage: value.storage, }; }); } diff --git a/src/questions/commonQs.ts b/src/questions/commonQs.ts index 7c3d8fe..063cff2 100644 --- a/src/questions/commonQs.ts +++ b/src/questions/commonQs.ts @@ -19,7 +19,7 @@ export const genesisNodeAllocationQuestion: QuestionTree = { export const prefundedAccountsQuestion: QuestionTree = { name: "prefundedAccounts", prompt: - "Include JSON format of the prefunded account {'address': { 'balance': '100000'}}: (default {})", + "Include JSON format of the prefunded account {'address': { 'balance': '100000', 'code': '60808060405...', 'storage': { '0x0000000000000000000000000000000000000000000000000000000000000001': '0x000040' }}}: (default {})", }; export const accountPasswordQuestion: QuestionTree = { diff --git a/src/types/genesis.ts b/src/types/genesis.ts index 66bb218..e2aa2f8 100644 --- a/src/types/genesis.ts +++ b/src/types/genesis.ts @@ -43,6 +43,10 @@ export type GenesisConfig = { export type Alloc = { balance: string; + code?: string; + storage?: { + [id: string]: string; + }; comment?: string; privateKey?: string; }; diff --git a/tests/lib/testConstants.ts b/tests/lib/testConstants.ts index 6f7fbab..bfc3ea9 100644 --- a/tests/lib/testConstants.ts +++ b/tests/lib/testConstants.ts @@ -24,7 +24,7 @@ export const TEST_QUORUM_CONFIG: QuorumConfig = { tesseraPassword: "", quickstartDevAccounts: false, noOutputTimestamp: false, - prefundedAccounts: "{}", + prefundedAccounts: '{"0x000000000000000000000000000000000000000A": {"balance": 1000, "code": "060680880", "storage": "0x000001"}}', genesisNodeAllocation: "100", }; diff --git a/tsconfig.json b/tsconfig.json index 4693763..5c73104 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,8 +11,8 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "ES6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "target": "es2017", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "lib": ["es2017"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */