Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/marcosnils/bin
go 1.24.4

require (
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0
github.com/caarlos0/log v0.5.1
github.com/cheggaaa/pb v2.0.7+incompatible
github.com/coreos/go-semver v0.3.1
Expand All @@ -21,6 +22,7 @@ require (
)

require (
github.com/42wim/httpsig v1.2.3 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/charmbracelet/colorprofile v0.3.1 // indirect
Expand All @@ -31,10 +33,12 @@ require (
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-fed/httpsig v1.1.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
Expand Down
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0 h1:HTCWpzyWQOHDWt3LzI6/d2jvUDsw/vgGRWm/8BTvcqI=
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2 v2.2.0/go.mod h1:ZglEEDj+qkxYUb+SQIeqGtFxQrbaMYqIOgahNKb7uxs=
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
Expand Down Expand Up @@ -31,6 +35,8 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
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/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA=
Expand All @@ -43,6 +49,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
Expand Down Expand Up @@ -153,6 +161,7 @@ go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v8
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
Expand All @@ -164,6 +173,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
Expand All @@ -175,10 +185,14 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
Expand Down
144 changes: 144 additions & 0 deletions pkg/providers/forgejo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copied from the gitlab provider
package providers

import (
"fmt"
"net/url"
"os"
"strings"

forgejoSdk "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
"github.com/caarlos0/log"
"github.com/marcosnils/bin/pkg/assets"
)

type forgejo struct {
url *url.URL
client *forgejoSdk.Client
token string
owner string
repo string
tag string
}

func (f *forgejo) Fetch(opts *FetchOpts) (*File, error) {
var client = f.client
var release *forgejoSdk.Release

var err error
// If we have a tag, let's fetch from there
if len(f.tag) > 0 || len(opts.Version) > 0 {
if len(opts.Version) > 0 {
// this is used by for the `ensure` command
f.tag = opts.Version
}
log.Infof("Getting %s release for %s/%s", f.tag, f.owner, f.repo)
release, _, err = client.GetReleaseByTag(f.owner, f.repo, f.tag)
} else {
// TODO: handle case when repo doesn't have releases?
log.Infof("Fetch: Getting latest release for %s/%s", f.owner, f.repo)
var latestVersion string
latestVersion, _, err = f.GetLatestVersion()
if err != nil {
return nil, err
}
release, _, err = client.GetReleaseByTag(f.owner, f.repo, latestVersion)
}

if err != nil {
return nil, err
}

candidates := []*assets.Asset{}

if err != nil {
return nil, err
}

for _, attachment := range release.Attachments {
asset := &assets.Asset{
Name: attachment.Name,
URL: attachment.DownloadURL,
}

candidates = append(candidates, asset)
}

fi := assets.NewFilter(&assets.FilterOpts{SkipScoring: opts.All, PackagePath: opts.PackagePath, SkipPathCheck: opts.SkipPatchCheck})

gf, err := fi.FilterAssets(f.repo, candidates)
if err != nil {
return nil, err
}

if f.token != "" {
if gf.ExtraHeaders == nil {
gf.ExtraHeaders = map[string]string{}
}
gf.ExtraHeaders["PRIVATE-TOKEN"] = f.token
}

outFile, err := fi.ProcessURL(gf)
if err != nil {
return nil, err
}

version := release.TagName

// TODO calculate file hash. Not sure if we can / should do it here
// since we don't want to read the file unnecesarily. Additionally, sometimes
// releases have .sha256 files, so it'd be nice to check for those also
file := &File{Data: outFile.Source, Name: outFile.Name, Version: version}

return file, nil
}

func (f *forgejo) GetID() string {
return "forgejo"
}

// GetLatestVersion checks the latest repo release and
// returns the corresponding name and url to fetch the version
func (f *forgejo) GetLatestVersion() (string, string, error) {
log.Debugf("Getting latest release for %s/%s", f.owner, f.repo)

var latestRelease, _, err = f.client.GetLatestRelease(f.owner, f.repo)
if err != nil {
return "", "", err
}

return latestRelease.TagName, latestRelease.URL, nil
}

func newForgejo(u *url.URL) (Provider, error) {
s := strings.Split(u.Path, "/")
if len(s) < 3 {
return nil, fmt.Errorf("Error parsing Forgejo URL %s, can't find owner and repo", u.String())
}

// it's a specific releases URL
var tag string
if strings.Contains(u.Path, "/releases/tag/") {
// For release URLs, the
// path is usually /releases/v0.1.
ps := strings.Split(u.Path, "/")
for i, p := range ps {
if p == "tag" {
tag = strings.Join(ps[i+1:], "/")
}
}
}

// Not familiar enough with forgejo (or bin) to know if this makes sense w/ forgejo
token := os.Getenv("FORGEJO_TOKEN")
hostnameSpecificEnvVarName := fmt.Sprintf("FORGEJO_TOKEN_%s", strings.ReplaceAll(u.Hostname(), `.`, "_"))
hostnameSpecificToken := os.Getenv(hostnameSpecificEnvVarName)
if hostnameSpecificToken != "" {
token = hostnameSpecificToken
}
client, err := forgejoSdk.NewClient("https://"+u.Host, forgejoSdk.SetToken(token))
if err != nil {
return nil, err
}
return &forgejo{url: u, client: client, token: token, owner: s[1], repo: s[2], tag: tag}, nil
}
6 changes: 6 additions & 0 deletions pkg/providers/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ func New(u, provider string) (Provider, error) {
return newGitLab(purl)
}

// codeberg uses (a fork of) forejo, so let that be the "flagship"
// instance
if strings.Contains(purl.Host, "codeberg") || provider == "forgejo" {
return newForgejo(purl)
}

if strings.Contains(purl.Host, "releases.hashicorp.com") || provider == "hashicorp" {
return newHashiCorp(purl)
}
Expand Down