diff --git a/modules/babylonlabs-io-btc-staking-ts/README.md b/modules/babylonlabs-io-btc-staking-ts/README.md index cecbcaff73..dfd8b1d100 100644 --- a/modules/babylonlabs-io-btc-staking-ts/README.md +++ b/modules/babylonlabs-io-btc-staking-ts/README.md @@ -1 +1 @@ -BitGo Fork of https://github.com/babylonlabs-io/btc-staking-ts/tree/v0.4.0-rc.2 \ No newline at end of file +BitGo Fork of https://github.com/babylonlabs-io/btc-staking-ts/tree/v1.0.3 \ No newline at end of file diff --git a/modules/babylonlabs-io-btc-staking-ts/src/index.ts b/modules/babylonlabs-io-btc-staking-ts/src/index.ts index 026c91c5e6..883c517ce8 100644 --- a/modules/babylonlabs-io-btc-staking-ts/src/index.ts +++ b/modules/babylonlabs-io-btc-staking-ts/src/index.ts @@ -1,13 +1,21 @@ -export { StakingScriptData, Staking } from './staking'; -export type { StakingScripts } from './staking'; -export { ObservableStaking, ObservableStakingScriptData } from './staking/observable'; -export * from './staking/transactions'; -export * from './types'; -export * from './utils/btc'; -export * from './utils/babylon'; -export * from './utils/staking'; -export * from './utils/utxo/findInputUTXO'; -export * from './utils/utxo/getPsbtInputFields'; -export * from './utils/utxo/getScriptType'; -export { getBabylonParamByBtcHeight, getBabylonParamByVersion } from './utils/staking/param'; -export * from './staking/manager'; +export { StakingScriptData, Staking } from "./staking"; +export type { StakingScripts } from "./staking"; +export { + ObservableStaking, + ObservableStakingScriptData, +} from "./staking/observable"; +export * from "./staking/transactions"; +export * from "./types"; +export * from "./utils/btc"; +export * from "./utils/utxo/findInputUTXO"; +export * from "./utils/utxo/getPsbtInputFields"; +export * from "./utils/utxo/getScriptType"; +export { + getBabylonParamByBtcHeight, + getBabylonParamByVersion, +} from "./utils/staking/param"; +export * from "./staking/manager"; + +// BitGo-specific exports +export * from "./utils/babylon"; +export * from "./utils/staking"; \ No newline at end of file diff --git a/package.json b/package.json index ab2a00c26b..66b6dd2872 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "eslint-plugin-import": "^2.19.1", "eslint-plugin-jsdoc": "^33.0.0", "eslint-plugin-prettier": "^3.4.0", - "execa": "^5.0.0", + "execa": "^9.6.0", "formidable": "3.5.4", "glob": "^7.1.3", "html-webpack-plugin": "^5.5.0", diff --git a/scripts/vendor-github-repo.ts b/scripts/vendor-github-repo.ts deleted file mode 100644 index 6122e332ec..0000000000 --- a/scripts/vendor-github-repo.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { ChildProcess, execFile } from 'child_process'; -import * as fs from 'fs/promises'; -import * as tmp from 'tmp'; -import * as yargs from 'yargs'; - -function isErrorExists(e: NodeJS.ErrnoException): boolean { - return e.code === 'EEXIST'; -} - -type GithubSource = { - org: string; - repo: string; -} & ({ tag: string } | { ref: string }); - -async function wait(p: ChildProcess): Promise { - p.stderr?.pipe(process.stderr); - p.stdout?.pipe(process.stdout); - return new Promise((resolve, reject) => { - p.on('exit', (code) => { - if (code === 0) { - resolve(); - } else { - reject(new Error(`Process exited with code ${code}`)); - } - }); - }); -} - -function getUrl(lib: GithubSource): string { - if ('tag' in lib) { - const { org, repo, tag } = lib; - return `https://github.com/${org}/${repo}/archive/refs/tags/${tag}.tar.gz`; - } - if ('ref' in lib) { - const { org, repo, ref } = lib; - return `https://github.com/${org}/${repo}/tarball/${ref}`; - } - throw new Error('Unsupported lib'); -} - -function getArchivePath(lib: GithubSource): string { - return tmp.fileSync({ postfix: `-${lib.repo}.tar.gz` }).name; -} - -async function fetchArchive(lib: GithubSource, outfile: string): Promise { - try { - const result = await fs.stat(outfile); - if (result.size > 0) { - console.log(`Archive already exists: ${outfile}`); - return; - } - } catch (e) {} - const url = getUrl(lib); - const result = await fetch(url); - if (!result.ok) { - throw new Error(`Failed to fetch ${url}: ${result.status} ${result.statusText}`); - } - await fs.writeFile(outfile, Buffer.from(await result.arrayBuffer())); -} - -async function extractArchive(archivePath: string, targetDir: string): Promise { - try { - await fs.mkdir(targetDir, { recursive: true }); - } catch (e) { - if (!isErrorExists(e)) { - throw e; - } - } - await wait(execFile('tar', ['-C', targetDir, '--strip-components', '1', '-xzf', archivePath])); -} - -type VendorConfig = GithubSource & { - targetDir: string; -}; - -async function main(cfgs: VendorConfig[]) { - for (const cfg of cfgs) { - const archivePath = getArchivePath(cfg); - await fetchArchive(cfg, archivePath); - await extractArchive(archivePath, cfg.targetDir); - } -} - -const vendorConfigs: VendorConfig[] = [ - { - org: 'babylonlabs-io', - repo: 'btc-staking-ts', - tag: 'v1.0.3', - targetDir: 'modules/babylonlabs-io-btc-staking-ts', - }, -]; - -yargs - .command({ - command: 'vendor', - builder(a) { - return a.options({ name: { type: 'string' } }); - }, - async handler(a) { - const matches = vendorConfigs.filter((cfg) => a.name === cfg.repo); - if (matches.length === 0) { - throw new Error(`no such vendor config ${a.name}`); - } - if (matches.length > 1) { - throw new Error(`ambiguous vendor config ${a.name}`); - } - await main(matches); - }, - }) - .help() - .strict() - .demandCommand().argv; diff --git a/scripts/vendor-github-repo/VendorConfig.ts b/scripts/vendor-github-repo/VendorConfig.ts new file mode 100644 index 0000000000..eb5e42ae97 --- /dev/null +++ b/scripts/vendor-github-repo/VendorConfig.ts @@ -0,0 +1,9 @@ +export type GithubSource = { + org: string; + repo: string; +} & ({ tag: string } | { ref: string }); + +export type VendorConfig = GithubSource & { + targetDir: string; + postExtract?: (src: GithubSource, targetDir: string) => Promise; +}; diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-BitGo-specific.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-BitGo-specific.patch new file mode 100644 index 0000000000..87d6e72683 --- /dev/null +++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-BitGo-specific.patch @@ -0,0 +1,35 @@ +From 1ffd7fac756de23859d3f1b61c26619c597e5021 Mon Sep 17 00:00:00 2001 +From: Otto Allmendinger +Date: Thu, 3 Jul 2025 11:03:24 +0200 +Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): add BitGo-specific + exports + +Add exports for babylon and staking utility functions needed by BitGo +integration code. + +Issue: BTC-2143 + +Co-authored-by: llm-git +--- + modules/babylonlabs-io-btc-staking-ts/src/index.ts | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/modules/babylonlabs-io-btc-staking-ts/src/index.ts b/modules/babylonlabs-io-btc-staking-ts/src/index.ts +index cd14e42be..883c517ce 100644 +--- a/modules/babylonlabs-io-btc-staking-ts/src/index.ts ++++ b/modules/babylonlabs-io-btc-staking-ts/src/index.ts +@@ -14,4 +14,8 @@ export { + getBabylonParamByBtcHeight, + getBabylonParamByVersion, + } from "./utils/staking/param"; +-export * from "./staking/manager"; +\ No newline at end of file ++export * from "./staking/manager"; ++ ++// BitGo-specific exports ++export * from "./utils/babylon"; ++export * from "./utils/staking"; +\ No newline at end of file +-- +2.43.0 + diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-build-dir-to-g.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-build-dir-to-g.patch new file mode 100644 index 0000000000..67c92c1786 --- /dev/null +++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-add-build-dir-to-g.patch @@ -0,0 +1,27 @@ +From a5f486a9d30209bf2a6d24abee61236d0aae7be9 Mon Sep 17 00:00:00 2001 +From: Otto Allmendinger +Date: Thu, 3 Jul 2025 13:28:38 +0200 +Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): add build dir to + gitignore + +Issue: BTC-2143 + +Co-authored-by: llm-git +--- + modules/babylonlabs-io-btc-staking-ts/.gitignore | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/modules/babylonlabs-io-btc-staking-ts/.gitignore b/modules/babylonlabs-io-btc-staking-ts/.gitignore +index 2f621f05c..6b0417d7d 100644 +--- a/modules/babylonlabs-io-btc-staking-ts/.gitignore ++++ b/modules/babylonlabs-io-btc-staking-ts/.gitignore +@@ -206,3 +206,5 @@ $RECYCLE.BIN/ + # End of https://www.toptal.com/developers/gitignore/api/node,macos,windows + *.swp + *.swo ++ ++build/ +\ No newline at end of file +-- +2.43.0 + diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-change-class-field.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-change-class-field.patch new file mode 100644 index 0000000000..07fc8a6166 --- /dev/null +++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-change-class-field.patch @@ -0,0 +1,57 @@ +From 75323a260f5b9031ce9c653ad3fe17755a4e25d0 Mon Sep 17 00:00:00 2001 +From: Otto Allmendinger +Date: Thu, 3 Jul 2025 11:03:39 +0200 +Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): change class fields and + methods visibility + +Changed class fields and methods visibility from private to protected or +public to allow better extensibility for subclasses. + +Issue: BTC-2143 + +Co-authored-by: llm-git +--- + .../src/staking/manager.ts | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/modules/babylonlabs-io-btc-staking-ts/src/staking/manager.ts b/modules/babylonlabs-io-btc-staking-ts/src/staking/manager.ts +index 778f3e700..af3dad5d0 100644 +--- a/modules/babylonlabs-io-btc-staking-ts/src/staking/manager.ts ++++ b/modules/babylonlabs-io-btc-staking-ts/src/staking/manager.ts +@@ -93,10 +93,10 @@ interface InclusionProof { + } + + export class BabylonBtcStakingManager { +- private stakingParams: VersionedStakingParams[]; +- private btcProvider: BtcProvider; +- private network: networks.Network; +- private babylonProvider: BabylonProvider; ++ protected stakingParams: VersionedStakingParams[]; ++ protected btcProvider: BtcProvider; ++ protected network: networks.Network; ++ protected babylonProvider: BabylonProvider; + + constructor( + network: networks.Network, +@@ -624,7 +624,8 @@ export class BabylonBtcStakingManager { + + /** + * Creates a proof of possession for the staker based on ECDSA signature. +- * @param bech32Address - The staker's bech32 address. ++ * @param bech32Address - The staker's bech32 address on the babylon chain ++ * @param stakerBtcAddress - The staker's BTC address. + * @returns The proof of possession. + */ + async createProofOfPossession( +@@ -709,7 +710,7 @@ export class BabylonBtcStakingManager { + * @param inclusionProof - The inclusion proof of the staking transaction. + * @returns The protobuf message. + */ +- private async createBtcDelegationMsg( ++ public async createBtcDelegationMsg( + stakingInstance: Staking, + stakingInput: StakingInputs, + stakingTx: Transaction, +-- +2.43.0 + diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-simplify-README-to.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-simplify-README-to.patch new file mode 100644 index 0000000000..f2603ff557 --- /dev/null +++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-simplify-README-to.patch @@ -0,0 +1,59 @@ +From fd94e62d8bdb05bbdd742b6b7dddf163f1a7dd35 Mon Sep 17 00:00:00 2001 +From: Otto Allmendinger +Date: Thu, 3 Jul 2025 11:01:15 +0200 +Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): simplify README to + identify as fork + +Issue: BTC-2143 + +Co-authored-by: llm-git +--- + .../babylonlabs-io-btc-staking-ts/README.md | 37 +------------------ + 1 file changed, 1 insertion(+), 36 deletions(-) + +diff --git a/modules/babylonlabs-io-btc-staking-ts/README.md b/modules/babylonlabs-io-btc-staking-ts/README.md +index ed35980ea..cecbcaff7 100644 +--- a/modules/babylonlabs-io-btc-staking-ts/README.md ++++ b/modules/babylonlabs-io-btc-staking-ts/README.md +@@ -1,36 +1 @@ +-

