v / cmd / tools / vpm / vcs.v
118 lines · 108 sloc · 3.6 KB · 0db62d57ca270e47d8ca10e7e20bebac25d391bb
Raw
1module main
2
3import os
4import semver
5
6// Supported version control system commands.
7enum VCS {
8 git
9 hg
10}
11
12struct VCSInfo {
13 dir string @[required]
14 args struct {
15 install string @[required]
16 version string @[required] // flag to specify a version, added to install.
17 path string @[required] // flag to specify a path. E.g., used to explicitly work on a path during multithreaded updating.
18 update string @[required]
19 outdated []string @[required]
20 }
21}
22
23const vcs_info = init_vcs_info() or {
24 vpm_error(err.msg())
25 exit(1)
26}
27
28fn init_vcs_info() !map[VCS]VCSInfo {
29 git_installed_raw_ver := parse_git_version(os.execute_opt('git --version')!.output) or { '' }
30 git_installed_ver := semver.from(git_installed_raw_ver)!
31 git_submod_filter_ver := semver.from('2.36.0')!
32 mut git_install_cmd := 'clone --recursive'
33 if os.user_os() != 'windows' {
34 // The variation of environment factors on windows is too high;
35 // the following options are known to work well on != windows,
36 // but can sometimes cause failures on windows for yet unknown reasons,
37 // see https://discord.com/channels/592103645835821068/665558664949530644/1345422482974310440
38 // for more details, about why this is now allowed only on != windows platforms.
39 git_install_cmd += ' --filter=blob:none'
40 if git_installed_ver >= git_submod_filter_ver {
41 git_install_cmd += ' --shallow-submodules'
42 git_install_cmd += ' --also-filter-submodules'
43 }
44 }
45 return {
46 VCS.git: VCSInfo{
47 dir: '.git'
48 args: struct {
49 install: git_install_cmd
50 version: '--single-branch -b'
51 update: 'pull --recurse-submodules' // pulling with `--depth=1` leads to conflicts when the upstream has more than 1 new commits.
52 path: '-C'
53 outdated: ['fetch', 'rev-parse @', 'rev-parse @{u}']
54 }
55 }
56 VCS.hg: VCSInfo{
57 dir: '.hg'
58 args: struct {
59 install: 'clone'
60 version: '--rev'
61 update: 'pull --update'
62 path: '-R'
63 outdated: ['incoming']
64 }
65 }
66 }
67}
68
69fn (vcs VCS) clone(url string, version string, path string) ! {
70 args := vcs_info[vcs].args
71 version_opt := if version != '' { '${args.version} ${version}' } else { '' }
72 cmd := [vcs.str(), args.install, version_opt, url, os.quoted_path(path)].join(' ')
73 vpm_log(@FILE_LINE, @FN, 'cmd: ${cmd}')
74 res := os.execute_opt(cmd)!
75 vpm_log(@FILE_LINE, @FN, 'cmd output: ${res.output}')
76}
77
78fn (vcs &VCS) is_executable() ! {
79 cmd := vcs.str()
80 os.find_abs_path_of_executable(cmd) or {
81 return error('VPM requires that `${cmd}` is executable.')
82 }
83}
84
85fn vcs_used_in_dir(dir string) ?VCS {
86 for vcs, info in vcs_info {
87 if os.is_dir(os.real_path(os.join_path(dir, info.dir))) {
88 return vcs
89 }
90 }
91 return none
92}
93
94fn vcs_from_str(str string) ?VCS {
95 return match str {
96 'git' { .git }
97 'hg' { .hg }
98 else { none }
99 }
100}
101
102// parse_git_version retrieves only the stable version part of the output of `git version`.
103// For example: parse_git_version('git version 2.39.3')! will return just '2.39.3'.
104pub fn parse_git_version(version string) !string {
105 git_version_start := 'git version '
106 // The output from `git version` varies, depending on how git was compiled. Here are some examples:
107 // `git version 2.44.0` when compiled from source, or from brew on macos.
108 // `git version 2.39.3 (Apple Git-146)` on macos with XCode's cli tools.
109 // `git version 2.44.0.windows.1` on windows's Git Bash shell.
110 if !version.starts_with(git_version_start) {
111 return error('should start with `${git_version_start}`')
112 }
113 suffixed := version.all_after(git_version_start).all_before(' ').trim_space()
114 parts := suffixed.split('.')
115 pure_version_parts := parts[0..3]
116 spure := pure_version_parts.join('.')
117 return spure
118}
119