Skip to main content

Interactive script

This script provides an interactive way to manage canisters on the toolkit (install, reinstall, or upgrade). It guides you step-by-step through the process of configuring canister IDs, specifying WASM module paths, and providing optional Candid arguments. Additionally, it remembers your previous inputs to save time during future runs.


How to Use

  1. Run the Script:

    ./scripts/manage-canister.sh
  2. Follow the Prompts:

    • The script will ask for:
      • Operation Type: Choose to install, reinstall, or upgrade.
      • Root Canister ID: The ID of the canister that manages other canisters.
      • Target Canister ID: The ID of the canister you want to manage.
      • WASM Module Path: The path to the .wasm.gz file for deployment.
      • Candid Arguments: Optional arguments in Candid format (e.g., (record { arg1 = 42; arg2 = "hello" })).
  3. Confirm Execution:

    • The script displays a summary of your inputs and asks for confirmation before proceeding.
  4. Script Execution:

    • It generates an ic-repl script based on your inputs and executes it to perform the desired operation.

Features

  1. Interactive Prompts:

    • Provides step-by-step guidance with the ability to review and confirm inputs.
  2. Input Persistence:

    • Saves your inputs in a local configuration file (manage_canister_config.txt) for reuse in subsequent runs.
  3. Secure Identity Handling:

    • Temporarily exports your DFX identity for authentication and deletes it after use.
  4. Supports Multiple Operations:

    • You can choose to:
      • Install: Deploy a new canister.
      • Reinstall: Reinstall the code for an existing canister without clearing its state.
      • Upgrade: Upgrade an existing canister while preserving its state.

Example Prompts

Step 1: Choose Operation

Choose an operation to perform:
1. Install a canister
2. Reinstall a canister
3. Upgrade a canister
Enter the number corresponding to the operation: 3
Selected operation: upgrade

Step 2: Enter Root Canister ID

Enter the root canister ID [previously_used_root_canister_id]: aaaa-bbbb-cccc-dddd
Root canister ID: aaaa-bbbb-cccc-dddd

Step 3: Enter Target Canister ID

Enter the target canister ID [previously_used_target_canister_id]: xxxx-yyyy-zzzz-wwww
Target canister ID: xxxx-yyyy-zzzz-wwww

Step 4: Provide WASM Module Path

Enter the path to the WASM module [previously_used_path_to_wasm.wasm.gz]: wasm/my_module.wasm.gz
WASM module path: wasm/my_module.wasm.gz

Step 5: Enter Optional Candid Arguments

Do you want to provide Candid arguments? (Press Enter to use default 'null')
example usage: (record { arg1 = 42; arg2 = "hello" })
Enter Candid arguments or leave blank for default [(null)]: (record { arg1 = 42; arg2 = "hello" })
Candid arguments: (record { arg1 = 42; arg2 = "hello" })

Step 9: Confirm Execution

-------------------------------------------------
You are about to execute the following operation:
Operation: upgrade
Root Canister ID: aaaa-bbbb-cccc-dddd
Target Canister ID: xxxx-yyyy-zzzz-wwww
WASM Module Path: wasm/my_module.wasm.gz
Candid Arguments: (record { arg1 = 42; arg2 = "hello" })
-------------------------------------------------
Do you want to proceed? (yes/no): yes

Notes

  • The script dynamically generates an ic-repl script for the selected operation and executes it.
  • Inputs are saved locally for convenience but can be updated interactively during the next run.

The script

#!/bin/bash

# File to store previously used inputs
CONFIG_FILE="manage_canister_config.txt"

# Function to load the configuration file
load_config() {
if [[ -f "$CONFIG_FILE" ]]; then
source "$CONFIG_FILE"
fi
}

# Function to save the configuration file
save_config() {
cat > "$CONFIG_FILE" <<EOF
ROOT_CANISTER_ID="$ROOT_CANISTER_ID"
CANISTER_ID="$CANISTER_ID"
WASM_MODULE_PATH="$WASM_MODULE_PATH"
CANDID_ARGS="$CANDID_ARGS"
EOF
}

# Function to prompt the user for input with a default value
prompt_with_default() {
local prompt_message=$1
local default_value=$2
local input_variable

read -p "$prompt_message [$default_value]: " input_variable
echo "${input_variable:-$default_value}"
}

# Load previous inputs
load_config

# Step 1: Select Operation
while true; do
echo "Choose an operation to perform:"
echo "1. Install a canister"
echo "2. Reinstall a canister"
echo "3. Upgrade a canister"
read -p "Enter the number corresponding to the operation: " OPERATION_CHOICE

case "$OPERATION_CHOICE" in
1) OPERATION="install"; break ;;
2) OPERATION="reinstall"; break ;;
3) OPERATION="upgrade"; break ;;
*)
echo "Invalid choice. Please enter 1, 2, or 3."
;;
esac
done
echo "Selected operation: $OPERATION"
echo "-------------------------------------------------"

# Step 2: Enter Root Canister ID
ROOT_CANISTER_ID=$(prompt_with_default "Enter the root canister ID" "$ROOT_CANISTER_ID")
echo "Root canister ID: $ROOT_CANISTER_ID"
echo "-------------------------------------------------"

# Step 3: Enter Target Canister ID
CANISTER_ID=$(prompt_with_default "Enter the target canister ID" "$CANISTER_ID")
echo "Target canister ID: $CANISTER_ID"
echo "-------------------------------------------------"

