forked from toolshed/abra
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4c8733f90 | ||
|
|
0a8a599c9a | ||
|
|
353ceabfce | ||
|
|
6443e1b0d5 | ||
|
|
5fea6e0815 | ||
|
|
452cf1a468 | ||
|
|
14ede5af37 | ||
|
|
6c6294b2a2 | ||
|
|
3e1b275c88 | ||
|
|
e1e910dbfb | ||
|
|
6814a52b6e | ||
|
|
3cc527101a | ||
|
|
45d1cb022b | ||
|
|
86ca816271 | ||
|
|
7d90faa596 | ||
|
|
fadc327e28 | ||
|
|
66242a3adf | ||
|
|
4d60f6c2ce | ||
|
|
626e2cbf38 | ||
|
|
9fdb570855 | ||
|
|
3de49a7b70 |
@@ -19,5 +19,6 @@ var (
|
||||
Minor bool
|
||||
NoDomainChecks bool
|
||||
Patch bool
|
||||
PinDigests bool
|
||||
ShowUnchanged bool
|
||||
)
|
||||
|
||||
@@ -92,7 +92,7 @@ your private key and enter your passphrase beforehand.
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
mainAppVersion := imagesTmp[mainApp]
|
||||
mainAppVersion := formatter.RemoveSha(imagesTmp[mainApp])
|
||||
if mainAppVersion == "" {
|
||||
log.Fatal(i18n.G("main app service version for %s is empty?", recipe.Name))
|
||||
}
|
||||
@@ -192,6 +192,9 @@ likely to change.
|
||||
serviceMeta := serviceVersions[serviceName]
|
||||
|
||||
existingImageTag := fmt.Sprintf("%s:%s", serviceMeta.Image, serviceMeta.Tag)
|
||||
if serviceMeta.Digest != "" {
|
||||
existingImageTag += "@" + serviceMeta.Digest
|
||||
}
|
||||
newImageTag := fmt.Sprintf("%s:%s", serviceMeta.Image, imagesTmp[serviceMeta.Image])
|
||||
|
||||
if existingImageTag == newImageTag {
|
||||
@@ -331,10 +334,8 @@ func GetImageVersions(recipe recipePkg.Recipe) (map[string]string, error) {
|
||||
|
||||
path = formatter.StripTagMeta(path)
|
||||
|
||||
var tag string
|
||||
switch img.(type) {
|
||||
case reference.NamedTagged:
|
||||
tag = img.(reference.NamedTagged).Tag()
|
||||
case reference.Named:
|
||||
if service.Name == "app" {
|
||||
missingTag = true
|
||||
@@ -342,7 +343,7 @@ func GetImageVersions(recipe recipePkg.Recipe) (map[string]string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
services[path] = tag
|
||||
services[path] = formatter.GetTagAndDigest(img)
|
||||
}
|
||||
|
||||
if missingTag {
|
||||
|
||||
+42
-8
@@ -130,7 +130,7 @@ interface.`),
|
||||
}
|
||||
|
||||
for _, service := range config.Services {
|
||||
img, err := reference.ParseNormalizedNamed(service.Image)
|
||||
img, err := reference.ParseNormalizedNamed(formatter.RemoveSha(service.Image))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -178,9 +178,14 @@ interface.`),
|
||||
|
||||
sort.Sort(tagcmp.ByTagDesc(compatible))
|
||||
|
||||
var upgradeTag string
|
||||
if len(compatible) == 0 && !allTags {
|
||||
log.Info(i18n.G("no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)", image, tag))
|
||||
continue // skip on to the next tag and don't update any compose files
|
||||
|
||||
if !shouldPin(service.Image, internal.PinDigests) {
|
||||
continue
|
||||
}
|
||||
upgradeTag = tag.String()
|
||||
}
|
||||
|
||||
catlVersions, err := recipePkg.VersionsOfService(recipe.Name, service.Name, internal.Offline)
|
||||
@@ -203,9 +208,8 @@ interface.`),
|
||||
|
||||
log.Debug(i18n.G("detected compatible upgradable tags %s for %s", compatibleStrings, service.Name))
|
||||
|
||||
var upgradeTag string
|
||||
_, ok := servicePins[service.Name]
|
||||
if versionsPresent && ok {
|
||||
if upgradeTag == "" && versionsPresent && ok {
|
||||
pinnedTag := servicePins[service.Name].version
|
||||
if tag.IsLessThan(pinnedTag) {
|
||||
pinnedTagString := pinnedTag.String()
|
||||
@@ -227,7 +231,7 @@ interface.`),
|
||||
log.Fatal(i18n.G("service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!", service.Name, tag.String(), pinnedTag.String()))
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
} else if upgradeTag == "" {
|
||||
if bumpType != 0 {
|
||||
for _, upTag := range compatible {
|
||||
upElement, err := tag.UpgradeDelta(upTag)
|
||||
@@ -242,7 +246,10 @@ interface.`),
|
||||
}
|
||||
if upgradeTag == "" {
|
||||
log.Warn(i18n.G("not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants", tag.String(), compatible[0].String(), image))
|
||||
continue
|
||||
if !shouldPin(service.Image, internal.PinDigests) {
|
||||
continue
|
||||
}
|
||||
upgradeTag = tag.String()
|
||||
}
|
||||
} else {
|
||||
msg := i18n.G("upgrade to which tag? (service: %s, image: %s, tag: %s)", service.Name, image, tag)
|
||||
@@ -293,12 +300,25 @@ interface.`),
|
||||
}
|
||||
}
|
||||
if upgradeTag != "skip" {
|
||||
ok, err := recipe.UpdateTag(image, upgradeTag)
|
||||
var resolvedDigest string
|
||||
if shouldPin(service.Image, internal.PinDigests) {
|
||||
resolvedDigest, err = client.GetImageMultiplatformDigest(img, upgradeTag)
|
||||
if err != nil {
|
||||
log.Fatal(i18n.G("failed to resolve digest for %s:%s: %s", image, upgradeTag, err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
ok, err := recipe.UpdateTag(service.Image, upgradeTag, resolvedDigest)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if ok {
|
||||
log.Info(i18n.G("tag upgraded from %s to %s for %s", tag.String(), upgradeTag, image))
|
||||
if shouldPin(service.Image, internal.PinDigests) {
|
||||
log.Info(i18n.G("tag upgraded and pinned from %s to %s@%s for %s", tag.String(), upgradeTag, resolvedDigest, image))
|
||||
} else {
|
||||
log.Info(i18n.G("tag upgraded from %s to %s for %s", tag.String(), upgradeTag, image))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !internal.NoInput {
|
||||
@@ -364,6 +384,12 @@ interface.`),
|
||||
},
|
||||
}
|
||||
|
||||
// shouldPin reports whether a service image should be pinned to a digest.
|
||||
// An image that is already pinned stays pinned, even without --pindigests.
|
||||
func shouldPin(image string, pinDigestsFlag bool) bool {
|
||||
return pinDigestsFlag || strings.Contains(image, "@sha256:")
|
||||
}
|
||||
|
||||
var (
|
||||
allTags bool
|
||||
createCommit bool
|
||||
@@ -417,4 +443,12 @@ func init() {
|
||||
false,
|
||||
i18n.G("commit changes"),
|
||||
)
|
||||
|
||||
RecipeUpgradeCommand.Flags().BoolVarP(
|
||||
&internal.PinDigests,
|
||||
i18n.G("pindigests"),
|
||||
i18n.GC("p", "pin container image by digest"),
|
||||
true,
|
||||
i18n.G("pin the container image version by manifest digest (image:tag@sha256:...)"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package recipe
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestShouldPin(t *testing.T) {
|
||||
tests := []struct {
|
||||
image string
|
||||
pinDigests bool
|
||||
want bool
|
||||
}{
|
||||
{"nginx:1.21.0", false, false},
|
||||
{"nginx:1.21.0", true, true},
|
||||
{"nginx:1.21.0@sha256:2bcabc23b45489fb0885d69a06ba1d648aeda973fae7bb981bafbb884165e514", false, true},
|
||||
{"nginx:1.21.0@sha256:2bcabc23b45489fb0885d69a06ba1d648aeda973fae7bb981bafbb884165e514", true, true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if got := shouldPin(test.image, test.pinDigests); got != test.want {
|
||||
t.Errorf("shouldPin(%q, %v) = %v, want %v", test.image, test.pinDigests, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-6
@@ -13,8 +13,6 @@ import (
|
||||
"github.com/regclient/regclient/types/ref"
|
||||
)
|
||||
|
||||
var rc = regclient.New()
|
||||
|
||||
// GetRegistryTags retrieves all tags of an image from a container registry.
|
||||
func GetRegistryTags(img reference.Named) ([]string, error) {
|
||||
var tags []string
|
||||
@@ -39,15 +37,26 @@ func GetRegistryTags(img reference.Named) ([]string, error) {
|
||||
// See: https://docs.docker.com/build/building/multi-platform/#why-multi-platform-builds
|
||||
// and: https://docs.docker.com/dhi/explore/security-concepts/digests/#multi-platform-images-and-manifests
|
||||
func GetImageMultiplatformDigest(img reference.Named, tag string) (string, error) {
|
||||
r, err := ref.New(fmt.Sprintf("%s:%s", img.Name(), tag))
|
||||
tagged, err := reference.WithTag(reference.TrimNamed(img), tag)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parsing reference %s:%s: %w", img.Name(), tag, err)
|
||||
return "", errors.New(i18n.G("failed to parse image %s:%s, saw: %s", img.Name(), tag, err.Error()))
|
||||
}
|
||||
r, err := ref.New(tagged.String())
|
||||
if err != nil {
|
||||
return "", errors.New(i18n.G("failed to parse image %s:%s, saw: %s", img.Name(), tag, err.Error()))
|
||||
}
|
||||
|
||||
rc := regclient.New(regclient.WithDockerCreds())
|
||||
|
||||
m, err := rc.ManifestHead(context.Background(), r)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("fetching manifest for %s:%s: %w", img.Name(), tag, err)
|
||||
return "", errors.New(i18n.G("failed to fetch manifest for %s:%s, saw: %s", img.Name(), tag, err.Error()))
|
||||
}
|
||||
|
||||
return m.GetDescriptor().Digest.String(), nil
|
||||
digest := m.GetDescriptor().Digest.String()
|
||||
if digest == "" {
|
||||
return "", errors.New(i18n.G("registry did not return a digest for %s:%s", img.Name(), tag))
|
||||
}
|
||||
|
||||
return digest, nil
|
||||
}
|
||||
|
||||
+6
-2
@@ -171,7 +171,7 @@ func GetImagesForStack(cl *dockerClient.Client, app appPkg.App) (map[string]stri
|
||||
images[imageBaseName] = ""
|
||||
continue
|
||||
}
|
||||
imageTag := namedTag.Tag()
|
||||
imageTag := formatter.GetTagAndDigest(namedTag)
|
||||
|
||||
existingImageVersion, ok := images[imageBaseName]
|
||||
if !ok {
|
||||
@@ -294,7 +294,7 @@ func GatherImagesForDeploy(cl *dockerClient.Client, app appPkg.App, compose *com
|
||||
newImages[imageBaseName] = ""
|
||||
continue
|
||||
}
|
||||
imageTag := namedTag.Tag()
|
||||
imageTag := formatter.GetTagAndDigest(namedTag)
|
||||
|
||||
existingImageVersion, ok := newImages[imageBaseName]
|
||||
if !ok {
|
||||
@@ -312,6 +312,10 @@ func GatherImagesForDeploy(cl *dockerClient.Client, app appPkg.App, compose *com
|
||||
var imageInfo []string
|
||||
for newImageName, newImageVersion := range newImages {
|
||||
if currentVersion, exists := currentImages[newImageName]; exists {
|
||||
// Swarm adds a resolved digest even for unpinned compose images.
|
||||
if !strings.Contains(newImageVersion, "@") {
|
||||
currentVersion = formatter.RemoveSha(currentVersion)
|
||||
}
|
||||
if currentVersion == newImageVersion {
|
||||
if showUnchanged {
|
||||
imageInfo = append(imageInfo, i18n.G("%s: %s (unchanged)", formatter.StripTagMeta(newImageName), newImageVersion))
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"coopcloud.tech/abra/pkg/i18n"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/lipgloss/table"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/go-units"
|
||||
"golang.org/x/term"
|
||||
|
||||
@@ -40,6 +41,18 @@ func RemoveSha(str string) string {
|
||||
return strings.Split(str, "@")[0]
|
||||
}
|
||||
|
||||
// GetTagAndDigest returns the tag and any digest for comparison and display.
|
||||
func GetTagAndDigest(image reference.Named) string {
|
||||
var version string
|
||||
if tagged, ok := image.(reference.NamedTagged); ok {
|
||||
version = tagged.Tag()
|
||||
}
|
||||
if pinned, ok := image.(reference.Digested); ok {
|
||||
version += "@" + pinned.Digest().String()
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
// HumanDuration from docker/cli RunningFor() to be accessible outside of the class
|
||||
func HumanDuration(timestamp int64) string {
|
||||
date := time.Unix(timestamp, 0)
|
||||
@@ -223,7 +236,8 @@ func CreateProgressbar(length int, title string) *progressbar.ProgressBar {
|
||||
)
|
||||
}
|
||||
|
||||
// StripTagMeta strips front-matter image tag data that we don't need for parsing.
|
||||
// StripTagMeta strips front-matter image tag data that we don't need for parsing,
|
||||
// like registry host and library prefix.
|
||||
func StripTagMeta(image string) string {
|
||||
originalImage := image
|
||||
|
||||
|
||||
@@ -3,12 +3,13 @@ package formatter
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRemoveSha(t *testing.T) {
|
||||
assert.Equal(t, "ubuntu:latest", RemoveSha("ubuntu:latest@sha256:1234567890abcdef"))
|
||||
assert.Equal(t, "ubuntu:latest", RemoveSha("ubuntu:latest"))
|
||||
assert.Equal(t, "nginx:latest", RemoveSha("nginx:latest@sha256:1234567890abcdef"))
|
||||
assert.Equal(t, "nginx:latest", RemoveSha("nginx:latest"))
|
||||
assert.Equal(t, "my-repo/my-image:v1", RemoveSha("my-repo/my-image:v1@sha256:abcdef1234567890"))
|
||||
assert.Equal(t, "my-repo/my-image", RemoveSha("my-repo/my-image@sha256:abcdef1234567890"))
|
||||
}
|
||||
@@ -16,3 +17,21 @@ func TestRemoveSha(t *testing.T) {
|
||||
func TestBoldDirtyDefault(t *testing.T) {
|
||||
assert.Equal(t, "foo", BoldDirtyDefault("foo"))
|
||||
}
|
||||
|
||||
func TestGetTagAndDigest(t *testing.T) {
|
||||
digest := "sha256:908dc23e643a1447dbfb2e189ed268bfde6a51a5bf9a34d3dd3440a24f58ccf7"
|
||||
for _, tc := range []struct{ image, want string }{
|
||||
{"nginx:1.21.6", "1.21.6"},
|
||||
{"ghcr.io/foo/bar:1.0@" + digest, "1.0@" + digest},
|
||||
{"nginx@" + digest, "@" + digest},
|
||||
{"nginx", ""},
|
||||
} {
|
||||
img, err := reference.ParseNormalizedNamed(tc.image)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := GetTagAndDigest(img); got != tc.want {
|
||||
t.Errorf("%s: got %s, want %s", tc.image, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+160
-94
@@ -7,7 +7,7 @@
|
||||
msgid ""
|
||||
msgstr "Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-08-31 19:21+0000\n"
|
||||
"POT-Creation-Date: 2026-10-02 16:58-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -165,7 +165,7 @@ msgid " # standard rollback\n"
|
||||
" abra app rollback 1312.net 2.0.0+1.2.3"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/formatter/formatter.go:46
|
||||
#: ./pkg/formatter/formatter.go:47
|
||||
msgid " ago"
|
||||
msgstr ""
|
||||
|
||||
@@ -219,7 +219,7 @@ msgstr ""
|
||||
msgid "%s created (version: %s)"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:335
|
||||
#: ./cli/recipe/upgrade.go:364
|
||||
#, c-format
|
||||
msgid "%s currently has these unstaged changes 👇"
|
||||
msgstr ""
|
||||
@@ -304,7 +304,7 @@ msgstr ""
|
||||
msgid "%s has no secrets to generate, skipping..."
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:107
|
||||
#: ./pkg/recipe/compose.go:110
|
||||
#, c-format
|
||||
msgid "%s has no version label? try running \"abra recipe sync %s\" first?"
|
||||
msgstr ""
|
||||
@@ -324,12 +324,12 @@ msgstr ""
|
||||
msgid "%s is already fetched"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:233
|
||||
#: ./pkg/recipe/compose.go:374
|
||||
#, c-format
|
||||
msgid "%s is already set, nothing to do?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:72
|
||||
#: ./pkg/recipe/compose.go:75
|
||||
#, c-format
|
||||
msgid "%s is missing a compose.yml or compose.*.yml file?"
|
||||
msgstr ""
|
||||
@@ -568,12 +568,12 @@ msgstr ""
|
||||
msgid "--local detected, running %s on local work station"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:261 ./pkg/recipe/files.go:18
|
||||
#: ./pkg/lint/recipe.go:268 ./pkg/recipe/files.go:18
|
||||
#, c-format
|
||||
msgid ".env.sample for %s couldn't be read: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:133
|
||||
#: ./pkg/lint/recipe.go:140
|
||||
msgid ".env.sample provided"
|
||||
msgstr ""
|
||||
|
||||
@@ -638,17 +638,17 @@ msgstr ""
|
||||
msgid "CHAOS DEPLOY"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:47
|
||||
#: ./pkg/recipe/compose.go:50
|
||||
#, c-format
|
||||
msgid "COMPOSE_FILE (=\"%s\") parsing failed?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:58
|
||||
#: ./pkg/recipe/compose.go:61
|
||||
#, c-format
|
||||
msgid "COMPOSE_FILE detected (%s), loading %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:38
|
||||
#: ./pkg/recipe/compose.go:41
|
||||
#, c-format
|
||||
msgid "COMPOSE_FILE detected, loading %s"
|
||||
msgstr ""
|
||||
@@ -739,7 +739,7 @@ msgid "Create a new version of a recipe.\n"
|
||||
" ssh-add ~/.ssh/<my-ssh-private-key-for-git-coopcloud-tech>"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/formatter/formatter.go:134
|
||||
#: ./pkg/formatter/formatter.go:135
|
||||
msgid "CreateOverview: only accepts rows of len == 2"
|
||||
msgstr ""
|
||||
|
||||
@@ -1644,7 +1644,7 @@ msgid "\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:203
|
||||
#: ./pkg/lint/recipe.go:210
|
||||
#, c-format
|
||||
msgid "\n"
|
||||
"lint %s: %s"
|
||||
@@ -1687,7 +1687,7 @@ msgctxt "recipe fetch"
|
||||
msgid "a"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:408
|
||||
#: ./cli/recipe/upgrade.go:444
|
||||
msgctxt "recipe upgrade"
|
||||
msgid "a"
|
||||
msgstr ""
|
||||
@@ -1737,7 +1737,7 @@ msgstr ""
|
||||
msgid "accepts at most 2 args with --local/-l"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:249
|
||||
#: ./pkg/recipe/compose.go:390
|
||||
#, c-format
|
||||
msgid "add '- \"%s\"' manually to the 'app' service in %s"
|
||||
msgstr ""
|
||||
@@ -1764,11 +1764,15 @@ msgstr ""
|
||||
msgid "all containers did really go away"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:131
|
||||
msgid "all images use a digest"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:82
|
||||
msgid "all images use a tag"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:162
|
||||
#: ./pkg/lint/recipe.go:169
|
||||
msgid "all services have images"
|
||||
msgstr ""
|
||||
|
||||
@@ -1780,7 +1784,7 @@ msgstr ""
|
||||
msgid "all-services"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:407
|
||||
#: ./cli/recipe/upgrade.go:443
|
||||
msgid "all-tags"
|
||||
msgstr ""
|
||||
|
||||
@@ -1919,7 +1923,7 @@ msgstr ""
|
||||
msgid "c"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:416
|
||||
#: ./cli/recipe/upgrade.go:452
|
||||
msgctxt "recipe upgrade"
|
||||
msgid "c"
|
||||
msgstr ""
|
||||
@@ -1928,7 +1932,7 @@ msgstr ""
|
||||
msgid "can not insert from file and read from stdin"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:323
|
||||
#: ./cli/recipe/upgrade.go:352
|
||||
#, c-format
|
||||
msgid "can upgrade service: %s, image: %s, tag: %s ::"
|
||||
msgstr ""
|
||||
@@ -2108,7 +2112,7 @@ msgstr ""
|
||||
msgid "chore: publish new catalogue release changes"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:352
|
||||
#: ./cli/recipe/upgrade.go:381
|
||||
msgid "chore: update image tags"
|
||||
msgstr ""
|
||||
|
||||
@@ -2170,19 +2174,19 @@ msgstr ""
|
||||
msgid "commandconn: starting %s with %v"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:415
|
||||
#: ./cli/recipe/upgrade.go:451
|
||||
msgid "commit"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:418
|
||||
#: ./cli/recipe/upgrade.go:454
|
||||
msgid "commit changes"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:342
|
||||
#: ./cli/recipe/upgrade.go:371
|
||||
msgid "commit changes?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:356
|
||||
#: ./cli/recipe/upgrade.go:385
|
||||
#, c-format
|
||||
msgid "committed changes as '%s'"
|
||||
msgstr ""
|
||||
@@ -2221,7 +2225,7 @@ msgstr ""
|
||||
msgid "config not found: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:169
|
||||
#: ./pkg/lint/recipe.go:176
|
||||
msgid "config version are vendored"
|
||||
msgstr ""
|
||||
|
||||
@@ -2230,14 +2234,14 @@ msgstr ""
|
||||
msgid "connection timed out for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:191
|
||||
#: ./pkg/recipe/compose.go:146
|
||||
#, c-format
|
||||
msgid "considering %s config(s) for label update"
|
||||
msgid "considering %s config(s) for image update"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:123
|
||||
#: ./pkg/recipe/compose.go:332
|
||||
#, c-format
|
||||
msgid "considering %s config(s) for tag update"
|
||||
msgid "considering %s config(s) for label update"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/undeploy.go:141 ./cli/server/prune.go:53
|
||||
@@ -2258,7 +2262,7 @@ msgstr ""
|
||||
msgid "context lacks Docker endpoint"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:229
|
||||
#: ./pkg/recipe/compose.go:370
|
||||
#, c-format
|
||||
msgid "coop-cloud.${STACK_NAME}.version=%s"
|
||||
msgstr ""
|
||||
@@ -2303,7 +2307,7 @@ msgstr ""
|
||||
msgid "create <domain> [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:134
|
||||
#: ./pkg/lint/recipe.go:141
|
||||
msgid "create an example .env.sample"
|
||||
msgstr ""
|
||||
|
||||
@@ -2418,7 +2422,7 @@ msgstr ""
|
||||
msgid "deploy in progress 🟠"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:147
|
||||
#: ./pkg/lint/recipe.go:154
|
||||
msgid "deploy labels stanza present"
|
||||
msgstr ""
|
||||
|
||||
@@ -2461,11 +2465,11 @@ msgstr ""
|
||||
msgid "detected %s as tags for recipe %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/formatter/formatter.go:80
|
||||
#: ./pkg/formatter/formatter.go:81
|
||||
msgid "detected ABRA_CI=1"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:204
|
||||
#: ./cli/recipe/upgrade.go:208
|
||||
#, c-format
|
||||
msgid "detected compatible upgradable tags %s for %s"
|
||||
msgstr ""
|
||||
@@ -2644,7 +2648,7 @@ msgstr ""
|
||||
msgid "ensure 'version: \"3.8\"' in compose configs"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:163
|
||||
#: ./pkg/lint/recipe.go:170
|
||||
msgid "ensure \"image: ...\" set on all services"
|
||||
msgstr ""
|
||||
|
||||
@@ -2658,7 +2662,7 @@ msgstr ""
|
||||
msgid "ensuring env version %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:285
|
||||
#: ./cli/recipe/upgrade.go:291
|
||||
msgid "enter / return to confirm, choose 'skip' to not upgrade this tag, vim mode is enabled"
|
||||
msgstr ""
|
||||
|
||||
@@ -2682,7 +2686,7 @@ msgstr ""
|
||||
msgid "err: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/lint.go:82 ./cli/recipe/lint.go:144 ./pkg/lint/recipe.go:132 ./pkg/lint/recipe.go:139 ./pkg/lint/recipe.go:146 ./pkg/lint/recipe.go:153 ./pkg/lint/recipe.go:161 ./pkg/lint/recipe.go:168 ./pkg/lint/recipe.go:175
|
||||
#: ./cli/recipe/lint.go:82 ./cli/recipe/lint.go:144 ./pkg/lint/recipe.go:139 ./pkg/lint/recipe.go:146 ./pkg/lint/recipe.go:153 ./pkg/lint/recipe.go:160 ./pkg/lint/recipe.go:168 ./pkg/lint/recipe.go:175 ./pkg/lint/recipe.go:182
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
@@ -2801,6 +2805,11 @@ msgstr ""
|
||||
msgid "failed to create volume %s on %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/client/registry.go:54
|
||||
#, c-format
|
||||
msgid "failed to fetch manifest for %s:%s, saw: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/secret/secret.go:104
|
||||
#, c-format
|
||||
msgid "failed to generate random bytes: %w"
|
||||
@@ -2839,11 +2848,16 @@ msgstr ""
|
||||
msgid "failed to migrate app config: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/client/registry.go:20
|
||||
#: ./pkg/client/registry.go:22
|
||||
#, c-format
|
||||
msgid "failed to parse image %s, saw: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/client/registry.go:45
|
||||
#, c-format
|
||||
msgid "failed to parse image %s:%s, saw: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/git.go:437
|
||||
#, c-format
|
||||
msgid "failed to parse image for %s in %s: %s"
|
||||
@@ -2898,6 +2912,11 @@ msgstr ""
|
||||
msgid "failed to resize tty, using default size"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:313
|
||||
#, c-format
|
||||
msgid "failed to resolve digest for %s:%s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:138
|
||||
#, c-format
|
||||
msgid "failed to retrieve latest commit for %s: %s"
|
||||
@@ -3206,16 +3225,26 @@ msgstr ""
|
||||
msgid "ignore uncommitted recipes changes"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:248
|
||||
#, c-format
|
||||
msgid "image %s for %s uses a variable, skipping update, please update it manually"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:183
|
||||
#, c-format
|
||||
msgid "image for %s uses a variable, skipping update, please update %s manually"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/undeploy.go:156 ./cli/server/prune.go:74
|
||||
#, c-format
|
||||
msgid "images pruned: %d; space reclaimed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:148
|
||||
#: ./pkg/lint/recipe.go:155
|
||||
msgid "include \"deploy: labels: ...\" stanza"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:155
|
||||
#: ./pkg/lint/recipe.go:162
|
||||
msgid "include \"traefik.enable=true\" deploy label"
|
||||
msgstr ""
|
||||
|
||||
@@ -3291,15 +3320,15 @@ msgstr ""
|
||||
msgid "including VOLUMES=%v in backupbot exec invocation"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/release.go:634 ./cli/recipe/upgrade.go:378
|
||||
#: ./cli/recipe/release.go:634 ./cli/recipe/upgrade.go:414
|
||||
msgid "increase the major part of the version"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/release.go:642 ./cli/recipe/upgrade.go:386
|
||||
#: ./cli/recipe/release.go:642 ./cli/recipe/upgrade.go:422
|
||||
msgid "increase the minor part of the version"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/release.go:650 ./cli/recipe/upgrade.go:394
|
||||
#: ./cli/recipe/release.go:650 ./cli/recipe/upgrade.go:430
|
||||
msgid "increase the patch part of the version"
|
||||
msgstr ""
|
||||
|
||||
@@ -3369,7 +3398,12 @@ msgstr ""
|
||||
msgid "invalid detach escape keys, using default: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:504
|
||||
#: ./pkg/recipe/compose.go:308
|
||||
#, c-format
|
||||
msgid "invalid digest %s, want sha256:<64 hex digits>"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:533
|
||||
msgid "invalid lightweight tag detected"
|
||||
msgstr ""
|
||||
|
||||
@@ -3430,12 +3464,12 @@ msgstr ""
|
||||
msgid "lint <recipe> [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:187
|
||||
#: ./pkg/lint/recipe.go:194
|
||||
#, c-format
|
||||
msgid "linting for critical errors in %s configs"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:215
|
||||
#: ./pkg/lint/recipe.go:222
|
||||
#, c-format
|
||||
msgid "linting successful, %s is well configured"
|
||||
msgstr ""
|
||||
@@ -3464,7 +3498,7 @@ msgstr ""
|
||||
msgid "list [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:410
|
||||
#: ./cli/recipe/upgrade.go:446
|
||||
msgid "list all tags, not just upgrades"
|
||||
msgstr ""
|
||||
|
||||
@@ -3534,11 +3568,11 @@ msgstr ""
|
||||
#. with no spaces in between
|
||||
#. translators: `abra man` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: ./cli/app/list.go:319 ./cli/app/move.go:34 ./cli/app/ps.go:205 ./cli/app/secret.go:553 ./cli/app/secret.go:649 ./cli/recipe/list.go:104 ./cli/recipe/upgrade.go:400 ./cli/recipe/version.go:139 ./cli/run.go:152 ./cli/server/list.go:106
|
||||
#: ./cli/app/list.go:319 ./cli/app/move.go:34 ./cli/app/ps.go:205 ./cli/app/secret.go:553 ./cli/app/secret.go:649 ./cli/recipe/list.go:104 ./cli/recipe/upgrade.go:436 ./cli/recipe/version.go:139 ./cli/run.go:152 ./cli/server/list.go:106
|
||||
msgid "m"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/list.go:318 ./cli/app/ps.go:204 ./cli/app/secret.go:552 ./cli/app/secret.go:648 ./cli/recipe/list.go:103 ./cli/recipe/upgrade.go:399 ./cli/recipe/version.go:138 ./cli/server/list.go:105
|
||||
#: ./cli/app/list.go:318 ./cli/app/ps.go:204 ./cli/app/secret.go:552 ./cli/app/secret.go:648 ./cli/recipe/list.go:103 ./cli/recipe/upgrade.go:435 ./cli/recipe/version.go:138 ./cli/server/list.go:105
|
||||
msgid "machine"
|
||||
msgstr ""
|
||||
|
||||
@@ -3547,7 +3581,7 @@ msgstr ""
|
||||
msgid "main app service version for %s is empty?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:66 ./cli/internal/recipe.go:80 ./cli/recipe/release.go:631 ./cli/recipe/upgrade.go:375
|
||||
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:66 ./cli/internal/recipe.go:80 ./cli/recipe/release.go:631 ./cli/recipe/upgrade.go:411
|
||||
msgid "major"
|
||||
msgstr ""
|
||||
|
||||
@@ -3576,7 +3610,7 @@ msgstr ""
|
||||
msgid "migrating app config from %s to %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:68 ./cli/internal/recipe.go:82 ./cli/recipe/release.go:639 ./cli/recipe/upgrade.go:383
|
||||
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:68 ./cli/internal/recipe.go:82 ./cli/recipe/release.go:639 ./cli/recipe/upgrade.go:419
|
||||
msgid "minor"
|
||||
msgstr ""
|
||||
|
||||
@@ -3627,7 +3661,7 @@ msgstr ""
|
||||
msgid "name"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:141
|
||||
#: ./pkg/lint/recipe.go:148
|
||||
msgid "name a servce 'app'"
|
||||
msgstr ""
|
||||
|
||||
@@ -3705,7 +3739,7 @@ msgstr ""
|
||||
msgid "no .git directory in %s?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:29
|
||||
#: ./pkg/recipe/compose.go:32
|
||||
#, c-format
|
||||
msgid "no COMPOSE_FILE detected, loading default: %s"
|
||||
msgstr ""
|
||||
@@ -3731,7 +3765,7 @@ msgstr ""
|
||||
msgid "no changes discovered in %s, nothing to publish?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:361
|
||||
#: ./cli/recipe/upgrade.go:390
|
||||
msgid "no changes, skip creating commit"
|
||||
msgstr ""
|
||||
|
||||
@@ -3756,7 +3790,7 @@ msgstr ""
|
||||
msgid "no domain provided"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:248
|
||||
#: ./pkg/recipe/compose.go:389
|
||||
msgid "no existing label found, automagic insertion not supported yet"
|
||||
msgstr ""
|
||||
|
||||
@@ -3765,7 +3799,7 @@ msgstr ""
|
||||
msgid "no git tags found for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:182
|
||||
#: ./cli/recipe/upgrade.go:183
|
||||
#, c-format
|
||||
msgid "no new versions available for %s, assuming %s is the latest (use -a/--all-tags to see all anyway)"
|
||||
msgstr ""
|
||||
@@ -3897,12 +3931,12 @@ msgstr ""
|
||||
msgid "not requesting a remote TTY"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:305
|
||||
#: ./cli/recipe/upgrade.go:334
|
||||
#, c-format
|
||||
msgid "not upgrading %s, skipping as requested"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:244
|
||||
#: ./cli/recipe/upgrade.go:247
|
||||
#, c-format
|
||||
msgid "not upgrading from %s to %s for %s, because the upgrade type is more serious than what user wants"
|
||||
msgstr ""
|
||||
@@ -3928,11 +3962,11 @@ msgstr ""
|
||||
msgid "one of <src>/<dest> arguments must take $SERVICE:$PATH form"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:140
|
||||
#: ./pkg/lint/recipe.go:147
|
||||
msgid "one service named 'app'"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:176
|
||||
#: ./pkg/lint/recipe.go:183
|
||||
msgid "only annotated tags used for recipe version"
|
||||
msgstr ""
|
||||
|
||||
@@ -3956,16 +3990,16 @@ msgstr ""
|
||||
msgid "p"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:460
|
||||
msgctxt "pin container image by digest"
|
||||
msgid "p"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:163
|
||||
#, c-format
|
||||
msgid "parsed %s for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:159
|
||||
#, c-format
|
||||
msgid "parsed %s from %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/cmd.go:134 ./cli/app/cmd.go:180
|
||||
#, c-format
|
||||
msgid "parsed following command arguments: %s"
|
||||
@@ -3999,7 +4033,7 @@ msgstr ""
|
||||
msgid "pass command not found on $PATH, is it installed?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:70 ./cli/internal/recipe.go:84 ./cli/recipe/release.go:647 ./cli/recipe/upgrade.go:391
|
||||
#: ./cli/internal/recipe.go:48 ./cli/internal/recipe.go:70 ./cli/internal/recipe.go:84 ./cli/recipe/release.go:647 ./cli/recipe/upgrade.go:427
|
||||
msgid "patch"
|
||||
msgstr ""
|
||||
|
||||
@@ -4015,6 +4049,14 @@ msgstr ""
|
||||
msgid "perform action without further prompt"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:462
|
||||
msgid "pin the container image version by manifest digest (image:tag@sha256:...)"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:459
|
||||
msgid "pindigests"
|
||||
msgstr ""
|
||||
|
||||
#. translators: `abra app env pull` aliases. use a comma separated list of
|
||||
#. aliases with no spaces in between
|
||||
#: ./cli/app/env.go:39
|
||||
@@ -4053,7 +4095,7 @@ msgstr ""
|
||||
msgid "prefer offline & filesystem access"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/list.go:321 ./cli/app/ps.go:207 ./cli/app/secret.go:555 ./cli/app/secret.go:651 ./cli/recipe/list.go:106 ./cli/recipe/upgrade.go:402 ./cli/recipe/version.go:141 ./cli/server/list.go:108
|
||||
#: ./cli/app/list.go:321 ./cli/app/ps.go:207 ./cli/app/secret.go:555 ./cli/app/secret.go:651 ./cli/recipe/list.go:106 ./cli/recipe/upgrade.go:438 ./cli/recipe/version.go:141 ./cli/server/list.go:108
|
||||
msgid "print machine-readable output"
|
||||
msgstr ""
|
||||
|
||||
@@ -4206,7 +4248,7 @@ msgstr ""
|
||||
msgid "recipe %s does not exist?"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:212
|
||||
#: ./pkg/lint/recipe.go:219
|
||||
#, c-format
|
||||
msgid "recipe '%s' failed lint checks:\n"
|
||||
"%s"
|
||||
@@ -4230,6 +4272,11 @@ msgstr ""
|
||||
msgid "ref"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/client/registry.go:59
|
||||
#, c-format
|
||||
msgid "registry did not return a digest for %s:%s"
|
||||
msgstr ""
|
||||
|
||||
#. translators: `recipe release` command
|
||||
#: ./cli/recipe/release.go:35
|
||||
msgid "release <recipe> [version] [flags]"
|
||||
@@ -4328,6 +4375,11 @@ msgstr ""
|
||||
msgid "removing configs failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:122
|
||||
#, c-format
|
||||
msgid "removing digest from %s for %s, use --pindigests to pin the new tag"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/upstream/stack/remove.go:148
|
||||
#, c-format
|
||||
msgid "removing network %s"
|
||||
@@ -4353,7 +4405,7 @@ msgstr ""
|
||||
msgid "removing volumes failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:177
|
||||
#: ./pkg/lint/recipe.go:184
|
||||
msgid "replace lightweight tag with annotated tag"
|
||||
msgstr ""
|
||||
|
||||
@@ -4436,7 +4488,7 @@ msgstr ""
|
||||
msgid "retrieved %s as target container on %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:59
|
||||
#: ./pkg/recipe/compose.go:62
|
||||
#, c-format
|
||||
msgid "retrieved %s configs for %s"
|
||||
msgstr ""
|
||||
@@ -4607,7 +4659,7 @@ msgstr ""
|
||||
msgid "secret %s is > %d chars when combined with %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:481
|
||||
#: ./pkg/lint/recipe.go:510
|
||||
#, c-format
|
||||
msgid "secret %s is longer than 12 characters"
|
||||
msgstr ""
|
||||
@@ -4723,12 +4775,12 @@ msgstr ""
|
||||
msgid "serverAdd: cleanUp: unable to list files in %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:227
|
||||
#: ./cli/recipe/upgrade.go:230
|
||||
#, c-format
|
||||
msgid "service %s is at version %s, but pinned to %s, please correct your compose.yml file manually!"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:223
|
||||
#: ./cli/recipe/upgrade.go:226
|
||||
#, c-format
|
||||
msgid "service %s, image %s pinned to %s, no compatible upgrade found"
|
||||
msgstr ""
|
||||
@@ -4969,7 +5021,7 @@ msgstr ""
|
||||
msgid "store secrets in a local pass store"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/formatter/formatter.go:238
|
||||
#: ./pkg/formatter/formatter.go:239
|
||||
#, c-format
|
||||
msgid "stripped %s to %s for parsing"
|
||||
msgstr ""
|
||||
@@ -5007,7 +5059,7 @@ msgstr ""
|
||||
msgid "sync [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:243
|
||||
#: ./pkg/recipe/compose.go:384
|
||||
#, c-format
|
||||
msgid "synced label %s to service %s"
|
||||
msgstr ""
|
||||
@@ -5025,7 +5077,12 @@ msgstr ""
|
||||
msgid "tag at commit %s is unannotated or otherwise broken"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:301
|
||||
#: ./cli/recipe/upgrade.go:327
|
||||
#, c-format
|
||||
msgid "tag upgraded and pinned from %s to %s@%s for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:329
|
||||
#, c-format
|
||||
msgid "tag upgraded from %s to %s for %s"
|
||||
msgstr ""
|
||||
@@ -5099,7 +5156,7 @@ msgstr ""
|
||||
msgid "toggle non-interactive mode"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:154
|
||||
#: ./pkg/lint/recipe.go:161
|
||||
msgid "traefik routing enabled"
|
||||
msgstr ""
|
||||
|
||||
@@ -5212,7 +5269,7 @@ msgstr ""
|
||||
msgid "unable to determine server of app %s, please pass --server/-s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:252
|
||||
#: ./cli/recipe/upgrade.go:258
|
||||
#, c-format
|
||||
msgid "unable to determine versioning semantics of %s, listing all tags"
|
||||
msgstr ""
|
||||
@@ -5227,6 +5284,11 @@ msgstr ""
|
||||
msgid "unable to fetch tags in %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:238 ./pkg/recipe/compose.go:245 ./pkg/recipe/compose.go:255 ./pkg/recipe/compose.go:260
|
||||
#, c-format
|
||||
msgid "unable to find image %s for %s, please update it manually"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/move.go:301
|
||||
#, c-format
|
||||
msgid "unable to get container matching %s: %s"
|
||||
@@ -5247,7 +5309,7 @@ msgstr ""
|
||||
msgid "unable to inspect container for %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:496
|
||||
#: ./pkg/lint/recipe.go:525
|
||||
#, c-format
|
||||
msgid "unable to list local tags for %s"
|
||||
msgstr ""
|
||||
@@ -5261,7 +5323,7 @@ msgstr ""
|
||||
msgid "unable to look up server context for %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:491 ./pkg/recipe/git.go:243
|
||||
#: ./cli/recipe/fetch.go:77 ./pkg/git/read.go:26 ./pkg/lint/recipe.go:520 ./pkg/recipe/git.go:243
|
||||
#, c-format
|
||||
msgid "unable to open %s: %s"
|
||||
msgstr ""
|
||||
@@ -5291,11 +5353,6 @@ msgstr ""
|
||||
msgid "unable to parse %s, error was: %s, skipping upgrade for %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:153
|
||||
#, c-format
|
||||
msgid "unable to parse %s, skipping"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/list.go:167
|
||||
#, c-format
|
||||
msgid "unable to parse %s, skipping as upgrade option"
|
||||
@@ -5311,6 +5368,11 @@ msgstr ""
|
||||
msgid "unable to parse deploy version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:305
|
||||
#, c-format
|
||||
msgid "unable to parse image %s: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/run.go:176
|
||||
#, c-format
|
||||
msgid "unable to proceed, %s does not exist?"
|
||||
@@ -5518,7 +5580,7 @@ msgstr ""
|
||||
msgid "updating %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/recipe/compose.go:171 ./pkg/recipe/compose.go:237
|
||||
#: ./pkg/recipe/compose.go:203 ./pkg/recipe/compose.go:378
|
||||
#, c-format
|
||||
msgid "updating %s to %s in %s"
|
||||
msgstr ""
|
||||
@@ -5543,17 +5605,17 @@ msgstr ""
|
||||
msgid "upgrade [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:248
|
||||
#: ./cli/recipe/upgrade.go:254
|
||||
#, c-format
|
||||
msgid "upgrade to which tag? (service: %s, image: %s, tag: %s)"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:254
|
||||
#: ./cli/recipe/upgrade.go:260
|
||||
#, c-format
|
||||
msgid "upgrade to which tag? (service: %s, tag: %s)"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/upgrade.go:221
|
||||
#: ./cli/recipe/upgrade.go:224
|
||||
#, c-format
|
||||
msgid "upgrading service %s from %s to %s (pinned tag: %s)"
|
||||
msgstr ""
|
||||
@@ -5562,6 +5624,10 @@ msgstr ""
|
||||
msgid "upload your recipe to git.coopcloud.tech/coop-cloud/..."
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:132
|
||||
msgid "use a digest for all images"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:83
|
||||
msgid "use a tag for all images"
|
||||
msgstr ""
|
||||
@@ -5612,7 +5678,7 @@ msgstr ""
|
||||
msgid "validated %s as server argument"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:170
|
||||
#: ./pkg/lint/recipe.go:177
|
||||
msgid "vendor config versions in an abra.sh"
|
||||
msgstr ""
|
||||
|
||||
@@ -5763,7 +5829,7 @@ msgstr ""
|
||||
msgid "waiting for %d task(s) to reach terminal state"
|
||||
msgstr ""
|
||||
|
||||
#: ./pkg/lint/recipe.go:21 ./pkg/lint/recipe.go:67 ./pkg/lint/recipe.go:74 ./pkg/lint/recipe.go:81 ./pkg/lint/recipe.go:88 ./pkg/lint/recipe.go:95 ./pkg/lint/recipe.go:102 ./pkg/lint/recipe.go:109 ./pkg/lint/recipe.go:116 ./pkg/lint/recipe.go:123
|
||||
#: ./pkg/lint/recipe.go:21 ./pkg/lint/recipe.go:67 ./pkg/lint/recipe.go:74 ./pkg/lint/recipe.go:81 ./pkg/lint/recipe.go:88 ./pkg/lint/recipe.go:95 ./pkg/lint/recipe.go:102 ./pkg/lint/recipe.go:109 ./pkg/lint/recipe.go:116 ./pkg/lint/recipe.go:123 ./pkg/lint/recipe.go:130
|
||||
msgid "warn"
|
||||
msgstr ""
|
||||
|
||||
@@ -5806,11 +5872,11 @@ msgstr ""
|
||||
msgid "writing recipe version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/release.go:632 ./cli/recipe/upgrade.go:376
|
||||
#: ./cli/recipe/release.go:632 ./cli/recipe/upgrade.go:412
|
||||
msgid "x"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/release.go:640 ./cli/recipe/upgrade.go:384
|
||||
#: ./cli/recipe/release.go:640 ./cli/recipe/upgrade.go:420
|
||||
msgid "y"
|
||||
msgstr ""
|
||||
|
||||
@@ -5822,7 +5888,7 @@ msgstr ""
|
||||
msgid "you can only use one version flag: --major, --minor or --patch"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/recipe/release.go:648 ./cli/recipe/upgrade.go:392
|
||||
#: ./cli/recipe/release.go:648 ./cli/recipe/upgrade.go:428
|
||||
msgid "z"
|
||||
msgstr ""
|
||||
|
||||
|
||||
Binary file not shown.
+672
-311
File diff suppressed because it is too large
Load Diff
@@ -125,6 +125,13 @@ var LintRules = map[string][]LintRule{
|
||||
HowToResolve: i18n.G("reduce length of secret names to 12 chars"),
|
||||
Function: LintSecretLengths,
|
||||
},
|
||||
{
|
||||
Ref: "R016",
|
||||
Level: i18n.G("warn"),
|
||||
Description: i18n.G("all images use a digest"),
|
||||
HowToResolve: i18n.G("use a digest for all images"),
|
||||
Function: LintAllImagesTaggedWithDigest,
|
||||
},
|
||||
},
|
||||
"error": {
|
||||
{
|
||||
@@ -333,6 +340,28 @@ func LintAllImagesTagged(recipe recipe.Recipe) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func LintAllImagesTaggedWithDigest(recipe recipe.Recipe) (bool, error) {
|
||||
config, err := recipe.GetComposeConfig(nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, service := range config.Services {
|
||||
img, err := reference.ParseNormalizedNamed(service.Image)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if reference.IsNameOnly(img) {
|
||||
return false, nil
|
||||
}
|
||||
_, ok := img.(reference.Digested)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func LintNoUnstableTags(recipe recipe.Recipe) (bool, error) {
|
||||
config, err := recipe.GetComposeConfig(nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package recipe
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
gitPkg "coopcloud.tech/abra/pkg/git"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
// Use the test recipe at the given commit, in a temporary recipes directory.
|
||||
func setupTestRecipe(t *testing.T, commit string) Recipe {
|
||||
t.Helper()
|
||||
// Other tests in this package use the real RECIPES_DIR.
|
||||
recipesDir := config.RECIPES_DIR
|
||||
config.RECIPES_DIR = t.TempDir()
|
||||
t.Cleanup(func() { config.RECIPES_DIR = recipesDir })
|
||||
|
||||
r := Get("abra-test-recipe")
|
||||
if err := gitPkg.Clone(r.Dir, "https://git.coopcloud.tech/toolshed/abra-test-recipe"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := r.EnsureVersion(commit); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestGetRecipeVersionsIncludesDigest(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping e2e test in short mode")
|
||||
}
|
||||
// https://git.coopcloud.tech/toolshed/abra-test-recipe/commit/4f0642719d2e2a568f4350cf7eea182438d58be3
|
||||
r := setupTestRecipe(t, "4f0642719d2e2a568f4350cf7eea182438d58be3")
|
||||
repo, err := git.PlainOpen(r.Dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for version, commit := range map[string]string{
|
||||
"9.0.0+1.21.5": "4f0642719d2e2a568f4350cf7eea182438d58be3",
|
||||
"9.0.1+1.21.6": "77d752b3c61a71bfb2c45f40ec07c9d626a0e580",
|
||||
} {
|
||||
if _, err := repo.CreateTag(version, plumbing.NewHash(commit), nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
versions, _, err := r.GetRecipeVersions()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
found := map[string]ServiceMeta{}
|
||||
for _, version := range versions {
|
||||
for tag, services := range version {
|
||||
if strings.HasPrefix(tag, "9.0.") {
|
||||
found[tag] = services["app"]
|
||||
}
|
||||
}
|
||||
}
|
||||
pinned := found["9.0.0+1.21.5"]
|
||||
if pinned.Tag != "1.21.5" || pinned.Digest != "sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31" {
|
||||
t.Fatalf("got pinned metadata %+v", pinned)
|
||||
}
|
||||
unpinned := found["9.0.1+1.21.6"]
|
||||
if unpinned.Tag != "1.21.6" || unpinned.Digest != "" {
|
||||
t.Fatalf("got unpinned metadata %+v", unpinned)
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
meta ServiceMeta
|
||||
wantDigest bool
|
||||
}{{pinned, true}, {unpinned, false}} {
|
||||
encoded, err := json.Marshal(tc.meta)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(encoded), `"digest"`) != tc.wantDigest {
|
||||
t.Fatalf("unexpected JSON %s", encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
-13
@@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
@@ -110,10 +111,23 @@ func (r Recipe) GetVersionLabelLocal() (string, error) {
|
||||
return label, nil
|
||||
}
|
||||
|
||||
var sha256Digest = regexp.MustCompile(`^sha256:[a-f0-9]{64}$`)
|
||||
|
||||
// UpdateTag updates an image tag in-place on file system local compose files.
|
||||
func (r Recipe) UpdateTag(image, tag string) (bool, error) {
|
||||
// A non-empty digest pins the tag (image:tag@sha256:...).
|
||||
func (r Recipe) UpdateTag(image, tag, digest string) (bool, error) {
|
||||
// Optional parameter, but must be a valid sha256 digest if provided.
|
||||
if digest != "" && !sha256Digest.MatchString(digest) {
|
||||
return false, errors.New(i18n.G("invalid digest %s, want sha256:<64 hex digits>", digest))
|
||||
}
|
||||
|
||||
fullPattern := fmt.Sprintf("%s/compose**yml", r.Dir)
|
||||
image = formatter.StripTagMeta(image)
|
||||
source, parseErr := reference.ParseNormalizedNamed(image)
|
||||
_, exactImage := source.(reference.NamedTagged)
|
||||
if parseErr != nil {
|
||||
return false, parseErr
|
||||
}
|
||||
imageName := formatter.StripTagMeta(reference.Path(source))
|
||||
|
||||
composeFiles, err := filepath.Glob(fullPattern)
|
||||
if err != nil {
|
||||
@@ -122,6 +136,7 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) {
|
||||
|
||||
log.Debug(i18n.G("considering %s config(s) for tag update", strings.Join(composeFiles, ", ")))
|
||||
|
||||
updated := false
|
||||
for _, composeFile := range composeFiles {
|
||||
opts := stack.Deploy{Composefiles: []string{composeFile}}
|
||||
|
||||
@@ -140,44 +155,58 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) {
|
||||
continue // may be a compose.$optional.yml file
|
||||
}
|
||||
|
||||
img, _ := reference.ParseNormalizedNamed(service.Image)
|
||||
img, err := reference.ParseNormalizedNamed(formatter.RemoveSha(service.Image))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var composeTag string
|
||||
switch img.(type) {
|
||||
case reference.NamedTagged:
|
||||
composeTag = img.(reference.NamedTagged).Tag()
|
||||
default:
|
||||
log.Debug(i18n.G("unable to parse %s, skipping", img))
|
||||
continue
|
||||
}
|
||||
|
||||
// Found image that needs replacing
|
||||
composeImage := formatter.StripTagMeta(reference.Path(img))
|
||||
|
||||
log.Debug(i18n.G("parsed %s from %s", composeTag, service.Image))
|
||||
|
||||
if image == composeImage {
|
||||
if imageName == composeImage && (!exactImage || service.Image == image) {
|
||||
bytes, err := ioutil.ReadFile(composeFile)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
old := fmt.Sprintf("%s:%s", composeImage, composeTag)
|
||||
new := fmt.Sprintf("%s:%s", composeImage, tag)
|
||||
replacedBytes := strings.Replace(string(bytes), old, new, -1)
|
||||
old := service.Image
|
||||
tagged, err := reference.WithTag(reference.TrimNamed(img), tag)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
new := reference.FamiliarString(tagged)
|
||||
if digest != "" {
|
||||
new += "@" + digest
|
||||
}
|
||||
if old == new {
|
||||
continue
|
||||
}
|
||||
|
||||
// Don't confuse digests if the image isn't an exact match. E.g. if old is postgres:13, don't
|
||||
// match postgres:13-alpine
|
||||
pattern := regexp.MustCompile(`(?m)(^\s*image:\s*["']?)` + regexp.QuoteMeta(old) + `(["']?\s*(?:#.*)?$)`)
|
||||
replacedBytes := pattern.ReplaceAllString(string(bytes), "${1}"+new+"${2}")
|
||||
if replacedBytes == string(bytes) {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug(i18n.G("updating %s to %s in %s", old, new, compose.Filename))
|
||||
|
||||
if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil {
|
||||
return false, err
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
// UpdateLabel updates a label in-place on file system local compose files.
|
||||
|
||||
+7
-6
@@ -164,7 +164,7 @@ func (r Recipe) EnsureVersion(version string) (bool, error) {
|
||||
|
||||
hash, err := repo.ResolveRevision(plumbing.Revision(version))
|
||||
if err != nil {
|
||||
log.Fatal(i18n.G("unable to resolve '%s': %s", version, err))
|
||||
return isChaosCommit, errors.New(i18n.G("unable to resolve '%s': %s", version, err))
|
||||
}
|
||||
|
||||
opts = &git.CheckoutOptions{Hash: *hash, Create: false, Force: true}
|
||||
@@ -175,11 +175,11 @@ func (r Recipe) EnsureVersion(version string) (bool, error) {
|
||||
|
||||
worktree, err := repo.Worktree()
|
||||
if err != nil {
|
||||
return isChaosCommit, nil
|
||||
return isChaosCommit, err
|
||||
}
|
||||
|
||||
if err := worktree.Checkout(opts); err != nil {
|
||||
return isChaosCommit, nil
|
||||
return isChaosCommit, err
|
||||
}
|
||||
|
||||
log.Debug(i18n.G("successfully checked %s out to %s in %s", r.Name, tagRef.Short(), r.Dir))
|
||||
@@ -452,10 +452,11 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
versionMeta[service.Name] = ServiceMeta{
|
||||
Image: path,
|
||||
Tag: tag,
|
||||
meta := ServiceMeta{Image: path, Tag: tag}
|
||||
if pinned, ok := img.(reference.Digested); ok {
|
||||
meta.Digest = pinned.Digest().String()
|
||||
}
|
||||
versionMeta[service.Name] = meta
|
||||
}
|
||||
|
||||
versions = append(versions, map[string]map[string]ServiceMeta{tag: versionMeta})
|
||||
|
||||
@@ -39,8 +39,9 @@ type service = string
|
||||
|
||||
// ServiceMeta represents meta info associated with a service.
|
||||
type ServiceMeta struct {
|
||||
Image string `json:"image"`
|
||||
Tag string `json:"tag"`
|
||||
Image string `json:"image"`
|
||||
Tag string `json:"tag"`
|
||||
Digest string `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
// RecipeVersions are the versions associated with a recipe.
|
||||
|
||||
@@ -702,3 +702,65 @@ teardown(){
|
||||
assert_failure
|
||||
assert_output --partial "unexpected character"
|
||||
}
|
||||
|
||||
# Deploy pinned 1.21.5, upgrade to pinned 1.21.6, then roll back to 1.21.5.
|
||||
# bats test_tags=slow
|
||||
@test "deploy, upgrade and rollback digest-pinned images" {
|
||||
recipeDir="$ABRA_DIR/recipes/$TEST_RECIPE"
|
||||
service="${TEST_APP_DOMAIN//./_}_app"
|
||||
|
||||
# Manually upgrade the unpinned 0.3.5 recipe to 1.21.5 and pin it.
|
||||
run git -C "$recipeDir" checkout "0.3.5+1.21.0"
|
||||
assert_success
|
||||
run docker pull nginx:1.21.5
|
||||
assert_success
|
||||
run docker image inspect --format '{{index .RepoDigests 0}}' nginx:1.21.5
|
||||
assert_success
|
||||
oldImage="nginx:1.21.5@${output#*@}"
|
||||
run sed -i "s|image: nginx:1.21.0|image: $oldImage|" "$recipeDir/compose.yml"
|
||||
assert_success
|
||||
|
||||
# Label and tag the local recipe release so app commands can track it.
|
||||
run sed -i 's/version=0.3.5+1.21.0/version=0.4.0+1.21.5/' "$recipeDir/compose.yml"
|
||||
assert_success
|
||||
_git_commit
|
||||
run git -C "$recipeDir" tag "0.4.0+1.21.5"
|
||||
assert_success
|
||||
|
||||
# Manually upgrade and pin the next release to 1.21.6.
|
||||
run docker pull nginx:1.21.6
|
||||
assert_success
|
||||
run docker image inspect --format '{{index .RepoDigests 0}}' nginx:1.21.6
|
||||
assert_success
|
||||
newImage="nginx:1.21.6@${output#*@}"
|
||||
run sed -i "s|image: $oldImage|image: $newImage|" "$recipeDir/compose.yml"
|
||||
assert_success
|
||||
|
||||
# Update the release label to match the next recipe tag.
|
||||
run sed -i 's/version=0.4.0+1.21.5/version=0.4.1+1.21.6/' "$recipeDir/compose.yml"
|
||||
assert_success
|
||||
_git_commit
|
||||
run git -C "$recipeDir" tag "0.4.1+1.21.6"
|
||||
assert_success
|
||||
|
||||
# Deploy recipe 0.4.0 with the exact pinned 1.21.5 image.
|
||||
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.4.0+1.21.5" --offline --no-input --no-converge-checks
|
||||
assert_success
|
||||
run docker service inspect --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' "$service"
|
||||
assert_success
|
||||
assert_output "$oldImage"
|
||||
|
||||
# Upgrade to recipe 0.4.1 and the exact pinned 1.21.6 image.
|
||||
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.4.1+1.21.6" --offline --no-input --no-converge-checks
|
||||
assert_success
|
||||
run docker service inspect --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' "$service"
|
||||
assert_success
|
||||
assert_output "$newImage"
|
||||
|
||||
# Roll back to recipe 0.4.0 and its original pinned image.
|
||||
run $ABRA app rollback "$TEST_APP_DOMAIN" "0.4.0+1.21.5" --offline --no-input --no-converge-checks
|
||||
assert_success
|
||||
run docker service inspect --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' "$service"
|
||||
assert_success
|
||||
assert_output "$oldImage"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user