Skip to content

feat(initia move cli): add decode cli & add docker image to use cli in docker container #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/move-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,42 @@ jobs:
tag_name: ${{ github.event.inputs.version || github.event.workflow_run.head_branch }}
files: initia-move-cli-*.tar.gz

docker-build-push:
needs: [linux-build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Download Linux AMD64 artifact
uses: actions/download-artifact@v4
with:
name: x86_64-unknown-linux-gnu-build
path: tools/initia-move-cli

- name: Extract binary
run: |
cd tools/initia-move-cli
tar -xzvf initia-move-cli-*.tar.gz
rm initia-move-cli-*.tar.gz

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: tools/initia-move-cli
file: tools/initia-move-cli/Dockerfile
push: true
provenance: false
platforms: linux/amd64
tags: |
ghcr.io/${{ github.repository_owner }}/initia-move-cli:latest
ghcr.io/${{ github.repository_owner }}/initia-move-cli:${{ github.event.inputs.version || github.event.workflow_run.head_branch }}
3 changes: 3 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ initia-move-gas = { path = "crates/gas" }
initia-move-compiler = { path = "crates/compiler" }
initia-move-json = { path = "crates/json" }
initia-move-resource-viewer = { path = "crates/resource-viewer" }
movevm = { path = "libmovevm" }

# External crate dependencies.
# Please do not add any test features here: they should be declared by the individual crate.
Expand Down
2 changes: 1 addition & 1 deletion libmovevm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod error;
mod interface;
mod iterator;
mod memory;
mod move_api;
pub mod move_api;
mod result;
mod storage;
mod table_storage;
Expand Down
6 changes: 3 additions & 3 deletions libmovevm/src/move_api/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ struct ModuleInfoResponse {
pub name: String,
}

pub(crate) fn read_module_info(compiled: &[u8]) -> Result<Vec<u8>, Error> {
pub fn read_module_info(compiled: &[u8]) -> Result<Vec<u8>, Error> {
let m = CompiledModule::deserialize_with_config(compiled, &DeserializerConfig::default())
.map_err(|e| Error::backend_failure(e.to_string()))?;

Expand Down Expand Up @@ -76,7 +76,7 @@ pub(crate) fn decode_move_value(
serde_json::to_vec(&value).map_err(|e| Error::BackendFailure { msg: e.to_string() })
}

pub(crate) fn decode_script_bytes(script_bytes: Vec<u8>) -> Result<Vec<u8>, Error> {
pub fn decode_script_bytes(script_bytes: Vec<u8>) -> Result<Vec<u8>, Error> {
let script: MoveScriptBytecode = MoveScriptBytecode::new(script_bytes);
let abi = script
.try_parse_abi()
Expand All @@ -86,7 +86,7 @@ pub(crate) fn decode_script_bytes(script_bytes: Vec<u8>) -> Result<Vec<u8>, Erro
serde_json::to_vec(&abi).map_err(|e| Error::BackendFailure { msg: e.to_string() })
}

pub(crate) fn decode_module_bytes(module_bytes: Vec<u8>) -> Result<Vec<u8>, Error> {
pub fn decode_module_bytes(module_bytes: Vec<u8>) -> Result<Vec<u8>, Error> {
// deserialized request from the json
let module: MoveModuleBytecode = MoveModuleBytecode::new(module_bytes);
let abi = module
Expand Down
5 changes: 4 additions & 1 deletion tools/initia-move-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ path = "src/main.rs"
[dependencies]
anyhow.workspace = true
clap.workspace = true
initia-move-compiler.workspace = true
initia-move-compiler.workspace = true
movevm.workspace = true
serde.workspace = true
serde_json.workspace = true
11 changes: 11 additions & 0 deletions tools/initia-move-cli/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM debian:bullseye-slim

RUN apt-get update && \
apt-get install -y --no-install-recommends git ca-certificates && \
rm -rf /var/lib/apt/lists/*

WORKDIR /usr/local/bin
COPY initia-move-cli .
RUN chmod +x initia-move-cli

ENTRYPOINT ["initia-move-cli"]
63 changes: 32 additions & 31 deletions tools/initia-move-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,38 +72,39 @@ sudo install -m 755 initia-move-cli /usr/local/bin/initia-move
rm initia-move-cli-$VERSION-linux-arm64.tar.gz
```

### How to use it
### Using Docker Container

You can run the Move CLI tool using Docker without installing it locally. Here's how to use it:

```bash
Initia Move CLI

Usage: initia-move [OPTIONS] <COMMAND>

Commands:
build Build the package at `path`. If no path is provided defaults to current directory
coverage Inspect test coverage for this package
new Create a new Move package
test Run Move unit tests in this package
help Print this message or the help of the given subcommand(s)

Options:
-p, --path <PACKAGE_PATH> Path to a package which the command should be run with respect to
-v Print additional diagnostics if available
-d, --dev Compile in 'dev' mode. The 'dev-addresses' and 'dev-dependencies' fields will be used if this flag is set. This flag is
useful for development of packages that expose named addresses that are not set to a specific value
--test Compile in 'test' mode. The 'dev-addresses' and 'dev-dependencies' fields will be used along with any code in the 'tests'
directory
--override-std <OVERRIDE_STD> Whether to override the standard library with the given version [possible values: mainnet, testnet, devnet]
--doc Generate documentation for packages
--abi Generate ABIs for packages
--install-dir <INSTALL_DIR> Installation directory for compiled artifacts. Defaults to current directory
--force Force recompilation of all packages
--fetch-deps-only Only fetch dependency repos to MOVE_HOME
--skip-fetch-latest-git-deps Skip fetching latest git dependencies
--bytecode-version <BYTECODE_VERSION> Bytecode version to compile move code
--skip-attribute-checks Do not complain about an unknown attribute in Move code
--compiler-version <COMPILER_VERSION> Compiler version to use
--language-version <LANGUAGE_VERSION> Language version to support
--experiments <EXPERIMENTS> Experiments for v2 compiler to set to true
docker run --rm \
-v "$(pwd):/code:delegated" \
-w /code \
ghcr.io/initia-labs/initia-move-cli:latest \
<command>
```

Example commands:
```bash
# Build Move modules
docker run --rm -v "$(pwd):/code" -w /code ghcr.io/initia-labs/initia-move-cli:latest build

# Run tests
docker run --rm -v "$(pwd):/code" -w /code ghcr.io/initia-labs/initia-move-cli:latest test

# Decode Move module
docker run --rm -v "$(pwd):/code" -w /code ghcr.io/initia-labs/initia-move-cli:latest decode read my_package my_module
```

For easier use, you can create an alias in your shell:
```bash
alias initia-move='docker run --rm -v "$(pwd):/code" -w /code ghcr.io/initia-labs/initia-move-cli:latest'
```

Then use it like the native command:
```bash
initia-move build
initia-move test

### How to use it
initia-move
156 changes: 156 additions & 0 deletions tools/initia-move-cli/src/decode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
use crate::{InitiaCLI, InitiaCommand};
use anyhow::Context;
use clap::{Parser, Subcommand};
use movevm::move_api::handler::{decode_module_bytes, decode_script_bytes, read_module_info};
use std::{fs, path::PathBuf};

#[derive(Parser)]
#[command(
name = "decode",
about = "Read or Decode Move modules and scripts",
long_about = "Read or Decode Move modules and prints the result in JSON format"
)]
pub struct Decode {
#[command(subcommand)]
pub command: DecodeCommands,
}

#[derive(Subcommand)]
pub enum DecodeCommands {
#[command(
name = "read",
about = "Read Move module info from bytecode",
long_about = "Read and display basic information about a Move module from its bytecode file.\n\
Example: initia-move decode read ./build/package/bytecode_modules/my_module.mv"
)]
Read {
#[arg(value_name = "PACKAGE_NAME")]
package_name: String,
#[arg(value_name = "MODULE_NAME")]
module_name: String,
#[clap(
long = "path",
short = 'p',
value_name = "PACKAGE_PATH",
help = "Path to the package directory"
)]
package_path: Option<PathBuf>,
},

#[command(
name = "script",
about = "Decode Move script bytecode",
long_about = "Decode Move script bytecode and display its ABI (Application Binary Interface).\n\
Example: initia-move decode script ./build/package/scripts/my_script.mv"
)]
Script {
#[arg(value_name = "PACKAGE_NAME")]
package_name: String,
#[arg(value_name = "SCRIPT_NAME")]
script_name: String,
#[clap(
long = "path",
short = 'p',
value_name = "PACKAGE_PATH",
help = "Path to the package directory"
)]
package_path: Option<PathBuf>,
},

#[command(
name = "module",
about = "Decode Move module bytecode",
long_about = "Decode Move module bytecode and display its ABI (Application Binary Interface).\n\
Example: initia-move decode module ./build/package/bytecode_modules/my_module.mv"
)]
Module {
#[arg(value_name = "PACKAGE_NAME")]
package_name: String,
#[arg(value_name = "MODULE_NAME")]
module_name: String,
#[clap(
long = "path",
short = 'p',
value_name = "PACKAGE_PATH",
help = "Path to the package directory"
)]
package_path: Option<PathBuf>,
},
}

pub trait Decoder {
fn decode(self) -> anyhow::Result<()>;
}

fn read_file(package_path: &Option<PathBuf>, path: &str) -> anyhow::Result<Vec<u8>> {
let current_dir = package_path
.clone()
.unwrap_or_else(|| std::env::current_dir().expect("Failed to get current directory"));
let file_path = current_dir.join(PathBuf::from(path));
fs::read(&file_path).with_context(|| format!("Failed to read file: {}", file_path.display()))
}

impl Decoder for InitiaCLI {
fn decode(self) -> anyhow::Result<()> {
match &self.cmd {
InitiaCommand::Decode(cmd) => {
match &cmd.command {
DecodeCommands::Read {
package_name,
module_name,
package_path,
} => {
let path =
format!("build/{}/bytecode_modules/{}.mv", package_name, module_name);
let bytes = read_file(package_path, &path)?;
let result = read_module_info(&bytes)?;
let mut json: serde_json::Value = serde_json::from_slice(&result)?;

if let Some(address) = json.get_mut("address") {
if let serde_json::Value::Array(bytes) = address {
let hex = format!(
"0x{}",
bytes
.iter()
.filter_map(|b| b.as_u64())
.map(|b| format!("{:02x}", b))
.collect::<String>()
);
*address = serde_json::json!(hex);
}
}
println!("{}", serde_json::to_string_pretty(&json)?);
}
DecodeCommands::Script {
package_name,
script_name,
package_path,
} => {
let path = format!(
"build/{}/scripts/bytecode_scripts/{}.mv",
package_name, script_name
);
let bytes = read_file(package_path, &path)?;
let result = decode_script_bytes(bytes)?;
let json: serde_json::Value = serde_json::from_slice(&result)?;
println!("{}", serde_json::to_string_pretty(&json)?);
}
DecodeCommands::Module {
package_name,
module_name,
package_path,
} => {
let path =
format!("build/{}/bytecode_modules/{}.mv", package_name, module_name);
let bytes = read_file(package_path, &path)?;
let result = decode_module_bytes(bytes)?;
let json: serde_json::Value = serde_json::from_slice(&result)?;
println!("{}", serde_json::to_string_pretty(&json)?);
}
}
Ok(())
}
_ => unreachable!(),
}
}
}
4 changes: 2 additions & 2 deletions tools/initia-move-cli/src/execute.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use anyhow::Error;
use initia_move_compiler::{execute, Command};

use crate::{InitiaCLI, InitiaCommand};
Expand All @@ -8,13 +7,14 @@ pub trait Execute {
}

impl Execute for InitiaCLI {
fn execute(self) -> anyhow::Result<(), Error> {
fn execute(self) -> anyhow::Result<()> {
let move_args = self.move_args;
let cmd = match self.cmd {
InitiaCommand::Build(build) => Command::Build(build),
InitiaCommand::Coverage(coverage) => Command::Coverage(coverage),
InitiaCommand::New(new) => Command::New(new),
InitiaCommand::Test(test) => Command::Test(test),
_ => unreachable!(),
};
execute(move_args, cmd)
}
Expand Down
Loading
Loading