+- Babylon Logo +-

@babylonlabs-io/btc-staking-ts

+-

Babylon Bitcoin Staking Protocol

+-

TypeScript library

+-

+- npm version +-

+-

+-
+- +-## Installation +- +-```console +-npm i @babylonlabs-io/btc-staking-ts +-``` +- +-## Version Release +- +-### Stable version +- +-Stable release versions are manually released from the main branch. +- +-### Canary version +- +-A canary version is a pre-release version from `dev` branch. +-Make sure all changes are added and committed before running the command below: +- +-```console +-npm run version:canary +-``` +- +-## Usage Guide +- +-Details on the usage of the library can be found +-on the [usage guide](./docs/usage.md). ++BitGo Fork of https://github.com/babylonlabs-io/btc-staking-ts/tree/v1.0.3 +\ No newline at end of file +-- +2.43.0 + diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-package-met.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-package-met.patch new file mode 100644 index 0000000000..f70268d6a4 --- /dev/null +++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-package-met.patch @@ -0,0 +1,111 @@ +From 77fe384ba5a4b62ed4e65b7fc8bd7bc0ccadc3f0 Mon Sep 17 00:00:00 2001 +From: Otto Allmendinger +Date: Thu, 3 Jul 2025 11:01:36 +0200 +Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): update package metadata + for BitGo fork + +Update package name, version, and dependencies to support BitGo's fork of the +babylonlabs BTC staking library. Simplified build configuration and updated +Node.js version requirements. + +Issue: BTC-2143 + +Co-authored-by: llm-git +--- + .../package.json | 55 +++++-------------- + 1 file changed, 13 insertions(+), 42 deletions(-) + +diff --git a/modules/babylonlabs-io-btc-staking-ts/package.json b/modules/babylonlabs-io-btc-staking-ts/package.json +index f3034eec2..34be5ad5b 100644 +--- a/modules/babylonlabs-io-btc-staking-ts/package.json ++++ b/modules/babylonlabs-io-btc-staking-ts/package.json +@@ -1,20 +1,20 @@ + { +- "name": "@babylonlabs-io/btc-staking-ts", +- "version": "1.0.3", ++ "name": "@bitgo/babylonlabs-io-btc-staking-ts", ++ "version": "1.2.0", + "description": "Library exposing methods for the creation and consumption of Bitcoin transactions pertaining to Babylon's Bitcoin Staking protocol.", + "module": "dist/index.js", + "main": "dist/index.cjs", + "typings": "dist/index.d.ts", + "type": "module", ++ "exports": { ++ "import": "./dist/index.js", ++ "require": "./dist/index.cjs" ++ }, + "scripts": { +- "generate-types": "dts-bundle-generator -o ./dist/index.d.ts ./src/index.ts", ++ "generate-types": "dts-bundle-generator -o ./dist/index.d.cts ./src/index.ts --no-check", + "build": "node build.js && npm run generate-types", +- "format": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"", +- "format:fix": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"", +- "lint": "eslint ./src --fix", + "prepare": "husky", + "prepublishOnly": "npm run build", +- "test": "jest --verbose", + "version:canary": "npm version prerelease --preid=canary" + }, + "files": [ +@@ -27,51 +27,22 @@ + "btc-staking" + ], + "engines": { +- "node": "22.3.0" +- }, +- "husky": { +- "hooks": { +- "pre-commit": "lint-staged" +- } +- }, +- "lint-staged": { +- "src/**/*.ts": [ +- "prettier --write", +- "eslint --fix" +- ], +- "tests/**/*.ts": [ +- "prettier --write", +- "eslint --fix" +- ] ++ "node": ">=18 < 23" + }, + "author": "Babylon Labs Ltd.", + "license": "SEE LICENSE IN LICENSE", + "devDependencies": { +- "@types/jest": "^29.5.12", +- "@types/node": "^20.11.30", +- "@typescript-eslint/parser": "^7.4.0", + "dts-bundle-generator": "^9.3.1", +- "ecpair": "^2.1.0", +- "esbuild": "^0.20.2", +- "eslint": "^8.57.0", +- "eslint-config-prettier": "^9.1.0", +- "eslint-plugin-prettier": "^5.1.3", +- "husky": "^9.0.11", +- "jest": "^29.7.0", +- "lint-staged": "^15.2.7", +- "prettier": "^3.2.5", +- "prettier-plugin-organize-imports": "^3.2.4", +- "ts-jest": "^29.1.4", +- "typescript": "^5.4.5", +- "typescript-eslint": "^7.4.0" ++ "esbuild": "^0.20.2" + }, + "dependencies": { +- "@babylonlabs-io/babylon-proto-ts": "1.0.1", ++ "@babylonlabs-io/babylon-proto-ts": "1.0.0", + "@bitcoin-js/tiny-secp256k1-asmjs": "2.2.3", + "@cosmjs/encoding": "^0.33.0", +- "bitcoinjs-lib": "6.1.5" ++ "bip174": "=2.1.1", ++ "bitcoinjs-lib": "^6.1.7" + }, + "publishConfig": { + "access": "public" + } +-} +\ No newline at end of file ++} +-- +2.43.0 + diff --git a/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-tsconfig-fo.patch b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-tsconfig-fo.patch new file mode 100644 index 0000000000..2de122bce7 --- /dev/null +++ b/scripts/vendor-github-repo/btc-staking-ts/patches/0001-fix-babylonlabs-io-btc-staking-ts-update-tsconfig-fo.patch @@ -0,0 +1,39 @@ +From 1f802177441fd09087b4ecfac254dac214dd29bf Mon Sep 17 00:00:00 2001 +From: Otto Allmendinger +Date: Thu, 3 Jul 2025 11:03:55 +0200 +Subject: [PATCH] fix(babylonlabs-io-btc-staking-ts): update tsconfig for + correct builds + +Update tsconfig.json to include composite mode, specify outDir to build +folder, and exclude build from compilation. This fixes issues with the +vendor script. + +Issue: BTC-2143 + +Co-authored-by: llm-git +--- + modules/babylonlabs-io-btc-staking-ts/tsconfig.json | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/modules/babylonlabs-io-btc-staking-ts/tsconfig.json b/modules/babylonlabs-io-btc-staking-ts/tsconfig.json +index 838bfdba1..965f1a3de 100644 +--- a/modules/babylonlabs-io-btc-staking-ts/tsconfig.json ++++ b/modules/babylonlabs-io-btc-staking-ts/tsconfig.json +@@ -7,10 +7,13 @@ + "strict": true, + "skipLibCheck": true, + "isolatedModules": true, +- "declaration": true ++ "declaration": true, ++ "composite": true, ++ "outDir": "./build", + }, + "exclude": [ + "node_modules", ++ "build", + "dist", + "**/*.test.ts" + ] +-- +2.43.0 + diff --git a/scripts/vendor-github-repo/btc-staking-ts/postExtract.ts b/scripts/vendor-github-repo/btc-staking-ts/postExtract.ts new file mode 100644 index 0000000000..2a29a4e048 --- /dev/null +++ b/scripts/vendor-github-repo/btc-staking-ts/postExtract.ts @@ -0,0 +1,38 @@ +import * as fs from 'fs/promises'; + +import { GithubSource } from '../VendorConfig'; +import { applyPatchesFromDir } from '../git'; + +// we only want to vendor prod source code +// remove dev and test files +const removePaths = [ + '.eslintrc.json', + '.github/', + '.husky/', + '.npmrc', + '.nvmrc', + '.prettierignore', + '.prettierrc.json', + 'docs/', + 'tests/', +]; + +async function removeDevAndTestFiles(targetDir: string): Promise { + for (const path of removePaths) { + console.log(`Removing dev/test file: ${path}`); + const fullPath = `${targetDir}/${path}`; + await fs.rm(fullPath, { recursive: true, force: true }); + } +} + +export default async function hook(gitSource: GithubSource, targetDir: string): Promise { + if (!('tag' in gitSource)) { + throw new Error('unsupported github source, expected tag'); + } + if (gitSource.tag !== 'v1.0.3') { + throw new Error(`unexpected tag ${gitSource.tag}, expected v1.0.3`); + } + + await removeDevAndTestFiles(targetDir); + await applyPatchesFromDir(gitSource, targetDir); +} diff --git a/scripts/vendor-github-repo/exec.ts b/scripts/vendor-github-repo/exec.ts new file mode 100644 index 0000000000..01887c6d93 --- /dev/null +++ b/scripts/vendor-github-repo/exec.ts @@ -0,0 +1,19 @@ +import { ChildProcess } from 'child_process'; + +/** + * Executes a child process and waits for it to finish. + * Pipes the process's stdout and stderr to the parent process's stdout and stderr. + */ +export async function wait(p: ChildProcess): Promise { + p.stderr?.pipe(process.stderr); + p.stdout?.pipe(process.stdout); + return new Promise((resolve, reject) => { + p.on('exit', (code) => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`Process exited with code ${code}`)); + } + }); + }); +} diff --git a/scripts/vendor-github-repo/git.ts b/scripts/vendor-github-repo/git.ts new file mode 100644 index 0000000000..8168880b72 --- /dev/null +++ b/scripts/vendor-github-repo/git.ts @@ -0,0 +1,56 @@ +import * as fs from 'fs/promises'; +import { execa } from 'execa'; + +import { GithubSource } from './VendorConfig'; + +export async function getCommitsFromRange(from: string, to: string): Promise { + try { + const { stdout } = await execa('git', ['log', '--pretty=format:%H', `${from}..${to}`]); + return stdout.split('\n').filter((commit) => commit.trim() !== ''); + } catch (error) { + throw new Error(`Failed to get commits from range ${from} to ${to}: ${error}`); + } +} + +export async function getCommitsFromSpec(spec: string): Promise { + if (spec.includes('..')) { + const [from, to, ...rest] = spec.split('..'); + if (rest.length > 0) { + throw new Error(`Invalid commit range specifier: ${spec}`); + } + return getCommitsFromRange(from, to); + } + return [spec]; +} + +export function getPatchDir(cfg: { repo: string }): string { + return `${__dirname}/${cfg.repo}/patches`; +} + +export async function createPatchForCommit(cfg: GithubSource, commit: string, targetDir: string): Promise { + const patchDir = getPatchDir(cfg); + console.log(`Creating patch for commit ${commit} in ${cfg.repo} at ${patchDir}`); + await execa('git', ['format-patch', '-1', commit, '--output-directory', getPatchDir(cfg)], { + cwd: targetDir, + stdio: 'inherit', + }); +} + +export async function applyPatch(cfg: GithubSource, patchFile: string, targetDir: string): Promise { + console.log(`Applying patch ${patchFile} to ${cfg.repo}`); + await execa('git', ['apply', patchFile], { + cwd: targetDir, + stdio: 'inherit', + }); +} + +export async function applyPatchesFromDir(cfg: GithubSource, targetDir: string): Promise { + const patchDir = getPatchDir(cfg); + console.log(`Applying patches from ${patchDir} to ${cfg.repo}`); + const patchFiles = await fs.readdir(patchDir); + for (const patchFile of patchFiles) { + if (patchFile.endsWith('.patch')) { + await applyPatch(cfg, `${patchDir}/${patchFile}`, targetDir); + } + } +} diff --git a/scripts/vendor-github-repo/main.ts b/scripts/vendor-github-repo/main.ts new file mode 100644 index 0000000000..6ed5f45e53 --- /dev/null +++ b/scripts/vendor-github-repo/main.ts @@ -0,0 +1,144 @@ +import { execa, ResultPromise } from 'execa'; +import * as fs from 'fs/promises'; +import * as tmp from 'tmp'; +import * as yargs from 'yargs'; +import { GithubSource, VendorConfig } from './VendorConfig'; +import { createPatchForCommit, getCommitsFromRange, getCommitsFromSpec } from './git'; + +function isErrorExists(e: NodeJS.ErrnoException): boolean { + return e.code === 'EEXIST'; +} + +function getUrl(lib: GithubSource): string { + if ('tag' in lib) { + const { org, repo, tag } = lib; + return `https://github.com/${org}/${repo}/archive/refs/tags/${tag}.tar.gz`; + } + if ('ref' in lib) { + const { org, repo, ref } = lib; + return `https://github.com/${org}/${repo}/tarball/${ref}`; + } + throw new Error('Unsupported lib'); +} + +function getArchivePath(lib: GithubSource): string { + return tmp.fileSync({ postfix: `-${lib.repo}.tar.gz` }).name; +} + +async function fetchArchive(lib: GithubSource, outfile: string): Promise { + try { + const result = await fs.stat(outfile); + if (result.size > 0) { + console.log(`Archive already exists: ${outfile}`); + return; + } + } catch (e) {} + const url = getUrl(lib); + const result = await fetch(url); + if (!result.ok) { + throw new Error(`Failed to fetch ${url}: ${result.status} ${result.statusText}`); + } + await fs.writeFile(outfile, Buffer.from(await result.arrayBuffer())); +} + +function pipe(cmd: ResultPromise): ResultPromise { + cmd.stdout?.pipe(process.stdout); + cmd.stderr?.pipe(process.stderr); + return cmd; +} + +async function extractArchive(archivePath: string, targetDir: string): Promise { + try { + await fs.mkdir(targetDir, { recursive: true }); + } catch (e) { + if (!isErrorExists(e)) { + throw e; + } + } + await pipe(execa('tar', ['-C', targetDir, '--strip-components', '1', '-xzf', archivePath])); +} + +async function cmdVendor(cfgs: VendorConfig[]) { + for (const cfg of cfgs) { + const archivePath = getArchivePath(cfg); + await fetchArchive(cfg, archivePath); + await extractArchive(archivePath, cfg.targetDir); + if (cfg.postExtract) { + console.log(`Running post-extract hook for ${cfg.repo}`); + await cfg.postExtract(cfg, cfg.targetDir); + } + } +} + +async function cmdGeneratePatches(cfgs: VendorConfig[], spec: string) { + for (const cfg of cfgs) { + const commitRange = await getCommitsFromSpec(spec); + for (const commit of commitRange) { + await createPatchForCommit(cfg, commit, cfg.targetDir); + } + } +} + +const vendorConfigs: VendorConfig[] = [ + { + org: 'babylonlabs-io', + repo: 'btc-staking-ts', + tag: 'v1.0.3', + targetDir: 'modules/babylonlabs-io-btc-staking-ts', + async postExtract(githubSource: GithubSource, targetDir: string) { + const m = await import('./btc-staking-ts/postExtract'); + await m.default(githubSource, targetDir); + }, + }, +]; + +function getMatches(name: string): VendorConfig[] { + const matches = vendorConfigs.filter((cfg) => name === cfg.repo); + if (matches.length === 0) { + throw new Error(`no such vendor config ${name}`); + } + if (matches.length > 1) { + throw new Error(`ambiguous vendor config ${name}`); + } + return vendorConfigs.filter((cfg) => cfg.repo === name); +} + +const optName = { + type: 'string', + demand: true, + description: 'Name of the vendor config to use, e.g. btc-staking-ts', +} as const; + +yargs + .command({ + command: 'vendor', + describe: 'Vendor a github repo', + builder(a) { + return a.options({ + name: optName, + }); + }, + async handler(a) { + await cmdVendor(getMatches(a.name)); + }, + }) + .command({ + command: 'generate-patches', + describe: 'Convert a commit range to a set of patches', + builder(a) { + return a.options({ + name: optName, + commitSpec: { + type: 'string', + demand: true, + description: 'Commit range in the form of "from..to", e.g. "v1.0.0..v1.0.3" or a single commit "v1.0.3"', + }, + }); + }, + async handler(a) { + await cmdGeneratePatches(getMatches(a.name), a.commitSpec); + }, + }) + .help() + .strict() + .demandCommand().argv; diff --git a/yarn.lock b/yarn.lock index 97338874b2..b98901384d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4971,6 +4971,11 @@ "@noble/hashes" "~1.7.1" "@scure/base" "~1.2.4" +"@sec-ant/readable-stream@^0.4.1": + version "0.4.1" + resolved "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c" + integrity sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg== + "@sideway/address@^4.1.5": version "4.1.5" resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" @@ -5042,6 +5047,11 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@sindresorhus/merge-streams@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz#abb11d99aeb6d27f1b563c38147a72d50058e339" + integrity sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ== + "@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3": version "1.8.6" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" @@ -11091,6 +11101,24 @@ execa@^8.0.1: signal-exit "^4.1.0" strip-final-newline "^3.0.0" +execa@^9.6.0: + version "9.6.0" + resolved "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz#38665530e54e2e018384108322f37f35ae74f3bc" + integrity sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw== + dependencies: + "@sindresorhus/merge-streams" "^4.0.0" + cross-spawn "^7.0.6" + figures "^6.1.0" + get-stream "^9.0.0" + human-signals "^8.0.1" + is-plain-obj "^4.1.0" + is-stream "^4.0.1" + npm-run-path "^6.0.0" + pretty-ms "^9.2.0" + signal-exit "^4.1.0" + strip-final-newline "^4.0.0" + yoctocolors "^2.1.1" + executable@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" @@ -11418,6 +11446,13 @@ figures@3.2.0, figures@^3.0.0, figures@^3.2.0: dependencies: escape-string-regexp "^1.0.5" +figures@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a" + integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg== + dependencies: + is-unicode-supported "^2.0.0" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -11920,6 +11955,14 @@ get-stream@^8.0.1: resolved "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== +get-stream@^9.0.0: + version "9.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz#95157d21df8eb90d1647102b63039b1df60ebd27" + integrity sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA== + dependencies: + "@sec-ant/readable-stream" "^0.4.1" + is-stream "^4.0.1" + get-symbol-description@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" @@ -12740,6 +12783,11 @@ human-signals@^5.0.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== +human-signals@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz#f08bb593b6d1db353933d06156cedec90abe51fb" + integrity sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ== + humanize-duration@^3.24.0: version "3.32.1" resolved "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.32.1.tgz#922beff5da36fb1cee3de26ada24c592b0fe519b" @@ -13306,6 +13354,11 @@ is-plain-obj@^3.0.0: resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== +is-plain-obj@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -13375,6 +13428,11 @@ is-stream@^3.0.0: resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== +is-stream@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz#375cf891e16d2e4baec250b85926cffc14720d9b" + integrity sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A== + is-string@^1.0.7, is-string@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" @@ -13423,6 +13481,11 @@ is-unicode-supported@^0.1.0: resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-unicode-supported@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a" + integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== + is-weakmap@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" @@ -15817,6 +15880,14 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" +npm-run-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz#25cfdc4eae04976f3349c0b1afc089052c362537" + integrity sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA== + dependencies: + path-key "^4.0.0" + unicorn-magic "^0.3.0" + npmlog@^6.0.0, npmlog@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" @@ -16488,6 +16559,11 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-ms@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz#c0c058edd47c2a590151a718990533fd62803df4" + integrity sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw== + parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" @@ -17142,6 +17218,13 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-ms@^9.2.0: + version "9.2.0" + resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz#e14c0aad6493b69ed63114442a84133d7e560ef0" + integrity sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg== + dependencies: + parse-ms "^4.0.0" + proc-log@^2.0.0, proc-log@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" @@ -19458,6 +19541,11 @@ strip-final-newline@^3.0.0: resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== +strip-final-newline@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz#35a369ec2ac43df356e3edd5dcebb6429aa1fa5c" + integrity sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw== + strip-hex-prefix@1.0.0, strip-hex-prefix@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" @@ -20406,6 +20494,11 @@ unicorn-magic@^0.1.0: resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== +unicorn-magic@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz#4efd45c85a69e0dd576d25532fbfa22aa5c8a104" + integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA== + unique-filename@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" @@ -21722,6 +21815,11 @@ yocto-queue@^1.0.0: resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz#36d7c4739f775b3cbc28e6136e21aa057adec418" integrity sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg== +yoctocolors@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz#e0167474e9fbb9e8b3ecca738deaa61dd12e56fc" + integrity sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ== + zod@^3.21.4: version "3.25.67" resolved "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz#62987e4078e2ab0f63b491ef0c4f33df24236da8"