Skip to content

Add support for Vagrant packages #20930

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add integration tests.
  • Loading branch information
KN4CK3R committed Aug 22, 2022
commit fecbbc26cec324d452ea0f1bb5d9e07f88b30506
170 changes: 170 additions & 0 deletions integrations/api_packages_vagrant_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package integrations

import (
"archive/tar"
"bytes"
"compress/gzip"
"fmt"
"net/http"
"strings"
"testing"

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/json"
vagrant_module "code.gitea.io/gitea/modules/packages/vagrant"

"github.com/stretchr/testify/assert"
)

func TestPackageVagrant(t *testing.T) {
defer prepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})

token := "Bearer " + getUserToken(t, user.Name)

packageName := "test_package"
packageVersion := "1.0.1"
packageDescription := "Test Description"
packageProvider := "virtualbox"

filename := fmt.Sprintf("%s.box", packageProvider)

infoContent, _ := json.Marshal(map[string]string{
"description": packageDescription,
})

var buf bytes.Buffer
zw := gzip.NewWriter(&buf)
archive := tar.NewWriter(zw)
archive.WriteHeader(&tar.Header{
Name: "info.json",
Mode: 0o600,
Size: int64(len(infoContent)),
})
archive.Write(infoContent)
archive.Close()
zw.Close()
content := buf.Bytes()

root := fmt.Sprintf("/api/packages/%s/vagrant", user.Name)

t.Run("Authenticate", func(t *testing.T) {
defer PrintCurrentTest(t)()

authenticateURL := fmt.Sprintf("%s/authenticate", root)

req := NewRequest(t, "GET", authenticateURL)
MakeRequest(t, req, http.StatusUnauthorized)

req = NewRequest(t, "GET", authenticateURL)
addTokenAuthHeader(req, token)
MakeRequest(t, req, http.StatusOK)
})

boxURL := fmt.Sprintf("%s/%s", root, packageName)

t.Run("Upload", func(t *testing.T) {
defer PrintCurrentTest(t)()

req := NewRequest(t, "HEAD", boxURL)
MakeRequest(t, req, http.StatusNotFound)

uploadURL := fmt.Sprintf("%s/%s/%s", boxURL, packageVersion, filename)

req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(content))
MakeRequest(t, req, http.StatusUnauthorized)

req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(content))
addTokenAuthHeader(req, token)
MakeRequest(t, req, http.StatusCreated)

req = NewRequest(t, "HEAD", boxURL)
resp := MakeRequest(t, req, http.StatusOK)
assert.True(t, strings.HasPrefix(resp.HeaderMap.Get("Content-Type"), "application/json"))

pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeVagrant)
assert.NoError(t, err)
assert.Len(t, pvs, 1)

pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
assert.NoError(t, err)
assert.NotNil(t, pd.SemVer)
assert.IsType(t, &vagrant_module.Metadata{}, pd.Metadata)
assert.Equal(t, packageName, pd.Package.Name)
assert.Equal(t, packageVersion, pd.Version.Version)

pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
assert.NoError(t, err)
assert.Len(t, pfs, 1)
assert.Equal(t, filename, pfs[0].Name)
assert.True(t, pfs[0].IsLead)

pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID)
assert.NoError(t, err)
assert.Equal(t, int64(len(content)), pb.Size)

req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(content))
addTokenAuthHeader(req, token)
MakeRequest(t, req, http.StatusConflict)
})

t.Run("Download", func(t *testing.T) {
defer PrintCurrentTest(t)()

req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s", boxURL, packageVersion, filename))
resp := MakeRequest(t, req, http.StatusOK)

assert.Equal(t, content, resp.Body.Bytes())
})

