mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-01-11 03:11:50 +00:00
Allow management of components
This commit allows to add, remove and update components of published repositories without the need to recreate them. Signed-off-by: Christoph Fiehe <c.fiehe@eurodata.de>
This commit is contained in:
committed by
André Roth
parent
767bc6bd0b
commit
bd64232eb6
2
Makefile
2
Makefile
@@ -59,7 +59,7 @@ flake8: ## run flake8 on system test python files
|
|||||||
|
|
||||||
lint:
|
lint:
|
||||||
# Install golangci-lint
|
# Install golangci-lint
|
||||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
|
@test -f $(BINPATH)/golangci-lint || go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
|
||||||
# Running lint
|
# Running lint
|
||||||
@PATH=$(BINPATH)/:$(PATH) golangci-lint run
|
@PATH=$(BINPATH)/:$(PATH) golangci-lint run
|
||||||
|
|
||||||
|
|||||||
530
api/publish.go
530
api/publish.go
@@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aptly-dev/aptly/aptly"
|
"github.com/aptly-dev/aptly/aptly"
|
||||||
@@ -13,17 +14,29 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SigningOptions is a shared between publish API GPG options structure
|
type signingParams struct {
|
||||||
type SigningOptions struct {
|
// Don't sign published repository
|
||||||
Skip bool
|
Skip bool ` json:"Skip"`
|
||||||
GpgKey string
|
// GPG key ID to use when signing the release, if not specified default key is used
|
||||||
Keyring string
|
GpgKey string ` json:"GpgKey"`
|
||||||
SecretKeyring string
|
// GPG keyring to use (instead of default)
|
||||||
Passphrase string
|
Keyring string ` json:"Keyring"`
|
||||||
PassphraseFile string
|
// GPG secret keyring to use (instead of default)
|
||||||
|
SecretKeyring string ` json:"SecretKeyring"`
|
||||||
|
// GPG passphrase to unlock private key (possibly insecure)
|
||||||
|
Passphrase string ` json:"Passphrase"`
|
||||||
|
// GPG passphrase file to unlock private key (possibly insecure)
|
||||||
|
PassphraseFile string ` json:"PassphraseFile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSigner(options *SigningOptions) (pgp.Signer, error) {
|
type sourceParams struct {
|
||||||
|
// Name of the component
|
||||||
|
Component string `binding:"required" json:"Component" example:"contrib"`
|
||||||
|
// Name of the local repository/snapshot
|
||||||
|
Name string `binding:"required" json:"Name" example:"snap1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSigner(options *signingParams) (pgp.Signer, error) {
|
||||||
if options.Skip {
|
if options.Skip {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -54,10 +67,10 @@ func slashEscape(path string) string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary Get publish points
|
// @Summary List published repositories
|
||||||
// @Description Get list of available publish points. Each publish point is returned as in “show” API.
|
// @Description **Get a list of all published repositories**
|
||||||
// @Tags Publish
|
// @Tags Publish
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {array} deb.PublishedRepo
|
// @Success 200 {array} deb.PublishedRepo
|
||||||
// @Failure 500 {object} Error "Internal Error"
|
// @Failure 500 {object} Error "Internal Error"
|
||||||
// @Router /api/publish [get]
|
// @Router /api/publish [get]
|
||||||
@@ -79,37 +92,97 @@ func apiPublishList(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
AbortWithJSONError(c, 500, err)
|
AbortWithJSONError(c, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(200, result)
|
c.JSON(http.StatusOK, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST /publish/:prefix
|
// @Summary Show published repository
|
||||||
func apiPublishRepoOrSnapshot(c *gin.Context) {
|
// @Description **Get published repository by name**
|
||||||
|
// @Tags Publish
|
||||||
|
// @Consume json
|
||||||
|
// @Produce json
|
||||||
|
// @Param prefix path string true "publishing prefix, use `:.` instead of `.` because it is ambigious in URLs"
|
||||||
|
// @Param distribution path string true "distribution name"
|
||||||
|
// @Success 200 {object} deb.RemoteRepo
|
||||||
|
// @Failure 404 {object} Error "Published repository not found"
|
||||||
|
// @Failure 500 {object} Error "Internal Error"
|
||||||
|
// @Router /api/publish/{prefix}/{distribution} [get]
|
||||||
|
func apiPublishShow(c *gin.Context) {
|
||||||
param := slashEscape(c.Params.ByName("prefix"))
|
param := slashEscape(c.Params.ByName("prefix"))
|
||||||
storage, prefix := deb.ParsePrefix(param)
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
distribution := parseEscapedPath(c.Params.ByName("distribution"))
|
||||||
|
|
||||||
var b struct {
|
collectionFactory := context.NewCollectionFactory()
|
||||||
SourceKind string `binding:"required"`
|
collection := collectionFactory.PublishedRepoCollection()
|
||||||
Sources []struct {
|
|
||||||
Component string
|
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
Name string `binding:"required"`
|
if err != nil {
|
||||||
} `binding:"required"`
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to show: %s", err))
|
||||||
Distribution string
|
return
|
||||||
Label string
|
|
||||||
Origin string
|
|
||||||
NotAutomatic string
|
|
||||||
ButAutomaticUpgrades string
|
|
||||||
ForceOverwrite bool
|
|
||||||
SkipContents *bool
|
|
||||||
SkipBz2 *bool
|
|
||||||
Architectures []string
|
|
||||||
Signing SigningOptions
|
|
||||||
AcquireByHash *bool
|
|
||||||
MultiDist bool
|
|
||||||
}
|
}
|
||||||
|
err = collection.LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, published)
|
||||||
|
}
|
||||||
|
|
||||||
|
type publishedRepoCreateParams struct {
|
||||||
|
// 'local' for local repositories and 'snapshot' for snapshots
|
||||||
|
SourceKind string `binding:"required" json:"SourceKind" example:"snapshot"`
|
||||||
|
// List of 'Component/Name' objects, 'Name' is either local repository or snapshot name
|
||||||
|
Sources []sourceParams `binding:"required" json:"Sources"`
|
||||||
|
// Distribution name, if missing Aptly would try to guess from sources
|
||||||
|
Distribution string ` json:"Distribution"`
|
||||||
|
// Value of Label: field in published repository stanza
|
||||||
|
Label string ` json:"Label"`
|
||||||
|
// Value of Origin: field in published repository stanza
|
||||||
|
Origin string ` json:"Origin"`
|
||||||
|
// when publishing, overwrite files in pool/ directory without notice
|
||||||
|
ForceOverwrite bool ` json:"ForceOverwrite"`
|
||||||
|
// Override list of published architectures
|
||||||
|
Architectures []string ` json:"Architectures"`
|
||||||
|
// GPG options
|
||||||
|
Signing signingParams ` json:"Signing"`
|
||||||
|
// Setting to yes indicates to the package manager to not install or upgrade packages from the repository without user consent
|
||||||
|
NotAutomatic string ` json:"NotAutomatic"`
|
||||||
|
// setting to yes excludes upgrades from the NotAutomic setting
|
||||||
|
ButAutomaticUpgrades string ` json:"ButAutomaticUpgrades"`
|
||||||
|
// Don't generate contents indexes
|
||||||
|
SkipContents *bool ` json:"SkipContents"`
|
||||||
|
// Don't remove unreferenced files in prefix/component
|
||||||
|
SkipCleanup *bool ` json:"SkipCleanup"`
|
||||||
|
// Skip bz2 compression for index files
|
||||||
|
SkipBz2 *bool ` json:"SkipBz2"`
|
||||||
|
// Provide index files by hash
|
||||||
|
AcquireByHash *bool ` json:"AcquireByHash"`
|
||||||
|
// Enable multiple packages with the same filename in different distributions
|
||||||
|
MultiDist *bool ` json:"MultiDist"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Create published repository
|
||||||
|
// @Description Publish local repository or snapshot under specified prefix. Storage might be passed in prefix as well, e.g. `s3:packages/`.
|
||||||
|
// @Description To supply empty prefix, just remove last part (`POST /api/publish`).
|
||||||
|
// @Tags Publish
|
||||||
|
// @Param prefix path string true "publishing prefix"
|
||||||
|
// @Consume json
|
||||||
|
// @Param request body publishedRepoCreateParams true "Parameters"
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} deb.RemoteRepo
|
||||||
|
// @Failure 400 {object} Error "Bad Request"
|
||||||
|
// @Failure 404 {object} Error "Source not found"
|
||||||
|
// @Failure 500 {object} Error "Internal Error"
|
||||||
|
// @Router /api/publish/{prefix} [post]
|
||||||
|
func apiPublishRepoOrSnapshot(c *gin.Context) {
|
||||||
|
var b publishedRepoCreateParams
|
||||||
|
|
||||||
|
param := parseEscapedPath(c.Params.ByName("prefix"))
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
|
||||||
if c.Bind(&b) != nil {
|
if c.Bind(&b) != nil {
|
||||||
return
|
return
|
||||||
@@ -119,12 +192,12 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
|
|||||||
|
|
||||||
signer, err := getSigner(&b.Signing)
|
signer, err := getSigner(&b.Signing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
AbortWithJSONError(c, 500, fmt.Errorf("unable to initialize GPG signer: %s", err))
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to initialize GPG signer: %s", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b.Sources) == 0 {
|
if len(b.Sources) == 0 {
|
||||||
AbortWithJSONError(c, 400, fmt.Errorf("unable to publish: soures are empty"))
|
AbortWithJSONError(c, http.StatusBadRequest, fmt.Errorf("unable to publish: soures are empty"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,12 +218,11 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
|
|||||||
|
|
||||||
snapshot, err = snapshotCollection.ByName(source.Name)
|
snapshot, err = snapshotCollection.ByName(source.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
AbortWithJSONError(c, 404, fmt.Errorf("unable to publish: %s", err))
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to publish: %s", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resources = append(resources, string(snapshot.ResourceKey()))
|
resources = append(resources, string(snapshot.ResourceKey()))
|
||||||
|
|
||||||
sources = append(sources, snapshot)
|
sources = append(sources, snapshot)
|
||||||
}
|
}
|
||||||
} else if b.SourceKind == deb.SourceLocalRepo {
|
} else if b.SourceKind == deb.SourceLocalRepo {
|
||||||
@@ -164,18 +236,24 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
|
|||||||
|
|
||||||
localRepo, err = localCollection.ByName(source.Name)
|
localRepo, err = localCollection.ByName(source.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
AbortWithJSONError(c, 404, fmt.Errorf("unable to publish: %s", err))
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to publish: %s", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resources = append(resources, string(localRepo.Key()))
|
||||||
sources = append(sources, localRepo)
|
sources = append(sources, localRepo)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AbortWithJSONError(c, 400, fmt.Errorf("unknown SourceKind"))
|
AbortWithJSONError(c, http.StatusBadRequest, fmt.Errorf("unknown SourceKind"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
taskName := fmt.Sprintf("Publish %s: %s", b.SourceKind, strings.Join(names, ", "))
|
taskName := fmt.Sprintf("Publish %s repository %s/%s with components \"%s\" and sources \"%s\"",
|
||||||
|
b.SourceKind, published.StoragePrefix(), published.Distribution, strings.Join(components, `", "`), strings.Join(names, `", "`))
|
||||||
|
|
||||||
|
resources = append(resources, string(published.Key()))
|
||||||
|
collection := collectionFactory.PublishedRepoCollection()
|
||||||
|
|
||||||
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
|
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, detail *task.Detail) (*task.ProcessReturnValue, error) {
|
||||||
taskDetail := task.PublishDetail{
|
taskDetail := task.PublishDetail{
|
||||||
Detail: detail,
|
Detail: detail,
|
||||||
@@ -233,7 +311,10 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
|
|||||||
published.AcquireByHash = *b.AcquireByHash
|
published.AcquireByHash = *b.AcquireByHash
|
||||||
}
|
}
|
||||||
|
|
||||||
collection := collectionFactory.PublishedRepoCollection()
|
if b.MultiDist != nil {
|
||||||
|
published.MultiDist = *b.MultiDist
|
||||||
|
}
|
||||||
|
|
||||||
duplicate := collection.CheckDuplicate(published)
|
duplicate := collection.CheckDuplicate(published)
|
||||||
if duplicate != nil {
|
if duplicate != nil {
|
||||||
collectionFactory.PublishedRepoCollection().LoadComplete(duplicate, collectionFactory)
|
collectionFactory.PublishedRepoCollection().LoadComplete(duplicate, collectionFactory)
|
||||||
@@ -254,7 +335,18 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUT /publish/:prefix/:distribution
|
// @Summary Update published repository
|
||||||
|
// @Description Update a published repository.
|
||||||
|
// @Tags Publish
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param prefix path string true "publishing prefix"
|
||||||
|
// @Param distribution path string true "distribution name"
|
||||||
|
// @Success 200 {object} deb.RemoteRepo
|
||||||
|
// @Failure 400 {object} Error "Bad Request"
|
||||||
|
// @Failure 404 {object} Error "Published repository or source not found"
|
||||||
|
// @Failure 500 {object} Error "Internal Error"
|
||||||
|
// @Router /api/publish/{prefix}/{distribution} [put]
|
||||||
func apiPublishUpdateSwitch(c *gin.Context) {
|
func apiPublishUpdateSwitch(c *gin.Context) {
|
||||||
param := slashEscape(c.Params.ByName("prefix"))
|
param := slashEscape(c.Params.ByName("prefix"))
|
||||||
storage, prefix := deb.ParsePrefix(param)
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
@@ -262,7 +354,7 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
|
|
||||||
var b struct {
|
var b struct {
|
||||||
ForceOverwrite bool
|
ForceOverwrite bool
|
||||||
Signing SigningOptions
|
Signing signingParams
|
||||||
SkipContents *bool
|
SkipContents *bool
|
||||||
SkipBz2 *bool
|
SkipBz2 *bool
|
||||||
SkipCleanup *bool
|
SkipCleanup *bool
|
||||||
@@ -280,7 +372,7 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
|
|
||||||
signer, err := getSigner(&b.Signing)
|
signer, err := getSigner(&b.Signing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
AbortWithJSONError(c, 500, fmt.Errorf("unable to initialize GPG signer: %s", err))
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to initialize GPG signer: %s", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,12 +382,13 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
|
|
||||||
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
AbortWithJSONError(c, 404, fmt.Errorf("unable to update: %s", err))
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to update: %s", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var updatedComponents []string
|
var updatedComponents []string
|
||||||
var updatedSnapshots []string
|
var updatedSnapshots []string
|
||||||
|
|
||||||
if published.SourceKind == deb.SourceLocalRepo {
|
if published.SourceKind == deb.SourceLocalRepo {
|
||||||
if len(b.Snapshots) > 0 {
|
if len(b.Snapshots) > 0 {
|
||||||
AbortWithJSONError(c, 400, fmt.Errorf("snapshots shouldn't be given when updating local repo"))
|
AbortWithJSONError(c, 400, fmt.Errorf("snapshots shouldn't be given when updating local repo"))
|
||||||
@@ -335,7 +428,7 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
|
|
||||||
var resources []string
|
var resources []string
|
||||||
resources = append(resources, string(published.Key()))
|
resources = append(resources, string(published.Key()))
|
||||||
taskName := fmt.Sprintf("Update published %s (%s): %s", published.SourceKind, strings.Join(updatedComponents, " "), strings.Join(updatedSnapshots, ", "))
|
taskName := fmt.Sprintf("Update published %s repository %s/%s", published.SourceKind, published.StoragePrefix(), published.Distribution)
|
||||||
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
||||||
err = collection.LoadComplete(published, collectionFactory)
|
err = collection.LoadComplete(published, collectionFactory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -362,6 +455,11 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result, err := published.Update(collectionFactory, out)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
err = published.Publish(context.PackagePool(), context, collectionFactory, signer, out, b.ForceOverwrite)
|
err = published.Publish(context.PackagePool(), context, collectionFactory, signer, out, b.ForceOverwrite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
||||||
@@ -372,26 +470,53 @@ func apiPublishUpdateSwitch(c *gin.Context) {
|
|||||||
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to save to DB: %s", err)
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to save to DB: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatedComponents := result.UpdatedComponents()
|
||||||
|
removedComponents := result.RemovedComponents()
|
||||||
|
|
||||||
if b.SkipCleanup == nil || !*b.SkipCleanup {
|
if b.SkipCleanup == nil || !*b.SkipCleanup {
|
||||||
err = collection.CleanupPrefixComponentFiles(published.Prefix, updatedComponents,
|
publishedStorage := context.GetPublishedStorage(storage)
|
||||||
context.GetPublishedStorage(storage), collectionFactory, out)
|
|
||||||
|
err = collection.CleanupPrefixComponentFiles(published.Prefix, updatedComponents, publishedStorage, collectionFactory, out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(removedComponents) > 0 {
|
||||||
|
// Cleanup files belonging to a removed component by dropping the component directory from the storage backend.
|
||||||
|
for _, component := range removedComponents {
|
||||||
|
err = publishedStorage.RemoveDirs(filepath.Join(prefix, "dists", distribution, component), out)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &task.ProcessReturnValue{Code: http.StatusOK, Value: published}, nil
|
return &task.ProcessReturnValue{Code: http.StatusOK, Value: published}, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// DELETE /publish/:prefix/:distribution
|
// @Summary Delete published repository
|
||||||
|
// @Description Delete a published repository.
|
||||||
|
// @Tags Publish
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param prefix path string true "publishing prefix"
|
||||||
|
// @Param distribution path string true "distribution name"
|
||||||
|
// @Param force query int true "force: 1 to enable"
|
||||||
|
// @Param skipCleanup query int true "skipCleanup: 1 to enable"
|
||||||
|
// @Success 200 {object} task.ProcessReturnValue
|
||||||
|
// @Failure 400 {object} Error "Bad Request"
|
||||||
|
// @Failure 404 {object} Error "Published repository not found"
|
||||||
|
// @Failure 500 {object} Error "Internal Error"
|
||||||
|
// @Router /api/publish/{prefix}/{distribution} [delete]
|
||||||
func apiPublishDrop(c *gin.Context) {
|
func apiPublishDrop(c *gin.Context) {
|
||||||
force := c.Request.URL.Query().Get("force") == "1"
|
force := c.Request.URL.Query().Get("force") == "1"
|
||||||
skipCleanup := c.Request.URL.Query().Get("SkipCleanup") == "1"
|
skipCleanup := c.Request.URL.Query().Get("SkipCleanup") == "1"
|
||||||
|
|
||||||
param := slashEscape(c.Params.ByName("prefix"))
|
param := slashEscape(c.Params.ByName("prefix"))
|
||||||
storage, prefix := deb.ParsePrefix(param)
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
distribution := c.Params.ByName("distribution")
|
distribution := parseEscapedPath(c.Params.ByName("distribution"))
|
||||||
|
|
||||||
collectionFactory := context.NewCollectionFactory()
|
collectionFactory := context.NewCollectionFactory()
|
||||||
collection := collectionFactory.PublishedRepoCollection()
|
collection := collectionFactory.PublishedRepoCollection()
|
||||||
@@ -404,7 +529,7 @@ func apiPublishDrop(c *gin.Context) {
|
|||||||
|
|
||||||
resources := []string{string(published.Key())}
|
resources := []string{string(published.Key())}
|
||||||
|
|
||||||
taskName := fmt.Sprintf("Delete published %s (%s)", prefix, distribution)
|
taskName := fmt.Sprintf("Delete published %s repository %s/%s", published.SourceKind, published.StoragePrefix(), published.Distribution)
|
||||||
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
||||||
err := collection.Remove(context, storage, prefix, distribution,
|
err := collection.Remove(context, storage, prefix, distribution,
|
||||||
collectionFactory, out, force, skipCleanup)
|
collectionFactory, out, force, skipCleanup)
|
||||||
@@ -415,3 +540,304 @@ func apiPublishDrop(c *gin.Context) {
|
|||||||
return &task.ProcessReturnValue{Code: http.StatusOK, Value: gin.H{}}, nil
|
return &task.ProcessReturnValue{Code: http.StatusOK, Value: gin.H{}}, nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Router /api/publish/{prefix}/{distribution}/sources/{component} [put]
|
||||||
|
func apiPublishSourceUpdate(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
b sourceParams
|
||||||
|
)
|
||||||
|
|
||||||
|
param := parseEscapedPath(c.Params.ByName("prefix"))
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
distribution := parseEscapedPath(c.Params.ByName("distribution"))
|
||||||
|
component := parseEscapedPath(c.Params.ByName("component"))
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
collection := collectionFactory.PublishedRepoCollection()
|
||||||
|
|
||||||
|
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
revision := published.ObtainRevision()
|
||||||
|
sources := revision.Sources
|
||||||
|
|
||||||
|
b.Component = component
|
||||||
|
b.Name = revision.Sources[component]
|
||||||
|
|
||||||
|
if c.Bind(&b) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Component != component {
|
||||||
|
delete(sources, component)
|
||||||
|
}
|
||||||
|
sources[b.Component] = b.Name
|
||||||
|
|
||||||
|
resources := []string{string(published.Key())}
|
||||||
|
|
||||||
|
taskName := fmt.Sprintf("Update published %s repository %s/%s", published.SourceKind, published.StoragePrefix(), published.Distribution)
|
||||||
|
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
||||||
|
err = collection.Update(published)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to save to DB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusOK, Value: published}, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Router /api/publish/{prefix}/{distribution}/sources [put]
|
||||||
|
func apiPublishSourcesUpdate(c *gin.Context) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
b []sourceParams
|
||||||
|
)
|
||||||
|
|
||||||
|
param := parseEscapedPath(c.Params.ByName("prefix"))
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
distribution := parseEscapedPath(c.Params.ByName("distribution"))
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
collection := collectionFactory.PublishedRepoCollection()
|
||||||
|
|
||||||
|
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Bind(&b) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
revision := published.ObtainRevision()
|
||||||
|
sources := make(map[string]string, len(b))
|
||||||
|
revision.Sources = sources
|
||||||
|
|
||||||
|
for _, source := range b {
|
||||||
|
component := source.Component
|
||||||
|
name := source.Name
|
||||||
|
sources[component] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
resources := []string{string(published.Key())}
|
||||||
|
|
||||||
|
taskName := fmt.Sprintf("Update published %s repository %s/%s", published.SourceKind, published.StoragePrefix(), published.Distribution)
|
||||||
|
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
||||||
|
err = collection.Update(published)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to save to DB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusOK, Value: published}, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Router /api/publish/{prefix}/{distribution}/sources [get]
|
||||||
|
func apiPublishSourceList(c *gin.Context) {
|
||||||
|
param := parseEscapedPath(c.Params.ByName("prefix"))
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
distribution := parseEscapedPath(c.Params.ByName("distribution"))
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
collection := collectionFactory.PublishedRepoCollection()
|
||||||
|
|
||||||
|
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
revision := published.Revision
|
||||||
|
if revision == nil {
|
||||||
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to show: No source changes exist"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, revision.ToJSON()["Sources"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Router /api/publish/{prefix}/{distribution}/sources/{component} [delete]
|
||||||
|
func apiPublishSourceDelete(c *gin.Context) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
param := parseEscapedPath(c.Params.ByName("prefix"))
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
distribution := parseEscapedPath(c.Params.ByName("distribution"))
|
||||||
|
component := parseEscapedPath(c.Params.ByName("component"))
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
collection := collectionFactory.PublishedRepoCollection()
|
||||||
|
|
||||||
|
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
revision := published.ObtainRevision()
|
||||||
|
sources := revision.Sources
|
||||||
|
|
||||||
|
delete(sources, component)
|
||||||
|
|
||||||
|
resources := []string{string(published.Key())}
|
||||||
|
|
||||||
|
taskName := fmt.Sprintf("Update published %s repository %s/%s", published.SourceKind, published.StoragePrefix(), published.Distribution)
|
||||||
|
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
||||||
|
err = collection.Update(published)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to save to DB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusOK, Value: published}, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Router /api/publish/{prefix}/{distribution}/sources [delete]
|
||||||
|
func apiPublishSourcesDelete(c *gin.Context) {
|
||||||
|
param := parseEscapedPath(c.Params.ByName("prefix"))
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
distribution := parseEscapedPath(c.Params.ByName("distribution"))
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
collection := collectionFactory.PublishedRepoCollection()
|
||||||
|
|
||||||
|
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to show: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
published.DropRevision()
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Router /api/publish/{prefix}/{distribution}/update [post]
|
||||||
|
func apiPublishUpdate(c *gin.Context) {
|
||||||
|
param := parseEscapedPath(c.Params.ByName("prefix"))
|
||||||
|
storage, prefix := deb.ParsePrefix(param)
|
||||||
|
distribution := parseEscapedPath(c.Params.ByName("distribution"))
|
||||||
|
|
||||||
|
var b struct {
|
||||||
|
AcquireByHash *bool
|
||||||
|
ForceOverwrite bool
|
||||||
|
Signing signingParams
|
||||||
|
SkipBz2 *bool
|
||||||
|
SkipCleanup *bool
|
||||||
|
SkipContents *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Bind(&b) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err := getSigner(&b.Signing)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to initialize GPG signer: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
collection := collectionFactory.PublishedRepoCollection()
|
||||||
|
|
||||||
|
published, err := collection.ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusNotFound, fmt.Errorf("unable to update: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
AbortWithJSONError(c, http.StatusInternalServerError, fmt.Errorf("unable to update: %s", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.SkipContents != nil {
|
||||||
|
published.SkipContents = *b.SkipContents
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.SkipBz2 != nil {
|
||||||
|
published.SkipBz2 = *b.SkipBz2
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.AcquireByHash != nil {
|
||||||
|
published.AcquireByHash = *b.AcquireByHash
|
||||||
|
}
|
||||||
|
|
||||||
|
resources := []string{string(published.Key())}
|
||||||
|
|
||||||
|
taskName := fmt.Sprintf("Update published %s repository %s/%s", published.SourceKind, published.StoragePrefix(), published.Distribution)
|
||||||
|
maybeRunTaskInBackground(c, taskName, resources, func(out aptly.Progress, _ *task.Detail) (*task.ProcessReturnValue, error) {
|
||||||
|
result, err := published.Update(collectionFactory, out)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedComponents := result.UpdatedComponents()
|
||||||
|
removedComponents := result.RemovedComponents()
|
||||||
|
|
||||||
|
err = published.Publish(context.PackagePool(), context, collectionFactory, signer, out, b.ForceOverwrite)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collection.Update(published)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to save to DB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.SkipCleanup == nil || !*b.SkipCleanup {
|
||||||
|
publishedStorage := context.GetPublishedStorage(storage)
|
||||||
|
|
||||||
|
err = collection.CleanupPrefixComponentFiles(published.Prefix, updatedComponents, publishedStorage, collectionFactory, out)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(removedComponents) > 0 {
|
||||||
|
// Cleanup files belonging to a removed component by dropping the component directory from the storage backend.
|
||||||
|
for _, component := range removedComponents {
|
||||||
|
err = publishedStorage.RemoveDirs(filepath.Join(prefix, "dists", distribution, component), out)
|
||||||
|
if err != nil {
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusInternalServerError, Value: nil}, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &task.ProcessReturnValue{Code: http.StatusOK, Value: published}, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -185,12 +185,18 @@ func Router(c *ctx.AptlyContext) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
api.GET("/publish", apiPublishList)
|
api.GET("/publish", apiPublishList)
|
||||||
|
api.GET("/publish/:prefix/:distribution", apiPublishShow)
|
||||||
api.POST("/publish", apiPublishRepoOrSnapshot)
|
api.POST("/publish", apiPublishRepoOrSnapshot)
|
||||||
api.POST("/publish/:prefix", apiPublishRepoOrSnapshot)
|
api.POST("/publish/:prefix", apiPublishRepoOrSnapshot)
|
||||||
api.PUT("/publish/:prefix/:distribution", apiPublishUpdateSwitch)
|
api.PUT("/publish/:prefix/:distribution", apiPublishUpdateSwitch)
|
||||||
api.DELETE("/publish/:prefix/:distribution", apiPublishDrop)
|
api.DELETE("/publish/:prefix/:distribution", apiPublishDrop)
|
||||||
|
api.GET("/publish/:prefix/:distribution/sources", apiPublishSourceList)
|
||||||
|
api.PUT("/publish/:prefix/:distribution/sources", apiPublishSourcesUpdate)
|
||||||
|
api.PUT("/publish/:prefix/:distribution/sources/:component", apiPublishSourceUpdate)
|
||||||
|
api.DELETE("/publish/:prefix/:distribution/sources/:component", apiPublishSourceDelete)
|
||||||
|
api.DELETE("/publish/:prefix/:distribution/sources", apiPublishSourcesDelete)
|
||||||
|
api.POST("/publish/:prefix/:distribution/update", apiPublishUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,10 +34,25 @@ func makeCmdPublish() *commander.Command {
|
|||||||
makeCmdPublishDrop(),
|
makeCmdPublishDrop(),
|
||||||
makeCmdPublishList(),
|
makeCmdPublishList(),
|
||||||
makeCmdPublishRepo(),
|
makeCmdPublishRepo(),
|
||||||
|
makeCmdPublishShow(),
|
||||||
makeCmdPublishSnapshot(),
|
makeCmdPublishSnapshot(),
|
||||||
|
makeCmdPublishSource(),
|
||||||
makeCmdPublishSwitch(),
|
makeCmdPublishSwitch(),
|
||||||
makeCmdPublishUpdate(),
|
makeCmdPublishUpdate(),
|
||||||
makeCmdPublishShow(),
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdPublishSource() *commander.Command {
|
||||||
|
return &commander.Command{
|
||||||
|
UsageLine: "source",
|
||||||
|
Short: "manage sources of published repository",
|
||||||
|
Subcommands: []*commander.Command{
|
||||||
|
makeCmdPublishSourceAdd(),
|
||||||
|
makeCmdPublishSourceDrop(),
|
||||||
|
makeCmdPublishSourceList(),
|
||||||
|
makeCmdPublishSourceRemove(),
|
||||||
|
makeCmdPublishSourceUpdate(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ func aptlyPublishShowTxt(_ *commander.Command, args []string) error {
|
|||||||
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, " "))
|
fmt.Printf("Architectures: %s\n", strings.Join(repo.Architectures, " "))
|
||||||
|
|
||||||
fmt.Printf("Sources:\n")
|
fmt.Printf("Sources:\n")
|
||||||
for component, sourceID := range repo.Sources {
|
for _, component := range repo.Components() {
|
||||||
|
sourceID := repo.Sources[component]
|
||||||
var name string
|
var name string
|
||||||
if repo.SourceKind == deb.SourceSnapshot {
|
if repo.SourceKind == deb.SourceSnapshot {
|
||||||
source, e := collectionFactory.SnapshotCollection().ByUUID(sourceID)
|
source, e := collectionFactory.SnapshotCollection().ByUUID(sourceID)
|
||||||
|
|||||||
@@ -150,6 +150,10 @@ func aptlyPublishSnapshotOrRepo(cmd *commander.Command, args []string) error {
|
|||||||
published.AcquireByHash = context.Flags().Lookup("acquire-by-hash").Value.Get().(bool)
|
published.AcquireByHash = context.Flags().Lookup("acquire-by-hash").Value.Get().(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.Flags().IsSet("multi-dist") {
|
||||||
|
published.MultiDist = context.Flags().Lookup("multi-dist").Value.Get().(bool)
|
||||||
|
}
|
||||||
|
|
||||||
duplicate := collectionFactory.PublishedRepoCollection().CheckDuplicate(published)
|
duplicate := collectionFactory.PublishedRepoCollection().CheckDuplicate(published)
|
||||||
if duplicate != nil {
|
if duplicate != nil {
|
||||||
collectionFactory.PublishedRepoCollection().LoadComplete(duplicate, collectionFactory)
|
collectionFactory.PublishedRepoCollection().LoadComplete(duplicate, collectionFactory)
|
||||||
|
|||||||
89
cmd/publish_source_add.go
Normal file
89
cmd/publish_source_add.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aptly-dev/aptly/deb"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func aptlyPublishSourceAdd(cmd *commander.Command, args []string) error {
|
||||||
|
if len(args) < 2 {
|
||||||
|
cmd.Usage()
|
||||||
|
return commander.ErrCommandError
|
||||||
|
}
|
||||||
|
|
||||||
|
distribution := args[0]
|
||||||
|
names := args[1:]
|
||||||
|
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
|
||||||
|
|
||||||
|
if len(names) != len(components) {
|
||||||
|
return fmt.Errorf("mismatch in number of components (%d) and sources (%d)", len(components), len(names))
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := context.Flags().Lookup("prefix").Value.String()
|
||||||
|
storage, prefix := deb.ParsePrefix(prefix)
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
published, err := collectionFactory.PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to add: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to add: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
revision := published.ObtainRevision()
|
||||||
|
sources := revision.Sources
|
||||||
|
|
||||||
|
for i, component := range components {
|
||||||
|
name := names[i]
|
||||||
|
_, exists := sources[component]
|
||||||
|
if exists {
|
||||||
|
return fmt.Errorf("unable to add: Component %q has already been added", component)
|
||||||
|
}
|
||||||
|
context.Progress().Printf("Adding component %q with source %q [%s]...\n", component, name, published.SourceKind)
|
||||||
|
|
||||||
|
sources[component] = names[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collectionFactory.PublishedRepoCollection().Update(published)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to save to DB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdPublishSourceAdd() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyPublishSourceAdd,
|
||||||
|
UsageLine: "add <distribution> <source>",
|
||||||
|
Short: "add package source to published repository",
|
||||||
|
Long: `
|
||||||
|
The command adds (in place) one or multiple package sources to a published repository.
|
||||||
|
|
||||||
|
The flag -component is mandatory. Use a comma-separated list of components, if
|
||||||
|
multiple components should be modified. The number of given components must be
|
||||||
|
equal to the number of given sources, e.g.:
|
||||||
|
|
||||||
|
aptly publish add -component=main,contrib wheezy wheezy-main wheezy-contrib
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly publish add -component=contrib wheezy ppa wheezy-contrib
|
||||||
|
|
||||||
|
This command assigns the snapshot wheezy-contrib to the component contrib and
|
||||||
|
adds it to published repository revision of ppa/wheezy.
|
||||||
|
`,
|
||||||
|
Flag: *flag.NewFlagSet("aptly-publish-add", flag.ExitOnError),
|
||||||
|
}
|
||||||
|
cmd.Flag.String("prefix", ".", "publishing prefix in the form of [<endpoint>:]<prefix>")
|
||||||
|
cmd.Flag.String("component", "", "component names to add (for multi-component publishing, separate components with commas)")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
62
cmd/publish_source_drop.go
Normal file
62
cmd/publish_source_drop.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/aptly-dev/aptly/deb"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func aptlyPublishSourceDrop(cmd *commander.Command, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
return commander.ErrCommandError
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := context.Flags().Lookup("prefix").Value.String()
|
||||||
|
distribution := args[0]
|
||||||
|
storage, prefix := deb.ParsePrefix(prefix)
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
published, err := collectionFactory.PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to drop: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to drop: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
published.DropRevision()
|
||||||
|
|
||||||
|
err = collectionFactory.PublishedRepoCollection().Update(published)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to save to DB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Progress().Printf("Source changes have been removed successfully.\n")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdPublishSourceDrop() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyPublishSourceDrop,
|
||||||
|
UsageLine: "drop <distribution>",
|
||||||
|
Short: "drops revision of published repository",
|
||||||
|
Long: `
|
||||||
|
Command drops revision of a published repository.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly publish revision drop wheezy
|
||||||
|
`,
|
||||||
|
Flag: *flag.NewFlagSet("aptly-publish-revision-create", flag.ExitOnError),
|
||||||
|
}
|
||||||
|
cmd.Flag.String("prefix", ".", "publishing prefix in the form of [<endpoint>:]<prefix>")
|
||||||
|
cmd.Flag.String("component", "", "component names to add (for multi-component publishing, separate components with commas)")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
89
cmd/publish_source_list.go
Normal file
89
cmd/publish_source_list.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/aptly-dev/aptly/deb"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func aptlyPublishSourceList(cmd *commander.Command, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
return commander.ErrCommandError
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := context.Flags().Lookup("prefix").Value.String()
|
||||||
|
distribution := args[0]
|
||||||
|
storage, prefix := deb.ParsePrefix(prefix)
|
||||||
|
|
||||||
|
published, err := context.NewCollectionFactory().PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to list: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = context.NewCollectionFactory().PublishedRepoCollection().LoadComplete(published, context.NewCollectionFactory())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if published.Revision == nil {
|
||||||
|
return fmt.Errorf("unable to list: No source changes exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonFlag := cmd.Flag.Lookup("json").Value.Get().(bool)
|
||||||
|
|
||||||
|
if jsonFlag {
|
||||||
|
return aptlyPublishSourceListJSON(published)
|
||||||
|
}
|
||||||
|
|
||||||
|
return aptlyPublishSourceListTxt(published)
|
||||||
|
}
|
||||||
|
|
||||||
|
func aptlyPublishSourceListTxt(published *deb.PublishedRepo) error {
|
||||||
|
revision := published.Revision
|
||||||
|
|
||||||
|
fmt.Printf("Sources:\n")
|
||||||
|
for _, component := range revision.Components() {
|
||||||
|
name := revision.Sources[component]
|
||||||
|
fmt.Printf(" %s: %s [%s]\n", component, name, published.SourceKind)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func aptlyPublishSourceListJSON(published *deb.PublishedRepo) error {
|
||||||
|
revision := published.Revision
|
||||||
|
|
||||||
|
output, err := json.MarshalIndent(revision.ToJSON()["Sources"], "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to list: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(output))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdPublishSourceList() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyPublishSourceList,
|
||||||
|
UsageLine: "list <distribution>",
|
||||||
|
Short: "lists revision of published repository",
|
||||||
|
Long: `
|
||||||
|
Command lists sources of a published repository.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly publish source list wheezy
|
||||||
|
`,
|
||||||
|
Flag: *flag.NewFlagSet("aptly-publish-source-list", flag.ExitOnError),
|
||||||
|
}
|
||||||
|
cmd.Flag.Bool("json", false, "display record in JSON format")
|
||||||
|
cmd.Flag.String("prefix", ".", "publishing prefix in the form of [<endpoint>:]<prefix>")
|
||||||
|
cmd.Flag.String("component", "", "component names to add (for multi-component publishing, separate components with commas)")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
81
cmd/publish_source_remove.go
Normal file
81
cmd/publish_source_remove.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aptly-dev/aptly/deb"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func aptlyPublishSourceRemove(cmd *commander.Command, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
return commander.ErrCommandError
|
||||||
|
}
|
||||||
|
|
||||||
|
distribution := args[0]
|
||||||
|
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
|
||||||
|
|
||||||
|
if len(components) == 0 {
|
||||||
|
return fmt.Errorf("unable to remove: Missing components, specify at least one component")
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := context.Flags().Lookup("prefix").Value.String()
|
||||||
|
storage, prefix := deb.ParsePrefix(prefix)
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
published, err := collectionFactory.PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to remove: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to remove: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
revision := published.ObtainRevision()
|
||||||
|
sources := revision.Sources
|
||||||
|
|
||||||
|
for _, component := range components {
|
||||||
|
name, exists := sources[component]
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("unable to remove: Component %q is not part of revision", component)
|
||||||
|
}
|
||||||
|
context.Progress().Printf("Removing component %q with source %q [%s]...\n", component, name, published.SourceKind)
|
||||||
|
|
||||||
|
delete(sources, component)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collectionFactory.PublishedRepoCollection().Update(published)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to save to DB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdPublishSourceRemove() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyPublishSourceRemove,
|
||||||
|
UsageLine: "remove <distribution> [[<endpoint>:]<prefix>] <source>",
|
||||||
|
Short: "remove package source to published repository",
|
||||||
|
Long: `
|
||||||
|
The command removes one or multiple components from a published repository.
|
||||||
|
|
||||||
|
The flag -component is mandatory. Use a comma-separated list of components, if
|
||||||
|
multiple components should be removed, e.g.:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly publish remove -component=contrib,non-free wheezy filesystem:symlink:debian
|
||||||
|
`,
|
||||||
|
Flag: *flag.NewFlagSet("aptly-publish-remove", flag.ExitOnError),
|
||||||
|
}
|
||||||
|
cmd.Flag.String("prefix", ".", "publishing prefix in the form of [<endpoint>:]<prefix>")
|
||||||
|
cmd.Flag.String("component", "", "component names to remove (for multi-component publishing, separate components with commas)")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
86
cmd/publish_source_update.go
Normal file
86
cmd/publish_source_update.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aptly-dev/aptly/deb"
|
||||||
|
"github.com/smira/commander"
|
||||||
|
"github.com/smira/flag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func aptlyPublishSourceUpdate(cmd *commander.Command, args []string) error {
|
||||||
|
if len(args) < 2 {
|
||||||
|
cmd.Usage()
|
||||||
|
return commander.ErrCommandError
|
||||||
|
}
|
||||||
|
|
||||||
|
distribution := args[0]
|
||||||
|
names := args[1:]
|
||||||
|
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
|
||||||
|
|
||||||
|
if len(names) != len(components) {
|
||||||
|
return fmt.Errorf("mismatch in number of components (%d) and sources (%d)", len(components), len(names))
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := context.Flags().Lookup("prefix").Value.String()
|
||||||
|
storage, prefix := deb.ParsePrefix(prefix)
|
||||||
|
|
||||||
|
collectionFactory := context.NewCollectionFactory()
|
||||||
|
published, err := collectionFactory.PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
revision := published.ObtainRevision()
|
||||||
|
sources := revision.Sources
|
||||||
|
|
||||||
|
for i, component := range components {
|
||||||
|
name := names[i]
|
||||||
|
_, exists := sources[component]
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("unable to update: Component %q does not exist", component)
|
||||||
|
}
|
||||||
|
context.Progress().Printf("Updating component %q with source %q [%s]...\n", component, name, published.SourceKind)
|
||||||
|
|
||||||
|
sources[component] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
err = collectionFactory.PublishedRepoCollection().Update(published)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to save to DB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCmdPublishSourceUpdate() *commander.Command {
|
||||||
|
cmd := &commander.Command{
|
||||||
|
Run: aptlyPublishSourceUpdate,
|
||||||
|
UsageLine: "update <distribution> <source>",
|
||||||
|
Short: "update package source to published repository",
|
||||||
|
Long: `
|
||||||
|
The command updates one or multiple components in a published repository.
|
||||||
|
|
||||||
|
The flag -component is mandatory. Use a comma-separated list of components, if
|
||||||
|
multiple components should be modified. The number of given components must be
|
||||||
|
equal to the number of given sources, e.g.:
|
||||||
|
|
||||||
|
aptly publish update -component=main,contrib wheezy wheezy-main wheezy-contrib
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ aptly publish update -component=contrib wheezy ppa wheezy-contrib
|
||||||
|
`,
|
||||||
|
Flag: *flag.NewFlagSet("aptly-publish-revision-source-update", flag.ExitOnError),
|
||||||
|
}
|
||||||
|
cmd.Flag.String("prefix", ".", "publishing prefix in the form of [<endpoint>:]<prefix>")
|
||||||
|
cmd.Flag.String("component", "", "component names to add (for multi-component publishing, separate components with commas)")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -5,12 +5,16 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aptly-dev/aptly/deb"
|
"github.com/aptly-dev/aptly/deb"
|
||||||
|
"github.com/aptly-dev/aptly/utils"
|
||||||
"github.com/smira/commander"
|
"github.com/smira/commander"
|
||||||
"github.com/smira/flag"
|
"github.com/smira/flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
||||||
var err error
|
var (
|
||||||
|
err error
|
||||||
|
names []string
|
||||||
|
)
|
||||||
|
|
||||||
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
|
components := strings.Split(context.Flags().Lookup("component").Value.String(), ",")
|
||||||
|
|
||||||
@@ -22,11 +26,6 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
distribution := args[0]
|
distribution := args[0]
|
||||||
param := "."
|
param := "."
|
||||||
|
|
||||||
var (
|
|
||||||
names []string
|
|
||||||
snapshot *deb.Snapshot
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(args) == len(components)+2 {
|
if len(args) == len(components)+2 {
|
||||||
param = args[1]
|
param = args[1]
|
||||||
names = args[2:]
|
names = args[2:]
|
||||||
@@ -41,16 +40,12 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
collectionFactory := context.NewCollectionFactory()
|
collectionFactory := context.NewCollectionFactory()
|
||||||
published, err = collectionFactory.PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
published, err = collectionFactory.PublishedRepoCollection().ByStoragePrefixDistribution(storage, prefix, distribution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to switch: %s", err)
|
||||||
}
|
|
||||||
|
|
||||||
if published.SourceKind != deb.SourceSnapshot {
|
|
||||||
return fmt.Errorf("unable to update: not a snapshot publish")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
|
err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to switch: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
publishedComponents := published.Components()
|
publishedComponents := published.Components()
|
||||||
@@ -62,18 +57,46 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("mismatch in number of components (%d) and snapshots (%d)", len(components), len(names))
|
return fmt.Errorf("mismatch in number of components (%d) and snapshots (%d)", len(components), len(names))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, component := range components {
|
if published.SourceKind == deb.SourceLocalRepo {
|
||||||
snapshot, err = collectionFactory.SnapshotCollection().ByName(names[i])
|
localRepoCollection := collectionFactory.LocalRepoCollection()
|
||||||
if err != nil {
|
for i, component := range components {
|
||||||
return fmt.Errorf("unable to switch: %s", err)
|
if !utils.StrSliceHasItem(publishedComponents, component) {
|
||||||
}
|
return fmt.Errorf("unable to switch: component %s does not exist in published repository", component)
|
||||||
|
}
|
||||||
|
|
||||||
err = collectionFactory.SnapshotCollection().LoadComplete(snapshot)
|
localRepo, err := localRepoCollection.ByName(names[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to switch: %s", err)
|
return fmt.Errorf("unable to switch: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
published.UpdateSnapshot(component, snapshot)
|
err = localRepoCollection.LoadComplete(localRepo)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to switch: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
published.UpdateLocalRepo(component, localRepo)
|
||||||
|
}
|
||||||
|
} else if published.SourceKind == deb.SourceSnapshot {
|
||||||
|
snapshotCollection := collectionFactory.SnapshotCollection()
|
||||||
|
for i, component := range components {
|
||||||
|
if !utils.StrSliceHasItem(publishedComponents, component) {
|
||||||
|
return fmt.Errorf("unable to switch: component %s does not exist in published repository", component)
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot, err := snapshotCollection.ByName(names[i])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to switch: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snapshotCollection.LoadComplete(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to switch: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
published.UpdateSnapshot(component, snapshot)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("unknown published repository type")
|
||||||
}
|
}
|
||||||
|
|
||||||
signer, err := getSigner(context.Flags())
|
signer, err := getSigner(context.Flags())
|
||||||
@@ -114,11 +137,11 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
err = collectionFactory.PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
|
err = collectionFactory.PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
|
||||||
context.GetPublishedStorage(storage), collectionFactory, context.Progress())
|
context.GetPublishedStorage(storage), collectionFactory, context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to switch: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Progress().Printf("\nPublish for snapshot %s has been successfully switched to new snapshot.\n", published.String())
|
context.Progress().Printf("\nPublished %s repository %s has been successfully switched to new source.\n", published.SourceKind, published.String())
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -126,15 +149,15 @@ func aptlyPublishSwitch(cmd *commander.Command, args []string) error {
|
|||||||
func makeCmdPublishSwitch() *commander.Command {
|
func makeCmdPublishSwitch() *commander.Command {
|
||||||
cmd := &commander.Command{
|
cmd := &commander.Command{
|
||||||
Run: aptlyPublishSwitch,
|
Run: aptlyPublishSwitch,
|
||||||
UsageLine: "switch <distribution> [[<endpoint>:]<prefix>] <new-snapshot>",
|
UsageLine: "switch <distribution> [[<endpoint>:]<prefix>] <new-source>",
|
||||||
Short: "update published repository by switching to new snapshot",
|
Short: "update published repository by switching to new source",
|
||||||
Long: `
|
Long: `
|
||||||
Command switches in-place published snapshots with new snapshot contents. All
|
Command switches in-place published snapshots with new source contents. All
|
||||||
publishing parameters are preserved (architecture list, distribution,
|
publishing parameters are preserved (architecture list, distribution,
|
||||||
component).
|
component).
|
||||||
|
|
||||||
For multiple component repositories, flag -component should be given with
|
For multiple component repositories, flag -component should be given with
|
||||||
list of components to update. Corresponding snapshots should be given in the
|
list of components to update. Corresponding sources should be given in the
|
||||||
same order, e.g.:
|
same order, e.g.:
|
||||||
|
|
||||||
aptly publish switch -component=main,contrib wheezy wh-main wh-contrib
|
aptly publish switch -component=main,contrib wheezy wh-main wh-contrib
|
||||||
|
|||||||
@@ -31,18 +31,14 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
|
|||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if published.SourceKind != deb.SourceLocalRepo {
|
|
||||||
return fmt.Errorf("unable to update: not a local repository publish")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
|
err = collectionFactory.PublishedRepoCollection().LoadComplete(published, collectionFactory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
components := published.Components()
|
result, err := published.Update(collectionFactory, context.Progress())
|
||||||
for _, component := range components {
|
if err != nil {
|
||||||
published.UpdateLocalRepo(component)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
signer, err := getSigner(context.Flags())
|
signer, err := getSigner(context.Flags())
|
||||||
@@ -80,14 +76,14 @@ func aptlyPublishUpdate(cmd *commander.Command, args []string) error {
|
|||||||
|
|
||||||
skipCleanup := context.Flags().Lookup("skip-cleanup").Value.Get().(bool)
|
skipCleanup := context.Flags().Lookup("skip-cleanup").Value.Get().(bool)
|
||||||
if !skipCleanup {
|
if !skipCleanup {
|
||||||
err = collectionFactory.PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, components,
|
err = collectionFactory.PublishedRepoCollection().CleanupPrefixComponentFiles(published.Prefix, result.UpdatedComponents(),
|
||||||
context.GetPublishedStorage(storage), collectionFactory, context.Progress())
|
context.GetPublishedStorage(storage), collectionFactory, context.Progress())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to update: %s", err)
|
return fmt.Errorf("unable to update: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Progress().Printf("\nPublish for local repo %s has been successfully updated.\n", published.String())
|
context.Progress().Printf("\nPublished %s repository %s has been successfully updated.\n", published.SourceKind, published.String())
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
229
deb/publish.go
229
deb/publish.go
@@ -21,6 +21,12 @@ import (
|
|||||||
"github.com/aptly-dev/aptly/utils"
|
"github.com/aptly-dev/aptly/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PublishedRepoUpdateResult struct {
|
||||||
|
AddedSources map[string]string
|
||||||
|
UpdatedSources map[string]string
|
||||||
|
RemovedSources map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
type repoSourceItem struct {
|
type repoSourceItem struct {
|
||||||
// Pointer to snapshot if SourceKind == "snapshot"
|
// Pointer to snapshot if SourceKind == "snapshot"
|
||||||
snapshot *Snapshot
|
snapshot *Snapshot
|
||||||
@@ -73,6 +79,168 @@ type PublishedRepo struct {
|
|||||||
|
|
||||||
// Support multiple distributions
|
// Support multiple distributions
|
||||||
MultiDist bool
|
MultiDist bool
|
||||||
|
|
||||||
|
// Revision
|
||||||
|
Revision *PublishedRepoRevision
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublishedRepoRevision struct {
|
||||||
|
// Map of sources: component name -> snapshot name/local repo Name
|
||||||
|
Sources map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (result *PublishedRepoUpdateResult) AddedComponents() []string {
|
||||||
|
components := make([]string, 0, len(result.AddedSources))
|
||||||
|
for component := range result.AddedSources {
|
||||||
|
components = append(components, component)
|
||||||
|
}
|
||||||
|
sort.Strings(components)
|
||||||
|
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
|
||||||
|
func (result *PublishedRepoUpdateResult) UpdatedComponents() []string {
|
||||||
|
components := make([]string, 0, len(result.UpdatedSources))
|
||||||
|
for component := range result.UpdatedSources {
|
||||||
|
components = append(components, component)
|
||||||
|
}
|
||||||
|
sort.Strings(components)
|
||||||
|
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
|
||||||
|
func (result *PublishedRepoUpdateResult) RemovedComponents() []string {
|
||||||
|
components := make([]string, 0, len(result.RemovedSources))
|
||||||
|
for component := range result.RemovedSources {
|
||||||
|
components = append(components, component)
|
||||||
|
}
|
||||||
|
sort.Strings(components)
|
||||||
|
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
|
||||||
|
func (revision *PublishedRepoRevision) Components() []string {
|
||||||
|
components := make([]string, 0, len(revision.Sources))
|
||||||
|
for component := range revision.Sources {
|
||||||
|
components = append(components, component)
|
||||||
|
}
|
||||||
|
sort.Strings(components)
|
||||||
|
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
|
||||||
|
func (revision *PublishedRepoRevision) SourceNames() []string {
|
||||||
|
sources := revision.Sources
|
||||||
|
names := make([]string, 0, len(sources))
|
||||||
|
for _, component := range revision.Components() {
|
||||||
|
names = append(names, sources[component])
|
||||||
|
}
|
||||||
|
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PublishedRepo) DropRevision() *PublishedRepoRevision {
|
||||||
|
revision := p.Revision
|
||||||
|
p.Revision = nil
|
||||||
|
|
||||||
|
return revision
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PublishedRepo) ObtainRevision() *PublishedRepoRevision {
|
||||||
|
revision := p.Revision
|
||||||
|
if revision == nil {
|
||||||
|
sources := make(map[string]string, len(p.Sources))
|
||||||
|
for _, component := range p.Components() {
|
||||||
|
item := p.sourceItems[component]
|
||||||
|
if item.snapshot != nil {
|
||||||
|
sources[component] = item.snapshot.Name
|
||||||
|
} else if item.localRepo != nil {
|
||||||
|
sources[component] = item.localRepo.Name
|
||||||
|
} else {
|
||||||
|
panic("no snapshot/localRepo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
revision = &PublishedRepoRevision{
|
||||||
|
Sources: sources,
|
||||||
|
}
|
||||||
|
p.Revision = revision
|
||||||
|
}
|
||||||
|
return revision
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PublishedRepo) Update(collectionFactory *CollectionFactory, _ aptly.Progress) (*PublishedRepoUpdateResult, error) {
|
||||||
|
result := &PublishedRepoUpdateResult{
|
||||||
|
AddedSources: map[string]string{},
|
||||||
|
UpdatedSources: map[string]string{},
|
||||||
|
RemovedSources: map[string]string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
revision := p.ObtainRevision()
|
||||||
|
p.DropRevision()
|
||||||
|
|
||||||
|
publishedComponents := p.Components()
|
||||||
|
|
||||||
|
for _, component := range publishedComponents {
|
||||||
|
name, exists := revision.Sources[component]
|
||||||
|
if !exists {
|
||||||
|
p.RemoveComponent(component)
|
||||||
|
result.RemovedSources[component] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.SourceKind == SourceLocalRepo {
|
||||||
|
localRepoCollection := collectionFactory.LocalRepoCollection()
|
||||||
|
for component, name := range revision.Sources {
|
||||||
|
localRepo, err := localRepoCollection.ByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = localRepoCollection.LoadComplete(localRepo)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, exists := p.Sources[component]
|
||||||
|
if exists {
|
||||||
|
// Even in the case, when the local repository has not been changed as package source,
|
||||||
|
// it may contain a modified set of packages that requires (re-)publication.
|
||||||
|
p.UpdateLocalRepo(component, localRepo)
|
||||||
|
result.UpdatedSources[component] = name
|
||||||
|
} else {
|
||||||
|
p.UpdateLocalRepo(component, localRepo)
|
||||||
|
result.AddedSources[component] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if p.SourceKind == SourceSnapshot {
|
||||||
|
snapshotCollection := collectionFactory.SnapshotCollection()
|
||||||
|
for component, name := range revision.Sources {
|
||||||
|
snapshot, err := snapshotCollection.ByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = snapshotCollection.LoadComplete(snapshot)
|
||||||
|
if err != nil {
|
||||||
|
return result, fmt.Errorf("unable to update: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceUUID, exists := p.Sources[component]
|
||||||
|
if exists {
|
||||||
|
if snapshot.UUID != sourceUUID {
|
||||||
|
p.UpdateSnapshot(component, snapshot)
|
||||||
|
result.UpdatedSources[component] = name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.UpdateSnapshot(component, snapshot)
|
||||||
|
result.AddedSources[component] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return result, fmt.Errorf("unknown published repository type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParsePrefix splits [storage:]prefix into components
|
// ParsePrefix splits [storage:]prefix into components
|
||||||
@@ -281,14 +449,42 @@ func NewPublishedRepo(storage, prefix, distribution string, architectures []stri
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON requires object to filled by "LoadShallow" or "LoadComplete"
|
type sourceInfo struct {
|
||||||
func (p *PublishedRepo) MarshalJSON() ([]byte, error) {
|
Component, Name string
|
||||||
type sourceInfo struct {
|
}
|
||||||
Component, Name string
|
|
||||||
|
func (revision *PublishedRepoRevision) ToJSON() map[string]any {
|
||||||
|
sources := []sourceInfo{}
|
||||||
|
for _, component := range revision.Components() {
|
||||||
|
name := revision.Sources[component]
|
||||||
|
sources = append(sources, sourceInfo{
|
||||||
|
Component: component,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return map[string]any{"Sources": sources}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (revision *PublishedRepoRevision) MarshalJSON() ([]byte, error) {
|
||||||
|
sources := []sourceInfo{}
|
||||||
|
for _, component := range revision.Components() {
|
||||||
|
name := revision.Sources[component]
|
||||||
|
sources = append(sources, sourceInfo{
|
||||||
|
Component: component,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"Sources": sources,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON requires object to filled by "LoadShallow" or "LoadComplete"
|
||||||
|
func (p *PublishedRepo) MarshalJSON() ([]byte, error) {
|
||||||
sources := []sourceInfo{}
|
sources := []sourceInfo{}
|
||||||
for component, item := range p.sourceItems {
|
for _, component := range p.Components() {
|
||||||
|
item := p.sourceItems[component]
|
||||||
name := ""
|
name := ""
|
||||||
if item.snapshot != nil {
|
if item.snapshot != nil {
|
||||||
name = item.snapshot.Name
|
name = item.snapshot.Name
|
||||||
@@ -444,20 +640,25 @@ func (p *PublishedRepo) SourceNames() []string {
|
|||||||
return sources
|
return sources
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateLocalRepo updates content from local repo in component
|
// UpdateLocalRepo inserts/updates local repository source for component
|
||||||
func (p *PublishedRepo) UpdateLocalRepo(component string) {
|
func (p *PublishedRepo) UpdateLocalRepo(component string, localRepo *LocalRepo) {
|
||||||
if p.SourceKind != SourceLocalRepo {
|
if p.SourceKind != SourceLocalRepo {
|
||||||
panic("not local repo publish")
|
panic("not local repo publish")
|
||||||
}
|
}
|
||||||
|
|
||||||
item := p.sourceItems[component]
|
item, exists := p.sourceItems[component]
|
||||||
item.packageRefs = item.localRepo.RefList()
|
if !exists {
|
||||||
|
item = repoSourceItem{}
|
||||||
|
}
|
||||||
|
item.localRepo = localRepo
|
||||||
|
item.packageRefs = localRepo.RefList()
|
||||||
p.sourceItems[component] = item
|
p.sourceItems[component] = item
|
||||||
|
|
||||||
|
p.Sources[component] = localRepo.UUID
|
||||||
p.rePublishing = true
|
p.rePublishing = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateSnapshot switches snapshot for component
|
// UpdateSnapshot inserts/updates snapshot source for component
|
||||||
func (p *PublishedRepo) UpdateSnapshot(component string, snapshot *Snapshot) {
|
func (p *PublishedRepo) UpdateSnapshot(component string, snapshot *Snapshot) {
|
||||||
if p.SourceKind != SourceSnapshot {
|
if p.SourceKind != SourceSnapshot {
|
||||||
panic("not snapshot publish")
|
panic("not snapshot publish")
|
||||||
@@ -474,6 +675,14 @@ func (p *PublishedRepo) UpdateSnapshot(component string, snapshot *Snapshot) {
|
|||||||
p.rePublishing = true
|
p.rePublishing = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveComponent removes component from published repository
|
||||||
|
func (p *PublishedRepo) RemoveComponent(component string) {
|
||||||
|
delete(p.Sources, component)
|
||||||
|
delete(p.sourceItems, component)
|
||||||
|
|
||||||
|
p.rePublishing = true
|
||||||
|
}
|
||||||
|
|
||||||
// Encode does msgpack encoding of PublishedRepo
|
// Encode does msgpack encoding of PublishedRepo
|
||||||
func (p *PublishedRepo) Encode() []byte {
|
func (p *PublishedRepo) Encode() []byte {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
package docs
|
package docs
|
||||||
|
|
||||||
|
import _ "github.com/swaggo/swag" // make sure swag is in go.mod
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
#!/bin/sh -e
|
|
||||||
|
|
||||||
usermod -u `stat -c %u /work/src` aptly >/dev/null
|
|
||||||
chown -R `stat -c %u /work/src` /var/lib/aptly
|
|
||||||
|
|
||||||
su aptly -c 'set -e; cd /work/src;
|
|
||||||
GOPATH=$PWD/.go go generate -v
|
|
||||||
# install and initialize swagger
|
|
||||||
GOPATH=$PWD/.go go install github.com/swaggo/swag/cmd/swag@latest
|
|
||||||
PATH=$PWD/.go/bin:$PATH swag init -q --markdownFiles docs
|
|
||||||
git checkout debian/changelog
|
|
||||||
DEBEMAIL="CI <runner@github>" dch -v `make version` "CI build"
|
|
||||||
dpkg-buildpackage -us -uc -b -d
|
|
||||||
mkdir -p build && mv ../*.deb build/
|
|
||||||
rm -rf obj-*-linux-gnu*
|
|
||||||
git checkout debian/changelog
|
|
||||||
cd build && ls -l *.deb
|
|
||||||
'
|
|
||||||
@@ -319,40 +319,36 @@ class BaseTest(object):
|
|||||||
return subprocess.Popen(command, stderr=stderr, stdout=stdout, env=environ)
|
return subprocess.Popen(command, stderr=stderr, stdout=stdout, env=environ)
|
||||||
|
|
||||||
def run_cmd(self, command, expected_code=0):
|
def run_cmd(self, command, expected_code=0):
|
||||||
try:
|
proc = self._start_process(command, stdout=subprocess.PIPE)
|
||||||
proc = self._start_process(command, stdout=subprocess.PIPE)
|
raw_output, _ = proc.communicate()
|
||||||
raw_output, _ = proc.communicate()
|
|
||||||
|
|
||||||
raw_output = raw_output.decode("utf-8", errors='replace')
|
raw_output = raw_output.decode("utf-8", errors='replace')
|
||||||
|
|
||||||
returncodes = [proc.returncode]
|
returncodes = [proc.returncode]
|
||||||
is_aptly_command = False
|
is_aptly_command = False
|
||||||
if isinstance(command, str):
|
if isinstance(command, str):
|
||||||
is_aptly_command = command.startswith("aptly")
|
is_aptly_command = command.startswith("aptly")
|
||||||
|
|
||||||
if isinstance(command, list):
|
if isinstance(command, list):
|
||||||
is_aptly_command = command[0] == "aptly"
|
is_aptly_command = command[0] == "aptly"
|
||||||
|
|
||||||
if is_aptly_command:
|
if is_aptly_command:
|
||||||
# remove the last two rows as go tests always print PASS/FAIL and coverage in those
|
# remove the last two rows as go tests always print PASS/FAIL and coverage in those
|
||||||
# two lines. This would otherwise fail the tests as they would not match gold
|
# two lines. This would otherwise fail the tests as they would not match gold
|
||||||
matches = re.findall(r"((.|\n)*)EXIT: (\d)\n.*\ncoverage: .*", raw_output)
|
matches = re.findall(r"((.|\n)*)EXIT: (\d)\n.*\ncoverage: .*", raw_output)
|
||||||
if not matches:
|
if not matches:
|
||||||
raise Exception("no matches found in output '%s'" % raw_output)
|
raise Exception("no matches found in command output '%s'" % raw_output)
|
||||||
|
|
||||||
output, _, returncode = matches[0]
|
output, _, returncode = matches[0]
|
||||||
returncodes.append(int(returncode))
|
returncodes.append(int(returncode))
|
||||||
else:
|
else:
|
||||||
output = raw_output
|
output = raw_output
|
||||||
|
|
||||||
if expected_code is not None:
|
if expected_code is not None:
|
||||||
if expected_code not in returncodes:
|
if expected_code not in returncodes:
|
||||||
raise Exception("exit code %d != %d (output: %s)" % (
|
raise Exception("command expected to return %d, but returned %d: \n%s" % (
|
||||||
proc.returncode, expected_code, raw_output))
|
expected_code, proc.returncode, raw_output))
|
||||||
return output
|
return output
|
||||||
except Exception as e:
|
|
||||||
raise Exception("Running command '%s' failed: %s" %
|
|
||||||
(command, str(e)))
|
|
||||||
|
|
||||||
def gold_processor(self, gold):
|
def gold_processor(self, gold):
|
||||||
return gold
|
return gold
|
||||||
@@ -379,6 +375,8 @@ class BaseTest(object):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
def check_output(self):
|
def check_output(self):
|
||||||
|
gold_file = self.get_gold_filename()
|
||||||
|
print(f"Verifying gold file: {gold_file}")
|
||||||
try:
|
try:
|
||||||
self.verify_match(self.get_gold(), self.output,
|
self.verify_match(self.get_gold(), self.output,
|
||||||
match_prepare=self.outputMatchPrepare)
|
match_prepare=self.outputMatchPrepare)
|
||||||
@@ -464,11 +462,11 @@ class BaseTest(object):
|
|||||||
|
|
||||||
def check_in(self, item, l):
|
def check_in(self, item, l):
|
||||||
if item not in l:
|
if item not in l:
|
||||||
raise Exception("item %r not in %r", item, l)
|
raise Exception("expected item: %r\nnot found in: %r" % (item, l))
|
||||||
|
|
||||||
def check_not_in(self, item, l):
|
def check_not_in(self, item, l):
|
||||||
if item in l:
|
if item in l:
|
||||||
raise Exception("item %r in %r", item, l)
|
raise Exception("unexpected item: %r\n found in: %r" % (item, l))
|
||||||
|
|
||||||
def check_subset(self, a, b):
|
def check_subset(self, a, b):
|
||||||
diff = ''
|
diff = ''
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo azure:test1:./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository azure:test1:./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for snapshot azure:test1:./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
|
Published snapshot repository azure:test1:./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for snapshot ./maverick [i386, source] publishes {main: [snap2]: Snapshot from local repo [local-repo2]} has been successfully switched to new snapshot.
|
Published snapshot repository ./maverick [i386, source] publishes {main: [snap2]: Snapshot from local repo [local-repo2]} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for snapshot ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
|
Published snapshot repository ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ Finalizing metadata files...
|
|||||||
Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
|
|
||||||
Publish for snapshot ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
|
Published snapshot repository ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for snapshot ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
|
Published snapshot repository ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for snapshot ./bookworm (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
|
Published snapshot repository ./bookworm (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for snapshot ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
|
Published snapshot repository ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "ppa" components main...
|
Cleaning up prefix "ppa" components main...
|
||||||
|
|
||||||
Publish for snapshot ppa/maverick [amd64, i386] publishes {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick} has been successfully switched to new snapshot.
|
Published snapshot repository ppa/maverick [amd64, i386] publishes {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for snapshot ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
|
Published snapshot repository ./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "ppa" components main...
|
Cleaning up prefix "ppa" components main...
|
||||||
|
|
||||||
Publish for snapshot ppa/maverick [i386] publishes {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick} has been successfully switched to new snapshot.
|
Published snapshot repository ppa/maverick [i386] publishes {main: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
ERROR: unable to update: published repo with storage:prefix/distribution ppa/maverick not found
|
ERROR: unable to switch: published repo with storage:prefix/distribution ppa/maverick not found
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
ERROR: unable to update: not a snapshot publish
|
ERROR: unable to switch: local repo with name snap1 not found
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components b, c...
|
Cleaning up prefix "." components b, c...
|
||||||
|
|
||||||
Publish for snapshot ./maverick [amd64, i386, source] publishes {a: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick}, {b: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'}, {c: [local2]: Snapshot from local repo [local-repo]} has been successfully switched to new snapshot.
|
Published snapshot repository ./maverick [amd64, i386, source] publishes {a: [snap1]: Snapshot from mirror [gnuplot-maverick]: http://ppa.launchpad.net/gladky-anton/gnuplot/ubuntu/ maverick}, {b: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'}, {c: [local2]: Snapshot from local repo [local-repo]} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ Finalizing metadata files...
|
|||||||
Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
|
|
||||||
Publish for local repo ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo ./bookworm [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository ./bookworm [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository ./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo ./maverick [source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository ./maverick [source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components contrib, main...
|
Cleaning up prefix "." components contrib, main...
|
||||||
|
|
||||||
Publish for local repo ./maverick [i386, source] publishes {contrib: [repo2]}, {main: [repo1]} has been successfully updated.
|
Published local repository ./maverick [i386, source] publishes {contrib: [repo2]}, {main: [repo1]} has been successfully updated.
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ Generating metadata files and linking package files...
|
|||||||
Finalizing metadata files...
|
Finalizing metadata files...
|
||||||
Cleaning up prefix "." components contrib, main...
|
Cleaning up prefix "." components contrib, main...
|
||||||
|
|
||||||
Publish for local repo ./squeeze [i386] publishes {contrib: [repo2]}, {main: [repo1]} has been successfully updated.
|
Published local repository ./squeeze [i386] publishes {contrib: [repo2]}, {main: [repo1]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo s3:test1:./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository s3:test1:./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for snapshot s3:test1:./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new snapshot.
|
Published snapshot repository s3:test1:./maverick (origin: LP-PPA-gladky-anton-gnuplot) [amd64, i386] publishes {main: [snap3]: Pulled into 'snap2' with 'snap1' as source, pull request was: 'gnuplot-x11'} has been successfully switched to new source.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Signing file 'Release' with gpg, please enter your passphrase when prompted:
|
|||||||
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:
|
||||||
Cleaning up prefix "." components main...
|
Cleaning up prefix "." components main...
|
||||||
|
|
||||||
Publish for local repo s3:test1:./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
Published local repository s3:test1:./maverick [i386, source] publishes {main: [local-repo]} has been successfully updated.
|
||||||
|
|||||||
Reference in New Issue
Block a user