20
20
is_installed bool
21
21
is_external bool
22
22
vcs ? VCS
23
+ manifest vmod.Manifest
23
24
}
24
25
25
26
struct ModuleVpmInfo {
@@ -43,7 +44,10 @@ struct ErrorOptions {
43
44
verbose bool // is used to only output the error message if the verbose setting is enabled.
44
45
}
45
46
46
- const home_dir = os.home_dir ()
47
+ const (
48
+ vexe = os.quoted_path (os.getenv ('VEXE' ))
49
+ home_dir = os.home_dir ()
50
+ )
47
51
48
52
fn parse_query (query []string ) ([]Module, []Module) {
49
53
mut vpm_modules, mut external_modules := []Module{}, []Module{}
@@ -61,26 +65,30 @@ fn parse_query(query []string) ([]Module, []Module) {
61
65
false
62
66
}
63
67
mut mod := if is_http || ident.starts_with ('https://' ) {
64
- // External module.
68
+ // External module. The idenifier is an URL.
65
69
publisher , name := get_ident_from_url (ident) or {
66
70
vpm_error (err.msg ())
67
71
errors++
68
72
continue
69
73
}
70
- // Resolve path, verify existence of manifest.
71
- base := if is_http { publisher } else { '' }
72
- install_path := normalize_mod_path (os.real_path (os.join_path (settings.vmodules_path,
73
- base, name)))
74
- if is_git_setting && ! has_vmod (ident, install_path) {
75
- vpm_error ('failed to find `v.mod` for `${ident} `.' )
74
+ // Fetch manifest.
75
+ manifest := fetch_manifest (name, ident, version, is_git_setting) or {
76
+ vpm_error ('failed to find `v.mod` for `${ident}${at_version(version)} `.' ,
77
+ details: err.msg ()
78
+ )
76
79
errors++
77
80
continue
78
81
}
82
+ // Resolve path.
83
+ base := if is_http { publisher } else { '' }
84
+ install_path := normalize_mod_path (os.real_path (os.join_path (settings.vmodules_path,
85
+ base, manifest.name)))
79
86
Module{
80
- name: name
87
+ name: manifest. name
81
88
url: ident
82
89
install_path: install_path
83
90
is_external: true
91
+ manifest: manifest
84
92
}
85
93
} else {
86
94
// VPM registered module.
@@ -112,11 +120,9 @@ fn parse_query(query []string) ([]Module, []Module) {
112
120
errors++
113
121
continue
114
122
}
115
- // Resolve path, verify existence of manifest.
116
- ident_as_path := info.name.replace ('.' , os.path_separator)
117
- install_path := normalize_mod_path (os.real_path (os.join_path (settings.vmodules_path,
118
- ident_as_path)))
119
- if is_git_module && ! has_vmod (info.url, install_path) {
123
+ // Fetch manifest.
124
+ manifest := fetch_manifest (info.name, info.url, version, is_git_module) or {
125
+ // Add link with issue template requesting to add a manifest.
120
126
mut details := ''
121
127
if resp := http.head ('${info.url} /issues/new' ) {
122
128
if resp.status_code == 200 {
@@ -125,34 +131,23 @@ fn parse_query(query []string) ([]Module, []Module) {
125
131
}
126
132
}
127
133
vpm_warn ('`${info.name} ` is missing a manifest file.' , details: details)
134
+ vpm_log (@FILE_LINE, @FN, 'vpm manifest detection error: ${err} ' )
135
+ vmod.Manifest{}
128
136
}
137
+ // Resolve path.
138
+ ident_as_path := info.name.replace ('.' , os.path_separator)
139
+ install_path := normalize_mod_path (os.real_path (os.join_path (settings.vmodules_path,
140
+ ident_as_path)))
129
141
Module{
130
142
name: info.name
131
143
url: info.url
132
144
vcs: vcs
133
145
install_path: install_path
146
+ manifest: manifest
134
147
}
135
148
}
136
149
mod.version = version
137
- mod.install_path_fmted = fmt_mod_path (mod.install_path)
138
- if refs := os.execute_opt ('git ls-remote --refs ${mod.install_path} ' ) {
139
- mod.is_installed = true
140
- // In case the head just temporarily matches a tag, make sure that there
141
- // really is a version installation before adding it as `installed_version`.
142
- // NOTE: can be refined for branch installations. E.g., for `sdl`.
143
- if refs.output.contains ('refs/tags/' ) {
144
- tag := refs.output.all_after_last ('refs/tags/' ).trim_space ()
145
- head := if refs.output.contains ('refs/heads/' ) {
146
- refs.output.all_after_last ('refs/heads/' ).trim_space ()
147
- } else {
148
- tag
149
- }
150
- vpm_log (@FILE_LINE, @FN, 'head: ${head} , tag: ${tag} ' )
151
- if tag == head {
152
- mod.installed_version = tag
153
- }
154
- }
155
- }
150
+ mod.get_installed ()
156
151
if mod.is_external {
157
152
external_modules << mod
158
153
} else {
@@ -171,23 +166,59 @@ fn parse_query(query []string) ([]Module, []Module) {
171
166
return vpm_modules, external_modules
172
167
}
173
168
174
- fn has_vmod (url string , install_path string ) bool {
175
- if install_path != '' && os.exists ((os.join_path (install_path, 'v.mod' ))) {
176
- // Skip fetching the repo when the module is already installed and has a `v.mod`.
177
- return true
178
- }
179
- head_branch := os.execute_opt ('git ls-remote --symref ${url} HEAD' ) or {
180
- vpm_error ('failed to find git HEAD for `${url} `.' , details: err.msg ())
181
- return false
182
- }.output.all_after_last ('/' ).all_before (' ' ).all_before ('\t ' )
169
+ // TODO: add unit test
170
+ fn (mut m Module) get_installed () {
171
+ refs := os.execute_opt ('git ls-remote --refs ${m.install_path} ' ) or { return }
172
+ m.is_installed = true
173
+ // In case the head just temporarily matches a tag, make sure that there
174
+ // really is a version installation before adding it as `installed_version`.
175
+ // NOTE: can be refined for branch installations. E.g., for `sdl`.
176
+ if refs.output.contains ('refs/tags/' ) {
177
+ tag := refs.output.all_after_last ('refs/tags/' ).all_before ('\n ' ).trim_space ()
178
+ head := if refs.output.contains ('refs/heads/' ) {
179
+ refs.output.all_after_last ('refs/heads/' ).all_before ('\n ' ).trim_space ()
180
+ } else {
181
+ tag
182
+ }
183
+ vpm_log (@FILE_LINE, @FN, 'head: ${head} , tag: ${tag} ' )
184
+ if tag == head {
185
+ m.installed_version = tag
186
+ }
187
+ }
188
+ }
189
+
190
+ fn fetch_manifest (name string , url string , version string , is_git bool ) ! vmod.Manifest {
191
+ if ! is_git {
192
+ // TODO: fetch manifest for mercurial repositories
193
+ return vmod.Manifest{
194
+ name: name
195
+ }
196
+ }
197
+ v := if version != '' {
198
+ version
199
+ } else {
200
+ head_branch := os.execute_opt ('git ls-remote --symref ${url} HEAD' ) or {
201
+ return error ('failed to find git HEAD. ${err} ' )
202
+ }
203
+ head_branch.output.all_after_last ('/' ).all_before (' ' ).all_before ('\t ' )
204
+ }
183
205
url_ := if url.ends_with ('.git' ) { url.replace ('.git' , '' ) } else { url }
184
- manifest_url := '${url_} /blob/${head_branch} /v.mod'
185
- vpm_log (@FILE_LINE, @FN, 'manifest_url: ${manifest_url} ' )
186
- has_vmod := http.head (manifest_url) or {
187
- vpm_error ('failed to retrieve module data for `${url} `.' )
188
- return false
189
- }.status_code == 200
190
- return has_vmod
206
+ // Scan both URLS. E.g.:
207
+ // https://github.com/publisher/module/raw/v0.7.0/v.mod
208
+ // https://gitlab.com/publisher/module/-/raw/main/v.mod
209
+ raw_paths := ['raw/' , '/-/raw/' ]
210
+ for i, raw_p in raw_paths {
211
+ manifest_url := '${url_} /${raw_p} /${v} /v.mod'
212
+ vpm_log (@FILE_LINE, @FN, 'manifest_url ${i} : ${manifest_url} ' )
213
+ raw_manifest_resp := http.get (manifest_url) or { continue }
214
+ if raw_manifest_resp.status_code != 200 {
215
+ return error ('unsuccessful response status `${raw_manifest_resp.status_code} `.' )
216
+ }
217
+ return vmod.decode (raw_manifest_resp.body) or {
218
+ return error ('failed to decode manifest `${raw_manifest_resp.body} `. ${err} ' )
219
+ }
220
+ }
221
+ return error ('failed to retrieve manifest.' )
191
222
}
192
223
193
224
fn get_mod_date_info (mut pp pool.PoolProcessor, idx int , wid int ) & ModuleDateInfo {
0 commit comments