1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-02-07 13:31:37 +02:00

feat: Use GitLab Direct Asset Links (#2219)

* feat: Use GitLab Direct Asset Links

Implement the use of Direct Asset Links when uploading artifacts to a GitLab release

* fix: Remove ArtifactUploadHash

As GitLab support for direct asset linking exists, remove ArtifactUploadHash due to it no longer being required

* test: fix unit tests for gitlab urls

* fix: Use artifact name during GitLab upload

file.Name() included the path to the file, which isn't needed and breaks other areas such as homebrew releases

* docs: Require GitLab version v12.9+

Due to newly introduced dependency on direct asset linking
This commit is contained in:
Matt Palmer 2021-05-17 18:33:04 +01:00 committed by GitHub
parent 513503644e
commit bf4359017b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 29 additions and 131 deletions

View File

@ -2,11 +2,9 @@ package client
import (
"crypto/tls"
"errors"
"fmt"
"net/http"
"os"
"strings"
"github.com/apex/log"
"github.com/goreleaser/goreleaser/internal/artifact"
@ -18,9 +16,6 @@ import (
const DefaultGitLabDownloadURL = "https://gitlab.com"
// ErrExtractHashFromFileUploadURL indicates the file upload hash could not ne extracted from the url.
var ErrExtractHashFromFileUploadURL = errors.New("could not extract hash from gitlab file upload url")
type gitlabClient struct {
client *gitlab.Client
}
@ -264,7 +259,7 @@ func (c *gitlabClient) CreateRelease(ctx *context.Context, body string) (release
func (c *gitlabClient) ReleaseURLTemplate(ctx *context.Context) (string, error) {
return fmt.Sprintf(
"%s/%s/%s/uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}",
"%s/%s/%s/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}",
ctx.Config.GitLabURLs.Download,
ctx.Config.Release.GitLab.Owner,
ctx.Config.Release.GitLab.Name,
@ -299,12 +294,14 @@ func (c *gitlabClient) Upload(
// projectFile.URL from upload: /uploads/<hash>/filename.txt
linkURL := gitlabBaseURL + "/" + projectID + projectFile.URL
name := artifact.Name
filename := "/" + name
releaseLink, _, err := c.client.ReleaseLinks.CreateReleaseLink(
projectID,
releaseID,
&gitlab.CreateReleaseLinkOptions{
Name: &name,
URL: &linkURL,
Name: &name,
URL: &linkURL,
FilePath: &filename,
})
if err != nil {
return RetriableError{err}
@ -312,43 +309,17 @@ func (c *gitlabClient) Upload(
log.WithFields(log.Fields{
"id": releaseLink.ID,
"url": releaseLink.URL,
"url": releaseLink.DirectAssetURL,
}).Debug("created release link")
fileUploadHash, err := extractProjectFileHashFrom(projectFile.URL)
if err != nil {
return err
}
// for checksums.txt the field is nil, so we initialize it
if artifact.Extra == nil {
artifact.Extra = make(map[string]interface{})
}
// we set this hash to be able to download the file
// in following publish pipes like brew, scoop
artifact.Extra["ArtifactUploadHash"] = fileUploadHash
return nil
}
// extractProjectFileHashFrom extracts the hash from the
// relative project file url of the format '/uploads/<hash>/filename.ext'.
func extractProjectFileHashFrom(projectFileURL string) (string, error) {
log.WithField("projectFileURL", projectFileURL).Debug("extract file hash from")
splittedProjectFileURL := strings.Split(projectFileURL, "/")
if len(splittedProjectFileURL) != 4 {
log.WithField("projectFileURL", projectFileURL).Debug("could not extract file hash")
return "", ErrExtractHashFromFileUploadURL
}
fileHash := splittedProjectFileURL[2]
log.WithFields(log.Fields{
"projectFileURL": projectFileURL,
"fileHash": fileHash,
}).Debug("extracted file hash")
return fileHash, nil
}
// getMilestoneByTitle returns a milestone by title.
func (c *gitlabClient) getMilestoneByTitle(repo Repo, title string) (*gitlab.Milestone, error) {
opts := &gitlab.ListMilestonesOptions{

View File

@ -1,7 +1,6 @@
package client
import (
"fmt"
"testing"
"github.com/goreleaser/goreleaser/pkg/config"
@ -9,31 +8,6 @@ import (
"github.com/stretchr/testify/require"
)
func TestExtractHashFromProjectFileURL(t *testing.T) {
givenHash := "22e8b1508b0f28433b94754a5ea2f4aa"
projectFileURL := fmt.Sprintf("/uploads/%s/release-testing_0.3.7_Darwin_x86_64.tar.gz", givenHash)
extractedHash, err := extractProjectFileHashFrom(projectFileURL)
if err != nil {
t.Errorf("expexted no error but got: %v", err)
}
require.Equal(t, givenHash, extractedHash)
}
func TestFailToExtractHashFromProjectFileURL(t *testing.T) {
givenHash := "22e8b1508b0f28433b94754a5ea2f4aa"
projectFileURL := fmt.Sprintf("/uploads/%s/new-path/file.ext", givenHash)
_, err := extractProjectFileHashFrom(projectFileURL)
if err == nil {
t.Errorf("expected an error but got none for new-path in url")
}
projectFileURL = fmt.Sprintf("/%s/file.ext", givenHash)
_, err = extractProjectFileHashFrom(projectFileURL)
if err == nil {
t.Errorf("expected an error but got none for path-too-small in url")
}
}
func TestGitLabReleaseURLTemplate(t *testing.T) {
ctx := context.New(config.Project{
GitLabURLs: config.GitLabURLs{
@ -53,6 +27,6 @@ func TestGitLabReleaseURLTemplate(t *testing.T) {
urlTpl, err := client.ReleaseURLTemplate(ctx)
require.NoError(t, err)
expectedUrl := "https://gitlab.com/owner/name/uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}"
expectedUrl := "https://gitlab.com/owner/name/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}"
require.Equal(t, expectedUrl, urlTpl)
}

View File

@ -223,9 +223,8 @@ func TestRunPipe(t *testing.T) {
Goarch: "amd64",
Type: artifact.UploadableArchive,
Extra: map[string]interface{}{
"ID": "bar",
"Format": "tar.gz",
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
"ID": "bar",
"Format": "tar.gz",
},
})
path := filepath.Join(folder, "bin.tar.gz")
@ -236,9 +235,8 @@ func TestRunPipe(t *testing.T) {
Goarch: "amd64",
Type: artifact.UploadableArchive,
Extra: map[string]interface{}{
"ID": "foo",
"Format": "tar.gz",
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
"ID": "foo",
"Format": "tar.gz",
},
})
@ -301,9 +299,8 @@ func TestRunPipeNameTemplate(t *testing.T) {
Goarch: "amd64",
Type: artifact.UploadableArchive,
Extra: map[string]interface{}{
"ID": "foo",
"Format": "tar.gz",
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
"ID": "foo",
"Format": "tar.gz",
},
})
@ -386,9 +383,8 @@ func TestRunPipeMultipleBrewsWithSkip(t *testing.T) {
Goarch: "amd64",
Type: artifact.UploadableArchive,
Extra: map[string]interface{}{
"ID": "foo",
"Format": "tar.gz",
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
"ID": "foo",
"Format": "tar.gz",
},
})

View File

@ -274,18 +274,12 @@ func Test_doRun(t *testing.T) {
Goos: "windows",
Goarch: "amd64",
Path: file,
Extra: map[string]interface{}{
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
},
},
{
Name: "foo_1.0.1_windows_386.tar.gz",
Goos: "windows",
Goarch: "386",
Path: file,
Extra: map[string]interface{}{
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
},
},
},
shouldNotErr,
@ -334,18 +328,12 @@ func Test_doRun(t *testing.T) {
Goos: "windows",
Goarch: "amd64",
Path: file,
Extra: map[string]interface{}{
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
},
},
{
Name: "foo_1.0.1_windows_386.tar.gz",
Goos: "windows",
Goarch: "386",
Path: file,
Extra: map[string]interface{}{
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
},
},
},
shouldNotErr,
@ -904,7 +892,7 @@ func Test_buildManifest(t *testing.T) {
},
Description: "A run pipe test formula",
Homepage: "https://gitlab.com/goreleaser",
URLTemplate: "http://gitlab.mycompany.com/foo/bar/uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}",
URLTemplate: "http://gitlab.mycompany.com/foo/bar/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}",
CommitMessageTemplate: "chore(scoop): update {{ .ProjectName }} version {{ .Tag }}",
Persist: []string{"data.cfg", "etc"},
},
@ -929,7 +917,6 @@ func Test_buildManifest(t *testing.T) {
Goarch: "amd64",
Path: file,
Extra: map[string]interface{}{
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
"Builds": []*artifact.Artifact{
{
Name: "foo.exe",
@ -946,7 +933,6 @@ func Test_buildManifest(t *testing.T) {
Goarch: "arm",
Path: file,
Extra: map[string]interface{}{
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
"Builds": []*artifact.Artifact{
{
Name: "foo.exe",
@ -963,7 +949,6 @@ func Test_buildManifest(t *testing.T) {
Goarch: "386",
Path: file,
Extra: map[string]interface{}{
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
"Builds": []*artifact.Artifact{
{
Name: "foo.exe",
@ -1026,7 +1011,7 @@ func TestWrapInDirectory(t *testing.T) {
},
Description: "A run pipe test formula",
Homepage: "https://gitlab.com/goreleaser",
URLTemplate: "http://gitlab.mycompany.com/foo/bar/uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}",
URLTemplate: "http://gitlab.mycompany.com/foo/bar/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}",
CommitMessageTemplate: "chore(scoop): update {{ .ProjectName }} version {{ .Tag }}",
Persist: []string{"data.cfg", "etc"},
},
@ -1042,8 +1027,7 @@ func TestWrapInDirectory(t *testing.T) {
Goarch: "amd64",
Path: file,
Extra: map[string]interface{}{
"ArtifactUploadHash": "820ead5d9d2266c728dce6d4d55b6460",
"WrappedIn": "foo_1.0.1_windows_amd64",
"WrappedIn": "foo_1.0.1_windows_amd64",
"Builds": []*artifact.Artifact{
{
Name: "foo.exe",

View File

@ -2,7 +2,7 @@
"version": "1.0.1",
"architecture": {
"32bit": {
"url": "http://gitlab.mycompany.com/foo/bar/uploads/820ead5d9d2266c728dce6d4d55b6460/foo_1.0.1_windows_386.tar.gz",
"url": "http://gitlab.mycompany.com/foo/bar/-/releases/v1.0.1/downloads/foo_1.0.1_windows_386.tar.gz",
"bin": [
"foo.exe",
"bar.exe"
@ -10,7 +10,7 @@
"hash": "5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269"
},
"64bit": {
"url": "http://gitlab.mycompany.com/foo/bar/uploads/820ead5d9d2266c728dce6d4d55b6460/foo_1.0.1_windows_amd64.tar.gz",
"url": "http://gitlab.mycompany.com/foo/bar/-/releases/v1.0.1/downloads/foo_1.0.1_windows_amd64.tar.gz",
"bin": [
"foo.exe",
"bar.exe"

View File

@ -2,7 +2,7 @@
"version": "1.0.1",
"architecture": {
"64bit": {
"url": "http://gitlab.mycompany.com/foo/bar/uploads/820ead5d9d2266c728dce6d4d55b6460/foo_1.0.1_windows_amd64.tar.gz",
"url": "http://gitlab.mycompany.com/foo/bar/-/releases/v1.0.1/downloads/foo_1.0.1_windows_amd64.tar.gz",
"bin": [
"foo_1.0.1_windows_amd64/foo.exe",
"foo_1.0.1_windows_amd64/bar.exe"

View File

@ -55,9 +55,6 @@ const (
artifactName = "ArtifactName"
artifactPath = "ArtifactPath"
// gitlab only.
artifactUploadHash = "ArtifactUploadHash"
// build keys.
name = "Name"
ext = "Ext"
@ -135,11 +132,6 @@ func (t *Template) WithArtifact(a *artifact.Artifact, replacements map[string]st
t.fields[binary] = bin.(string)
t.fields[artifactName] = a.Name
t.fields[artifactPath] = a.Path
if val, ok := a.Extra["ArtifactUploadHash"]; ok {
t.fields[artifactUploadHash] = val
} else {
t.fields[artifactUploadHash] = ""
}
return t
}

View File

@ -47,7 +47,6 @@ func TestWithArtifact(t *testing.T) {
"shortcommit": "{{.ShortCommit}}",
"binary": "{{.Binary}}",
"proj": "{{.ProjectName}}",
"": "{{.ArtifactUploadHash}}",
"github.com/goreleaser/goreleaser": "{{ .ModulePath }}",
} {
tmpl := tmpl
@ -72,24 +71,6 @@ func TestWithArtifact(t *testing.T) {
})
}
t.Run("artifact with gitlab ArtifactUploadHash", func(t *testing.T) {
t.Parallel()
uploadHash := "820ead5d9d2266c728dce6d4d55b6460"
result, err := New(ctx).WithArtifact(
&artifact.Artifact{
Name: "another-binary",
Goarch: "amd64",
Goos: "linux",
Goarm: "6",
Extra: map[string]interface{}{
"ArtifactUploadHash": uploadHash,
},
}, map[string]string{},
).Apply("{{ .ArtifactUploadHash }}")
require.NoError(t, err)
require.Equal(t, uploadHash, result)
})
t.Run("artifact without binary name", func(t *testing.T) {
t.Parallel()
result, err := New(ctx).WithArtifact(

View File

@ -49,7 +49,7 @@ brews:
# Template for the url which is determined by the given Token (github or gitlab)
# Default for github is "https://github.com/<repo_owner>/<repo_name>/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
# Default for gitlab is "https://gitlab.com/<repo_owner>/<repo_name>/uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}"
# Default for gitlab is "https://gitlab.com/<repo_owner>/<repo_name>/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}"
# Default for gitea is "https://gitea.com/<repo_owner>/<repo_name>/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
url_template: "http://github.mycompany.com/foo/bar/releases/{{ .Tag }}/{{ .ArtifactName }}"
@ -132,7 +132,7 @@ brews:
install: |
bin.install "program"
...
# Custom post_install script for brew.
# Could be used to do any additional work after the "install" script
# Default is empty.
@ -178,7 +178,7 @@ class Program < Formula
def install
bin.install "program"
end
def post_install
etc.install "app-config.conf"
end

View File

@ -95,7 +95,7 @@ release:
If you use GitLab subgroups, you need to specify it in the `owner` field, e.g. `mygroup/mysubgroup`.
!!! warning
Only GitLab `v11.7+` are supported for releases.
Only GitLab `v12.9+` are supported for releases.
You can also configure the `release` section to upload to a [Gitea](https://gitea.io) instance:

View File

@ -13,7 +13,7 @@ the commented example below:
scoop:
# Template for the url which is determined by the given Token (github or gitlab)
# Default for github is "https://github.com/<repo_owner>/<repo_name>/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
# Default for gitlab is "https://gitlab.com/<repo_owner>/<repo_name>/uploads/{{ .ArtifactUploadHash }}/{{ .ArtifactName }}"
# Default for gitlab is "https://gitlab.com/<repo_owner>/<repo_name>/-/releases/{{ .Tag }}/downloads/{{ .ArtifactName }}"
# Default for gitea is "https://gitea.com/<repo_owner>/<repo_name>/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
url_template: "http://github.mycompany.com/foo/bar/releases/{{ .Tag }}/{{ .ArtifactName }}"

View File

@ -104,9 +104,9 @@ Or, if you released to GitLab, check it out too!
</a>
!!! note
Releasing to a private-hosted GitLab CE will only work for version `v11.7+`,
because the release feature was introduced in this
[version](https://docs.gitlab.com/ee/user/project/releases/index.html).
Releasing to a private-hosted GitLab CE will only work for version `v12.9+`, due to dependencies
on [release](https://docs.gitlab.com/ee/user/project/releases/index.html) functionality
and [direct asset linking](https://docs.gitlab.com/ee/user/project/releases/index.html#permanent-links-to-release-assets).
## Dry run