Gentoo Archives: gentoo-dev

From: "Michał Górny" <mgorny@g.o>
To: gentoo-dev@l.g.o
Cc: "Robin H. Johnson" <robbat2@g.o>
Subject: Re: [gentoo-dev] [PATCH 1/3] eclass/go-module: add support for building based on go.sum
Date: Thu, 13 Feb 2020 16:58:10
Message-Id: 092ae60dfccaac8975d224b048fc1daf87a77a7a.camel@gentoo.org
In Reply to: [gentoo-dev] [PATCH 1/3] eclass/go-module: add support for building based on go.sum by "Robin H. Johnson"
1 On Sun, 2020-02-09 at 12:31 -0800, Robin H. Johnson wrote:
2 > EGO_SUM mode now supplements the existing EGO_VENDOR mode.
3 >
4 > EGO_SUM should be populated by the maintainer, directly from the go.sum
5 > file of the root package. See eclass and conversion example
6 > (dev-go/go-tour & app-admin/kube-bench) for further details.
7 >
8 > The go-module_set_globals function performs validation of
9 > inputs and does die on fatal errors.
10 >
11 > Signed-off-by: Robin H. Johnson <robbat2@g.o>
12 > ---
13 > eclass/go-module.eclass | 328 +++++++++++++++++++++++++++++++++++--
14 > profiles/thirdpartymirrors | 1 +
15 > 2 files changed, 311 insertions(+), 18 deletions(-)
16 >
17 > diff --git eclass/go-module.eclass eclass/go-module.eclass
18 > index d5de5f60ccdf..b8a635d52de7 100644
19 > --- eclass/go-module.eclass
20 > +++ eclass/go-module.eclass
21 > @@ -4,22 +4,46 @@
22 > # @ECLASS: go-module.eclass
23 > # @MAINTAINER:
24 > # William Hubbs <williamh@g.o>
25 > +# @AUTHOR:
26 > +# William Hubbs <williamh@g.o>
27 > +# Robin H. Johnson <robbat2@g.o>
28 > # @SUPPORTED_EAPIS: 7
29 > # @BLURB: basic eclass for building software written as go modules
30 > # @DESCRIPTION:
31 > -# This eclass provides basic settings and functions
32 > -# needed by all software written in the go programming language that uses
33 > -# go modules.
34 > +# This eclass provides basic settings and functions needed by all software
35 > +# written in the go programming language that uses go modules.
36 > +#
37 > +# You might know the software you are packaging uses modules because
38 > +# it has files named go.sum and go.mod in its top-level source directory.
39 > +# If it does not have these files, try use the golang-* eclasses FIRST!
40 > +# There ARE legacy Golang packages that use external modules with none of
41 > +# go.mod, go.sum, vendor/ that can use this eclass regardless.
42 > +#
43 > +# Guidelines for usage:
44 > +# "go.mod" && "go.sum" && "vendor/":
45 > +# - pre-vendored package. Do NOT set EGO_SUM or EGO_VENDOR.
46 > +#
47 > +# "go.mod" && "go.sum":
48 > +# - Populate EGO_SUM with entries from go.sum
49 > +# - Do NOT include any lines that contain <version>/go.mod
50 > +#
51 > +# "go.mod" only:
52 > +# - Populate EGO_VENDOR
53 > #
54 > -# You will know the software you are packaging uses modules because
55 > -# it will have files named go.sum and go.mod in its top-level source
56 > -# directory. If it does not have these files, use the golang-* eclasses.
57 > +# None of the above:
58 > +# - Did you try golang-* eclasses first? Upstream has undeclared dependencies
59 > +# (perhaps really old source). You can use either EGO_SUM or EGO_VENDOR.
60 > +
61 > #
62 > -# If it has these files and a directory named vendor in its top-level
63 > -# source directory, you only need to inherit the eclass since upstream
64 > -# is vendoring the dependencies.
65 > +# If it has these files AND a directory named "vendor" in its top-level source
66 > +# directory, you only need to inherit the eclass since upstream has already
67 > +# vendored the dependencies.
68 > +
69 > +# If it does not have a vendor directory, you should use the EGO_SUM
70 > +# variable and the go-module_gosum_uris function as shown in the
71 > +# example below to handle dependencies.
72 > #
73 > -# If it does not have a vendor directory, you should use the EGO_VENDOR
74 > +# Alternatively, older versions of this eclass used the EGO_VENDOR
75 > # variable and the go-module_vendor_uris function as shown in the
76 > # example below to handle dependencies.
77 > #
78 > @@ -28,6 +52,21 @@
79 > # dependencies. So please make sure it is accurate.
80 > #
81 > # @EXAMPLE:
82 > +# @CODE
83 > +#
84 > +# inherit go-module
85 > +#
86 > +# EGO_SUM=(
87 > +# "github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ="
88 > +# "github.com/BurntSushi/toml v0.3.1/go.mod h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ="
89
90 Is it expected that the two entries would have the same hash?
91
92 > +# )
93 > +# S="${WORKDIR}/${MY_P}"
94 > +# go-module_set_globals
95 > +#
96 > +# SRC_URI="https://github.com/example/${PN}/archive/v${PV}.tar.gz -> ${P}.tar.gz
97 > +# ${EGO_SUM_SRC_URI}"
98 > +#
99 > +# @CODE
100 > #
101 > # @CODE
102 > #
103 > @@ -35,7 +74,7 @@
104 > #
105 > # EGO_VENDOR=(
106 > # "github.com/xenolf/lego 6cac0ea7d8b28c889f709ec7fa92e92b82f490dd"
107 > -# "golang.org/x/crypto 453249f01cfeb54c3d549ddb75ff152ca243f9d8 github.com/golang/crypto"
108 > +# "golang.org/x/crypto 453249f01cfeb54c3d549ddb75ff152ca243f9d8 github.com/golang/crypto"
109 > # )
110 > #
111 > # SRC_URI="https://github.com/example/${PN}/archive/v${PV}.tar.gz -> ${P}.tar.gz
112 > @@ -64,10 +103,12 @@ export GO111MODULE=on
113 > export GOCACHE="${T}/go-build"
114 >
115 > # The following go flags should be used for all builds.
116 > -# -mod=vendor stopps downloading of dependencies from the internet.
117 > # -v prints the names of packages as they are compiled
118 > # -x prints commands as they are executed
119 > -export GOFLAGS="-mod=vendor -v -x"
120 > +# -mod=vendor use the vendor directory instead of downloading dependencies
121 > +# -mod=readonly do not update go.mod/go.sum but fail if updates are needed
122 > +export GOFLAGS="-v -x -mod=readonly"
123 > +[[ ${#EGO_VENDOR[@]} -gt 0 ]] && GOFLAGS+=" -mod=vendor"
124 >
125 > # Do not complain about CFLAGS etc since go projects do not use them.
126 > QA_FLAGS_IGNORED='.*'
127 > @@ -75,7 +116,23 @@ QA_FLAGS_IGNORED='.*'
128 > # Go packages should not be stripped with strip(1).
129 > RESTRICT="strip"
130 >
131 > -EXPORT_FUNCTIONS src_unpack pkg_postinst
132 > +EXPORT_FUNCTIONS src_unpack src_prepare pkg_postinst
133
134 Exporting a new phase looks potentially dangerous. Are you sure no
135 ebuilds are broken by this?
136
137 > +
138 > +# @ECLASS-VARIABLE: EGO_SUM
139 > +# @DESCRIPTION:
140 > +# This variable duplicates the go.sum content from inside the target package.
141 > +# Entries of the form <version>/go.mod should be excluded.
142
143 ...but you've included one of them in the example on top of the eclass.
144
145 > +#
146 > +# <module> <version> <hash>
147
148 Now I'm confused. Unless my eyes betray me, PATCH 2 has entries without
149 hash.
150
151 Also, the description fails to mention that you're supposed to quote
152 each line.
153
154 > +#
155 > +# The format is described upstream here:
156 > +# https://tip.golang.org/cmd/go/#hdr-Module_authentication_using_go_sum
157 > +#
158 > +# <hash> is the Hash1 structure used by upstream Go
159 > +# Note that Hash1 is MORE stable than Gentoo distfile hashing, and upstream
160 > +# warns that it's conceptually possible for the Hash1 value to remain stable
161 > +# while the upstream zipfiles change. E.g. it does NOT capture mtime changes in
162 > +# files within a zipfile.
163
164 I think it would be valuable to include an example here as well.
165
166 >
167 > # @ECLASS-VARIABLE: EGO_VENDOR
168 > # @DESCRIPTION:
169 > @@ -106,13 +163,202 @@ go-module_vendor_uris() {
170 > done
171 > }
172 >
173 > +# @ECLASS-VARIABLE: GOMODULE_GOPROXY_BASEURI
174 > +# @DESCRIPTION:
175 > +# Golagg module proxy service to fetch module files from. Note that the module
176
177 Typo: golagg -> golang.
178
179 > +# proxy generally verifies modules via the Hash1 code.
180 > +#
181 > +# Note: Users in China may find some mirrors in the list blocked, and may wish
182 > +# to an explicit entry to /etc/portage/mirrors pointing mirror://goproxy/ to
183 > +# https://goproxy.cn/, or change this variable.
184 > +# See https://arslan.io/2019/08/02/why-you-should-use-a-go-module-proxy/ for further details
185 > +: "${GOMODULE_GOPROXY_BASEURI:=mirror://goproxy/}"
186
187 'Changing this variable' sounds like violating metadata immutability
188 rule and running in trouble with the caches.
189
190 > +
191 > +# @FUNCTION: go-module_set_globals
192 > +# @DESCRIPTION:
193 > +# Convert the information in EGO_SUM for other usage in the ebuild.
194 > +# - Populates EGO_SUM_SRC_URI that can be added to SRC_URI
195 > +# - Exports _EGO_SUM_MAPPING which provides reverse mapping from distfile back
196 > +# to the relative part of SRC_URI, as needed for GOPROXY=file:///...
197 > +go-module_set_globals() {
198 > + local line error_in_gosum errorlines errormsg exts
199 > + local newline=$'\n'
200 > + error_in_gosum=0
201 > + errorlines=( )
202 > + for line in "${EGO_SUM[@]}"; do
203 > + local module version modfile version_modfile hash1 x
204 > + read -r module version_modfile hash1 x <<< "${line}"
205 > + # Validate input
206 > + if [[ -n $hash1 ]] && [[ ${hash1:0:3} != "h1:" ]] ; then
207
208 Please use ${foo} everywhere consistently, and put && inside [[ ]].
209 Also, I dare say wildcard match is more readable than hardcoding string
210 length, i.e.:
211
212 [[ -n ${hash1} && ${hash1} != h1:* ]]
213
214 > + error_in_gosum=1
215 > + errorlines+=( "Unknown hash: ${line}" )
216 > + elif [[ -n $x ]]; then
217 > + error_in_gosum=1
218 > + errorlines+=( "Trailing data: ${line}" )
219 > + fi
220 > +
221 > + # Split 'v0.3.0/go.mod' into 'v0.3.0' and '/go.mod'
222 > + version=${version_modfile%%/*}
223 > + modfile=${version_modfile#*/}
224 > + [[ "$modfile" == "${version_modfile}" ]] && modfile=
225
226 Check the initial string, not the result of arbitrary manipulations
227 on it. This would wrongly evaluate true for 'v0.3.0/v0.3.0'.
228
229 > +
230 > + # The trailing part should be either empty or '/go.mod'
231 > + # There is a chance that upstream Go might add something else here in
232 > + # future, and we should be prepared to capture it.
233 > + exts=()
234 > + errormsg=''
235 > + case "$modfile" in
236 > + '') exts=( mod info zip ) ;;
237 > + 'go.mod'|'/go.mod') exts=( mod info ) ;;
238 > + #'go.mod'|'/go.mod') errormsg="Prohibited file: You must exclude /go.mod lines from EGO_SUM! " ;;
239
240 Why is it commented out?
241
242 > + *) errormsg="Unknown modfile: line='${line}', modfile='${modfile}'" ;;
243 > + esac
244 > +
245 > + # If it was a bad entry, restart the loop
246 > + if [[ -n $errormsg ]]; then
247 > + error_in_gosum=1
248 > + errorlines+=( "${errormsg} line='${line}', modfile='${modfile}'" )
249 > + continue
250 > + fi
251 > +
252 > + # Directory structure for Go proxy hosts:
253 > + # - def encode(s):
254 > + # return re.sub('([A-Z]{1})', r'!\1', s).lower()
255 > + #
256 > + # Sed variant:
257 > + # This uses GNU Sed extension \l to downcase the match
258 > + #_dir=$(echo "${module}" |sed 's,[A-Z],!\l&,g')
259 > + #
260 > + # Bash variant:
261 > + re='(.*)([A-Z])(.*)'
262 > + input=${module}
263 > + while [[ $input =~ $re ]]; do
264 > + lower='!'"${BASH_REMATCH[2],}"
265 > + input="${BASH_REMATCH[1]}${lower}${BASH_REMATCH[3]}"
266 > + done
267 > + _dir=$input
268 > + unset lower input re
269 > +
270 > + for _ext in "${exts[@]}" ; do
271 > + # Relative URI within a GOPROXY for a file
272 > + _reluri="${_dir}/@v/${version}.${_ext}"
273 > + # SRC_URI: LHS entry
274 > + _uri="${GOMODULE_GOPROXY_BASEURI}/${_reluri}"
275 > + # SRC_URI: RHS entry, encode any slash in the path as %2F in the filename
276 > + _distfile="${_reluri//\//%2F}"
277 > +
278 > + EGO_SUM_SRC_URI+=" ${_uri} -> ${_distfile}${newline}"
279 > + _EGO_SUM_MAPPING+=" ${_distfile}:${_reluri}${newline}"
280 > + done
281 > + done
282 > +
283 > + if [[ $error_in_gosum != 0 ]]; then
284 > + eerror "Trailing information in EGO_SUM in ${P}.ebuild"
285 > + for line in "${errorlines[@]}" ; do
286 > + eerror "${line}"
287 > + done
288 > + die "Invalid EGO_SUM format"
289 > + fi
290 > +
291 > + # Ensure these variables not not changed past this point
292 > + readonly EGO_SUM
293 > + readonly EGO_SUM_SRC_URI
294 > + readonly _EGO_SUM_MAPPING
295 > +
296 > + # Set the guard that we are safe
297 > + _GO_MODULE_SET_GLOBALS_CALLED=1
298 > +}
299 > +
300 > +
301 > # @FUNCTION: go-module_src_unpack
302 > # @DESCRIPTION:
303 > +# Extract & configure Go modules for consumpations.
304 > +# - Modules listed in EGO_SUM are configured as a local GOPROXY via symlinks (fast!)
305 > +# - Modules listed in EGO_VENDOR are extracted to "${S}/vendor" (slow)
306 > +#
307 > +# This function does NOT unpack the base distfile of a Go-based package.
308 > +# While the entries in EGO_SUM will be listed in ${A}, they should NOT be
309 > +# unpacked, Go will directly consume the files, including zips.
310 > +go-module_src_unpack() {
311 > + if [[ "${#EGO_VENDOR[@]}" -gt 0 ]]; then
312 > + _go-module_src_unpack_vendor
313 > + elif [[ "${#EGO_SUM[@]}" -gt 0 ]]; then
314 > + _go-module_src_unpack_gosum
315
316 Does that mean those two are mutually exclusive?
317
318 > + else
319 > + die "Neither EGO_SUM nor EGO_VENDOR are set!"
320 > + fi
321 > +}
322 > +
323 > +# @FUNCTION: go-module_src_prepare
324 > +# @DESCRIPTION:
325 > +# Prepare for building. Presently only needed for EGO_SUM variant.
326 > +go-module_src_prepare() {
327 > + # shellcheck disable=SC2120
328 > + debug-print-function "${FUNCNAME}" "$@"
329 > +
330 > + if [[ "${#EGO_SUM[@]}" -gt 0 ]]; then
331 > + _go-module_src_prepare_gosum
332 > + fi
333
334 Wouldn't it be better to append this to src_unpack? Overriding
335 src_prepare is generally problematic, and as I've said above, you're
336 already risking by adding a new export.
337
338 > +
339 > + default
340 > +}
341 > +
342 > +# @ECLASS-VARIABLE: GOMODULE_GOSUM_PATH
343 > +# @DESCRIPTION:
344 > +# Path to root go.sum of package. If your ebuild modifies S after inheriting
345 > +# the eclass, you may need to update this variable.
346 > +: "${GO_MODULE_GOSUM_PATH:=${S}/go.sum}"
347
348 Wouldn't it be cleaner to have the path relative to ${S} by default?
349
350 > +
351 > +# @FUNCTION: _go-module_src_unpack_gosum
352 > +# @DESCRIPTION:
353 > +# Populate a GOPROXY directory hierarchy with distfiles from EGO_SUM
354 > +#
355 > +# Exports GOPROXY environment variable so that Go calls will source the
356 > +# directory correctly.
357 > +_go-module_src_unpack_gosum() {
358 > + # shellcheck disable=SC2120
359 > + debug-print-function "${FUNCNAME}" "$@"
360 > +
361 > + if [[ ! ${_GO_MODULE_SET_GLOBALS_CALLED} ]]; then
362 > + die "go-module_set_globals must be called in global scope"
363 > + fi
364 > +
365 > + local goproxy_dir="${T}/goproxy"
366 > + local goproxy_mod_dir
367 > + mkdir -p "${goproxy_dir}"
368 > + # Convert the list format to an associative array to avoid O(N*M)
369 > + # performance when populating the GOPROXY directory structure.
370 > + declare -A _EGO_SUM_MAPPING_ASSOC
371
372 Why not make it local?
373
374 > + for s in ${_EGO_SUM_MAPPING}; do
375 > + a=${s//:*}
376 > + b=${s//*:}
377 > + _EGO_SUM_MAPPING_ASSOC["$a"]=$b
378 > + done
379 > +
380 > + # For each Golang module distfile, look up where it's supposed to go, and
381 > + # symlink into place.
382 > + for _A in ${A}; do
383
384 This one looks like local candidate as well.
385
386 > + goproxy_mod_path="${_EGO_SUM_MAPPING_ASSOC["${_A}"]}"
387 > + if [[ -n "${goproxy_mod_path}" ]]; then
388 > + einfo "Populating goproxy for $goproxy_mod_path"
389 > + # Build symlink hierarchy
390 > + goproxy_mod_dir=$( dirname "${goproxy_dir}"/"${goproxy_mod_path}" )
391 > + mkdir -p "${goproxy_mod_dir}"
392
393 || die
394
395 > + ln -sf "${DISTDIR}"/"${_A}" "${goproxy_dir}/${goproxy_mod_path}" || die "Failed to ln"
396 > + fi
397 > + done
398 > + export GOPROXY="file://${goproxy_dir}"
399 > + unset _EGO_SUM_MAPPING_ASSOC
400 > +}
401 > +
402 > +# @FUNCTION: _go-module_src_unpack_vendor
403 > +# @DESCRIPTION:
404 > # Extract all archives in ${a} which are not nentioned in ${EGO_VENDOR}
405 > # to their usual locations then extract all archives mentioned in
406 > # ${EGO_VENDOR} to ${S}/vendor.
407 > -go-module_src_unpack() {
408 > - debug-print-function ${FUNCNAME} "$@"
409 > +_go-module_src_unpack_vendor() {
410 > + # shellcheck disable=SC2120
411 > + debug-print-function "${FUNCNAME}" "$@"
412 > local f hash import line repo tarball vendor_tarballs x
413 > vendor_tarballs=()
414 > for line in "${EGO_VENDOR[@]}"; do
415 > @@ -145,13 +391,59 @@ go-module_src_unpack() {
416 > done
417 > }
418 >
419 > +# @FUNCTION: _go-module_src_prepare_gosum
420 > +# @DESCRIPTION:
421 > +# Validate the Go modules declared by EGO_SUM are sufficent to cover building
422 > +# the package, without actually building it yet.
423 > +_go-module_src_prepare_gosum() {
424 > + # shellcheck disable=SC2120
425 > + debug-print-function "${FUNCNAME}" "$@"
426 > +
427 > + if [[ ! ${_GO_MODULE_SET_GLOBALS_CALLED} ]]; then
428 > + die "go-module_set_globals must be called in global scope"
429 > + fi
430 > +
431 > + # go.sum entries ending in /go.mod aren't strictly needed at this phase
432 > + if [[ ! -e "${GO_MODULE_GOSUM_PATH}" ]]; then
433 > + die "Could not find package root go.sum, please update GO_MODULE_GOSUM_PATH"
434 > + fi
435 > + go-module_minimize_gosum "${GO_MODULE_GOSUM_PATH}"
436 > +
437 > + # Verify that all needed modules are present.
438 > + GO111MODULE=on \
439 > + go get -v -d -mod readonly || die "Some module is missing, update EGO_SUM"
440 > +
441 > + # Need to re-minimize because go-get expands it again
442
443 Why not create and restore a copy? Or does go-get make other changes?
444
445 > + go-module_minimize_gosum "${GO_MODULE_GOSUM_PATH}"
446 > +}
447 > +
448 > +# @FUNCTION: go-module_minimize_gosum
449 > +# @DESCRIPTION:
450 > +# Remove all /go.mod entries from go.sum files
451 > +# In most cases, if go.sum only has a /go.mod entry without a corresponding
452 > +# direct entry, this is a sign of a weak dependency that is NOT required for
453 > +# building the package.
454 > +go-module_minimize_gosum() {
455 > + local gosumfile=${1}
456 > + if test ! -e "${gosumfile}".orig; then
457
458 Use [[ ... ]]. Be consistent. This is an eclass, not a throwaway
459 script.
460
461 > + cp -f "${gosumfile}"{,.orig} || die
462 > + fi
463 > + awk -e '$2 ~ /\/go.mod$/{next} {print}' \
464 > + <"${gosumfile}".orig \
465 > + >"${gosumfile}" || die
466 > + if grep -sq /go.mod "${gosumfile}"; then
467 > + die "sed failed to remove all module go.mod entries from go.sum"
468
469 Err, but the rule for grep is inconsistent with the rule for awk. It's
470 going to fail when 'go.mod' (i.e. go<ANYCHAR>mod) happens anywhere
471 on the line.
472
473 > + fi
474 > +}
475 > +
476 > # @FUNCTION: go-module_live_vendor
477 > # @DESCRIPTION:
478 > # This function is used in live ebuilds to vendor the dependencies when
479 > # upstream doesn't vendor them.
480 > go-module_live_vendor() {
481 > - debug-print-function ${FUNCNAME} "$@"
482 > + debug-print-function "${FUNCNAME}" "$@"
483 >
484 > + # shellcheck disable=SC2086
485 > has live ${PROPERTIES} ||
486 > die "${FUNCNAME} only allowed in live ebuilds"
487 > [[ "${EBUILD_PHASE}" == unpack ]] ||
488 > @@ -168,7 +460,7 @@ go-module_live_vendor() {
489 > # @DESCRIPTION:
490 > # Display a warning about security updates for Go programs.
491 > go-module_pkg_postinst() {
492 > - debug-print-function ${FUNCNAME} "$@"
493 > + debug-print-function "${FUNCNAME}" "$@"
494 > [[ -n ${REPLACING_VERSIONS} ]] && return 0
495 > ewarn "${PN} is written in the Go programming language."
496 > ewarn "Since this language is statically linked, security"
497 > diff --git profiles/thirdpartymirrors profiles/thirdpartymirrors
498 > index ad4c4b972146..d60f166e07c9 100644
499 > --- profiles/thirdpartymirrors
500 > +++ profiles/thirdpartymirrors
501 > @@ -25,3 +25,4 @@ sourceforge https://download.sourceforge.net
502 > sourceforge.jp http://iij.dl.sourceforge.jp https://osdn.dl.sourceforge.jp https://jaist.dl.sourceforge.jp
503 > ubuntu http://mirror.internode.on.net/pub/ubuntu/ubuntu/ https://mirror.tcc.wa.edu.au/ubuntu/ http://ubuntu.uni-klu.ac.at/ubuntu/ http://mirror.dhakacom.com/ubuntu-archive/ http://ubuntu.c3sl.ufpr.br/ubuntu/ http://ubuntu.uni-sofia.bg/ubuntu/ http://hr.archive.ubuntu.com/ubuntu/ http://cz.archive.ubuntu.com/ubuntu/ https://mirror.dkm.cz/ubuntu http://ftp.cvut.cz/ubuntu/ http://ftp.stw-bonn.de/ubuntu/ https://ftp-stud.hs-esslingen.de/ubuntu/ https://mirror.netcologne.de/ubuntu/ https://mirror.unej.ac.id/ubuntu/ http://kr.archive.ubuntu.com/ubuntu/ https://mirror.nforce.com/pub/linux/ubuntu/ http://mirror.amsiohosting.net/archive.ubuntu.com/ http://nl3.archive.ubuntu.com/ubuntu/ https://mirror.timeweb.ru/ubuntu/ http://ubuntu.mirror.su.se/ubuntu/ https://ftp.yzu.edu.tw/ubuntu/ https://mirror.aptus.co.tz/pub/ubuntuarchive/ https://ubuntu.volia.net/ubuntu-archive/ https://mirror.sax.uk.as61049.net/ubuntu/ https://mirror.pnl.gov/ubuntu/ http://mirror.cc.columbia.edu/pub/linux/ubuntu/archive/ https://mirrors.namecheap.com/ubuntu/
504 > vdr-developerorg http://projects.vdr-developer.org/attachments/download
505 > +goproxy https://proxy.golang.org/ https://goproxy.io/ https://gocenter.io/
506
507 --
508 Best regards,
509 Michał Górny

Attachments

File name MIME type
signature.asc application/pgp-signature

Replies