mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
vpm: add support for other VCS for modules, like hg
Add ability to pass flags to vpm. Implement -help , -verbose and -server-url , to enable easier diagnostics and testing with a local server.
This commit is contained in:
parent
ddd83c2108
commit
aeda48d94e
214
tools/vpm.v
214
tools/vpm.v
@ -3,14 +3,25 @@ module main
|
|||||||
import (
|
import (
|
||||||
net.http
|
net.http
|
||||||
os
|
os
|
||||||
|
os.cmdline
|
||||||
json
|
json
|
||||||
filepath
|
filepath
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// url = 'http://localhost:8089'
|
default_vpm_server_urls = ['https://vpm.best', 'https://vpm.vlang.io']
|
||||||
url = 'https://vpm.best'
|
|
||||||
valid_vpm_commands = ['help', 'search', 'install', 'update', 'remove']
|
valid_vpm_commands = ['help', 'search', 'install', 'update', 'remove']
|
||||||
|
excluded_dirs = ['cache', 'vlib']
|
||||||
|
supported_vcs_systems = ['git', 'hg']
|
||||||
|
supported_vcs_folders = ['.git', '.hg']
|
||||||
|
supported_vcs_update_cmds = {
|
||||||
|
'git': 'git pull --depth=1'
|
||||||
|
'hg': 'hg pull --update'
|
||||||
|
}
|
||||||
|
supported_vcs_install_cmds = {
|
||||||
|
'git': 'git clone --depth=1'
|
||||||
|
'hg': 'hg clone'
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
struct Mod {
|
struct Mod {
|
||||||
@ -18,6 +29,7 @@ struct Mod {
|
|||||||
name string
|
name string
|
||||||
url string
|
url string
|
||||||
nr_downloads int
|
nr_downloads int
|
||||||
|
vcs string
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Vmod {
|
struct Vmod {
|
||||||
@ -28,17 +40,19 @@ mut:
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
ensure_vmodules_dir_exist()
|
init_settings()
|
||||||
change_to_vmodules_dir()
|
|
||||||
// This tool is intended to be launched by the v frontend,
|
// This tool is intended to be launched by the v frontend,
|
||||||
// which provides the path to V inside os.getenv('VEXE')
|
// which provides the path to V inside os.getenv('VEXE')
|
||||||
args := os.args // args are: vpm SUBCOMMAND module names
|
args := os.args // args are: vpm [options] SUBCOMMAND module names
|
||||||
if args.len < 2 {
|
params := cmdline.only_non_options(args[1..])
|
||||||
|
verbose_println('cli params: $params')
|
||||||
|
if params.len < 1 {
|
||||||
vpm_help([])
|
vpm_help([])
|
||||||
exit(5)
|
exit(5)
|
||||||
}
|
}
|
||||||
vpm_command := args[1]
|
vpm_command := params[0]
|
||||||
module_names := args[2..]
|
module_names := params[1..]
|
||||||
|
ensure_vmodules_dir_exist()
|
||||||
// println('module names: ') println(module_names)
|
// println('module names: ') println(module_names)
|
||||||
match vpm_command {
|
match vpm_command {
|
||||||
'help' {
|
'help' {
|
||||||
@ -68,11 +82,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn vpm_search(keywords []string) {
|
fn vpm_search(keywords []string) {
|
||||||
if user_asks_for_help(keywords) {
|
if settings.is_help {
|
||||||
println('Usage:')
|
println('Usage:')
|
||||||
println(' v search keyword1 [keyword2] [...]')
|
println(' v search keyword1 [keyword2] [...]')
|
||||||
println(' ^^^^^^^^^^^^^^^^^ will search https://vpm.vlang.io/ for matching modules,')
|
println(' ^^^^^^^^^^^^^^^^^ will search https://vpm.vlang.io/ for matching modules,')
|
||||||
println(' and will show details about them')
|
println(' and will show details about them')
|
||||||
|
show_vpm_options()
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
if keywords.len == 0 {
|
if keywords.len == 0 {
|
||||||
@ -109,10 +124,11 @@ fn vpm_search(keywords []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn vpm_install(module_names []string) {
|
fn vpm_install(module_names []string) {
|
||||||
if user_asks_for_help(module_names) {
|
if settings.is_help {
|
||||||
println('Usage:')
|
println('Usage:')
|
||||||
println(' v install module [module] [module] [...]')
|
println(' v install module [module] [module] [...]')
|
||||||
println(' ^^^^^^^^^^^^^ will install the modules you specified')
|
println(' ^^^^^^^^^^^^^ will install the modules you specified')
|
||||||
|
show_vpm_options()
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
if module_names.len == 0 {
|
if module_names.len == 0 {
|
||||||
@ -120,11 +136,15 @@ fn vpm_install(module_names []string) {
|
|||||||
exit(2)
|
exit(2)
|
||||||
}
|
}
|
||||||
mut errors := 0
|
mut errors := 0
|
||||||
|
url := get_working_server_url()
|
||||||
for n in module_names {
|
for n in module_names {
|
||||||
name := n.trim_space()
|
name := n.trim_space()
|
||||||
modurl := url + '/jsmod/$name'
|
modurl := url + '/jsmod/$name'
|
||||||
r := http.get(modurl) or {
|
r := http.get(modurl) or {
|
||||||
panic(err)
|
println('Http server did not respond to our request for ${modurl}.')
|
||||||
|
println('Error details: $err')
|
||||||
|
errors++
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if r.status_code == 404 {
|
if r.status_code == 404 {
|
||||||
println('Skipping module "$name", since $url reported that "$name" does not exist.')
|
println('Skipping module "$name", since $url reported that "$name" does not exist.')
|
||||||
@ -148,16 +168,36 @@ fn vpm_install(module_names []string) {
|
|||||||
println('Skipping module "$name", since it is missing name or url information.')
|
println('Skipping module "$name", since it is missing name or url information.')
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
final_module_path := os.realpath(filepath.join(get_vmodules_dir_path(),mod.name.replace('.', os.path_separator)))
|
mut vcs := mod.vcs
|
||||||
|
if vcs == '' {
|
||||||
|
vcs = supported_vcs_systems[0]
|
||||||
|
}
|
||||||
|
if !vcs in supported_vcs_systems {
|
||||||
|
errors++
|
||||||
|
println('Skipping module "$name", since it uses an unsupported VCS {$vcs} .')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
final_module_path := os.realpath(filepath.join(settings.vmodules_path,mod.name.replace('.', os.path_separator)))
|
||||||
if os.exists(final_module_path) {
|
if os.exists(final_module_path) {
|
||||||
vpm_update([name])
|
vpm_update([name])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
println('Installing module "$name" from $mod.url to $final_module_path ...')
|
println('Installing module "$name" from $mod.url to $final_module_path ...')
|
||||||
os.exec('git clone --depth=1 "$mod.url" "$final_module_path"') or {
|
vcs_install_cmd := supported_vcs_install_cmds[vcs]
|
||||||
|
cmd := '${vcs_install_cmd} "${mod.url}" "${final_module_path}"'
|
||||||
|
verbose_println(' command: $cmd')
|
||||||
|
cmdres := os.exec(cmd) or {
|
||||||
errors++
|
errors++
|
||||||
println('Could not install module "$name" to "$final_module_path" .')
|
println('Could not install module "$name" to "$final_module_path" .')
|
||||||
println('Error details: $err')
|
verbose_println('Error command: $cmd')
|
||||||
|
verbose_println('Error details: $err')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cmdres.exit_code != 0 {
|
||||||
|
errors++
|
||||||
|
println('Failed installing module "$name" to "$final_module_path" .')
|
||||||
|
verbose_println('Failed command: ${cmd}')
|
||||||
|
verbose_println('Failed command output:\n${cmdres.output}')
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
resolve_dependencies(name, final_module_path, module_names)
|
resolve_dependencies(name, final_module_path, module_names)
|
||||||
@ -169,12 +209,13 @@ fn vpm_install(module_names []string) {
|
|||||||
|
|
||||||
fn vpm_update(m []string) {
|
fn vpm_update(m []string) {
|
||||||
mut module_names := m
|
mut module_names := m
|
||||||
if user_asks_for_help(module_names) {
|
if settings.is_help {
|
||||||
println('Usage: ')
|
println('Usage: ')
|
||||||
println(' a) v update module [module] [module] [...]')
|
println(' a) v update module [module] [module] [...]')
|
||||||
println(' ^^^^^^^^^^^^ will update the listed modules to their latest versions')
|
println(' ^^^^^^^^^^^^ will update the listed modules to their latest versions')
|
||||||
println(' b) v update')
|
println(' b) v update')
|
||||||
println(' ^^^^^^^^^^^^ will update ALL installed modules to their latest versions')
|
println(' ^^^^^^^^^^^^ will update ALL installed modules to their latest versions')
|
||||||
|
show_vpm_options()
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
if module_names.len == 0 {
|
if module_names.len == 0 {
|
||||||
@ -182,17 +223,29 @@ fn vpm_update(m []string) {
|
|||||||
}
|
}
|
||||||
mut errors := 0
|
mut errors := 0
|
||||||
for name in module_names {
|
for name in module_names {
|
||||||
final_module_path := os.realpath(filepath.join(get_vmodules_dir_path(),name.replace('.', os.path_separator)))
|
final_module_path := valid_final_path_of_existing_module(name) or {
|
||||||
if !os.exists(final_module_path) {
|
|
||||||
println('No module with name "$name" exists at $final_module_path')
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
os.chdir(final_module_path)
|
os.chdir(final_module_path)
|
||||||
println('Updating module "$name"...')
|
println('Updating module "$name"...')
|
||||||
os.exec('git pull --depth=1') or {
|
verbose_println(' work folder: $final_module_path')
|
||||||
|
vcs := vcs_used_in_dir(final_module_path) or {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vcs_cmd := supported_vcs_update_cmds[vcs[0]]
|
||||||
|
verbose_println(' command: $vcs_cmd')
|
||||||
|
vcs_res := os.exec('${vcs_cmd}') or {
|
||||||
errors++
|
errors++
|
||||||
println('Could not update module "$name".')
|
println('Could not update module "$name".')
|
||||||
println('Error details: $err')
|
verbose_println('Error command: ${vcs_cmd}')
|
||||||
|
verbose_println('Error details:\n$err')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if vcs_res.exit_code != 0 {
|
||||||
|
errors++
|
||||||
|
println('Failed updating module "${name}".')
|
||||||
|
verbose_println('Failed command: ${vcs_cmd}')
|
||||||
|
verbose_println('Failed details:\n${vcs_res.output}')
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
resolve_dependencies(name, final_module_path, module_names)
|
resolve_dependencies(name, final_module_path, module_names)
|
||||||
@ -203,12 +256,13 @@ fn vpm_update(m []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn vpm_remove(module_names []string) {
|
fn vpm_remove(module_names []string) {
|
||||||
if user_asks_for_help(module_names) {
|
if settings.is_help {
|
||||||
println('Usage: ')
|
println('Usage: ')
|
||||||
println(' a) v remove module [module] [module] [...]')
|
println(' a) v remove module [module] [module] [...]')
|
||||||
println(' ^^^^^^^^^^^^ will remove the listed modules')
|
println(' ^^^^^^^^^^^^ will remove the listed modules')
|
||||||
println(' b) v remove')
|
println(' b) v remove')
|
||||||
println(' ^^^^^^^^^^^^ will remove ALL installed modules')
|
println(' ^^^^^^^^^^^^ will remove ALL installed modules')
|
||||||
|
show_vpm_options()
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
if module_names.len == 0 {
|
if module_names.len == 0 {
|
||||||
@ -216,45 +270,49 @@ fn vpm_remove(module_names []string) {
|
|||||||
exit(2)
|
exit(2)
|
||||||
}
|
}
|
||||||
for name in module_names {
|
for name in module_names {
|
||||||
final_module_path := os.realpath(filepath.join(get_vmodules_dir_path(),name.replace('.', os.path_separator)))
|
final_module_path := valid_final_path_of_existing_module(name) or {
|
||||||
if !os.exists(final_module_path) {
|
|
||||||
println('No module with name "$name" exists at $final_module_path')
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
println('Removing module "$name"...')
|
println('Removing module "$name"...')
|
||||||
|
verbose_println('removing folder $final_module_path')
|
||||||
os.rmdir_recursive(final_module_path)
|
os.rmdir_recursive(final_module_path)
|
||||||
// delete author directory if it is empty
|
// delete author directory if it is empty
|
||||||
author := name.split('.')[0]
|
author := name.split('.')[0]
|
||||||
author_dir := os.realpath(filepath.join(get_vmodules_dir_path(), author))
|
author_dir := os.realpath(filepath.join(settings.vmodules_path,author))
|
||||||
if os.is_dir_empty(author_dir) {
|
if os.is_dir_empty(author_dir) {
|
||||||
|
verbose_println('removing author folder $author_dir')
|
||||||
os.rmdir(author_dir)
|
os.rmdir(author_dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_vmodules_dir_path() string {
|
fn valid_final_path_of_existing_module(name string) ?string {
|
||||||
return os.home_dir() + '.vmodules'
|
name_of_vmodules_folder := filepath.join(settings.vmodules_path,name.replace('.', os.path_separator))
|
||||||
|
final_module_path := os.realpath(name_of_vmodules_folder)
|
||||||
|
if !os.exists(final_module_path) {
|
||||||
|
println('No module with name "$name" exists at $name_of_vmodules_folder')
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
if !os.is_dir(final_module_path) {
|
||||||
|
println('Skipping "$name_of_vmodules_folder", since it is not a folder.')
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
vcs_used_in_dir(final_module_path) or {
|
||||||
|
println('Skipping "$name_of_vmodules_folder", since it does not use a supported vcs.')
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
return final_module_path
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_vmodules_dir_exist() {
|
fn ensure_vmodules_dir_exist() {
|
||||||
home_vmodules := get_vmodules_dir_path()
|
if !os.is_dir(settings.vmodules_path) {
|
||||||
if !os.is_dir(home_vmodules) {
|
println('Creating $settings.vmodules_path/ ...')
|
||||||
println('Creating $home_vmodules/ ...')
|
os.mkdir(settings.vmodules_path) or {
|
||||||
os.mkdir(home_vmodules) or {
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_to_vmodules_dir() {
|
|
||||||
os.chdir(get_vmodules_dir_path())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn user_asks_for_help(module_names []string) bool {
|
|
||||||
return ('-h' in module_names) || ('--help' in module_names) || ('help' in module_names)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vpm_help(module_names []string) {
|
fn vpm_help(module_names []string) {
|
||||||
println('Usage:')
|
println('Usage:')
|
||||||
println(' a) v install module [module] [module] [...]')
|
println(' a) v install module [module] [module] [...]')
|
||||||
@ -265,20 +323,38 @@ fn vpm_help(module_names []string) {
|
|||||||
println(' You can also pass -h or --help after each vpm command from the above, to see more details about it.')
|
println(' You can also pass -h or --help after each vpm command from the above, to see more details about it.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn vcs_used_in_dir(dir string) ?[]string {
|
||||||
|
mut vcs := []string
|
||||||
|
for repo_subfolder in supported_vcs_folders {
|
||||||
|
checked_folder := os.realpath(filepath.join(dir,repo_subfolder))
|
||||||
|
if os.is_dir(checked_folder) {
|
||||||
|
vcs << repo_subfolder.replace('.', '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vcs.len == 0 {
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
return vcs
|
||||||
|
}
|
||||||
|
|
||||||
fn get_installed_modules() []string {
|
fn get_installed_modules() []string {
|
||||||
dirs := os.ls(get_vmodules_dir_path()) or {
|
dirs := os.ls(settings.vmodules_path) or {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
mut modules := []string
|
mut modules := []string
|
||||||
for dir in dirs {
|
for dir in dirs {
|
||||||
if dir in ['cache', 'vlib'] || !os.is_dir(dir) {
|
adir := filepath.join(settings.vmodules_path,dir)
|
||||||
|
if dir in excluded_dirs || !os.is_dir(adir) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
author := dir
|
author := dir
|
||||||
mods := os.ls('${filepath.join(get_vmodules_dir_path(), dir)}') or {
|
mods := os.ls(adir) or {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for m in mods {
|
for m in mods {
|
||||||
|
vcs_used_in_dir(filepath.join(adir,m)) or {
|
||||||
|
continue
|
||||||
|
}
|
||||||
modules << '${author}.$m'
|
modules << '${author}.$m'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,6 +362,7 @@ fn get_installed_modules() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_modules() []string {
|
fn get_all_modules() []string {
|
||||||
|
url := get_working_server_url()
|
||||||
r := http.get(url) or {
|
r := http.get(url) or {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -340,6 +417,7 @@ fn resolve_dependencies(name, module_path string, module_names []string) {
|
|||||||
}
|
}
|
||||||
if deps.len > 0 {
|
if deps.len > 0 {
|
||||||
println('Resolving ${deps.len} dependencies for module "$name"...')
|
println('Resolving ${deps.len} dependencies for module "$name"...')
|
||||||
|
verbose_println('Found dependencies: $deps')
|
||||||
vpm_install(deps)
|
vpm_install(deps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -366,3 +444,53 @@ fn parse_vmod(data string) Vmod {
|
|||||||
}
|
}
|
||||||
return vmod
|
return vmod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_working_server_url() string {
|
||||||
|
server_urls := if settings.server_urls.len > 0 { settings.server_urls } else { default_vpm_server_urls }
|
||||||
|
for url in server_urls {
|
||||||
|
verbose_println('Trying server url: $url')
|
||||||
|
http.head(url) or {
|
||||||
|
verbose_println(' $url failed.')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
panic('No responding vpm server found. Please check your network connectivity and try again later.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// settings context:
|
||||||
|
struct VpmSettings {
|
||||||
|
mut:
|
||||||
|
is_help bool
|
||||||
|
is_verbose bool
|
||||||
|
server_urls []string
|
||||||
|
vmodules_path string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
settings = &VpmSettings{}
|
||||||
|
)
|
||||||
|
|
||||||
|
fn init_settings() {
|
||||||
|
mut s := &VpmSettings(0)
|
||||||
|
unsafe{
|
||||||
|
s = settings
|
||||||
|
}
|
||||||
|
s.is_help = '-h' in os.args || '--help' in os.args || 'help' in os.args
|
||||||
|
s.is_verbose = '-verbose' in os.args || '--verbose' in os.args
|
||||||
|
s.server_urls = cmdline.many_values(os.args, '-server-url')
|
||||||
|
s.vmodules_path = os.home_dir() + '.vmodules'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_vpm_options() {
|
||||||
|
println('Options:')
|
||||||
|
println(' -help - Show usage info')
|
||||||
|
println(' -verbose - Print more details about the performed operation')
|
||||||
|
println(' -server-url - When doing network operations, use this vpm server. Can be given multiple times.')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verbose_println(s string) {
|
||||||
|
if settings.is_verbose {
|
||||||
|
println(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -54,6 +54,16 @@ pub fn post(url, data string) ?Response {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn head(url string) ?Response {
|
||||||
|
req := new_request('HEAD', url, '') or {
|
||||||
|
return error(err)
|
||||||
|
}
|
||||||
|
res := req.do() or {
|
||||||
|
return error(err)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// new_request creates a new HTTP request
|
// new_request creates a new HTTP request
|
||||||
pub fn new_request(typ, _url, _data string) ?Request {
|
pub fn new_request(typ, _url, _data string) ?Request {
|
||||||
if _url == '' {
|
if _url == '' {
|
||||||
|
Loading…
Reference in New Issue
Block a user