t.Run("EnumeratePackageVersions", func(t *testing.T) {
defer PrintCurrentTest(t)()

req := NewRequest(t, "GET", boxURL)
resp := MakeRequest(t, req, http.StatusOK)

type providerData struct {
Name string `json:"name"`
URL string `json:"url"`
Checksum string `json:"checksum"`
ChecksumType string `json:"checksum_type"`
}

type versionMetadata struct {
Version string `json:"version"`
Status string `json:"status"`
DescriptionHTML string `json:"description_html,omitempty"`
DescriptionMarkdown string `json:"description_markdown,omitempty"`
Providers []*providerData `json:"providers"`
}

type packageMetadata struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
ShortDescription string `json:"short_description,omitempty"`
Versions []*versionMetadata `json:"versions"`
}

var result packageMetadata
DecodeJSON(t, resp, &result)

assert.Equal(t, packageName, result.Name)
assert.Equal(t, packageDescription, result.Description)
assert.Len(t, result.Versions, 1)
version := result.Versions[0]
assert.Equal(t, packageVersion, version.Version)
assert.Equal(t, "active", version.Status)
assert.Len(t, version.Providers, 1)
provider := version.Providers[0]
assert.Equal(t, packageProvider, provider.Name)
assert.Equal(t, "sha512", provider.ChecksumType)
assert.Equal(t, "259bebd6160acad695016d22a45812e26f187aaf78e71a4c23ee3201528346293f991af3468a8c6c5d2a21d7d9e1bdc1bf79b87110b2fddfcc5a0d45963c7c30", provider.Checksum)
})
}
2 changes: 2 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3128,6 +3128,8 @@ rubygems.dependencies.development = Development Dependencies
rubygems.required.ruby = Requires Ruby version
rubygems.required.rubygems = Requires RubyGem version
rubygems.documentation = For more information on the RubyGems registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/rubygems/">the documentation</a>.
vagrant.install = To add a Vagrant box, run the following command:
vagrant.documentation = For more information on the Vagrant registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/vagrant/">the documentation</a>.
settings.link = Link this package to a repository
settings.link.description = If you link a package with a repository, the package is listed in the repository's package list.
settings.link.select = Select Repository
Expand Down
9 changes: 5 additions & 4 deletions routers/api/packages/vagrant/vagrant.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@ func EnumeratePackageVersions(ctx *context.Context) {
}

ctx.JSON(http.StatusOK, &packageMetadata{
Name: pds[0].Package.Name,
Versions: versions,
Name: pds[0].Package.Name,
Description: pds[len(pds)-1].Metadata.(*vagrant_module.Metadata).Description,
Versions: versions,
})
}

Expand Down Expand Up @@ -200,8 +201,8 @@ func UploadPackageFile(ctx *context.Context) {
},
)
if err != nil {
if err == packages_model.ErrDuplicatePackageVersion {
apiError(ctx, http.StatusBadRequest, err)
if err == packages_model.ErrDuplicatePackageFile {
apiError(ctx, http.StatusConflict, err)
return
}
apiError(ctx, http.StatusInternalServerError, err)
Expand Down
8 changes: 6 additions & 2 deletions templates/package/content/vagrant.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
<div class="ui attached segment">
<div class="ui form">
<div class="field">
<label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.vagrant.add"}}</label>
<div class="markup"><pre class="code-block"><code>vagrant box add "{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/vagrant/{{.PackageDescriptor.Package.Name}}"</code></pre></div>
<label>{{svg "octicon-terminal"}} {{.locale.Tr "packages.vagrant.install"}}</label>
<div class="markup"><pre class="code-block"><code>vagrant box add --box-version {{.PackageDescriptor.Version.Version}} "{{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/vagrant/{{.PackageDescriptor.Package.Name}}"</code></pre></div>
</div>
<div class="field">
<label>{{.locale.Tr "packages.vagrant.documentation" | Safe}}</label>
</div>
</div>
</div>
{{if .PackageDescriptor.Metadata.Description}}
<h4 class="ui top attached header">{{.locale.Tr "packages.about"}}</h4>
<div class="ui attached segment">{{.PackageDescriptor.Metadata.Description}}</div>
{{end}}
{{end}}