Skip to content
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

Add configFile support in cli #131

Merged
merged 17 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
4 changes: 3 additions & 1 deletion cli/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ coverage/
*.wasm

# wallets
wallet*.yaml
wallet*.yaml

deweb_cli_config.json
9 changes: 9 additions & 0 deletions cli/default_cli_config.json
thomas-senechal marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"wallet_config": {
"wallet": "",
"password": ""
},
"secret_key": "",
"node_url": "https://buildnet.massa.net/api/v2",
"chunk_size": 64000
}
40 changes: 40 additions & 0 deletions cli/src/commands/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { OptionValues } from 'commander'
import { readFileSync } from 'fs'

interface Config {
wallet_password: string
wallet_path: string
node_url: string
chunk_size: number
secret_key: string
}

export function parseConfigFile(filePath: string): Config {
const fileContent = readFileSync(filePath, 'utf-8')
try {
return JSON.parse(fileContent)
} catch (error) {
throw new Error(`Failed to parse file: ${error}`)
}
}
thomas-senechal marked this conversation as resolved.
Show resolved Hide resolved

export function mergeConfigAndOptions(
commandOptions: OptionValues,
configOptions: Config
): OptionValues {
if (!configOptions) return commandOptions

const wallet = commandOptions.wallet || configOptions.wallet_path
const password = commandOptions.password || configOptions.wallet_password
const node_url = commandOptions.node_url || configOptions.node_url
const chunk_size = commandOptions.chunk_size || configOptions.chunk_size
const secret_key = configOptions.secret_key || ''

return {
wallet,
password,
node_url,
chunk_size,
secret_key,
}
thomas-senechal marked this conversation as resolved.
Show resolved Hide resolved
}
2 changes: 1 addition & 1 deletion cli/src/commands/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const deleteCommand = new Command('delete')
.description('Delete the given website from Massa blockchain')
.argument('<address>', 'Address of the website to delete')
.action(async (address, _, command) => {
const globalOptions = command.parent?.opts()
const globalOptions = command.optsWithGlobals()

if (!globalOptions) {
throw new Error(
Expand Down
2 changes: 1 addition & 1 deletion cli/src/commands/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const listFilesCommand = new Command('list')
.description('Lists files from the given website on Massa blockchain')
.option('-a, --address <address>', 'Address of the website to list')
.action(async (options, command) => {
const globalOptions = command.parent?.opts()
const globalOptions = command.optsWithGlobals()

if (!globalOptions) {
throw new Error(
pivilartisant marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
2 changes: 1 addition & 1 deletion cli/src/commands/showFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const showFileCommand = new Command('show')
.argument('<file_path>', 'Path of the file to show')
.option('-a, --address <address>', 'Address of the website to edit')
.action(async (filePath, options, command) => {
const globalOptions = command.parent?.opts()
const globalOptions = command.optsWithGlobals()

if (!globalOptions) {
throw new Error(
Expand Down
7 changes: 5 additions & 2 deletions cli/src/commands/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ export const uploadCommand = new Command('upload')
DEFAULT_CHUNK_SIZE.toString()
)
.action(async (websiteDirPath, options, command) => {
const globalOptions = command.parent?.opts()
const globalOptions = command.optsWithGlobals()

// throw if global options are not defined
pivilartisant marked this conversation as resolved.
Show resolved Hide resolved
if (!globalOptions) {
throw new Error(
'Global options are not defined. This should never happen.'
Expand All @@ -39,7 +40,9 @@ export const uploadCommand = new Command('upload')

const provider = await makeProviderFromNodeURLAndSecret(globalOptions)

const chunkSize = parseInt(options.chunkSize)
// set chunksize from options or config
const chunkSize =
parseInt(options.chunkSize) || (globalOptions.chunk_size as number)
thomas-senechal marked this conversation as resolved.
Show resolved Hide resolved

const ctx: UploadCtx = {
provider: provider,
Expand Down
66 changes: 41 additions & 25 deletions cli/src/commands/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,41 @@ import { parse as yamlParse } from 'yaml'

const KEY_ENV_NAME = 'SECRET_KEY'

/**
* Load the keypair using environment variables, secret_key, or wallet file
* @param globalOptions - the global options
* @returns the keypair
*/
async function loadKeyPair(globalOptions: OptionValues): Promise<KeyPair> {
try {
const envSecretKey = process.env[KEY_ENV_NAME]

if (envSecretKey) {
return await KeyPair.fromEnv(KEY_ENV_NAME)
}

if (globalOptions.secret_key) {
return await KeyPair.fromPrivateKey(globalOptions.secret_key)
}

if (
globalOptions.wallet &&
globalOptions.password &&
globalOptions.wallet.endsWith('.yaml')
) {
return await importFromYamlKeyStore(
globalOptions.wallet,
globalOptions.password
)
}

throw new Error('No valid method to load keypair.')
} catch (error) {
console.warn(`Failed to initialize keyPair: ${error}`)
throw error
}
}

/**
* Make a provider from the node URL and secret key
* @param globalOptions - the global options
Expand All @@ -23,33 +58,14 @@ export async function makeProviderFromNodeURLAndSecret(
throw new Error('node_url is not defined. Please use --node_url to set one')
}

var keyPair: KeyPair
if (
(globalOptions.wallet && !globalOptions.password) ||
(!globalOptions.wallet && globalOptions.password)
) {
throw new Error('Both wallet and password must be provided together.')
}

if (globalOptions.wallet && globalOptions.password) {
if (!globalOptions.wallet.endsWith('.yaml')) {
throw new Error('Wallet file must be a YAML file')
}
try {
const keyPair = await loadKeyPair(globalOptions)

keyPair = await importFromYamlKeyStore(
globalOptions.wallet,
globalOptions.password
)
} else {
keyPair = await KeyPair.fromEnv(KEY_ENV_NAME)
return Web3Provider.fromRPCUrl(globalOptions.node_url as string, keyPair)
} catch (error) {
console.error(`Failed to initialize provider: ${error}`)
throw new Error('Failed to initialize provider with any available method')
}

const provider = Web3Provider.fromRPCUrl(
globalOptions.node_url as string,
keyPair
)

return provider
}

/**
Expand Down
24 changes: 23 additions & 1 deletion cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { listFilesCommand } from './commands/list'
import { showFileCommand } from './commands/showFile'
import { uploadCommand } from './commands/upload'

import { existsSync } from 'fs'
import { mergeConfigAndOptions, parseConfigFile } from './commands/config'

const version = process.env.VERSION || 'dev'
const defaultConfigPath = 'deweb_cli_config.json'
const defaultNodeUrl = PublicApiUrl.Buildnet
Expand All @@ -26,4 +29,23 @@ program.addCommand(deleteCommand)
program.addCommand(listFilesCommand)
program.addCommand(showFileCommand)

program.parse(process.argv)
interface OptionValues {
config: string
node_url: string
wallet: string
password: string
}

const commandOptions: OptionValues = program.opts() as OptionValues

if (existsSync(commandOptions.config)) {
const configOptions = parseConfigFile(commandOptions.config)

// commandOptions get priority over configOptions
const programOptions = mergeConfigAndOptions(commandOptions, configOptions)
for (const [key, value] of Object.entries(programOptions)) {
program.setOptionValue(key, value)
}
}

program.parse()
Loading