Compare commits

...

46 Commits

Author SHA1 Message Date
Georg K
0822dc2dc9 bump: packages 2023-02-19 02:53:19 +03:00
Georg K
49ce78ba79 Merge branch 'master_gh'
# Conflicts:
#	go.mod
2023-02-19 02:49:44 +03:00
Georg K
696a87c7a7 bump: golang bulder image to 1.20.1-alpine3.17 2023-02-19 02:47:22 +03:00
Marcin Bilski
9285c36f88
Merge pull request #12 from mjwhodur/feature/fix-build-on-apple-silicon
fix: Fix building on Apple Silicon darwin64
2022-08-11 10:48:33 +02:00
Michal Hodur
9caa24db3b fix: Fix building on Apple Silicon darwin64 2022-08-11 09:25:59 +02:00
Georg K
d162b6a2ba bump: golang bulder image to 1.17.13-alpine3.16 2022-08-11 04:11:04 +03:00
Georg K
c8409dd662 Merge remote-tracking branch 'upstream/master' 2022-08-11 04:08:39 +03:00
Marcin Bilski
eca3d1768e Generate universal binaries. 2022-05-11 11:30:19 +02:00
Marcin Bilski
4e2759e69f Update cache after tagging, so the cache reflects the updated version of
the source code.
2022-05-11 11:30:19 +02:00
Marcin Bilski
0837c80915
Merge pull request #1 from bilus/bugfix-gomod-sum-missing
Bugfix: return error when go.mod can't be loaded
2022-05-11 11:26:38 +02:00
BartekDymowski
34637ebe9d Bugfix: return error when go.mod can't be loaded 2022-05-11 11:21:24 +02:00
Georg K
864701dea1 fix: gitlab file [ci skip] 2022-03-03 23:15:12 +03:00
Georg K
2d868b4e26 fix: entrypoint.sh 2022-03-03 23:05:50 +03:00
Georg K
9de64c0d8d fix: add entrypoint.sh 2022-03-03 23:01:25 +03:00
Georg K
513e50f7f5 fix: add nosumdb 2022-03-03 22:16:56 +03:00
Georg K
3cd63129a6 fix: add ssh 2022-03-03 21:18:32 +03:00
Georg K
a4d54bc847 fix: add git 2022-03-03 20:51:30 +03:00
Georg K
00c4725d2a fix: use golang:1.17.7-alpine3.15 as app image 2022-03-03 02:43:24 +03:00
Georg K
92437761df chore: add docker-compose.yml 2022-03-03 02:39:44 +03:00
Georg K
9c4535cb52 feat: add deploy 2022-03-03 02:25:23 +03:00
Georg K
e690d870fb chore: add .idea to .gitignore 2022-03-03 02:08:58 +03:00
Georg K
e9b53b32ae feat: update Dockerfile 2022-03-03 02:08:40 +03:00
Georg K
d4916fc30d feat: change package for fork 2022-03-03 02:08:22 +03:00
Marcin Bilski
68959a741a Add M1. 2022-01-14 15:38:10 +01:00
Marcin Bilski
c327898faa Add support for ssh passphrases. 2022-01-14 12:35:20 +01:00
Marcin Bilski
6768e02d91 Fix tagging failing with 400 while no remote tags yet. 2022-01-12 12:44:09 +01:00
Marcin Bilski
656e7b2fc1 Configure goreleaser. 2022-01-05 11:42:45 +01:00
Marcin Bilski
9624b2b9ad Give up, change import paths to the fork. 2022-01-05 11:31:44 +01:00
Marcin Bilski
48d1cf9ff8 Add Dockerfile. 2022-01-05 11:30:27 +01:00
Marcin Bilski
52d4a68d86 Prevent tagging existing remote versions. 2021-12-22 21:53:45 +01:00
Marcin Bilski
e08d238243 Add support for ephemeral tags. 2021-12-22 14:04:57 +01:00
Marcin Bilski
92e846002e Do not list default 0.0.0 version for Go modules. 2021-12-22 14:01:32 +01:00
Marcin Bilski
708a07fec6 Fix broken support for monorepos. 2021-12-22 14:00:34 +01:00
Serge Zaitsev
db43a4e741 encode module name with gomod vcs provider to handle uppercase module paths 2019-11-08 10:08:36 +01:00
Serge Zaitsev
7a37ec05c4
Delete modules in case of breaking changes or invalid checksums, compatibility with go mod download (#10)
* add module invalidation via DELETE request
* add go mod download vcs provider
2019-10-07 08:10:02 +02:00
Serge Zaitsev
0868adad0a use AllTags fetch option 2019-02-11 14:42:21 +01:00
Serge Zaitsev
5a917d7c6f fix symlink checks to use OS file mode, not git file mode 2019-02-08 14:31:55 +01:00
Serge Zaitsev
c2825d2276 process git CLI flags before vcs flags 2019-02-04 15:33:54 +01:00
Serge Zaitsev
d4aafd60cf Merge branch 'master' of github.com:sixt/gomodproxy 2019-02-04 13:27:27 +01:00
Serge Zaitsev
c7f7535da8 expose module name in both, bang-encoded and normal mixed-case form 2019-02-04 13:24:47 +01:00
Andrii S
2bf79f79ab Add mount point for disk persistence (#8)
* Add mount point for disk persistence

* Use empty folder for both images
2019-01-28 09:03:37 +00:00
Serge Zaitsev
4b37c90d04 publish both, golang and scratch based containers 2019-01-25 13:50:06 +01:00
Serge Zaitsev
a7494ce36f ignore symlinks to match go1.11.4 checksum algorithm changes 2019-01-14 11:53:30 +01:00
Serge Zaitsev
c16efb7791 update dependencies 2019-01-14 11:53:08 +01:00
Serge Zaitsev
5fed7316c6 add support for custom vcs handlers 2018-11-06 14:38:55 +01:00
Serge Zaitsev
fc10c1c983 add option to restrict number of parallel vcs workers 2018-11-05 09:00:18 +01:00
21 changed files with 883 additions and 82 deletions

3
.gitignore vendored
View File

@ -15,3 +15,6 @@
# Misc
_gopath
_gocache
dist/
.idea/

20
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,20 @@
variables:
CI_REGISTRY: registry.connectone.pro
CONTAINER_IMAGE: ${CI_REGISTRY}/${CI_PROJECT_PATH}:${CI_BUILD_REF_NAME}_${CI_BUILD_REF}
CONTAINER_IMAGE_LATEST: ${CI_REGISTRY}/${CI_PROJECT_PATH}:latest
stages:
- release
release:
stage: release
image: docker:latest
before_script:
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
script:
- docker build -t ${CONTAINER_IMAGE_LATEST} .
- docker tag ${CONTAINER_IMAGE_LATEST} ${CONTAINER_IMAGE}
- docker push ${CONTAINER_IMAGE}
- docker push ${CONTAINER_IMAGE_LATEST}
only:
- master

39
.goreleaser.yaml Normal file
View File

@ -0,0 +1,39 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
# you may remove this if you don't need go generate
- go generate ./...
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- darwin
goarch:
- arm64
- amd64
- 386
main: ./cmd/gomodproxy/main.go
archives:
- replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
arm64: Arm64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
universal_binaries:
- name_template: "{{.ProjectName}}_MacOS_{{.Version}}"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

View File

@ -12,7 +12,17 @@ script:
- go test -v ./...
after_success:
- printf 'FROM scratch\nADD gomodproxy /\nCMD ["/gomodproxy"]' > Dockerfile
- mkdir gomods
- printf 'FROM scratch\nADD gomodproxy /\nADD gomods /\nCMD ["/gomodproxy"]' > Dockerfile
- docker build -t "sixtlabs/gomodproxy-slim:latest" .
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- docker push "sixtlabs/gomodproxy-slim:latest"
- if [ ! -z $TRAVIS_TAG ] ; then
TAG=$(echo $TRAVIS_TAG | sed 's/^v//');
docker tag sixtlabs/gomodproxy-slim:latest sixtlabs/gomodproxy-slim:$TAG;
docker push sixtlabs/gomodproxy-slim:$TAG;
fi
- printf 'FROM golang\nADD gomodproxy /\nADD gomods /\nCMD ["/gomodproxy"]' > Dockerfile
- docker build -t "sixtlabs/gomodproxy:latest" .
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- docker push "sixtlabs/gomodproxy:latest"

18
Dockerfile Normal file
View File

@ -0,0 +1,18 @@
FROM golang:1.20.1-alpine3.17 as build
WORKDIR /build
RUN apk add --no-cache git gcc musl-dev
ADD go.mod go.sum ./
RUN go mod download
ADD . .
RUN go build -o gomodproxy cmd/gomodproxy/main.go && \
chmod a+x gomodproxy
FROM golang:1.20.1-alpine3.17
ENV GONOSUMDB=gitlab.connectone.pro/*
WORKDIR /opt/app
RUN apk add --no-cache tzdata ca-certificates git openssh
COPY --from=build /build/gomodproxy ./
ADD entrypoint.sh .
RUN chmod a+x *.sh
ENTRYPOINT ["/opt/app/entrypoint.sh"]
CMD ["/opt/app/gomodproxy", "--addr", ":8080"]

View File

@ -8,6 +8,10 @@ gomodproxy is a caching proxy for [Go modules].
Go 1.11 has introduced optional proxy support via GOPROXY environment variable. It is essential for use cases where you want to have better control over your dependencies and handle scenarios when GitHub is down or some open-source dependency has been removed.
## Releasing
See https://goreleaser.com/quick-start/
## Getting started
gomodproxy requires Go 1.11 or newer. There are no plans to support `vgo` or Go 1.11 beta versions.

View File

@ -16,7 +16,7 @@ import (
"time"
"unicode"
"github.com/sixt/gomodproxy/pkg/api"
"gitlab.connectone.pro/github/gomodproxy/pkg/api"
"expvar"
_ "net/http/pprof"
@ -97,16 +97,19 @@ func (f *listFlag) Set(s string) error { *f = append(*f, s); return nil }
func main() {
gitPaths := listFlag{}
vcsPaths := listFlag{}
addr := flag.String("addr", ":0", "http server address")
verbose := flag.Bool("v", false, "verbose logging")
prometheus := flag.String("prometheus", "", "prometheus address")
debug := flag.Bool("debug", false, "enable debug HTTP API (pprof/expvar)")
json := flag.Bool("json", false, "json structured logging")
useJsonLog := flag.Bool("json", false, "json structured logging")
dir := flag.String("dir", filepath.Join(os.Getenv("HOME"), ".gomodproxy/cache"), "modules cache directory")
gitdir := flag.String("gitdir", filepath.Join(os.Getenv("HOME"), ".gomodproxy/git"), "git cache directory")
memLimit := flag.Int64("mem", 256, "in-memory cache size in MB")
workers := flag.Int("workers", 1, "number of parallel VCS workers")
flag.Var(&gitPaths, "git", "list of git settings")
flag.Var(&vcsPaths, "vcs", "list of custom VCS handlers")
flag.Parse()
@ -120,8 +123,8 @@ func main() {
options := []api.Option{}
logger := func(...interface{}) {}
if *verbose || *json {
if *json {
if *verbose || *useJsonLog {
if *useJsonLog {
logger = jsonLog
} else {
logger = prettyLog
@ -134,10 +137,20 @@ func main() {
if len(kv) != 2 {
log.Fatal("bad git path:", path)
}
options = append(options, api.Git(kv[0], kv[1]))
password := os.Getenv("SSH_PASSPHRASE")
options = append(options, api.GitWithEphemeralTags(kv[0], kv[1], password))
}
for _, path := range vcsPaths {
kv := strings.SplitN(path, ":", 2)
if len(kv) != 2 {
log.Fatal("bad VCS syntax:", path)
}
options = append(options, api.CustomVCS(kv[0], kv[1]))
}
options = append(options,
api.VCSWorkers(*workers),
api.GitDir(*gitdir),
api.Memory(logger, *memLimit*1024*1024),
api.CacheDir(*dir),

32
docker-compose.yml Normal file
View File

@ -0,0 +1,32 @@
version: '3.2'
services:
gomodproxy:
image: registry.connectone.pro/github/gomodproxy:latest
restart: unless-stopped
ports:
- "9030:8080"
volumes:
- type: bind
source: ./cache
target: /cache
bind:
propagation: shared
- type: bind
source: ./keys
target: /root/.ssh
bind:
propagation: shared
environment:
GIT_USER: ${GIT_USER}
GIT_PASS: ${GIT_PASS}
command:
- /opt/app/gomodproxy
- --dir
- /cache
- --addr
- :8080
- -git
- gitlab.connectone.pro:/root/.ssh/id_rsa
- -git
- https://gitlab.connectone.pro:${GIT_USER}:${GIT_PASS}

3
entrypoint.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/sh
git config --global url."https://${GIT_USER}:${GIT_PASS}@gitlab.connectone.pro".insteadOf "https://gitlab.connectone.pro"
exec "$@"

28
go.mod
View File

@ -1,8 +1,26 @@
module github.com/sixt/gomodproxy
module gitlab.connectone.pro/github/gomodproxy
go 1.20
require github.com/go-git/go-git/v5 v5.5.2
require (
github.com/emirpasic/gods v1.11.0 // indirect
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b // indirect
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3 // indirect
gopkg.in/src-d/go-git.v4 v4.7.1
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.4.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.3.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.3.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

154
go.sum
View File

@ -1,47 +1,129 @@
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/emirpasic/gods v1.11.0 h1:8KhjokrJy1+REZkLeSlnJvLKI4tRR8g65a4C07oQQ50=
github.com/emirpasic/gods v1.11.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.4.0 h1:Vaw7LaSTRJOUric7pe4vnzBSgyuf2KrLsu2Y4ZpQBDE=
github.com/go-git/go-billy/v5 v5.4.0/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ=
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/go-git/go-git/v5 v5.5.2 h1:v8lgZa5k9ylUw+OR/roJHTxR4QItsNFI5nKtAXFuynw=
github.com/go-git/go-git/v5 v5.5.2/go.mod h1:BE5hUJ5yaV2YMxhmaP4l6RBQ08kMxKSPD4BlxtH7OjI=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI=
github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/src-d/gcfg v1.3.0 h1:2BEDr8r0I0b8h/fOqwtxCEiq2HJu8n2JGZJQFGXWLjg=
github.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3 h1:czFLhve3vsQetD6JOJ8NZZvGQIXlnN3/yXxbT6/awxI=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo=
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.7.1 h1:phAV/kNULxfYEvyInGdPuq3U2MtPpJdgmtOUF3cghkQ=
gopkg.in/src-d/go-git.v4 v4.7.1/go.mod h1:xrJH/YX8uSWewT6evfocf8qsivF18JgCN7/IMitOptY=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -15,8 +15,8 @@ import (
"time"
"unicode"
"github.com/sixt/gomodproxy/pkg/store"
"github.com/sixt/gomodproxy/pkg/vcs"
"gitlab.connectone.pro/github/gomodproxy/pkg/store"
"gitlab.connectone.pro/github/gomodproxy/pkg/vcs"
)
type logger = func(v ...interface{})
@ -26,6 +26,9 @@ type api struct {
gitdir string
vcsPaths []vcsPath
stores []store.Store
semc chan struct{}
ephemeralTagStorage *vcs.EphemeralTagStorage
}
type vcsPath struct {
@ -41,6 +44,7 @@ var (
apiInfo = regexp.MustCompile(`^/(?P<module>.*)/@v/(?P<version>.*).info$`)
apiMod = regexp.MustCompile(`^/(?P<module>.*)/@v/(?P<version>.*).mod$`)
apiZip = regexp.MustCompile(`^/(?P<module>.*)/@v/(?P<version>.*).zip$`)
apiTag = regexp.MustCompile(`^/tags/(?P<module>.*)/@v/(?P<version>.*)$`)
)
var (
@ -53,7 +57,7 @@ var (
// New returns a configured http.Handler which implements GOPROXY API.
func New(options ...Option) http.Handler {
api := &api{log: func(...interface{}) {}}
api := &api{log: func(...interface{}) {}, semc: make(chan struct{}, 1)}
for _, opt := range options {
opt(api)
}
@ -70,9 +74,9 @@ func GitDir(dir string) Option { return func(api *api) { api.gitdir = dir } }
// Git configures API to use a specific git client when trying to download a
// repository with the given prefix. Auth string can be a path to the SSK key,
// or a colon-separated username:password string.
func Git(prefix string, auth string) Option {
a := vcs.Key(auth)
if creds := strings.SplitN(auth, ":", 2); len(creds) == 2 {
func Git(prefix, key, password string) Option {
a := vcs.Key(key, password)
if creds := strings.SplitN(key, ":", 2); len(creds) == 2 {
a = vcs.Password(creds[0], creds[1])
}
return func(api *api) {
@ -85,6 +89,40 @@ func Git(prefix string, auth string) Option {
}
}
// GitWithEphemeralTags configures API to use a specific git client when trying
// to download a repository with the given prefix. Auth string can be a path to
// the SSK key, or a colon-separated username:password string.
func GitWithEphemeralTags(prefix, key, password string) Option {
// TODO(bilus): Ugly but we don't want to mess with the : encoding so
// we'll work around the issue of having to pass a passphrase
// to decrypt a key.
storage := vcs.NewEphemeralTagStorage()
a := vcs.Key(key, password)
if creds := strings.SplitN(key, ":", 2); len(creds) == 2 {
a = vcs.Password(creds[0], creds[1])
}
return func(api *api) {
api.ephemeralTagStorage = storage
api.vcsPaths = append(api.vcsPaths, vcsPath{
prefix: prefix,
vcs: func(module string) vcs.VCS {
return vcs.NewGitWithEphemeralTags(api.log, api.gitdir, module, a, storage)
},
})
}
}
func CustomVCS(prefix string, cmd string) Option {
return func(api *api) {
api.vcsPaths = append(api.vcsPaths, vcsPath{
prefix: prefix,
vcs: func(module string) vcs.VCS {
return vcs.NewCommand(api.log, cmd, module)
},
})
}
}
// Memory configures API to use in-memory cache for downloaded modules.
func Memory(log logger, limit int64) Option {
return func(api *api) {
@ -99,6 +137,15 @@ func CacheDir(dir string) Option {
}
}
// VCSWorkers configures API to use at most n parallel workers when fetching
// from the VCS. The reason to restrict number of workers is to limit their
// memory usage.
func VCSWorkers(n int) Option {
return func(api *api) {
api.semc = make(chan struct{}, n)
}
}
func decodeBangs(s string) string {
buf := []rune{}
bang := false
@ -130,6 +177,7 @@ func (api *api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
{"info", apiInfo, api.info},
{"api", apiMod, api.mod},
{"zip", apiZip, api.zip},
{"tag", apiTag, api.tag},
} {
if m := route.regexp.FindStringSubmatch(r.URL.Path); m != nil {
module, version := m[1], ""
@ -137,6 +185,10 @@ func (api *api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
version = m[2]
}
module = decodeBangs(module)
if r.Method == http.MethodDelete && version != "" {
api.delete(w, r, module, version)
return
}
httpRequests.Add(route.id, 1)
defer func() {
v := &expvar.Float{}
@ -158,7 +210,7 @@ func (api *api) vcs(ctx context.Context, module string) vcs.VCS {
return path.vcs(module)
}
}
return vcs.NewGit(api.log, api.gitdir, module, vcs.NoAuth())
return vcs.NewGoMod(api.log, module)
}
func (api *api) module(ctx context.Context, module string, version vcs.Version) ([]byte, time.Time, error) {
@ -170,6 +222,16 @@ func (api *api) module(ctx context.Context, module string, version vcs.Version)
}
cacheMisses.Add(module, 1)
// wait for semaphore
api.semc <- struct{}{}
defer func() { <-api.semc }()
return api.store(ctx, module, version)
}
func (api *api) store(ctx context.Context, module string, version vcs.Version) ([]byte, time.Time, error) {
api.log("api.store", "module", module, "version", version.String())
timestamp, err := api.vcs(ctx, module).Timestamp(ctx, version)
if err != nil {
return nil, time.Time{}, err
@ -207,7 +269,7 @@ func (api *api) list(w http.ResponseWriter, r *http.Request, module, version str
if err != nil {
api.log("api.list", "module", module, "error", err)
httpErrors.Add(module, 1)
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, err.Error(), http.StatusNotFound)
return
}
@ -223,7 +285,7 @@ func (api *api) info(w http.ResponseWriter, r *http.Request, module, version str
if err != nil {
api.log("api.info", "module", module, "version", version, "error", err)
httpErrors.Add(module, 1)
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, err.Error(), http.StatusNotFound)
return
}
@ -247,9 +309,11 @@ func (api *api) mod(w http.ResponseWriter, r *http.Request, module, version stri
}
}
}
w.Write([]byte(fmt.Sprintf("module %s\n", module)))
return
}
}
w.Write([]byte(fmt.Sprintf("module %s\n", module)))
http.Error(w, err.Error(), http.StatusBadRequest)
}
func (api *api) zip(w http.ResponseWriter, r *http.Request, module, version string) {
@ -258,8 +322,60 @@ func (api *api) zip(w http.ResponseWriter, r *http.Request, module, version stri
if err != nil {
api.log("api.zip", "module", module, "version", version, "error", err)
httpErrors.Add(module, 1)
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, err.Error(), http.StatusNotFound)
return
}
io.Copy(w, bytes.NewReader(b))
}
func (api *api) delete(w http.ResponseWriter, r *http.Request, module, version string) {
for _, store := range api.stores {
if err := store.Del(r.Context(), module, vcs.Version(version)); err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
}
}
func (api *api) tag(w http.ResponseWriter, r *http.Request, module, version string) {
api.log("api.tag", "module", module, "version", version)
taggable, ok := api.vcs(r.Context(), module).(vcs.Taggable)
if !ok {
err := fmt.Errorf("repository for module %v is not taggable", module)
api.log("api.tag", "module", module, "version", version, "error", err)
httpErrors.Add(module, 1)
http.Error(w, err.Error(), http.StatusNotFound)
return
}
defer r.Body.Close()
req := struct {
Short string `json:"short"`
}{}
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
api.log("api.tag", "module", module, "version", version, "error", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// wait for semaphore
api.semc <- struct{}{}
defer func() { <-api.semc }()
err = taggable.Tag(r.Context(), vcs.Version(version), req.Short)
if err != nil {
api.log("api.tag", "module", module, "version", version, "error", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
_, _, err = api.store(r.Context(), module, vcs.Version(version))
if err != nil {
api.log("api.tag", "module", module, "version", version, "error", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}

View File

@ -6,7 +6,7 @@ import (
"os"
"path/filepath"
"github.com/sixt/gomodproxy/pkg/vcs"
"gitlab.connectone.pro/github/gomodproxy/pkg/vcs"
)
type disk string
@ -47,4 +47,15 @@ func (d disk) Get(ctx context.Context, module string, version vcs.Version) (Snap
return s, err
}
func (d disk) Del(ctx context.Context, module string, version vcs.Version) error {
dir := string(d)
s := Snapshot{Module: module, Version: version}
err := os.Remove(filepath.Join(dir, s.Key()+".time"))
if err != nil {
return err
}
err = os.Remove(filepath.Join(dir, s.Key()+".zip"))
return err
}
func (d disk) Close() error { return nil }

View File

@ -5,7 +5,7 @@ import (
"errors"
"sync"
"github.com/sixt/gomodproxy/pkg/vcs"
"gitlab.connectone.pro/github/gomodproxy/pkg/vcs"
)
type memory struct {
@ -45,17 +45,41 @@ func (m *memory) Put(ctx context.Context, snapshot Snapshot) error {
func (m *memory) Get(ctx context.Context, module string, version vcs.Version) (Snapshot, error) {
m.Lock()
defer m.Unlock()
return m.lookup(module, version)
item, err := m.lookup(module, version)
if err != nil {
return Snapshot{}, err
}
return item.Snapshot, nil
}
func (m *memory) lookup(module string, version vcs.Version) (Snapshot, error) {
func (m *memory) Del(ctx context.Context, module string, version vcs.Version) error {
m.Lock()
defer m.Unlock()
item, err := m.lookup(module, version)
if err != nil {
return err
}
if item.prev == nil {
m.head = item.next
} else {
item.prev.next = item.next
}
if item.next == nil {
m.tail = item.prev
} else {
item.next.prev = item.prev
}
return nil
}
func (m *memory) lookup(module string, version vcs.Version) (*lruItem, error) {
for item := m.head; item != nil; item = item.next {
if item.Module == module && item.Version == version {
m.update(item)
return item.Snapshot, nil
return item, nil
}
}
return Snapshot{}, errors.New("not found")
return nil, errors.New("not found")
}
func (m *memory) insert(item *lruItem) {

View File

@ -4,7 +4,7 @@ import (
"context"
"time"
"github.com/sixt/gomodproxy/pkg/vcs"
"gitlab.connectone.pro/github/gomodproxy/pkg/vcs"
)
type logger = func(...interface{})
@ -14,6 +14,7 @@ type logger = func(...interface{})
type Store interface {
Put(ctx context.Context, snapshot Snapshot) error
Get(ctx context.Context, module string, version vcs.Version) (Snapshot, error)
Del(ctx context.Context, module string, version vcs.Version) error
Close() error
}

107
pkg/vcs/cmd.go Normal file
View File

@ -0,0 +1,107 @@
package vcs
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"io/ioutil"
"os"
"os/exec"
"strconv"
"strings"
"time"
)
type cmdVCS struct {
log logger
module string
moduleEncoded string
cmd string
}
func encodeBangs(s string) string {
buf := []byte{}
for _, r := range s {
if 'A' <= r && r <= 'Z' {
buf = append(buf, '!', byte(r+'a'-'A'))
} else {
buf = append(buf, byte(r))
}
}
return string(buf)
}
func NewCommand(l logger, cmd string, module string) VCS {
return &cmdVCS{log: l, cmd: cmd, module: module, moduleEncoded: encodeBangs(module)}
}
func (c *cmdVCS) List(ctx context.Context) ([]Version, error) {
b, err := c.exec(ctx,
"MODULE="+c.module,
"MODULE_ENCODED="+c.moduleEncoded,
"ACTION=list",
"VERSION=latest",
"FILEPATH="+c.module+"/@v/list",
"FILEPATH_ENCODED="+c.moduleEncoded+"/@v/list",
)
if err != nil {
return nil, err
}
versions := []Version{}
for _, line := range strings.Split(string(b), "\n") {
versions = append(versions, Version(line))
}
return versions, nil
}
func (c *cmdVCS) Timestamp(ctx context.Context, version Version) (time.Time, error) {
b, err := c.exec(ctx,
"MODULE="+c.module,
"MODULE_ENCODED="+c.moduleEncoded,
"ACTION=timestamp",
"VERSION="+version.String(),
"FILEPATH="+c.module+"/@v/"+version.String()+".info",
"FILEPATH_ENCODED="+c.moduleEncoded+"/@v/"+version.String()+".info",
)
if err != nil {
return time.Time{}, err
}
info := struct {
Version string
Time time.Time
}{}
if json.Unmarshal(b, &info) == nil {
return info.Time, nil
}
if t, err := time.Parse(time.RFC3339, string(b)); err == nil {
return t, nil
}
if sec, err := strconv.ParseInt(string(b), 10, 64); err == nil {
return time.Unix(sec, 0), nil
}
return time.Time{}, errors.New("unknown time format")
}
func (c *cmdVCS) Zip(ctx context.Context, version Version) (io.ReadCloser, error) {
b, err := c.exec(ctx,
"MODULE="+c.module,
"MODULE_ENCODED="+c.moduleEncoded,
"ACTION=zip",
"VERSION="+version.String(),
"FILEPATH="+c.module+"/@v/"+version.String()+".zip",
"FILEPATH_ENCODED="+c.moduleEncoded+"/@v/"+version.String()+".zip",
)
if err != nil {
return nil, err
}
return ioutil.NopCloser(bytes.NewReader(b)), nil
}
func (c *cmdVCS) exec(ctx context.Context, env ...string) ([]byte, error) {
cmd := exec.Command("sh", "-c", c.cmd)
cmd.Env = append(os.Environ(), env...)
cmd.Stderr = os.Stderr
return cmd.Output()
}

View File

@ -6,6 +6,14 @@ import (
"context"
"errors"
"fmt"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/go-git/go-git/v5/storage/memory"
"io"
"io/ioutil"
"os"
@ -13,15 +21,6 @@ import (
"path/filepath"
"strings"
"time"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
"gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
"gopkg.in/src-d/go-git.v4/storage/memory"
)
const remoteName = "origin"
@ -34,6 +33,8 @@ type gitVCS struct {
auth Auth
}
var ErrNoMatchingVersion = errors.New("no matching versions")
// NewGit return a go-git VCS client implementation that provides information
// about the specific module using the pgiven authentication mechanism.
func NewGit(l logger, dir string, module string, auth Auth) VCS {
@ -61,7 +62,6 @@ func (g *gitVCS) List(ctx context.Context) ([]Version, error) {
if err != nil {
return nil, err
}
list := []Version{}
masterHash := ""
tagPrefix := ""
@ -81,18 +81,55 @@ func (g *gitVCS) List(ctx context.Context) ([]Version, error) {
if masterHash == "" {
return nil, errors.New("no tags and no master branch found")
}
short := masterHash[:12]
t, err := g.Timestamp(ctx, Version("v0.0.0-20060102150405-"+short))
version, err := g.versionFromHash(ctx, short)
if err != nil {
return nil, err
}
list = []Version{Version(fmt.Sprintf("v0.0.0-%s-%s", t.Format("20060102150405"), short))}
masterCommit, err := g.commit(ctx, version)
if err != nil {
return nil, err
}
tree, err := masterCommit.Tree()
if err != nil {
return nil, err
}
// No tags while it's a module.
if g.isModule(tree) {
return nil, ErrNoMatchingVersion
}
list = []Version{version}
}
g.log("gitVCS.List", "module", g.module, "list", list)
return list, nil
}
func (g *gitVCS) versionFromHash(ctx context.Context, hash string) (Version, error) {
t, err := g.Timestamp(ctx, Version("v0.0.0-20060102150405-"+hash))
if err != nil {
return Version(""), err
}
v := Version(fmt.Sprintf("v0.0.0-%s-%s", t.Format("20060102150405"), hash))
return v, nil
}
func (g *gitVCS) isModule(tree *object.Tree) bool {
mod := "go.mod"
for path := g.prefix; path != "."; path = filepath.Dir(path) {
_, err := tree.FindEntry(filepath.Join(path, mod))
if err == nil {
return true
}
}
return false
}
func (g *gitVCS) Timestamp(ctx context.Context, version Version) (time.Time, error) {
g.log("gitVCS.Timestamp", "module", g.module, "version", version)
ci, err := g.commit(ctx, version)
@ -116,6 +153,11 @@ func isVendoredPackage(name string) bool {
}
func (g *gitVCS) Zip(ctx context.Context, version Version) (io.ReadCloser, error) {
dirName := g.module + "@" + string(version)
return g.zipAs(ctx, version, dirName)
}
func (g *gitVCS) zipAs(ctx context.Context, version Version, dirName string) (io.ReadCloser, error) {
g.log("gitVCS.Zip", "module", g.module, "version", version)
ci, err := g.commit(ctx, version)
if err != nil {
@ -163,13 +205,20 @@ func (g *gitVCS) Zip(ctx context.Context, version Version) (io.ReadCloser, error
if submodule(f.Name) {
continue
}
mode, err := f.Mode.ToOSFileMode()
if err != nil {
return nil, err
}
if !mode.IsRegular() {
continue
}
name := f.Name
if strings.HasPrefix(name, prefix) {
name = strings.TrimPrefix(name, prefix)
} else {
continue
}
w, err := zw.Create(filepath.Join(g.module+"@"+string(version), name))
w, err := zw.Create(filepath.Join(dirName, name))
if err != nil {
return nil, err
}
@ -228,10 +277,15 @@ func (g *gitVCS) commit(ctx context.Context, version Version) (*object.Commit, e
err = repo.FetchContext(ctx, &git.FetchOptions{
RemoteName: remoteName,
Auth: auth,
Tags: git.AllTags,
})
if err != nil && err != git.NoErrAlreadyUpToDate {
return nil, err
}
tagPrefix := ""
if g.prefix != "" {
tagPrefix = g.prefix + "/"
}
version = Version(strings.TrimSuffix(string(version), "+incompatible"))
hash := version.Hash()
@ -241,7 +295,7 @@ func (g *gitVCS) commit(ctx context.Context, version Version) (*object.Commit, e
return nil, err
}
tags.ForEach(func(t *plumbing.Reference) error {
if t.Name().String() == "refs/tags/"+string(version) {
if t.Name().String() == path.Join("refs/tags", tagPrefix, string(version)) {
hash = t.Hash().String()
annotated, err := repo.TagObject(t.Hash())
if err == nil {
@ -269,7 +323,7 @@ func (g *gitVCS) commit(ctx context.Context, version Version) (*object.Commit, e
func (g *gitVCS) authMethod() (transport.AuthMethod, error) {
if g.auth.Key != "" {
return ssh.NewPublicKeysFromFile("git", g.auth.Key, "")
return ssh.NewPublicKeysFromFile("git", g.auth.Key, g.auth.Password)
} else if g.auth.Username != "" {
return &http.BasicAuth{Username: g.auth.Username, Password: g.auth.Password}, nil
}

View File

@ -10,6 +10,7 @@ import (
"io"
"io/ioutil"
"sort"
"strings"
"testing"
)
@ -73,12 +74,19 @@ func TestGit(t *testing.T) {
Timestamp: "2016-09-29",
Checksum: "WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=",
},
{
// A module with symlinks, should match Go 1.11.4 algorithm fix
Module: "github.com/hashicorp/go-rootcerts",
Tag: "v0.0.0-20160503143440-6bb64b370b90",
Timestamp: "2016-05-03",
Checksum: "VBj0QYQ0u2MCJzBfeYXGexnAl17GsH1yidnoxCqqD9E=",
},
} {
if test.Module == "" {
continue
}
auth := NoAuth()
if test.Tag != "" {
if test.Tag != "" && !strings.HasPrefix(test.Tag, "v0.0.0-") {
t.Run(test.Module+"/List", func(t *testing.T) {
git := NewGit(t.Log, "", test.Module, auth)
list, err := git.List(context.Background())

87
pkg/vcs/gomod.go Normal file
View File

@ -0,0 +1,87 @@
package vcs
import (
"bytes"
"context"
"encoding/json"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
)
type goVCS struct {
dir string
log logger
module string
}
func NewGoMod(l logger, module string) VCS {
return &goVCS{log: l, module: module, dir: "/tmp/_go"}
}
func (g *goVCS) List(ctx context.Context) ([]Version, error) {
if err := g.download(ctx, "latest"); err != nil {
return nil, err
}
b, err := g.file("list")
if err != nil {
return nil, err
}
versions := []Version{}
for _, line := range strings.Split(string(b), "\n") {
versions = append(versions, Version(line))
}
return versions, nil
}
func (g *goVCS) Timestamp(ctx context.Context, version Version) (time.Time, error) {
if err := g.download(ctx, version.String()); err != nil {
return time.Time{}, err
}
b, err := g.file(version.String() + ".info")
if err != nil {
return time.Time{}, err
}
info := struct {
Version string
Time time.Time
}{}
if json.Unmarshal(b, &info) == nil {
return info.Time, nil
}
if t, err := time.Parse(time.RFC3339, string(b)); err == nil {
return t, nil
}
if sec, err := strconv.ParseInt(string(b), 10, 64); err == nil {
return time.Unix(sec, 0), nil
}
return time.Time{}, nil
}
func (g *goVCS) Zip(ctx context.Context, version Version) (io.ReadCloser, error) {
if err := g.download(ctx, version.String()); err != nil {
return nil, err
}
b, err := g.file(version.String() + ".zip")
if err != nil {
return nil, err
}
return ioutil.NopCloser(bytes.NewReader(b)), nil
}
func (g *goVCS) download(ctx context.Context, version string) error {
cmd := exec.Command("go", "mod", "download", g.module+"@"+version)
cmd.Env = append(os.Environ(), "GOPATH="+g.dir)
cmd.Stderr = os.Stderr
return cmd.Run()
}
func (g *goVCS) file(name string) ([]byte, error) {
path := filepath.Join(g.dir, "pkg", "mod", "cache", "download", encodeBangs(g.module), "@v", name)
return ioutil.ReadFile(path)
}

146
pkg/vcs/tags.go Normal file
View File

@ -0,0 +1,146 @@
package vcs
import (
"context"
"fmt"
"io"
"time"
)
type ephemeralTag struct {
semVer Version
short string
}
type EphemeralTagStorage struct {
tagsByModule map[moduleName][]ephemeralTag
}
func NewEphemeralTagStorage() *EphemeralTagStorage {
return &EphemeralTagStorage{
tagsByModule: make(map[moduleName][]ephemeralTag),
}
}
func (s *EphemeralTagStorage) Tag(module string, semVer Version, short string) error {
tags := s.tagsByModule[module]
tmp := tags[:0]
for _, t := range tags {
if t.semVer != semVer {
tmp = append(tmp, t)
}
}
s.tagsByModule[module] = append(tmp, ephemeralTag{semVer, short})
return nil
}
func (s *EphemeralTagStorage) tags(module string) []ephemeralTag {
return s.tagsByModule[module]
}
type moduleName = string
type taggableVCS struct {
wrapped *gitVCS
module string
storage *EphemeralTagStorage
}
type Taggable interface {
Tag(ctx context.Context, semVer Version, short string) error
}
// NewGitWithEphemeralTags return a go-git VCS client implementation that
// provides information about the specific module using the given
// authentication mechanism while adding support to ephemeral tags.
func NewGitWithEphemeralTags(l logger, dir string, module string, auth Auth, storage *EphemeralTagStorage) VCS {
git := NewGit(l, dir, module, auth).(*gitVCS)
return &taggableVCS{
wrapped: git,
module: module,
storage: storage,
}
}
func (v *taggableVCS) safeList(ctx context.Context) ([]Version, error) {
remoteVersions, err := v.wrapped.List(ctx)
if err != nil {
// Ignore this error, we can still count on ephemeral tags.
if err != ErrNoMatchingVersion {
return nil, err
}
v.wrapped.log("No remote version tags yet:", err)
}
return remoteVersions, nil
}
func (v *taggableVCS) Tag(ctx context.Context, semVer Version, short string) error {
remoteVersions, err := v.safeList(ctx)
if err != nil {
return err
}
if versionExists(remoteVersions, semVer) {
return fmt.Errorf("remote version %s already exists for module %s", semVer, v.module)
}
v.wrapped.log("taggableVCS.Tag", "version", semVer, "short", short)
return v.storage.Tag(v.module, semVer, short)
}
func (v *taggableVCS) List(ctx context.Context) ([]Version, error) {
remoteVersions, err := v.safeList(ctx)
if err != nil {
return nil, err
}
tags := v.storage.tags(v.module)
// Remote versions win.
allVersions := appendEphemeralVersion(remoteVersions, tags...)
// TODO(bilus): BUG - tag version 1.0.0 and then 0.1.0 - 0.1.0 is the "latest".
// sort.Slice(allVersions, func(i, j) bool { return allVersions[i].Before(allVersions[j]) })
return allVersions, nil
}
func appendEphemeralVersion(versions []Version, tags ...ephemeralTag) []Version {
ephemeral := make([]Version, 0)
for _, tag := range tags {
if !versionExists(versions, tag.semVer) {
ephemeral = append(ephemeral, tag.semVer)
}
}
return append(versions, ephemeral...)
}
func versionExists(versions []Version, v Version) bool {
for _, v2 := range versions {
if v == v2 {
return true
}
}
return false
}
func (v *taggableVCS) Timestamp(ctx context.Context, version Version) (time.Time, error) {
version2, err := v.resolveVersion(ctx, version)
if err != nil {
return time.Time{}, err
}
return v.wrapped.Timestamp(ctx, version2)
}
func (v *taggableVCS) Zip(ctx context.Context, version Version) (io.ReadCloser, error) {
version2, err := v.resolveVersion(ctx, version)
if err != nil {
return nil, err
}
// Zip must contain the ephemeral version.
dirName := v.module + "@" + string(version)
return v.wrapped.zipAs(ctx, version2, dirName)
}
func (v *taggableVCS) resolveVersion(ctx context.Context, version Version) (Version, error) {
for _, tag := range v.storage.tags(v.module) {
if tag.semVer == version {
return v.wrapped.versionFromHash(ctx, tag.short)
}
}
return version, nil
}

View File

@ -27,6 +27,11 @@ func (v Version) Hash() string {
return fields[2]
}
// String returns a string representation of a version
func (v Version) String() string {
return string(v)
}
// Module is a source code snapshot for which one can get the commit timestamp
// or the actual ZIP with the source code in it.
type Module interface {
@ -56,4 +61,4 @@ func NoAuth() Auth { return Auth{} }
func Password(username, password string) Auth { return Auth{Username: username, Password: password} }
// Key returns an Auth implementation that uses key file authentication mechanism.
func Key(key string) Auth { return Auth{Key: key} }
func Key(key, password string) Auth { return Auth{Key: key, Password: password} }