# Step 4: Enter Path to WASM Module
while true; do
WASM_MODULE_PATH=$(prompt_with_default "Enter the path to the WASM module" "$WASM_MODULE_PATH")
if [[ -f "$WASM_MODULE_PATH" ]]; then
break
else
echo "Error: File not found at $WASM_MODULE_PATH. Please try again."
fi
done
echo "WASM module path: $WASM_MODULE_PATH"
echo "-------------------------------------------------"

# Step 4: Check Governance Status
echo "Checking governance status..."
GOV_STATUS=$(dfx canister call $ROOT_CANISTER_ID --ic get_config --output json | jq -r '.Ok.governance_enabled')
if [[ $? -ne 0 ]]; then
echo "Error: Failed to check governance status."
exit 1
fi
echo "Governance status: $GOV_STATUS"

# Step 5: Optional Candid Arguments
DEFAULT_ARGS="(null)"

echo "Do you want to provide Candid arguments? No? fill in the Candid arguments with '(null)'"
echo "example usage: (record { arg1 = 42; arg2 = \"hello\" })"
CANDID_ARGS=$(prompt_with_default "Enter Candid arguments" "${CANDID_ARGS:-$DEFAULT_ARGS}")
echo "Candid arguments: $CANDID_ARGS"

# Save the inputs for future use
save_config

# Step 6: Export Current Identity
IDENTITY=$(dfx identity whoami)
TEMP_PEM_FILE="temp_identity.pem"
dfx identity export "$IDENTITY" > "$TEMP_PEM_FILE"
if [[ $? -ne 0 ]]; then
echo "Error: Failed to export identity for '$IDENTITY'. Ensure DFX is properly configured."
exit 1
fi
echo "Exported identity: $IDENTITY"

# Step 7: Encode the Candid Arguments
ENCODED_ARGS=$(didc encode "$CANDID_ARGS" --format blob)
if [[ $? -ne 0 ]]; then
echo "Error: Failed to encode Candid arguments. Please check the syntax."
exit 1
fi

# Step 8: Gather Proposal Fields if Governance is Enabled
if [[ "$GOV_STATUS" == "true" ]]; then
echo "Governance is enabled. Additional proposal details are required."

PROPOSAL_TITLE=$(prompt_with_default "Enter the proposal title" "$OPERATION canister")
PROPOSAL_SUMMARY=$(prompt_with_default "Enter the proposal summary" "Proposal to $OPERATION canister $CANISTER_ID")
PROPOSAL_URL=$(prompt_with_default "Enter the proposal URL" "")

POST_PROPOSAL=$(cat <<EOF
(record {
title = "$PROPOSAL_TITLE";
summary = "$PROPOSAL_SUMMARY";
url = "$PROPOSAL_URL";
action = variant {
ManageCanister = variant {
Upgrade = record {
mode = variant { $OPERATION };
canister_id = principal "$CANISTER_ID";
wasm = file("$WASM_MODULE_PATH");
args = opt $ENCODED_ARGS;
}
}
}
})
EOF
)
fi

# Step 9: Generate the ic-repl Script
REPL_SCRIPT="manage_canister.repl"
if [[ "$GOV_STATUS" == "true" ]]; then
cat > "$REPL_SCRIPT" <<EOF
#!/usr/bin/ic-repl

identity file("$TEMP_PEM_FILE");
let root_canister = principal "$ROOT_CANISTER_ID";
call root_canister.create_proposal($POST_PROPOSAL);
EOF
echo "Generated proposal creation script."
else
cat > "$REPL_SCRIPT" <<EOF
#!/usr/bin/ic-repl

identity file("$TEMP_PEM_FILE");
let root_canister = principal "$ROOT_CANISTER_ID";
let canister_id = principal "$CANISTER_ID";
let wasm_blob = file("$WASM_MODULE_PATH");
let args_blob = $ENCODED_ARGS;
call root_canister.${OPERATION}_canister(canister_id, wasm_blob, opt args_blob);
EOF
echo "Generated ${OPERATION}_canister script."
fi

# Step 10: Confirm Execution
echo "-------------------------------------------------"
echo "You are about to execute the following operation:"
echo "Operation: $OPERATION"
echo "Root Canister ID: $ROOT_CANISTER_ID"
echo "Target Canister ID: $CANISTER_ID"
echo "WASM Module Path: $WASM_MODULE_PATH"
if [[ "$GOV_STATUS" == "true" ]]; then
echo "Proposal Title: $PROPOSAL_TITLE"
echo "Proposal Summary: $PROPOSAL_SUMMARY"
echo "Proposal URL: $PROPOSAL_URL"
fi
echo "Candid Arguments: $CANDID_ARGS"
echo "-------------------------------------------------"
read -p "Do you want to proceed? (yes/no): " CONFIRMATION
if [[ "$CONFIRMATION" != "yes" ]]; then
echo "Operation cancelled."
rm -f "$REPL_SCRIPT" "$TEMP_PEM_FILE"
exit 0
fi
echo "-------------------------------------------------"

# Step 11: Execute the ic-repl Script
echo "Executing $OPERATION on canister $CANISTER_ID..."
ic-repl -r ic "$REPL_SCRIPT"

# Step 12: Clean Up
rm -f "$REPL_SCRIPT" "$TEMP_PEM_FILE"
if [[ "$GOV_STATUS" == "true" ]]; then
echo "Proposal created successfully"
else
echo "$OPERATION completed successfully."
fi