From 7f2ef239acf6aa218b861a2fe9f76320722f6ffc Mon Sep 17 00:00:00 2001 From: Standaa Date: Wed, 31 Mar 2021 08:14:15 +0200 Subject: [PATCH] Feat/migrations typescript support (#132) --- .travis.yml | 1 + CHANGELOG.md | 1 + cli/src/main.rs | 53 ++++++++++++++----- cli/src/template.rs | 18 +++++++ examples/typescript/Anchor.toml | 2 + examples/typescript/Cargo.toml | 4 ++ examples/typescript/migrations/deploy.ts | 22 ++++++++ examples/typescript/package.json | 17 ++++++ .../typescript/programs/typescript/Cargo.toml | 18 +++++++ .../typescript/programs/typescript/Xargo.toml | 2 + .../typescript/programs/typescript/src/lib.rs | 17 ++++++ examples/typescript/tests/typescript.spec.ts | 14 +++++ examples/typescript/tsconfig.json | 10 ++++ 13 files changed, 167 insertions(+), 12 deletions(-) create mode 100644 examples/typescript/Anchor.toml create mode 100644 examples/typescript/Cargo.toml create mode 100644 examples/typescript/migrations/deploy.ts create mode 100644 examples/typescript/package.json create mode 100644 examples/typescript/programs/typescript/Cargo.toml create mode 100644 examples/typescript/programs/typescript/Xargo.toml create mode 100644 examples/typescript/programs/typescript/src/lib.rs create mode 100644 examples/typescript/tests/typescript.spec.ts create mode 100644 examples/typescript/tsconfig.json diff --git a/.travis.yml b/.travis.yml index 3f6d3ed275..0fddeaa57a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,6 +56,7 @@ jobs: - pushd examples/misc && anchor test && popd - pushd examples/events && anchor test && popd - pushd examples/cashiers-check && anchor test && popd + - pushd examples/typescript && yarn && anchor test && popd - <<: *examples name: Runs the examples 2 script: diff --git a/CHANGELOG.md b/CHANGELOG.md index aa0e1c86e5..8769091f71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ incremented for features. * lang: Allow overriding the `#[state]` account's size ([#121](https://github.com/project-serum/anchor/pull/121)). * lang, client, ts: Add event emission and subscriptions ([#89](https://github.com/project-serum/anchor/pull/89)). * lang/account: Allow namespacing account discriminators ([#128](https://github.com/project-serum/anchor/pull/128)). +* cli: TypeScript migrations ([#132](https://github.com/project-serum/anchor/pull/132)). ## Breaking Changes diff --git a/cli/src/main.rs b/cli/src/main.rs index 576fdcb6f6..fa463bc9f0 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -252,22 +252,26 @@ fn init(name: String, typescript: bool) -> Result<()> { // Build the test suite. fs::create_dir("tests")?; + // Build the migrations directory. + fs::create_dir("migrations")?; + if typescript { // Build typescript config let mut ts_config = File::create("tsconfig.json")?; ts_config.write_all(template::ts_config().as_bytes())?; + let mut deploy = File::create("migrations/deploy.ts")?; + deploy.write_all(&template::ts_deploy_script().as_bytes())?; + let mut mocha = File::create(&format!("tests/{}.spec.ts", name))?; mocha.write_all(template::ts_mocha(&name).as_bytes())?; } else { let mut mocha = File::create(&format!("tests/{}.js", name))?; mocha.write_all(template::mocha(&name).as_bytes())?; - } - // Build the migrations directory. - fs::create_dir("migrations")?; - let mut deploy = File::create("migrations/deploy.js")?; - deploy.write_all(&template::deploy_script().as_bytes())?; + let mut deploy = File::create("migrations/deploy.js")?; + deploy.write_all(&template::deploy_script().as_bytes())?; + } println!("{} initialized", name); @@ -1206,7 +1210,8 @@ fn launch(url: Option, keypair: Option, verifiable: bool) -> Res } // Run migration script. - if Path::new("migrations/deploy.js").exists() { + if Path::new("migrations/deploy.js").exists() || Path::new("migrations/deploy.ts").exists() + { migrate(Some(url))?; } @@ -1386,8 +1391,25 @@ fn migrate(url: Option) -> Result<()> { let url = url.unwrap_or_else(|| cfg.cluster.url().to_string()); let cur_dir = std::env::current_dir()?; - let module_path = format!("{}/migrations/deploy.js", cur_dir.display()); - let deploy_script_host_str = template::deploy_script_host(&url, &module_path); + let module_path = cur_dir.join("migrations/deploy.js"); + + let ts_config_exist = Path::new("tsconfig.json").exists(); + let ts_deploy_file_exists = Path::new("migrations/deploy.ts").exists(); + + if ts_config_exist && ts_deploy_file_exists { + let ts_module_path = cur_dir.join("migrations/deploy.ts"); + let exit = std::process::Command::new("tsc") + .arg(&ts_module_path) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .output()?; + if !exit.status.success() { + std::process::exit(exit.status.code().unwrap()); + } + }; + + let deploy_script_host_str = + template::deploy_script_host(&url, &module_path.display().to_string()); if !Path::new(".anchor").exists() { fs::create_dir(".anchor")?; @@ -1395,13 +1417,20 @@ fn migrate(url: Option) -> Result<()> { std::env::set_current_dir(".anchor")?; std::fs::write("deploy.js", deploy_script_host_str)?; - if let Err(_e) = std::process::Command::new("node") + let exit = std::process::Command::new("node") .arg("deploy.js") .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) - .output() - { - std::process::exit(1); + .output()?; + + if ts_config_exist && ts_deploy_file_exists { + std::fs::remove_file(&module_path) + .map_err(|_| anyhow!("Unable to remove file {}", module_path.display()))?; + } + + if !exit.status.success() { + println!("Deploy failed."); + std::process::exit(exit.status.code().unwrap()); } println!("Deploy complete."); diff --git a/cli/src/template.rs b/cli/src/template.rs index cbd5312335..a3add4228d 100644 --- a/cli/src/template.rs +++ b/cli/src/template.rs @@ -79,6 +79,24 @@ module.exports = async function (provider) { } "# } + +pub fn ts_deploy_script() -> &'static str { + r#" +// Migrations are an early feature. Currently, they're nothing more than this +// single deploy script that's invoked from the CLI, injecting a provider +// configured from the workspace's Anchor.toml. + +const anchor = require("@project-serum/anchor"); + +module.exports = async function (provider) { + // Configure client to use the provider. + anchor.setProvider(provider); + + // Add your deploy script here. +} +"# +} + pub fn xargo_toml() -> &'static str { r#"[target.bpfel-unknown-unknown.dependencies.std] features = []"# diff --git a/examples/typescript/Anchor.toml b/examples/typescript/Anchor.toml new file mode 100644 index 0000000000..2ebd5af99b --- /dev/null +++ b/examples/typescript/Anchor.toml @@ -0,0 +1,2 @@ +cluster = "localnet" +wallet = "~/.config/solana/id.json" diff --git a/examples/typescript/Cargo.toml b/examples/typescript/Cargo.toml new file mode 100644 index 0000000000..a60de986d3 --- /dev/null +++ b/examples/typescript/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = [ + "programs/*" +] diff --git a/examples/typescript/migrations/deploy.ts b/examples/typescript/migrations/deploy.ts new file mode 100644 index 0000000000..690db12fcd --- /dev/null +++ b/examples/typescript/migrations/deploy.ts @@ -0,0 +1,22 @@ +// Migrations are an early feature. Currently, they're nothing more than this +// single deploy script that's invoked from the CLI, injecting a provider +// configured from the workspace's Anchor.toml. + +const anchor = require("@project-serum/anchor"); + +module.exports = async function (provider) { + // Configure client to use the provider. + anchor.setProvider(provider); + + // Add your deploy script here. + async function deployAsync(exampleString: string): Promise { + return new Promise((resolve) => { + setTimeout(() => { + console.log(exampleString); + resolve(); + }, 2000); + }); + } + + await deployAsync("Typescript migration example complete."); +} diff --git a/examples/typescript/package.json b/examples/typescript/package.json new file mode 100644 index 0000000000..010ad4559e --- /dev/null +++ b/examples/typescript/package.json @@ -0,0 +1,17 @@ +{ + "name": "typescript", + "version": "1.0.0", + "description": "", + "main": "index.js", + "directories": { + "test": "tests" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@types/node": "^14.14.37" + } +} diff --git a/examples/typescript/programs/typescript/Cargo.toml b/examples/typescript/programs/typescript/Cargo.toml new file mode 100644 index 0000000000..51986756fb --- /dev/null +++ b/examples/typescript/programs/typescript/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "typescript" +version = "0.1.0" +description = "Created with Anchor" +edition = "2018" + +[lib] +crate-type = ["cdylib", "lib"] +name = "typescript" + +[features] +no-entrypoint = [] +no-idl = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = { path = "../../../../lang" } diff --git a/examples/typescript/programs/typescript/Xargo.toml b/examples/typescript/programs/typescript/Xargo.toml new file mode 100644 index 0000000000..1744f098ae --- /dev/null +++ b/examples/typescript/programs/typescript/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/examples/typescript/programs/typescript/src/lib.rs b/examples/typescript/programs/typescript/src/lib.rs new file mode 100644 index 0000000000..c5756c2c35 --- /dev/null +++ b/examples/typescript/programs/typescript/src/lib.rs @@ -0,0 +1,17 @@ +//! The typescript example serves to show how one would setup an Anchor +//! workspace with TypeScript tests and migrations. + +#![feature(proc_macro_hygiene)] + +use anchor_lang::prelude::*; + +#[program] +pub mod typescript { + use super::*; + pub fn initialize(ctx: Context) -> ProgramResult { + Ok(()) + } +} + +#[derive(Accounts)] +pub struct Initialize {} diff --git a/examples/typescript/tests/typescript.spec.ts b/examples/typescript/tests/typescript.spec.ts new file mode 100644 index 0000000000..6a828ab220 --- /dev/null +++ b/examples/typescript/tests/typescript.spec.ts @@ -0,0 +1,14 @@ +import * as anchor from '@project-serum/anchor'; + +describe('typescript', () => { + + // Configure the client to use the local cluster. + anchor.setProvider(anchor.Provider.env()); + + it('Is initialized!', async () => { + // Add your test here. + const program = anchor.workspace.Typescript; + const tx = await program.rpc.initialize(); + console.log("Your transaction signature", tx); + }); +}); diff --git a/examples/typescript/tsconfig.json b/examples/typescript/tsconfig.json new file mode 100644 index 0000000000..cd5d2e3d06 --- /dev/null +++ b/examples/typescript/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai"], + "typeRoots": ["./node_modules/@types"], + "lib": ["es2015"], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true + } +}