forked from toolshed/abra
Compare commits
22 Commits
devbox
...
type_remote
| Author | SHA1 | Date | |
|---|---|---|---|
| cd25c346c1 | |||
| 0397443de3 | |||
| 22cd3e1307 | |||
| 0ed1881374 | |||
| 31fe2fe2a6 | |||
| 0dec68294f | |||
| 69ba548dab | |||
| cfa9ff15bb | |||
| ddf7441152 | |||
| 20b4a3fb84 | |||
| 81f4420a67 | |||
| b13b10be9a | |||
| 8137e56d1e | |||
| f3943d25d1 | |||
| 9859ff46f5 | |||
| 3109a0da82 | |||
| b1146a707e | |||
| 6d13d14d75 | |||
| 427ec22cab | |||
| 1111b69f12 | |||
| 1541e6aa6a | |||
| 14ee80582a |
+11
-6
@@ -8,12 +8,17 @@ steps:
|
||||
- make check
|
||||
|
||||
- name: xgettext-go
|
||||
image: git.coopcloud.tech/toolshed/drone-xgettext-go:latest
|
||||
settings:
|
||||
keyword: i18n.G
|
||||
keyword_ctx: i18n.GC
|
||||
out: pkg/i18n/locales/abra.pot
|
||||
comments_tag: translators
|
||||
image: golang:1.26
|
||||
environment:
|
||||
GOPRIVATE: coopcloud.tech
|
||||
commands:
|
||||
- go run git.coopcloud.tech/toolshed/xgettext-go@latest
|
||||
-o pkg/i18n/locales/abra.pot
|
||||
--keyword=i18n.G
|
||||
--keyword-ctx=i18n.GC
|
||||
--sort-output
|
||||
--add-comments-tag="translators"
|
||||
$(find . -name "*.go" -not -path "*vendor*" | sort)
|
||||
depends_on:
|
||||
- make check
|
||||
when:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ABRA := ./cmd/abra
|
||||
XGETTEXT := ./bin/xgettext-go
|
||||
XGETTEXT := go run git.coopcloud.tech/toolshed/xgettext-go@latest
|
||||
COMMIT := $(shell git rev-list -1 HEAD)
|
||||
GOPATH := $(shell go env GOPATH)
|
||||
GOVERSION := 1.26
|
||||
@@ -62,8 +62,8 @@ update-po:
|
||||
done
|
||||
|
||||
.PHONY: update-pot
|
||||
update-pot: $(XGETTEXT)
|
||||
@${XGETTEXT} \
|
||||
update-pot:
|
||||
@$(XGETTEXT) \
|
||||
-o pkg/i18n/locales/$(DOMAIN).pot \
|
||||
--keyword=i18n.G \
|
||||
--keyword-ctx=i18n.GC \
|
||||
@@ -71,11 +71,6 @@ update-pot: $(XGETTEXT)
|
||||
--add-comments-tag="translators" \
|
||||
$$(find . -name "*.go" -not -path "*vendor*" | sort)
|
||||
|
||||
${XGETTEXT}:
|
||||
@mkdir -p ./bin && \
|
||||
wget -O ./bin/xgettext-go https://git.coopcloud.tech/toolshed/xgettext-go/raw/branch/main/xgettext-go && \
|
||||
chmod +x ./bin/xgettext-go
|
||||
|
||||
.PHONY: update-pot-po-metadata
|
||||
update-pot-po-metadata:
|
||||
@sed -i "s/charset=CHARSET/charset=UTF-8/g" pkg/i18n/locales/*.po pkg/i18n/locales/*.pot
|
||||
|
||||
+6
-5
@@ -151,11 +151,12 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
SendRegistryAuth: true,
|
||||
}
|
||||
compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env)
|
||||
if err != nil {
|
||||
|
||||
+7
-1
@@ -222,7 +222,13 @@ synchronize your local app environment values with what is deployed live.`),
|
||||
mergedEnv[k] = v
|
||||
}
|
||||
|
||||
if !strings.Contains(recipeEnvVar, ":") {
|
||||
// The recipe version is always carried by TYPE (RECIPE, when present,
|
||||
// is a bare source pointer). Stamp the deployed version onto TYPE and
|
||||
// leave RECIPE untouched so the two never drift apart.
|
||||
if t, ok := mergedEnv["TYPE"]; ok {
|
||||
name, _, _ := strings.Cut(t, ":")
|
||||
mergedEnv["TYPE"] = fmt.Sprintf("%s:%s", name, version)
|
||||
} else {
|
||||
mergedEnv[recipeKey] = fmt.Sprintf("%s:%s", mergedEnv[recipeKey], version)
|
||||
}
|
||||
|
||||
|
||||
+17
-1
@@ -32,6 +32,18 @@ deploy <domain>" to do so.
|
||||
You can see what recipes are available (i.e. values for the [recipe] argument)
|
||||
by running "abra recipe ls".
|
||||
|
||||
In addition to short catalogue names, [recipe] also accepts arbitrary git
|
||||
URLs to use a recipe from outside the catalogue (e.g. a fork or work in
|
||||
progress). Any of these forms is accepted:
|
||||
|
||||
abra app new git.example.com/user/recipe
|
||||
abra app new https://git.example.com/user/recipe
|
||||
abra app new git@git.example.com:user/recipe
|
||||
|
||||
In that case a RECIPE=<canonical name> line is written to the app's .env
|
||||
file so a subsequent "abra app deploy" (on this or another machine) will
|
||||
re-fetch the recipe from the same git source.
|
||||
|
||||
Recipe commit hashes are supported values for "[version]".
|
||||
|
||||
Passing the "--secrets/-S" flag will automatically generate secrets for your
|
||||
@@ -295,7 +307,7 @@ func ensureDomainFlag(recipe recipePkg.Recipe, server string) error {
|
||||
if appDomain == "" && !internal.NoInput {
|
||||
prompt := &survey.Input{
|
||||
Message: i18n.G("Specify app domain"),
|
||||
Default: fmt.Sprintf("%s.%s", recipe.Name, server),
|
||||
Default: fmt.Sprintf("%s.%s", recipe.ShortName(), server),
|
||||
}
|
||||
if err := survey.AskOne(prompt, &appDomain); err != nil {
|
||||
return err
|
||||
@@ -306,6 +318,10 @@ func ensureDomainFlag(recipe recipePkg.Recipe, server string) error {
|
||||
return errors.New(i18n.G("no domain provided"))
|
||||
}
|
||||
|
||||
if strings.ContainsAny(appDomain, "/\\") {
|
||||
return errors.New(i18n.G("invalid domain '%s': must not contain '/' or '\\'", appDomain))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+12
-5
@@ -166,11 +166,12 @@ beforehand. See "abra app backup" for more.`),
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
SendRegistryAuth: true,
|
||||
}
|
||||
|
||||
compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env)
|
||||
@@ -187,6 +188,12 @@ beforehand. See "abra app backup" for more.`),
|
||||
appPkg.SetChaosVersionLabel(compose, stackName, chosenDowngrade)
|
||||
}
|
||||
|
||||
// NOTE: stamp the deployed version label ourselves rather than relying
|
||||
// on the value baked into the recipe's compose.yml. Git-URL/WIP recipes
|
||||
// often carry a stale or missing label, which would otherwise leave the
|
||||
// live deployment reporting the wrong version after a rollback.
|
||||
appPkg.SetVersionLabel(compose, stackName, chosenDowngrade)
|
||||
|
||||
// Gather secrets
|
||||
secretInfo, err := deploy.GatherSecretsForDeploy(cl, app, internal.ShowUnchanged)
|
||||
if err != nil {
|
||||
|
||||
+30
-9
@@ -178,11 +178,12 @@ beforehand. See "abra app backup" for more.`),
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
SendRegistryAuth: true,
|
||||
}
|
||||
|
||||
compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env)
|
||||
@@ -199,6 +200,12 @@ beforehand. See "abra app backup" for more.`),
|
||||
appPkg.SetChaosVersionLabel(compose, stackName, chosenUpgrade)
|
||||
}
|
||||
|
||||
// NOTE: stamp the deployed version label ourselves rather than relying
|
||||
// on the value baked into the recipe's compose.yml. Git-URL/WIP recipes
|
||||
// often carry a stale or missing label, which would otherwise leave the
|
||||
// live deployment reporting the wrong version after an upgrade.
|
||||
appPkg.SetVersionLabel(compose, stackName, chosenUpgrade)
|
||||
|
||||
envVars, err := appPkg.CheckEnv(app)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -345,9 +352,17 @@ func getReleaseNotes(
|
||||
return errors.New(i18n.G("parsing chosen upgrade version failed: %s", err))
|
||||
}
|
||||
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return errors.New(i18n.G("parsing deployment version failed: %s", err))
|
||||
// The deployed version can be "unknown" (e.g. deployed without a version
|
||||
// label, as is common for git-URL/work-in-progress recipes). In that case
|
||||
// there is no lower bound to filter by, so we gather notes for every
|
||||
// version below the chosen one instead of failing.
|
||||
var parsedDeployedVersion tagcmp.Tag
|
||||
haveDeployedVersion := deployMeta.Version != config.UNKNOWN_DEFAULT
|
||||
if haveDeployedVersion {
|
||||
parsedDeployedVersion, err = tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return errors.New(i18n.G("parsing deployment version failed: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
for _, version := range internal.SortVersionsDesc(versions) {
|
||||
@@ -356,7 +371,7 @@ func getReleaseNotes(
|
||||
return errors.New(i18n.G("parsing recipe version failed: %s", err))
|
||||
}
|
||||
|
||||
if parsedVersion.IsGreaterThan(parsedDeployedVersion) &&
|
||||
if (!haveDeployedVersion || parsedVersion.IsGreaterThan(parsedDeployedVersion)) &&
|
||||
parsedVersion.IsLessThan(parsedChosenUpgrade) {
|
||||
note, err := app.Recipe.GetReleaseNotes(version, app.Domain)
|
||||
if err != nil {
|
||||
@@ -419,6 +434,12 @@ func validateUpgradeVersionArg(
|
||||
return errors.New(i18n.G("'%s' is not a known version for %s", specificVersion, app.Recipe.Name))
|
||||
}
|
||||
|
||||
// Without a known deployed version we cannot tell whether the requested
|
||||
// version is an upgrade; trust the explicit choice rather than failing.
|
||||
if deployMeta.Version == config.UNKNOWN_DEFAULT {
|
||||
return nil
|
||||
}
|
||||
|
||||
parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version)
|
||||
if err != nil {
|
||||
return errors.New(i18n.G("'%s' is not a known version", deployMeta.Version))
|
||||
|
||||
@@ -59,9 +59,15 @@ func ValidateRecipe(args []string, cmdName string) recipe.Recipe {
|
||||
log.Fatal(i18n.G("no recipe name provided"))
|
||||
}
|
||||
|
||||
if _, ok := knownRecipes[recipeName]; !ok {
|
||||
if !strings.Contains(recipeName, "/") {
|
||||
log.Fatal(i18n.G("no recipe '%s' exists?", recipeName))
|
||||
recipeName = recipe.NormalizeRecipeName(recipeName)
|
||||
|
||||
lookupName := recipeName
|
||||
if i := strings.LastIndex(lookupName, ":"); i >= 0 {
|
||||
lookupName = lookupName[:i]
|
||||
}
|
||||
if _, ok := knownRecipes[lookupName]; !ok {
|
||||
if !strings.Contains(lookupName, "/") {
|
||||
log.Fatal(i18n.G("no recipe '%s' exists? pass a git URL (e.g. https://git.example.com/user/recipe) to use a recipe outside the catalogue", lookupName))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ package recipe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"coopcloud.tech/abra/cli/internal"
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/formatter"
|
||||
"coopcloud.tech/abra/pkg/i18n"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
@@ -41,6 +43,7 @@ var RecipeListCommand = &cobra.Command{
|
||||
|
||||
headers := []string{
|
||||
i18n.G("name"),
|
||||
i18n.G("source"),
|
||||
i18n.G("category"),
|
||||
i18n.G("status"),
|
||||
i18n.G("healthcheck"),
|
||||
@@ -56,6 +59,7 @@ var RecipeListCommand = &cobra.Command{
|
||||
for _, recipe := range recipes {
|
||||
row := []string{
|
||||
recipe.Name,
|
||||
i18n.G("catalogue"),
|
||||
recipe.Category,
|
||||
strconv.Itoa(recipe.Features.Status),
|
||||
recipe.Features.Healthcheck,
|
||||
@@ -76,6 +80,23 @@ var RecipeListCommand = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
externals := externalRecipes(catl)
|
||||
sort.Strings(externals)
|
||||
for _, name := range externals {
|
||||
row := []string{
|
||||
name,
|
||||
i18n.G("external"),
|
||||
"-", "-", "-", "-", "-", "-", "-",
|
||||
}
|
||||
if pattern != "" {
|
||||
if !strings.Contains(name, pattern) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
table.Row(row...)
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
if len(rows) > 0 {
|
||||
if internal.MachineReadable {
|
||||
out, err := formatter.ToJSON(headers, rows)
|
||||
@@ -93,6 +114,30 @@ var RecipeListCommand = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
// externalRecipes returns canonical names of locally-cloned recipes that
|
||||
// were sourced from an arbitrary git URL (i.e. they carry a .abra-source
|
||||
// sidecar) and are not already listed in the catalogue.
|
||||
func externalRecipes(catl recipe.RecipeCatalogue) []string {
|
||||
dirs, err := recipe.GetRecipesLocal()
|
||||
if err != nil {
|
||||
log.Debug(i18n.G("can't read local recipes: %s", err))
|
||||
return nil
|
||||
}
|
||||
|
||||
var names []string
|
||||
for _, dir := range dirs {
|
||||
canonical := recipe.ReadRecipeSource(path.Join(config.RECIPES_DIR, dir))
|
||||
if canonical == "" {
|
||||
continue
|
||||
}
|
||||
if _, inCatalogue := catl[canonical]; inCatalogue {
|
||||
continue
|
||||
}
|
||||
names = append(names, canonical)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
var (
|
||||
pattern string
|
||||
)
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ func main() {
|
||||
Version = "dev"
|
||||
}
|
||||
if Commit == "" {
|
||||
Commit = " "
|
||||
Commit = "unknown-commit"
|
||||
}
|
||||
|
||||
cli.Run(Version, Commit)
|
||||
|
||||
@@ -19,8 +19,8 @@ require (
|
||||
github.com/moby/sys/signal v0.7.1
|
||||
github.com/moby/term v0.5.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/schollz/progressbar/v3 v3.19.0
|
||||
golang.org/x/term v0.44.0
|
||||
github.com/schollz/progressbar/v3 v3.19.1
|
||||
golang.org/x/term v0.45.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools/v3 v3.5.2
|
||||
)
|
||||
@@ -77,7 +77,7 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
@@ -155,7 +155,7 @@ require (
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
golang.org/x/sys v0.46.0
|
||||
golang.org/x/sys v0.47.0
|
||||
)
|
||||
|
||||
replace github.com/docker/cli v28.4.0+incompatible => git.coopcloud.tech/toolshed/docker-cli v28.5.3-0.20260202112816-30df2d0b3a00+incompatible
|
||||
|
||||
@@ -623,8 +623,8 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
@@ -808,8 +808,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/schollz/progressbar/v3 v3.19.0 h1:Ea18xuIRQXLAUidVDox3AbwfUhD0/1IvohyTutOIFoc=
|
||||
github.com/schollz/progressbar/v3 v3.19.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
|
||||
github.com/schollz/progressbar/v3 v3.19.1 h1:iv8BgwOvdML/S3p84uBpy/IMigv4U9594vPZYa2EdrU=
|
||||
github.com/schollz/progressbar/v3 v3.19.1/go.mod h1:LFL7jqimKxfhero4K1eCkUr/6R39AgQeiPCJtlTWIW8=
|
||||
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
@@ -1139,14 +1139,13 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
+111
-51
@@ -260,12 +260,30 @@ func ReadAppEnvFile(appFile AppFile, name AppName) (App, error) {
|
||||
func NewApp(env envfile.AppEnv, name string, appFile AppFile) (App, error) {
|
||||
domain := env["DOMAIN"]
|
||||
|
||||
recipeName, exists := env["RECIPE"]
|
||||
if !exists {
|
||||
recipeName, exists = env["TYPE"]
|
||||
if !exists {
|
||||
return App{}, errors.New(i18n.G("%s is missing the TYPE env var?", name))
|
||||
}
|
||||
typeVar, hasType := env["TYPE"]
|
||||
recipeVar, hasRecipe := env["RECIPE"]
|
||||
if !hasType && !hasRecipe {
|
||||
return App{}, errors.New(i18n.G("%s is missing the TYPE env var?", name))
|
||||
}
|
||||
|
||||
// The recipe identity is taken from RECIPE when present (it records the
|
||||
// canonical git source for externally-sourced recipes), otherwise from
|
||||
// TYPE. The deployed version is always carried by TYPE when a TYPE line
|
||||
// exists; only legacy apps that set RECIPE alone carry it there. RECIPE
|
||||
// itself must stay a bare source pointer, never a versioned value.
|
||||
identity := typeVar
|
||||
if hasRecipe {
|
||||
identity = recipeVar
|
||||
}
|
||||
|
||||
versionSource := identity
|
||||
if hasType {
|
||||
versionSource = typeVar
|
||||
}
|
||||
|
||||
recipeName, _, _ := strings.Cut(identity, ":")
|
||||
if _, version, found := strings.Cut(versionSource, ":"); found && version != "" {
|
||||
recipeName = fmt.Sprintf("%s:%s", recipeName, version)
|
||||
}
|
||||
|
||||
return App{
|
||||
@@ -392,11 +410,15 @@ func TemplateAppEnvSample(r recipe.Recipe, appName, server, domain string) error
|
||||
|
||||
newContents := strings.Replace(
|
||||
string(read),
|
||||
fmt.Sprintf("%s.example.com", r.Name),
|
||||
fmt.Sprintf("%s.example.com", r.ShortName()),
|
||||
domain,
|
||||
-1,
|
||||
)
|
||||
|
||||
if strings.Contains(r.Name, "/") {
|
||||
newContents = injectRecipeLine(newContents, r.Name)
|
||||
}
|
||||
|
||||
err = os.WriteFile(appEnvPath, []byte(newContents), 0)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -407,6 +429,37 @@ func TemplateAppEnvSample(r recipe.Recipe, appName, server, domain string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// injectRecipeLine ensures the env file carries a RECIPE=<canonical name>
|
||||
// line so a downstream `abra app deploy` (potentially on another machine)
|
||||
// can re-fetch the recipe from its original git source. If a RECIPE= line
|
||||
// already exists in the copied .env.sample it is replaced; otherwise a new
|
||||
// line is inserted immediately after the TYPE= line, or appended at the
|
||||
// end if no TYPE= line is present.
|
||||
func injectRecipeLine(contents, canonicalName string) string {
|
||||
lines := strings.Split(contents, "\n")
|
||||
for i, line := range lines {
|
||||
trimmed := strings.TrimLeft(line, " \t")
|
||||
if strings.HasPrefix(trimmed, "RECIPE=") {
|
||||
lines[i] = "RECIPE=" + canonicalName
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
}
|
||||
for i, line := range lines {
|
||||
trimmed := strings.TrimLeft(line, " \t")
|
||||
if strings.HasPrefix(trimmed, "TYPE=") {
|
||||
out := make([]string, 0, len(lines)+1)
|
||||
out = append(out, lines[:i+1]...)
|
||||
out = append(out, "RECIPE="+canonicalName)
|
||||
out = append(out, lines[i+1:]...)
|
||||
return strings.Join(out, "\n")
|
||||
}
|
||||
}
|
||||
if contents != "" && !strings.HasSuffix(contents, "\n") {
|
||||
contents += "\n"
|
||||
}
|
||||
return contents + "RECIPE=" + canonicalName + "\n"
|
||||
}
|
||||
|
||||
// SanitiseAppName makes a app name usable with Docker by replacing illegal
|
||||
// characters.
|
||||
func SanitiseAppName(name string) string {
|
||||
@@ -514,10 +567,22 @@ func ExposeAllEnv(
|
||||
_, exists := service.Environment[k]
|
||||
if !exists {
|
||||
value := v
|
||||
if k == "TYPE" || k == "RECIPE" {
|
||||
switch k {
|
||||
case "TYPE":
|
||||
// NOTE(d1): don't use the wrong version from the app env
|
||||
// since we are deploying a new version
|
||||
value = toDeployVersion
|
||||
// since we are deploying a new version. Keep the
|
||||
// recipe name already recorded in TYPE and only
|
||||
// swap in the version being deployed.
|
||||
name, _, _ := strings.Cut(v, ":")
|
||||
if _, version, found := strings.Cut(toDeployVersion, ":"); found {
|
||||
value = fmt.Sprintf("%s:%s", name, version)
|
||||
} else {
|
||||
value = name
|
||||
}
|
||||
case "RECIPE":
|
||||
// RECIPE records the canonical git source only; the
|
||||
// version is carried by TYPE, so keep it bare here.
|
||||
value, _, _ = strings.Cut(toDeployVersion, ":")
|
||||
}
|
||||
service.Environment[k] = &value
|
||||
log.Debug(i18n.G("%s: %s: %s", stackName, k, value))
|
||||
@@ -642,60 +707,55 @@ func (a App) WriteRecipeVersion(version string, dryRun bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var (
|
||||
dirtyVersion string
|
||||
skipped bool
|
||||
lines []string
|
||||
scanner = bufio.NewScanner(file)
|
||||
lines []string
|
||||
hasType bool
|
||||
scanner = bufio.NewScanner(file)
|
||||
)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if !strings.HasPrefix(line, "RECIPE=") && !strings.HasPrefix(line, "TYPE=") {
|
||||
lines = append(lines, line)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "#") {
|
||||
lines = append(lines, line)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(line, version) && !a.Recipe.Dirty && !strings.HasSuffix(line, config.DIRTY_DEFAULT) {
|
||||
skipped = true
|
||||
lines = append(lines, line)
|
||||
continue
|
||||
}
|
||||
|
||||
splitted := strings.Split(line, ":")
|
||||
|
||||
line = fmt.Sprintf("%s:%s", splitted[0], version)
|
||||
lines = append(lines, line)
|
||||
if strings.HasPrefix(line, "TYPE=") {
|
||||
hasType = true
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
file.Close()
|
||||
log.Fatal(err)
|
||||
}
|
||||
file.Close()
|
||||
|
||||
// The recipe version is carried by a single field: TYPE when present (the
|
||||
// standard recipe key), otherwise the legacy RECIPE key. When both exist
|
||||
// (git-URL recipes) RECIPE stays a bare source pointer and only TYPE is
|
||||
// versioned, so the two can never drift apart.
|
||||
targetPrefix := "RECIPE="
|
||||
if hasType {
|
||||
targetPrefix = "TYPE="
|
||||
}
|
||||
|
||||
for i, line := range lines {
|
||||
if !strings.HasPrefix(line, targetPrefix) || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
name, _, _ := strings.Cut(line, ":")
|
||||
lines[i] = fmt.Sprintf("%s:%s", name, version)
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
log.Debug(i18n.G("skipping writing version %s because dry run", version))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.WriteFile(a.Path, []byte(strings.Join(lines, "\n")), os.ModePerm); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if a.Recipe.Dirty && dirtyVersion != "" {
|
||||
version = dirtyVersion
|
||||
}
|
||||
|
||||
if !dryRun {
|
||||
if err := os.WriteFile(a.Path, []byte(strings.Join(lines, "\n")), os.ModePerm); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
log.Debug(i18n.G("skipping writing version %s because dry run", version))
|
||||
}
|
||||
|
||||
if !skipped {
|
||||
log.Debug(i18n.G("version %s saved to %s.env", version, a.Domain))
|
||||
} else {
|
||||
log.Debug(i18n.G("skipping version %s write as already exists in %s.env", version, a.Domain))
|
||||
}
|
||||
log.Debug(i18n.G("version %s saved to %s.env", version, a.Domain))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package app_test
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -271,6 +273,60 @@ func TestWriteRecipeVersionOverwrite(t *testing.T) {
|
||||
assert.Equal(t, "foo", app.Recipe.EnvVersion)
|
||||
}
|
||||
|
||||
// TestWriteRecipeVersionSingleField ensures the version is written to exactly
|
||||
// one field: TYPE when present (even alongside a RECIPE source pointer, as with
|
||||
// git-URL recipes), otherwise the legacy RECIPE field. RECIPE must stay bare
|
||||
// when a TYPE line exists so the two can never drift apart.
|
||||
func TestWriteRecipeVersionSingleField(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "git recipe: TYPE and RECIPE both present",
|
||||
input: "TYPE=myrecipe\nRECIPE=git.example.com/user/myrecipe\nDOMAIN=x\n",
|
||||
want: "TYPE=myrecipe:1.2.3+4.5.6\nRECIPE=git.example.com/user/myrecipe\nDOMAIN=x",
|
||||
},
|
||||
{
|
||||
name: "git recipe: re-versioning replaces only TYPE",
|
||||
input: "TYPE=myrecipe:0.1.0+1.0.0\nRECIPE=git.example.com/user/myrecipe\nDOMAIN=x\n",
|
||||
want: "TYPE=myrecipe:1.2.3+4.5.6\nRECIPE=git.example.com/user/myrecipe\nDOMAIN=x",
|
||||
},
|
||||
{
|
||||
name: "legacy recipe: only RECIPE present",
|
||||
input: "RECIPE=test_recipe\nDOMAIN=x\n",
|
||||
want: "RECIPE=test_recipe:1.2.3+4.5.6\nDOMAIN=x",
|
||||
},
|
||||
{
|
||||
name: "catalogue recipe: only TYPE present",
|
||||
input: "TYPE=nextcloud\nDOMAIN=x\n",
|
||||
want: "TYPE=nextcloud:1.2.3+4.5.6\nDOMAIN=x",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := filepath.Join(t.TempDir(), "app.env")
|
||||
if err := os.WriteFile(p, []byte(tc.input), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
app := appPkg.App{Path: p, Domain: "x"}
|
||||
if err := app.WriteRecipeVersion("1.2.3+4.5.6", false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.want, string(got))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteRecipeVersionUnknown(t *testing.T) {
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package app
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestInjectRecipeLineAfterType(t *testing.T) {
|
||||
in := "DOMAIN=example.com\nTYPE=foo\nVERSION=1.0\n"
|
||||
want := "DOMAIN=example.com\nTYPE=foo\nRECIPE=org/foo\nVERSION=1.0\n"
|
||||
got := injectRecipeLine(in, "org/foo")
|
||||
if got != want {
|
||||
t.Errorf("injectRecipeLine inserted RECIPE in wrong position\nwant:\n%q\ngot:\n%q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInjectRecipeLineReplacesExisting(t *testing.T) {
|
||||
in := "TYPE=foo\nRECIPE=old\nDOMAIN=example.com\n"
|
||||
want := "TYPE=foo\nRECIPE=org/foo\nDOMAIN=example.com\n"
|
||||
got := injectRecipeLine(in, "org/foo")
|
||||
if got != want {
|
||||
t.Errorf("injectRecipeLine should replace existing RECIPE line\nwant:\n%q\ngot:\n%q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInjectRecipeLineNoTypeAppends(t *testing.T) {
|
||||
in := "DOMAIN=example.com\n"
|
||||
want := "DOMAIN=example.com\nRECIPE=org/foo\n"
|
||||
got := injectRecipeLine(in, "org/foo")
|
||||
if got != want {
|
||||
t.Errorf("injectRecipeLine should append when TYPE is missing\nwant:\n%q\ngot:\n%q", want, got)
|
||||
}
|
||||
}
|
||||
@@ -27,10 +27,16 @@ var BoldUnderlineStyle = lipgloss.NewStyle().
|
||||
Underline(true)
|
||||
|
||||
func ShortenID(str string) string {
|
||||
if len(str) < 12 {
|
||||
return str
|
||||
}
|
||||
return str[:12]
|
||||
}
|
||||
|
||||
func SmallSHA(hash string) string {
|
||||
if len(hash) < 8 {
|
||||
return hash
|
||||
}
|
||||
return hash[:8]
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,11 @@ func IsClean(repoPath string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Ignore the abra-managed sidecar file that records the canonical
|
||||
// source URL for externally-cloned recipes; it lives alongside the
|
||||
// recipe but is not part of the upstream tree.
|
||||
patterns = append(patterns, gitignore.ParsePattern(".abra-source", nil))
|
||||
|
||||
if len(patterns) > 0 {
|
||||
worktree.Excludes = append(patterns, worktree.Excludes...)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
@@ -13,3 +15,37 @@ func TestIsClean(t *testing.T) {
|
||||
assert.Equal(t, isClean, false)
|
||||
assert.True(t, errors.Is(err, git.ErrRepositoryNotExists))
|
||||
}
|
||||
|
||||
// TestIsCleanIgnoresAbraSource confirms that the .abra-source sidecar
|
||||
// file written by abra next to externally-cloned recipes does not cause
|
||||
// IsClean to report the worktree as dirty.
|
||||
func TestIsCleanIgnoresAbraSource(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
if _, err := git.PlainInit(dir, false); err != nil {
|
||||
t.Fatalf("git init failed: %s", err)
|
||||
}
|
||||
|
||||
sidecar := filepath.Join(dir, ".abra-source")
|
||||
if err := os.WriteFile(sidecar, []byte("git.example.com/u/recipe\n"), 0o644); err != nil {
|
||||
t.Fatalf("writing sidecar failed: %s", err)
|
||||
}
|
||||
|
||||
isClean, err := IsClean(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("IsClean returned error: %s", err)
|
||||
}
|
||||
assert.True(t, isClean, "expected worktree with only .abra-source to be reported clean")
|
||||
|
||||
// Sanity check: an unrelated untracked file should still mark it dirty.
|
||||
other := filepath.Join(dir, "random.txt")
|
||||
if err := os.WriteFile(other, []byte("hello"), 0o644); err != nil {
|
||||
t.Fatalf("writing extra file failed: %s", err)
|
||||
}
|
||||
|
||||
isClean, err = IsClean(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("IsClean returned error: %s", err)
|
||||
}
|
||||
assert.False(t, isClean, "expected worktree with unrelated untracked file to be reported dirty")
|
||||
}
|
||||
|
||||
+56
-56
@@ -7,7 +7,7 @@
|
||||
msgid ""
|
||||
msgstr "Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-06-09 16:29+0200\n"
|
||||
"POT-Creation-Date: 2026-06-14 17:56+0200\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"
|
||||
@@ -339,17 +339,17 @@ msgstr ""
|
||||
msgid "%s is missing the TYPE env var?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:309 ./cli/app/rollback.go:313
|
||||
#: ./cli/app/rollback.go:310 ./cli/app/rollback.go:314
|
||||
#, c-format
|
||||
msgid "%s is not a downgrade for %s?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:429 ./cli/app/upgrade.go:433
|
||||
#: ./cli/app/upgrade.go:430 ./cli/app/upgrade.go:434
|
||||
#, c-format
|
||||
msgid "%s is not an upgrade for %s?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/env.go:146 ./cli/app/logs.go:65 ./cli/app/ps.go:62 ./cli/app/restart.go:100 ./cli/app/services.go:55 ./cli/app/undeploy.go:66 ./cli/app/upgrade.go:450
|
||||
#: ./cli/app/env.go:146 ./cli/app/logs.go:65 ./cli/app/ps.go:62 ./cli/app/restart.go:100 ./cli/app/services.go:55 ./cli/app/undeploy.go:66 ./cli/app/upgrade.go:451
|
||||
#, c-format
|
||||
msgid "%s is not deployed?"
|
||||
msgstr ""
|
||||
@@ -369,7 +369,7 @@ msgstr ""
|
||||
msgid "%s missing context, run \"abra server add %s\"?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:194 ./cli/app/upgrade.go:210
|
||||
#: ./cli/app/deploy.go:195 ./cli/app/upgrade.go:211
|
||||
#, c-format
|
||||
msgid "%s missing from %s.env"
|
||||
msgstr ""
|
||||
@@ -549,12 +549,12 @@ msgstr ""
|
||||
msgid "%s: waiting %d seconds before next retry"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:424
|
||||
#: ./cli/app/upgrade.go:425
|
||||
#, c-format
|
||||
msgid "'%s' is not a known version"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:304 ./cli/app/upgrade.go:419
|
||||
#: ./cli/app/rollback.go:305 ./cli/app/upgrade.go:420
|
||||
#, c-format
|
||||
msgid "'%s' is not a known version for %s"
|
||||
msgstr ""
|
||||
@@ -626,7 +626,7 @@ msgstr ""
|
||||
msgid "Both local recipe and live deployment labels are shown."
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/backup.go:319 ./cli/app/backup.go:335 ./cli/app/check.go:95 ./cli/app/cmd.go:285 ./cli/app/cp.go:385 ./cli/app/deploy.go:414 ./cli/app/labels.go:143 ./cli/app/list.go:335 ./cli/app/new.go:411 ./cli/app/ps.go:213 ./cli/app/restart.go:163 ./cli/app/restore.go:138 ./cli/app/secret.go:569 ./cli/app/secret.go:609 ./cli/app/secret.go:633 ./cli/app/secret.go:641 ./cli/catalogue/catalogue.go:318 ./cli/recipe/lint.go:137
|
||||
#: ./cli/app/backup.go:319 ./cli/app/backup.go:335 ./cli/app/check.go:95 ./cli/app/cmd.go:285 ./cli/app/cp.go:385 ./cli/app/deploy.go:415 ./cli/app/labels.go:143 ./cli/app/list.go:335 ./cli/app/new.go:411 ./cli/app/ps.go:213 ./cli/app/restart.go:163 ./cli/app/restore.go:138 ./cli/app/secret.go:569 ./cli/app/secret.go:609 ./cli/app/secret.go:633 ./cli/app/secret.go:641 ./cli/catalogue/catalogue.go:318 ./cli/recipe/lint.go:137
|
||||
msgid "C"
|
||||
msgstr ""
|
||||
|
||||
@@ -767,7 +767,7 @@ msgid "Creates a new app from a default recipe.\n"
|
||||
"on your $PATH."
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:430 ./cli/app/new.go:387 ./cli/app/rollback.go:361 ./cli/app/upgrade.go:470
|
||||
#: ./cli/app/deploy.go:431 ./cli/app/new.go:387 ./cli/app/rollback.go:362 ./cli/app/upgrade.go:471
|
||||
msgid "D"
|
||||
msgstr ""
|
||||
|
||||
@@ -1462,7 +1462,7 @@ msgid "To load completions:\n"
|
||||
" # and source this file from your PowerShell profile."
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:454 ./cli/app/rollback.go:377 ./cli/app/upgrade.go:494
|
||||
#: ./cli/app/deploy.go:455 ./cli/app/rollback.go:378 ./cli/app/upgrade.go:495
|
||||
msgid "U"
|
||||
msgstr ""
|
||||
|
||||
@@ -1835,7 +1835,7 @@ msgstr ""
|
||||
msgid "attempting to run %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:283 ./cli/app/upgrade.go:296
|
||||
#: ./cli/app/deploy.go:284 ./cli/app/upgrade.go:297
|
||||
#, c-format
|
||||
msgid "attempting to run post deploy commands, saw: %s"
|
||||
msgstr ""
|
||||
@@ -1915,7 +1915,7 @@ msgstr ""
|
||||
#. no spaces in between
|
||||
#. translators: `abra app cp` aliases. use a comma separated list of aliases with
|
||||
#. no spaces in between
|
||||
#: ./cli/app/backup.go:148 ./cli/app/cp.go:30 ./cli/app/deploy.go:438 ./cli/app/rollback.go:369 ./cli/app/upgrade.go:478
|
||||
#: ./cli/app/backup.go:148 ./cli/app/cp.go:30 ./cli/app/deploy.go:439 ./cli/app/rollback.go:370 ./cli/app/upgrade.go:479
|
||||
msgid "c"
|
||||
msgstr ""
|
||||
|
||||
@@ -1971,7 +1971,7 @@ msgstr ""
|
||||
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:388
|
||||
#: ./cli/app/deploy.go:389
|
||||
#, c-format
|
||||
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?\n"
|
||||
" to return to a regular release, specify a release tag, commit SHA or use \"--latest\""
|
||||
@@ -1990,7 +1990,7 @@ msgstr ""
|
||||
msgid "cannot use '[secret] [version]' and '--all' together"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:330
|
||||
#: ./cli/app/deploy.go:331
|
||||
msgid "cannot use --chaos and --latest together"
|
||||
msgstr ""
|
||||
|
||||
@@ -2014,11 +2014,11 @@ msgstr ""
|
||||
msgid "cannot use [service] and --all-services/-a together"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:322 ./cli/app/new.go:80
|
||||
#: ./cli/app/deploy.go:323 ./cli/app/new.go:80
|
||||
msgid "cannot use [version] and --chaos together"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:326
|
||||
#: ./cli/app/deploy.go:327
|
||||
msgid "cannot use [version] and --latest together"
|
||||
msgstr ""
|
||||
|
||||
@@ -2054,7 +2054,7 @@ msgstr ""
|
||||
msgid "cfg"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/backup.go:318 ./cli/app/backup.go:334 ./cli/app/check.go:94 ./cli/app/cmd.go:284 ./cli/app/cp.go:384 ./cli/app/deploy.go:413 ./cli/app/labels.go:142 ./cli/app/list.go:334 ./cli/app/new.go:410 ./cli/app/ps.go:212 ./cli/app/restart.go:162 ./cli/app/restore.go:137 ./cli/app/secret.go:568 ./cli/app/secret.go:608 ./cli/app/secret.go:632 ./cli/app/secret.go:640 ./cli/catalogue/catalogue.go:317 ./cli/recipe/lint.go:136
|
||||
#: ./cli/app/backup.go:318 ./cli/app/backup.go:334 ./cli/app/check.go:94 ./cli/app/cmd.go:284 ./cli/app/cp.go:384 ./cli/app/deploy.go:414 ./cli/app/labels.go:142 ./cli/app/list.go:334 ./cli/app/new.go:410 ./cli/app/ps.go:212 ./cli/app/restart.go:162 ./cli/app/restore.go:137 ./cli/app/secret.go:568 ./cli/app/secret.go:608 ./cli/app/secret.go:632 ./cli/app/secret.go:640 ./cli/catalogue/catalogue.go:317 ./cli/recipe/lint.go:136
|
||||
msgid "chaos"
|
||||
msgstr ""
|
||||
|
||||
@@ -2068,7 +2068,7 @@ msgstr ""
|
||||
msgid "check <domain> [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:95 ./cli/app/undeploy.go:58 ./cli/app/upgrade.go:442
|
||||
#: ./cli/app/deploy.go:95 ./cli/app/undeploy.go:58 ./cli/app/upgrade.go:443
|
||||
#, c-format
|
||||
msgid "checking whether %s is already deployed"
|
||||
msgstr ""
|
||||
@@ -2371,7 +2371,7 @@ msgstr ""
|
||||
msgid "critical errors present in %s config"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:299
|
||||
#: ./cli/app/rollback.go:300
|
||||
#, c-format
|
||||
msgid "current deployment '%s' is not a known version for %s"
|
||||
msgstr ""
|
||||
@@ -2422,7 +2422,7 @@ msgstr ""
|
||||
msgid "deploy labels stanza present"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:448
|
||||
#: ./cli/app/deploy.go:449
|
||||
msgid "deploy latest recipe version"
|
||||
msgstr ""
|
||||
|
||||
@@ -2524,11 +2524,11 @@ msgstr ""
|
||||
msgid "dirty: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:440 ./cli/app/rollback.go:371 ./cli/app/upgrade.go:480
|
||||
#: ./cli/app/deploy.go:441 ./cli/app/rollback.go:372 ./cli/app/upgrade.go:481
|
||||
msgid "disable converge logic checks"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:432 ./cli/app/rollback.go:363 ./cli/app/upgrade.go:472
|
||||
#: ./cli/app/deploy.go:433 ./cli/app/rollback.go:364 ./cli/app/upgrade.go:473
|
||||
msgid "disable public DNS checks"
|
||||
msgstr ""
|
||||
|
||||
@@ -2737,7 +2737,7 @@ msgstr ""
|
||||
|
||||
#. translators: `abra recipe fetch` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: ./cli/app/deploy.go:422 ./cli/app/env.go:325 ./cli/app/remove.go:163 ./cli/app/rollback.go:353 ./cli/app/secret.go:593 ./cli/app/upgrade.go:462 ./cli/app/volume.go:217 ./cli/recipe/fetch.go:20 ./cli/recipe/fetch.go:138
|
||||
#: ./cli/app/deploy.go:423 ./cli/app/env.go:325 ./cli/app/remove.go:163 ./cli/app/rollback.go:354 ./cli/app/secret.go:593 ./cli/app/upgrade.go:463 ./cli/app/volume.go:217 ./cli/recipe/fetch.go:20 ./cli/recipe/fetch.go:138
|
||||
msgid "f"
|
||||
msgstr ""
|
||||
|
||||
@@ -2988,7 +2988,7 @@ msgstr ""
|
||||
msgid "final merged env values for %s are: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:421 ./cli/app/env.go:324 ./cli/app/remove.go:162 ./cli/app/rollback.go:352 ./cli/app/upgrade.go:461 ./cli/app/volume.go:216 ./cli/recipe/fetch.go:137
|
||||
#: ./cli/app/deploy.go:422 ./cli/app/env.go:324 ./cli/app/remove.go:162 ./cli/app/rollback.go:353 ./cli/app/upgrade.go:462 ./cli/app/volume.go:216 ./cli/recipe/fetch.go:137
|
||||
msgid "force"
|
||||
msgstr ""
|
||||
|
||||
@@ -3202,7 +3202,7 @@ msgstr ""
|
||||
msgid "id: %s, "
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/backup.go:321 ./cli/app/backup.go:337 ./cli/app/check.go:97 ./cli/app/cmd.go:287 ./cli/app/cp.go:387 ./cli/app/deploy.go:416 ./cli/app/labels.go:145 ./cli/app/list.go:337 ./cli/app/new.go:413 ./cli/app/ps.go:215 ./cli/app/restart.go:165 ./cli/app/restore.go:140 ./cli/app/secret.go:571 ./cli/app/secret.go:611 ./cli/app/secret.go:635 ./cli/app/secret.go:643 ./cli/catalogue/catalogue.go:320 ./cli/recipe/lint.go:139
|
||||
#: ./cli/app/backup.go:321 ./cli/app/backup.go:337 ./cli/app/check.go:97 ./cli/app/cmd.go:287 ./cli/app/cp.go:387 ./cli/app/deploy.go:417 ./cli/app/labels.go:145 ./cli/app/list.go:337 ./cli/app/new.go:413 ./cli/app/ps.go:215 ./cli/app/restart.go:165 ./cli/app/restore.go:140 ./cli/app/secret.go:571 ./cli/app/secret.go:611 ./cli/app/secret.go:635 ./cli/app/secret.go:643 ./cli/catalogue/catalogue.go:320 ./cli/recipe/lint.go:139
|
||||
msgid "ignore uncommitted recipes changes"
|
||||
msgstr ""
|
||||
|
||||
@@ -3400,7 +3400,7 @@ msgstr ""
|
||||
#. no spaces in between
|
||||
#. translators: `abra recipe lint` aliases. use a comma separated list of
|
||||
#. aliases with no spaces in between
|
||||
#: ./cli/app/cmd.go:261 ./cli/app/deploy.go:446 ./cli/app/logs.go:20 ./cli/recipe/lint.go:17 ./cli/server/add.go:207
|
||||
#: ./cli/app/cmd.go:261 ./cli/app/deploy.go:447 ./cli/app/logs.go:20 ./cli/recipe/lint.go:17 ./cli/server/add.go:207
|
||||
msgid "l"
|
||||
msgstr ""
|
||||
|
||||
@@ -3415,7 +3415,7 @@ msgstr ""
|
||||
msgid "labels <domain> [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:445 ./cli/app/list.go:186
|
||||
#: ./cli/app/deploy.go:446 ./cli/app/list.go:186
|
||||
msgid "latest"
|
||||
msgstr ""
|
||||
|
||||
@@ -3779,7 +3779,7 @@ msgstr ""
|
||||
msgid "no recipe name provided"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:241
|
||||
#: ./cli/app/upgrade.go:242
|
||||
#, c-format
|
||||
msgid "no release notes for upgrading from %s to %s"
|
||||
msgstr ""
|
||||
@@ -3868,11 +3868,11 @@ msgstr ""
|
||||
msgid "no volumes to remove"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:437 ./cli/app/rollback.go:368 ./cli/app/upgrade.go:477
|
||||
#: ./cli/app/deploy.go:438 ./cli/app/rollback.go:369 ./cli/app/upgrade.go:478
|
||||
msgid "no-converge-checks"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:429 ./cli/app/rollback.go:360 ./cli/app/upgrade.go:469
|
||||
#: ./cli/app/deploy.go:430 ./cli/app/rollback.go:361 ./cli/app/upgrade.go:470
|
||||
msgid "no-domain-checks"
|
||||
msgstr ""
|
||||
|
||||
@@ -3940,7 +3940,7 @@ msgstr ""
|
||||
msgid "only show errors"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:488
|
||||
#: ./cli/app/upgrade.go:489
|
||||
msgid "only show release notes"
|
||||
msgstr ""
|
||||
|
||||
@@ -3971,22 +3971,22 @@ msgstr ""
|
||||
msgid "parsed following command arguments: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:345
|
||||
#: ./cli/app/upgrade.go:346
|
||||
#, c-format
|
||||
msgid "parsing chosen upgrade version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:389
|
||||
#: ./cli/app/upgrade.go:390
|
||||
#, c-format
|
||||
msgid "parsing deployed version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:350
|
||||
#: ./cli/app/upgrade.go:351
|
||||
#, c-format
|
||||
msgid "parsing deployment version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:356 ./cli/app/upgrade.go:395
|
||||
#: ./cli/app/upgrade.go:357 ./cli/app/upgrade.go:396
|
||||
#, c-format
|
||||
msgid "parsing recipe version failed: %s"
|
||||
msgstr ""
|
||||
@@ -4011,7 +4011,7 @@ msgstr ""
|
||||
msgid "pattern"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:424 ./cli/app/env.go:327 ./cli/app/remove.go:165 ./cli/app/rollback.go:355 ./cli/app/upgrade.go:464 ./cli/app/volume.go:219
|
||||
#: ./cli/app/deploy.go:425 ./cli/app/env.go:327 ./cli/app/remove.go:165 ./cli/app/rollback.go:356 ./cli/app/upgrade.go:465 ./cli/app/volume.go:219
|
||||
msgid "perform action without further prompt"
|
||||
msgstr ""
|
||||
|
||||
@@ -4021,22 +4021,22 @@ msgstr ""
|
||||
msgid "pl,p"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:267
|
||||
#: ./cli/app/rollback.go:268
|
||||
#, c-format
|
||||
msgid "please select a downgrade (version: %s):"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:272
|
||||
#: ./cli/app/rollback.go:273
|
||||
#, c-format
|
||||
msgid "please select a downgrade (version: %s, chaos: %s):"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:312
|
||||
#: ./cli/app/upgrade.go:313
|
||||
#, c-format
|
||||
msgid "please select an upgrade (version: %s):"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:317
|
||||
#: ./cli/app/upgrade.go:318
|
||||
#, c-format
|
||||
msgid "please select an upgrade (version: %s, chaos: %s):"
|
||||
msgstr ""
|
||||
@@ -4119,7 +4119,7 @@ msgstr ""
|
||||
#. with no spaces in between
|
||||
#. translators: `abra recipe` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: ./cli/app/backup.go:327 ./cli/app/list.go:304 ./cli/app/move.go:350 ./cli/app/run.go:23 ./cli/app/upgrade.go:486 ./cli/catalogue/catalogue.go:302 ./cli/recipe/recipe.go:12 ./cli/recipe/release.go:624
|
||||
#: ./cli/app/backup.go:327 ./cli/app/list.go:304 ./cli/app/move.go:350 ./cli/app/run.go:23 ./cli/app/upgrade.go:487 ./cli/catalogue/catalogue.go:302 ./cli/recipe/recipe.go:12 ./cli/recipe/release.go:624
|
||||
msgid "r"
|
||||
msgstr ""
|
||||
|
||||
@@ -4239,7 +4239,7 @@ msgstr ""
|
||||
msgid "release failed. any changes made have been reverted"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:485
|
||||
#: ./cli/app/upgrade.go:486
|
||||
msgid "releasenotes"
|
||||
msgstr ""
|
||||
|
||||
@@ -4543,7 +4543,7 @@ msgstr ""
|
||||
msgid "run command locally"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:281 ./cli/app/upgrade.go:293
|
||||
#: ./cli/app/deploy.go:282 ./cli/app/upgrade.go:294
|
||||
#, c-format
|
||||
msgid "run the following post-deploy commands: %s"
|
||||
msgstr ""
|
||||
@@ -4626,12 +4626,12 @@ msgstr ""
|
||||
msgid "secret not found: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:358
|
||||
#: ./cli/app/deploy.go:359
|
||||
#, c-format
|
||||
msgid "secret not generated: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:356
|
||||
#: ./cli/app/deploy.go:357
|
||||
#, c-format
|
||||
msgid "secret not inserted (#generate=false): %s"
|
||||
msgstr ""
|
||||
@@ -4650,7 +4650,7 @@ msgstr ""
|
||||
msgid "secrets are %s shown again, please save them %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:306
|
||||
#: ./cli/app/deploy.go:307
|
||||
#, c-format
|
||||
msgid "selected latest recipe version: %s (from %d available versions)"
|
||||
msgstr ""
|
||||
@@ -4775,7 +4775,7 @@ msgstr ""
|
||||
msgid "severity"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:456 ./cli/app/rollback.go:379 ./cli/app/upgrade.go:496
|
||||
#: ./cli/app/deploy.go:457 ./cli/app/rollback.go:380 ./cli/app/upgrade.go:497
|
||||
msgid "show all configs & images, including unchanged ones"
|
||||
msgstr ""
|
||||
|
||||
@@ -4799,7 +4799,7 @@ msgstr ""
|
||||
msgid "show debug messages"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:453 ./cli/app/rollback.go:376 ./cli/app/upgrade.go:493
|
||||
#: ./cli/app/deploy.go:454 ./cli/app/rollback.go:377 ./cli/app/upgrade.go:494
|
||||
msgid "show-unchanged"
|
||||
msgstr ""
|
||||
|
||||
@@ -4847,11 +4847,11 @@ msgstr ""
|
||||
msgid "skipping converge logic checks"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:208
|
||||
#: ./cli/app/deploy.go:209
|
||||
msgid "skipping domain checks"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:205
|
||||
#: ./cli/app/deploy.go:206
|
||||
msgid "skipping domain checks, no DOMAIN=... configured"
|
||||
msgstr ""
|
||||
|
||||
@@ -5653,27 +5653,27 @@ msgstr ""
|
||||
msgid "version wiped from %s.env"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:372
|
||||
#: ./cli/app/deploy.go:373
|
||||
#, c-format
|
||||
msgid "version: taking chaos version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:398
|
||||
#: ./cli/app/deploy.go:399
|
||||
#, c-format
|
||||
msgid "version: taking deployed version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:403
|
||||
#: ./cli/app/deploy.go:404
|
||||
#, c-format
|
||||
msgid "version: taking new recipe version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:392
|
||||
#: ./cli/app/deploy.go:393
|
||||
#, c-format
|
||||
msgid "version: taking version from .env file: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:378
|
||||
#: ./cli/app/deploy.go:379
|
||||
#, c-format
|
||||
msgid "version: taking version from cli arg: %s"
|
||||
msgstr ""
|
||||
@@ -5801,7 +5801,7 @@ msgstr ""
|
||||
msgid "writer: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:288 ./cli/app/new.go:255 ./cli/app/rollback.go:256 ./cli/app/undeploy.go:120 ./cli/app/upgrade.go:301
|
||||
#: ./cli/app/deploy.go:289 ./cli/app/new.go:255 ./cli/app/rollback.go:257 ./cli/app/undeploy.go:120 ./cli/app/upgrade.go:302
|
||||
#, c-format
|
||||
msgid "writing recipe version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
+62
-62
@@ -2,7 +2,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-06-09 16:29+0200\n"
|
||||
"POT-Creation-Date: 2026-06-14 17:56+0200\n"
|
||||
"PO-Revision-Date: 2026-02-28 13:52+0000\n"
|
||||
"Last-Translator: chasqui <chasqui@cryptolab.net>\n"
|
||||
"Language-Team: Spanish <https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/>\n"
|
||||
@@ -443,19 +443,19 @@ msgstr "¿%s no tiene un archivo compose.yml o compose.*.yml?"
|
||||
msgid "%s is missing the TYPE env var?"
|
||||
msgstr "¿A %s le falta la variable de entorno TYPE?"
|
||||
|
||||
#: cli/app/rollback.go:309 cli/app/rollback.go:313
|
||||
#: cli/app/rollback.go:310 cli/app/rollback.go:314
|
||||
#, c-format
|
||||
msgid "%s is not a downgrade for %s?"
|
||||
msgstr "¿%s no es un downgrade para %s?"
|
||||
|
||||
#: cli/app/upgrade.go:429 cli/app/upgrade.go:433
|
||||
#: cli/app/upgrade.go:430 cli/app/upgrade.go:434
|
||||
#, c-format
|
||||
msgid "%s is not an upgrade for %s?"
|
||||
msgstr "¿%s no es una actualización para %s?"
|
||||
|
||||
#: cli/app/env.go:146 cli/app/logs.go:65 cli/app/ps.go:62
|
||||
#: cli/app/restart.go:100 cli/app/services.go:55 cli/app/undeploy.go:66
|
||||
#: cli/app/upgrade.go:450
|
||||
#: cli/app/upgrade.go:451
|
||||
#, c-format
|
||||
msgid "%s is not deployed?"
|
||||
msgstr "¿%s no está desplegado?"
|
||||
@@ -475,7 +475,7 @@ msgstr "%s todavía está desplegado. Ejecuta \"abra aplicacion plegar %s\""
|
||||
msgid "%s missing context, run \"abra server add %s\"?"
|
||||
msgstr "falta contexto %s, ¿ejecutar \"abra servidor agregar %s\"?"
|
||||
|
||||
#: cli/app/deploy.go:194 cli/app/upgrade.go:210
|
||||
#: cli/app/deploy.go:195 cli/app/upgrade.go:211
|
||||
#, c-format
|
||||
msgid "%s missing from %s.env"
|
||||
msgstr "falta %s de %s.env"
|
||||
@@ -655,12 +655,12 @@ msgstr "%s: no se puede resolver la dirección IPv4: %s"
|
||||
msgid "%s: waiting %d seconds before next retry"
|
||||
msgstr "%s: esperando %d segundos antes del siguiente reintento"
|
||||
|
||||
#: cli/app/upgrade.go:424
|
||||
#: cli/app/upgrade.go:425
|
||||
#, c-format
|
||||
msgid "'%s' is not a known version"
|
||||
msgstr "'%s' no es una versión conocida"
|
||||
|
||||
#: cli/app/rollback.go:304 cli/app/upgrade.go:419
|
||||
#: cli/app/rollback.go:305 cli/app/upgrade.go:420
|
||||
#, c-format
|
||||
msgid "'%s' is not a known version for %s"
|
||||
msgstr "'%s' no es una versión conocida para %s"
|
||||
@@ -759,7 +759,7 @@ msgid "Both local recipe and live deployment labels are shown."
|
||||
msgstr "Se muestran tanto la receta local como las etiquetas del despliegue en vivo."
|
||||
|
||||
#: cli/app/backup.go:319 cli/app/backup.go:335 cli/app/check.go:95
|
||||
#: cli/app/cmd.go:285 cli/app/cp.go:385 cli/app/deploy.go:414
|
||||
#: cli/app/cmd.go:285 cli/app/cp.go:385 cli/app/deploy.go:415
|
||||
#: cli/app/labels.go:143 cli/app/list.go:335 cli/app/new.go:411
|
||||
#: cli/app/ps.go:213 cli/app/restart.go:163 cli/app/restore.go:138
|
||||
#: cli/app/secret.go:569 cli/app/secret.go:609 cli/app/secret.go:633
|
||||
@@ -959,8 +959,8 @@ msgstr ""
|
||||
"en un almacén pass (ver passwordstore.org). El comando pass debe estar \n"
|
||||
"disponible en tu $PATH."
|
||||
|
||||
#: cli/app/deploy.go:430 cli/app/new.go:387 cli/app/rollback.go:361
|
||||
#: cli/app/upgrade.go:470
|
||||
#: cli/app/deploy.go:431 cli/app/new.go:387 cli/app/rollback.go:362
|
||||
#: cli/app/upgrade.go:471
|
||||
msgid "D"
|
||||
msgstr ""
|
||||
|
||||
@@ -1881,7 +1881,7 @@ msgstr ""
|
||||
" PS> abra autocomplete powershell > abra.ps1\n"
|
||||
" # y fuente (source) ese archivo desde tu perfil de PowerShell."
|
||||
|
||||
#: cli/app/deploy.go:454 cli/app/rollback.go:377 cli/app/upgrade.go:494
|
||||
#: cli/app/deploy.go:455 cli/app/rollback.go:378 cli/app/upgrade.go:495
|
||||
msgid "U"
|
||||
msgstr ""
|
||||
|
||||
@@ -2368,7 +2368,7 @@ msgstr "Intentando generar y almacenar %s en %s"
|
||||
msgid "attempting to run %s"
|
||||
msgstr "Intentando ejecutar %s"
|
||||
|
||||
#: cli/app/deploy.go:283 cli/app/upgrade.go:296
|
||||
#: cli/app/deploy.go:284 cli/app/upgrade.go:297
|
||||
#, c-format
|
||||
msgid "attempting to run post deploy commands, saw: %s"
|
||||
msgstr "Intentando ejecutar los comandos posteriores al despliegue: %s"
|
||||
@@ -2454,8 +2454,8 @@ msgstr "enlace symlink roto en tus carpetas de configuración de abra: %s"
|
||||
#. no spaces in between
|
||||
#. translators: `abra app cp` aliases. use a comma separated list of aliases with
|
||||
#. no spaces in between
|
||||
#: cli/app/backup.go:148 cli/app/cp.go:30 cli/app/deploy.go:438
|
||||
#: cli/app/rollback.go:369 cli/app/upgrade.go:478
|
||||
#: cli/app/backup.go:148 cli/app/cp.go:30 cli/app/deploy.go:439
|
||||
#: cli/app/rollback.go:370 cli/app/upgrade.go:479
|
||||
msgid "c"
|
||||
msgstr ""
|
||||
|
||||
@@ -2511,7 +2511,7 @@ msgstr "no se puede obtener la etiqueta %s para %s"
|
||||
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?"
|
||||
msgstr "no se puede redeplegar la versión anterior de caos (%s), ¿Era tu intención usar \"--caos\"?"
|
||||
|
||||
#: cli/app/deploy.go:388
|
||||
#: cli/app/deploy.go:389
|
||||
#, c-format
|
||||
msgid ""
|
||||
"cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?\n"
|
||||
@@ -2533,7 +2533,7 @@ msgstr "no se puede especificar una etiqueta y un tipo de incremento al mismo ti
|
||||
msgid "cannot use '[secret] [version]' and '--all' together"
|
||||
msgstr "no se puede usar '[secreto] [versión]' y '--todos' juntos"
|
||||
|
||||
#: cli/app/deploy.go:330
|
||||
#: cli/app/deploy.go:331
|
||||
msgid "cannot use --chaos and --latest together"
|
||||
msgstr "no se puede usar --caos y --latest juntos"
|
||||
|
||||
@@ -2557,11 +2557,11 @@ msgstr "no se puede usar [server] y --local al mismo tiempo"
|
||||
msgid "cannot use [service] and --all-services/-a together"
|
||||
msgstr "no se puede usar [servicio] y --todos-los-servicios/-a juntos"
|
||||
|
||||
#: cli/app/deploy.go:322 cli/app/new.go:80
|
||||
#: cli/app/deploy.go:323 cli/app/new.go:80
|
||||
msgid "cannot use [version] and --chaos together"
|
||||
msgstr "no se puede usar [versión] y --caos juntos"
|
||||
|
||||
#: cli/app/deploy.go:326
|
||||
#: cli/app/deploy.go:327
|
||||
msgid "cannot use [version] and --latest together"
|
||||
msgstr "no se puede usar [versión] y --latest juntos"
|
||||
|
||||
@@ -2598,7 +2598,7 @@ msgid "cfg"
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/backup.go:318 cli/app/backup.go:334 cli/app/check.go:94
|
||||
#: cli/app/cmd.go:284 cli/app/cp.go:384 cli/app/deploy.go:413
|
||||
#: cli/app/cmd.go:284 cli/app/cp.go:384 cli/app/deploy.go:414
|
||||
#: cli/app/labels.go:142 cli/app/list.go:334 cli/app/new.go:410
|
||||
#: cli/app/ps.go:212 cli/app/restart.go:162 cli/app/restore.go:137
|
||||
#: cli/app/secret.go:568 cli/app/secret.go:608 cli/app/secret.go:632
|
||||
@@ -2616,7 +2616,7 @@ msgstr ""
|
||||
msgid "check <domain> [flags]"
|
||||
msgstr "verificar <aplicacion> [opciones]"
|
||||
|
||||
#: cli/app/deploy.go:95 cli/app/undeploy.go:58 cli/app/upgrade.go:442
|
||||
#: cli/app/deploy.go:95 cli/app/undeploy.go:58 cli/app/upgrade.go:443
|
||||
#, c-format
|
||||
msgid "checking whether %s is already deployed"
|
||||
msgstr "verificando si %s ya está desplegado"
|
||||
@@ -2919,7 +2919,7 @@ msgstr "crítico"
|
||||
msgid "critical errors present in %s config"
|
||||
msgstr "errores críticos presentes en la configuración de %s"
|
||||
|
||||
#: cli/app/rollback.go:299
|
||||
#: cli/app/rollback.go:300
|
||||
#, c-format
|
||||
msgid "current deployment '%s' is not a known version for %s"
|
||||
msgstr "el despliegue actual '%s' no es una versión conocida para %s"
|
||||
@@ -2971,7 +2971,7 @@ msgstr "despliegue en proceso 🟠"
|
||||
msgid "deploy labels stanza present"
|
||||
msgstr "stanza de etiquetas de despliegue presente"
|
||||
|
||||
#: cli/app/deploy.go:448
|
||||
#: cli/app/deploy.go:449
|
||||
msgid "deploy latest recipe version"
|
||||
msgstr "desplegar la última versión de la receta"
|
||||
|
||||
@@ -3073,11 +3073,11 @@ msgstr "el directorio está vacío: %s"
|
||||
msgid "dirty: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/deploy.go:440 cli/app/rollback.go:371 cli/app/upgrade.go:480
|
||||
#: cli/app/deploy.go:441 cli/app/rollback.go:372 cli/app/upgrade.go:481
|
||||
msgid "disable converge logic checks"
|
||||
msgstr "desactivar comprobaciones de lógica de convergencia"
|
||||
|
||||
#: cli/app/deploy.go:432 cli/app/rollback.go:363 cli/app/upgrade.go:472
|
||||
#: cli/app/deploy.go:433 cli/app/rollback.go:364 cli/app/upgrade.go:473
|
||||
msgid "disable public DNS checks"
|
||||
msgstr "desactivar comprobaciones de DNS público"
|
||||
|
||||
@@ -3288,8 +3288,8 @@ msgstr "extrayendo secreto %s en %s"
|
||||
|
||||
#. translators: `abra recipe fetch` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: cli/app/deploy.go:422 cli/app/env.go:325 cli/app/remove.go:163
|
||||
#: cli/app/rollback.go:353 cli/app/secret.go:593 cli/app/upgrade.go:462
|
||||
#: cli/app/deploy.go:423 cli/app/env.go:325 cli/app/remove.go:163
|
||||
#: cli/app/rollback.go:354 cli/app/secret.go:593 cli/app/upgrade.go:463
|
||||
#: cli/app/volume.go:217 cli/recipe/fetch.go:20 cli/recipe/fetch.go:138
|
||||
msgid "f"
|
||||
msgstr ""
|
||||
@@ -3541,8 +3541,8 @@ msgstr "Filtrar por receta"
|
||||
msgid "final merged env values for %s are: %s"
|
||||
msgstr "Los valores finales combinados de entorno para %s son: %s"
|
||||
|
||||
#: cli/app/deploy.go:421 cli/app/env.go:324 cli/app/remove.go:162
|
||||
#: cli/app/rollback.go:352 cli/app/upgrade.go:461 cli/app/volume.go:216
|
||||
#: cli/app/deploy.go:422 cli/app/env.go:324 cli/app/remove.go:162
|
||||
#: cli/app/rollback.go:353 cli/app/upgrade.go:462 cli/app/volume.go:216
|
||||
#: cli/recipe/fetch.go:137
|
||||
msgid "force"
|
||||
msgstr "forzar"
|
||||
@@ -3758,7 +3758,7 @@ msgid "id: %s, "
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/backup.go:321 cli/app/backup.go:337 cli/app/check.go:97
|
||||
#: cli/app/cmd.go:287 cli/app/cp.go:387 cli/app/deploy.go:416
|
||||
#: cli/app/cmd.go:287 cli/app/cp.go:387 cli/app/deploy.go:417
|
||||
#: cli/app/labels.go:145 cli/app/list.go:337 cli/app/new.go:413
|
||||
#: cli/app/ps.go:215 cli/app/restart.go:165 cli/app/restore.go:140
|
||||
#: cli/app/secret.go:571 cli/app/secret.go:611 cli/app/secret.go:635
|
||||
@@ -3960,7 +3960,7 @@ msgstr "versión %s especificada inválida"
|
||||
#. no spaces in between
|
||||
#. translators: `abra recipe lint` aliases. use a comma separated list of
|
||||
#. aliases with no spaces in between
|
||||
#: cli/app/cmd.go:261 cli/app/deploy.go:446 cli/app/logs.go:20
|
||||
#: cli/app/cmd.go:261 cli/app/deploy.go:447 cli/app/logs.go:20
|
||||
#: cli/recipe/lint.go:17 cli/server/add.go:207
|
||||
msgid "l"
|
||||
msgstr ""
|
||||
@@ -3976,7 +3976,7 @@ msgstr ""
|
||||
msgid "labels <domain> [flags]"
|
||||
msgstr "etiquetas <aplicacion> [opciones]"
|
||||
|
||||
#: cli/app/deploy.go:445 cli/app/list.go:186
|
||||
#: cli/app/deploy.go:446 cli/app/list.go:186
|
||||
msgid "latest"
|
||||
msgstr "ultima"
|
||||
|
||||
@@ -4352,7 +4352,7 @@ msgstr "¿no existe la receta '%s'?"
|
||||
msgid "no recipe name provided"
|
||||
msgstr "no se proporcionó el nombre de la receta"
|
||||
|
||||
#: cli/app/upgrade.go:241
|
||||
#: cli/app/upgrade.go:242
|
||||
#, fuzzy, c-format
|
||||
msgid "no release notes for upgrading from %s to %s"
|
||||
msgstr "ejecución de prueba: mover la nota de la versión de 'next' a %s"
|
||||
@@ -4441,11 +4441,11 @@ msgstr "no se eliminaron volúmenes"
|
||||
msgid "no volumes to remove"
|
||||
msgstr "no hay volúmenes para eliminar"
|
||||
|
||||
#: cli/app/deploy.go:437 cli/app/rollback.go:368 cli/app/upgrade.go:477
|
||||
#: cli/app/deploy.go:438 cli/app/rollback.go:369 cli/app/upgrade.go:478
|
||||
msgid "no-converge-checks"
|
||||
msgstr "sin-verificaciones-de-convergencia"
|
||||
|
||||
#: cli/app/deploy.go:429 cli/app/rollback.go:360 cli/app/upgrade.go:469
|
||||
#: cli/app/deploy.go:430 cli/app/rollback.go:361 cli/app/upgrade.go:470
|
||||
msgid "no-domain-checks"
|
||||
msgstr "sin-verificaciones-de-dominio"
|
||||
|
||||
@@ -4513,7 +4513,7 @@ msgstr "solo se usaron etiquetas anotadas para la versión de la receta"
|
||||
msgid "only show errors"
|
||||
msgstr "mostrar solo errores"
|
||||
|
||||
#: cli/app/upgrade.go:488
|
||||
#: cli/app/upgrade.go:489
|
||||
msgid "only show release notes"
|
||||
msgstr "solo mostrar notas de la versión"
|
||||
|
||||
@@ -4547,22 +4547,22 @@ msgstr "parseado %s de %s"
|
||||
msgid "parsed following command arguments: %s"
|
||||
msgstr "parseados los siguientes argumentos del comando: %s"
|
||||
|
||||
#: cli/app/upgrade.go:345
|
||||
#: cli/app/upgrade.go:346
|
||||
#, c-format
|
||||
msgid "parsing chosen upgrade version failed: %s"
|
||||
msgstr "falló la actualización de versión elegida para el parsing: %s"
|
||||
|
||||
#: cli/app/upgrade.go:389
|
||||
#: cli/app/upgrade.go:390
|
||||
#, c-format
|
||||
msgid "parsing deployed version failed: %s"
|
||||
msgstr "parsing fallido de la versión desplegada: %s"
|
||||
|
||||
#: cli/app/upgrade.go:350
|
||||
#: cli/app/upgrade.go:351
|
||||
#, c-format
|
||||
msgid "parsing deployment version failed: %s"
|
||||
msgstr "parsing fallido de la versión de despliegue: %s"
|
||||
|
||||
#: cli/app/upgrade.go:356 cli/app/upgrade.go:395
|
||||
#: cli/app/upgrade.go:357 cli/app/upgrade.go:396
|
||||
#, c-format
|
||||
msgid "parsing recipe version failed: %s"
|
||||
msgstr "parsing fallido de la versión de la receta: %s"
|
||||
@@ -4590,8 +4590,8 @@ msgstr "ruta"
|
||||
msgid "pattern"
|
||||
msgstr "patrón"
|
||||
|
||||
#: cli/app/deploy.go:424 cli/app/env.go:327 cli/app/remove.go:165
|
||||
#: cli/app/rollback.go:355 cli/app/upgrade.go:464 cli/app/volume.go:219
|
||||
#: cli/app/deploy.go:425 cli/app/env.go:327 cli/app/remove.go:165
|
||||
#: cli/app/rollback.go:356 cli/app/upgrade.go:465 cli/app/volume.go:219
|
||||
msgid "perform action without further prompt"
|
||||
msgstr "realizar acción sin más avisos"
|
||||
|
||||
@@ -4601,22 +4601,22 @@ msgstr "realizar acción sin más avisos"
|
||||
msgid "pl,p"
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/rollback.go:267
|
||||
#: cli/app/rollback.go:268
|
||||
#, c-format
|
||||
msgid "please select a downgrade (version: %s):"
|
||||
msgstr "por favor, selecciona una versión anterior (downgrade) (versión: %s):"
|
||||
|
||||
#: cli/app/rollback.go:272
|
||||
#: cli/app/rollback.go:273
|
||||
#, c-format
|
||||
msgid "please select a downgrade (version: %s, chaos: %s):"
|
||||
msgstr "por favor, selecciona una versión anterior (downgrade) (versión: %s, caos: %s):"
|
||||
|
||||
#: cli/app/upgrade.go:312
|
||||
#: cli/app/upgrade.go:313
|
||||
#, c-format
|
||||
msgid "please select an upgrade (version: %s):"
|
||||
msgstr "por favor, selecciona una actualización (versión: %s):"
|
||||
|
||||
#: cli/app/upgrade.go:317
|
||||
#: cli/app/upgrade.go:318
|
||||
#, c-format
|
||||
msgid "please select an upgrade (version: %s, chaos: %s):"
|
||||
msgstr "por favor, selecciona una actualización (versión: %s, caos: %s):"
|
||||
@@ -4702,7 +4702,7 @@ msgstr "consultando servidores remotos..."
|
||||
#. translators: `abra recipe` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: cli/app/backup.go:327 cli/app/list.go:304 cli/app/move.go:350
|
||||
#: cli/app/run.go:23 cli/app/upgrade.go:486 cli/catalogue/catalogue.go:302
|
||||
#: cli/app/run.go:23 cli/app/upgrade.go:487 cli/catalogue/catalogue.go:302
|
||||
#: cli/recipe/recipe.go:12 cli/recipe/release.go:624
|
||||
msgid "r"
|
||||
msgstr ""
|
||||
@@ -4827,7 +4827,7 @@ msgstr "publicar <receta> [version] [opciones]"
|
||||
msgid "release failed. any changes made have been reverted"
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/upgrade.go:485
|
||||
#: cli/app/upgrade.go:486
|
||||
msgid "releasenotes"
|
||||
msgstr "notas de la versión"
|
||||
|
||||
@@ -5132,7 +5132,7 @@ msgstr "ejecutar comando como usuario"
|
||||
msgid "run command locally"
|
||||
msgstr "ejecutar comando localmente"
|
||||
|
||||
#: cli/app/deploy.go:281 cli/app/upgrade.go:293
|
||||
#: cli/app/deploy.go:282 cli/app/upgrade.go:294
|
||||
#, c-format
|
||||
msgid "run the following post-deploy commands: %s"
|
||||
msgstr "Ejecutar los siguientes comandos después del despliegue: %s"
|
||||
@@ -5219,12 +5219,12 @@ msgstr "datos del secreto no proporcionados en la línea de comandos o stdin, so
|
||||
msgid "secret not found: %s"
|
||||
msgstr "secreto no encontrado: %s"
|
||||
|
||||
#: cli/app/deploy.go:358
|
||||
#: cli/app/deploy.go:359
|
||||
#, c-format
|
||||
msgid "secret not generated: %s"
|
||||
msgstr "secreto no generado: %s"
|
||||
|
||||
#: cli/app/deploy.go:356
|
||||
#: cli/app/deploy.go:357
|
||||
#, c-format
|
||||
msgid "secret not inserted (#generate=false): %s"
|
||||
msgstr "secreto no insertado (#generate=false): %s"
|
||||
@@ -5243,7 +5243,7 @@ msgstr "secretos"
|
||||
msgid "secrets are %s shown again, please save them %s"
|
||||
msgstr "los secretos se muestran %s nuevamente, por favor guárdalos %s"
|
||||
|
||||
#: cli/app/deploy.go:306
|
||||
#: cli/app/deploy.go:307
|
||||
#, c-format
|
||||
msgid "selected latest recipe version: %s (from %d available versions)"
|
||||
msgstr "versión de receta más reciente seleccionada: %s (de %d versiones disponibles)"
|
||||
@@ -5369,7 +5369,7 @@ msgstr "establecer referencia: %s"
|
||||
msgid "severity"
|
||||
msgstr "gravedad"
|
||||
|
||||
#: cli/app/deploy.go:456 cli/app/rollback.go:379 cli/app/upgrade.go:496
|
||||
#: cli/app/deploy.go:457 cli/app/rollback.go:380 cli/app/upgrade.go:497
|
||||
msgid "show all configs & images, including unchanged ones"
|
||||
msgstr "mostrar todas las configuraciones e imágenes, incluidas las no cambiadas"
|
||||
|
||||
@@ -5393,7 +5393,7 @@ msgstr "mostrar aplicaciones de un servidor específico"
|
||||
msgid "show debug messages"
|
||||
msgstr "mostrar mensajes para el debugeo"
|
||||
|
||||
#: cli/app/deploy.go:453 cli/app/rollback.go:376 cli/app/upgrade.go:493
|
||||
#: cli/app/deploy.go:454 cli/app/rollback.go:377 cli/app/upgrade.go:494
|
||||
msgid "show-unchanged"
|
||||
msgstr "mostrar-sin-cambios"
|
||||
|
||||
@@ -5441,11 +5441,11 @@ msgstr "omitiendo según lo solicitado, plegado aún está en progreso 🟠"
|
||||
msgid "skipping converge logic checks"
|
||||
msgstr "omitiendo comprobaciones de lógica de convergencia"
|
||||
|
||||
#: cli/app/deploy.go:208
|
||||
#: cli/app/deploy.go:209
|
||||
msgid "skipping domain checks"
|
||||
msgstr "omitiendo comprobaciones de dominio"
|
||||
|
||||
#: cli/app/deploy.go:205
|
||||
#: cli/app/deploy.go:206
|
||||
msgid "skipping domain checks, no DOMAIN=... configured"
|
||||
msgstr "omitiendo comprobaciones de dominio, no DOMAIN=... configurado"
|
||||
|
||||
@@ -6255,27 +6255,27 @@ msgstr "la versión parece inválida: %s"
|
||||
msgid "version wiped from %s.env"
|
||||
msgstr "versión eliminada de %s.env"
|
||||
|
||||
#: cli/app/deploy.go:372
|
||||
#: cli/app/deploy.go:373
|
||||
#, c-format
|
||||
msgid "version: taking chaos version: %s"
|
||||
msgstr "versión: tomando la versión de caos: %s"
|
||||
|
||||
#: cli/app/deploy.go:398
|
||||
#: cli/app/deploy.go:399
|
||||
#, c-format
|
||||
msgid "version: taking deployed version: %s"
|
||||
msgstr "versión: tomando la versión desplegada: %s"
|
||||
|
||||
#: cli/app/deploy.go:403
|
||||
#: cli/app/deploy.go:404
|
||||
#, c-format
|
||||
msgid "version: taking new recipe version: %s"
|
||||
msgstr "versión: tomando la nueva versión de la receta: %s"
|
||||
|
||||
#: cli/app/deploy.go:392
|
||||
#: cli/app/deploy.go:393
|
||||
#, c-format
|
||||
msgid "version: taking version from .env file: %s"
|
||||
msgstr "versión: tomando la versión desde el archivo .env: %s"
|
||||
|
||||
#: cli/app/deploy.go:378
|
||||
#: cli/app/deploy.go:379
|
||||
#, c-format
|
||||
msgid "version: taking version from cli arg: %s"
|
||||
msgstr "versión: tomando la versión desde el argumento de la línea de comandos: %s"
|
||||
@@ -6406,8 +6406,8 @@ msgstr "el directorio de trabajo no está limpio en %s, abortando"
|
||||
msgid "writer: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/deploy.go:288 cli/app/new.go:255 cli/app/rollback.go:256
|
||||
#: cli/app/undeploy.go:120 cli/app/upgrade.go:301
|
||||
#: cli/app/deploy.go:289 cli/app/new.go:255 cli/app/rollback.go:257
|
||||
#: cli/app/undeploy.go:120 cli/app/upgrade.go:302
|
||||
#, c-format
|
||||
msgid "writing recipe version failed: %s"
|
||||
msgstr "escritura de la versión de la receta fallida: %s"
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -19,6 +20,11 @@ import (
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
// SourceFile is the sidecar file written next to an externally-cloned
|
||||
// recipe so the canonical "host/path" name can be recovered later (the
|
||||
// on-disk directory name escapes "/" and "." to "_", which is lossy).
|
||||
const SourceFile = ".abra-source"
|
||||
|
||||
type EnsureContext struct {
|
||||
Chaos bool
|
||||
Offline bool
|
||||
@@ -78,6 +84,12 @@ func (r Recipe) EnsureExists() error {
|
||||
if err := gitPkg.Clone(r.Dir, r.GitURL); err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(r.Name, "/") {
|
||||
sidecar := path.Join(r.Dir, SourceFile)
|
||||
if err := os.WriteFile(sidecar, []byte(r.Name+"\n"), 0o644); err != nil {
|
||||
log.Debug(i18n.G("failed to write recipe source sidecar %s: %s", sidecar, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := gitPkg.EnsureGitRepo(r.Dir); err != nil {
|
||||
@@ -87,6 +99,18 @@ func (r Recipe) EnsureExists() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadRecipeSource returns the canonical name recorded in the .abra-source
|
||||
// sidecar inside the given recipe directory, or the empty string if no
|
||||
// sidecar exists. This lets callers recover the unescaped "host/path"
|
||||
// form for externally-cloned recipes.
|
||||
func ReadRecipeSource(recipeDir string) string {
|
||||
data, err := os.ReadFile(path.Join(recipeDir, SourceFile))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
// IsChaosCommit determines if a version sttring is a chaos commit or not.
|
||||
func (r Recipe) IsChaosCommit(version string) (bool, error) {
|
||||
isChaosCommit := false
|
||||
@@ -163,6 +187,9 @@ func (r Recipe) EnsureVersion(version string) (bool, error) {
|
||||
|
||||
hash, err := repo.ResolveRevision(plumbing.Revision(version))
|
||||
if err != nil {
|
||||
if isRemoteBranch(repo, version) {
|
||||
log.Fatal(i18n.G("'%s' is a branch name; ':<version>' only supports tags or commit hashes, not branches", version))
|
||||
}
|
||||
log.Fatal(i18n.G("unable to resolve '%s': %s", version, err))
|
||||
}
|
||||
|
||||
@@ -479,6 +506,28 @@ func (r Recipe) GetRecipeVersions() (RecipeVersions, []string, error) {
|
||||
return versions, uniqueWarnings, nil
|
||||
}
|
||||
|
||||
// isRemoteBranch reports whether name matches a branch on any configured
|
||||
// remote. Used to give a clearer error when a user passes a branch as the
|
||||
// ":version" suffix, which is unsupported.
|
||||
func isRemoteBranch(repo *git.Repository, name string) bool {
|
||||
remotes, err := repo.Remotes()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, remote := range remotes {
|
||||
refs, err := remote.List(&git.ListOptions{})
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, ref := range refs {
|
||||
if ref.Name().IsBranch() && ref.Name().Short() == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Head retrieves latest HEAD metadata.
|
||||
func (r Recipe) Head() (*plumbing.Reference, error) {
|
||||
repo, err := git.PlainOpen(r.Dir)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -121,7 +122,84 @@ type Features struct {
|
||||
SSO string `json:"sso"`
|
||||
}
|
||||
|
||||
// scpURLPattern matches SCP-style git URLs like git@host:path or git@host:port/path.
|
||||
// Captures: 1=host(:port)?, 2=path.
|
||||
var scpURLPattern = regexp.MustCompile(`^[\w.-]+@([\w.-]+(?::\d+)?):(.+)$`)
|
||||
|
||||
// NormalizeRecipeName canonicalizes a recipe identifier to a stable
|
||||
// "host/path" form (or returns short catalog names unchanged). Accepts:
|
||||
//
|
||||
// - https://host/path[.git][:version]
|
||||
// - http://host/path[.git][:version]
|
||||
// - ssh://git@host[:port]/path[.git][:version]
|
||||
// - git@host:path[.git][:version] (SCP-style)
|
||||
// - host/path[.git][:version] (already canonical)
|
||||
// - short-name[:version] (catalog recipe, pass through)
|
||||
//
|
||||
// The optional trailing :version suffix is preserved verbatim. The .git
|
||||
// suffix and trailing slashes on the path are stripped so the four URL
|
||||
// forms of the same repository collapse to one canonical value.
|
||||
func NormalizeRecipeName(input string) string {
|
||||
input = strings.TrimSpace(input)
|
||||
|
||||
// Split off the version suffix first, but only if it's not part of a
|
||||
// scheme (https://) or SCP-style git@host: prefix. The simplest way is
|
||||
// to detect those prefixes and treat the rest as the path-with-version.
|
||||
var (
|
||||
body = input
|
||||
version string
|
||||
)
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(input, "https://") || strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "ssh://"):
|
||||
u, err := url.Parse(input)
|
||||
if err != nil {
|
||||
return input
|
||||
}
|
||||
host := u.Hostname() // strip port to keep canonical form colon-free
|
||||
p := strings.TrimPrefix(u.Path, "/")
|
||||
p, version = splitVersion(p)
|
||||
body = host + "/" + p
|
||||
case scpURLPattern.MatchString(input):
|
||||
m := scpURLPattern.FindStringSubmatch(input)
|
||||
host := m[1]
|
||||
if i := strings.Index(host, ":"); i >= 0 {
|
||||
host = host[:i] // strip port
|
||||
}
|
||||
p, v := splitVersion(m[2])
|
||||
body = host + "/" + p
|
||||
version = v
|
||||
default:
|
||||
body, version = splitVersion(input)
|
||||
}
|
||||
|
||||
body = strings.TrimSuffix(body, "/")
|
||||
body = strings.TrimSuffix(body, ".git")
|
||||
|
||||
if version != "" {
|
||||
return body + ":" + version
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
// splitVersion separates a trailing ":version" suffix from a recipe path.
|
||||
// It only treats the final colon-separated segment as a version when it is
|
||||
// non-empty and contains no slash (a slash means the colon belonged to the
|
||||
// path, e.g. a host:port or scheme separator).
|
||||
func splitVersion(s string) (string, string) {
|
||||
idx := strings.LastIndex(s, ":")
|
||||
if idx < 0 {
|
||||
return s, ""
|
||||
}
|
||||
candidate := s[idx+1:]
|
||||
if candidate == "" || strings.Contains(candidate, "/") {
|
||||
return s, ""
|
||||
}
|
||||
return s[:idx], candidate
|
||||
}
|
||||
|
||||
func Get(name string) Recipe {
|
||||
name = NormalizeRecipeName(name)
|
||||
version := ""
|
||||
versionRaw := ""
|
||||
if strings.Contains(name, ":") {
|
||||
@@ -209,6 +287,16 @@ func (r Recipe) String() string {
|
||||
return out
|
||||
}
|
||||
|
||||
// ShortName returns the final path segment of the recipe name, i.e. the
|
||||
// bare recipe name without any "host/org/" prefix carried by externally
|
||||
// sourced (git URL) recipes. For catalogue recipes it returns Name unchanged.
|
||||
func (r Recipe) ShortName() string {
|
||||
if i := strings.LastIndex(r.Name, "/"); i >= 0 {
|
||||
return r.Name[i+1:]
|
||||
}
|
||||
return r.Name
|
||||
}
|
||||
|
||||
func escapeRecipeName(recipeName string) string {
|
||||
recipeName = strings.ReplaceAll(recipeName, "/", "_")
|
||||
recipeName = strings.ReplaceAll(recipeName, ".", "_")
|
||||
|
||||
@@ -88,6 +88,88 @@ func TestGet(t *testing.T) {
|
||||
AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "https://mygit.org/myorg/cool-recipe",
|
||||
recipe: Recipe{
|
||||
Name: "mygit.org/myorg/cool-recipe",
|
||||
Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"),
|
||||
GitURL: "https://mygit.org/myorg/cool-recipe.git",
|
||||
SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git",
|
||||
ComposePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/compose.yml"),
|
||||
ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/README.md"),
|
||||
SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/.env.sample"),
|
||||
AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "https://mygit.org/myorg/cool-recipe.git",
|
||||
recipe: Recipe{
|
||||
Name: "mygit.org/myorg/cool-recipe",
|
||||
Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"),
|
||||
GitURL: "https://mygit.org/myorg/cool-recipe.git",
|
||||
SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git",
|
||||
ComposePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/compose.yml"),
|
||||
ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/README.md"),
|
||||
SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/.env.sample"),
|
||||
AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "https://mygit.org/myorg/cool-recipe.git:1.2.4",
|
||||
recipe: Recipe{
|
||||
Name: "mygit.org/myorg/cool-recipe",
|
||||
EnvVersion: "1.2.4",
|
||||
EnvVersionRaw: "1.2.4",
|
||||
Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"),
|
||||
GitURL: "https://mygit.org/myorg/cool-recipe.git",
|
||||
SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git",
|
||||
ComposePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/compose.yml"),
|
||||
ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/README.md"),
|
||||
SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/.env.sample"),
|
||||
AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ssh://git@mygit.org/myorg/cool-recipe.git",
|
||||
recipe: Recipe{
|
||||
Name: "mygit.org/myorg/cool-recipe",
|
||||
Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"),
|
||||
GitURL: "https://mygit.org/myorg/cool-recipe.git",
|
||||
SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git",
|
||||
ComposePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/compose.yml"),
|
||||
ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/README.md"),
|
||||
SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/.env.sample"),
|
||||
AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "git@mygit.org:myorg/cool-recipe.git",
|
||||
recipe: Recipe{
|
||||
Name: "mygit.org/myorg/cool-recipe",
|
||||
Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"),
|
||||
GitURL: "https://mygit.org/myorg/cool-recipe.git",
|
||||
SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git",
|
||||
ComposePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/compose.yml"),
|
||||
ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/README.md"),
|
||||
SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/.env.sample"),
|
||||
AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "git@mygit.org:myorg/cool-recipe:1.2.4",
|
||||
recipe: Recipe{
|
||||
Name: "mygit.org/myorg/cool-recipe",
|
||||
EnvVersion: "1.2.4",
|
||||
EnvVersionRaw: "1.2.4",
|
||||
Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"),
|
||||
GitURL: "https://mygit.org/myorg/cool-recipe.git",
|
||||
SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git",
|
||||
ComposePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/compose.yml"),
|
||||
ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/README.md"),
|
||||
SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/.env.sample"),
|
||||
AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
@@ -101,6 +183,49 @@ func TestGet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeRecipeName(t *testing.T) {
|
||||
cases := []struct {
|
||||
in, want string
|
||||
}{
|
||||
// catalog short names pass through
|
||||
{"foo", "foo"},
|
||||
{"foo:1.2.3", "foo:1.2.3"},
|
||||
|
||||
// bare host/path form
|
||||
{"mygit.org/myorg/cool-recipe", "mygit.org/myorg/cool-recipe"},
|
||||
{"mygit.org/myorg/cool-recipe.git", "mygit.org/myorg/cool-recipe"},
|
||||
{"mygit.org/myorg/cool-recipe.git:1.2.3", "mygit.org/myorg/cool-recipe:1.2.3"},
|
||||
{"mygit.org/myorg/cool-recipe/", "mygit.org/myorg/cool-recipe"},
|
||||
|
||||
// https://
|
||||
{"https://mygit.org/myorg/cool-recipe", "mygit.org/myorg/cool-recipe"},
|
||||
{"https://mygit.org/myorg/cool-recipe.git", "mygit.org/myorg/cool-recipe"},
|
||||
{"https://mygit.org/myorg/cool-recipe.git:1.2.3", "mygit.org/myorg/cool-recipe:1.2.3"},
|
||||
{"http://mygit.org/myorg/cool-recipe", "mygit.org/myorg/cool-recipe"},
|
||||
|
||||
// ssh://, with and without port
|
||||
{"ssh://git@mygit.org/myorg/cool-recipe.git", "mygit.org/myorg/cool-recipe"},
|
||||
{"ssh://git@mygit.org:2222/myorg/cool-recipe.git", "mygit.org/myorg/cool-recipe"},
|
||||
|
||||
// SCP-style git@host:path
|
||||
{"git@mygit.org:myorg/cool-recipe", "mygit.org/myorg/cool-recipe"},
|
||||
{"git@mygit.org:myorg/cool-recipe.git", "mygit.org/myorg/cool-recipe"},
|
||||
{"git@mygit.org:myorg/cool-recipe:1.2.3", "mygit.org/myorg/cool-recipe:1.2.3"},
|
||||
|
||||
// whitespace
|
||||
{" https://mygit.org/myorg/cool-recipe ", "mygit.org/myorg/cool-recipe"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.in, func(t *testing.T) {
|
||||
got := NormalizeRecipeName(tc.in)
|
||||
if got != tc.want {
|
||||
t.Errorf("NormalizeRecipeName(%q) = %q, want %q", tc.in, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) {
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: nginx:1.31.1
|
||||
image: nginx:1.31.3
|
||||
secrets:
|
||||
- test_pass_one
|
||||
- test_pass_two
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: nginx:1.31.1
|
||||
image: nginx:1.31.3
|
||||
networks:
|
||||
- proxy
|
||||
deploy:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.20.8
|
||||
ARG ALPINE_VERSION=3.24
|
||||
ARG ALPINE_VERSION=3.18
|
||||
ARG XX_VERSION=1.2.1
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
//go:build (appengine || js || nacl || tinygo || wasm) && !windows
|
||||
// +build appengine js nacl tinygo wasm
|
||||
//go:build (appengine || js || nacl || tinygo || wasm || wasip1 || wasip2) && !windows
|
||||
// +build appengine js nacl tinygo wasm wasip1 wasip2
|
||||
// +build !windows
|
||||
|
||||
package isatty
|
||||
|
||||
+13
-2
@@ -31,6 +31,10 @@ func init() {
|
||||
if procGetFileInformationByHandleEx.Find() != nil {
|
||||
procGetFileInformationByHandleEx = nil
|
||||
}
|
||||
// Check if NtQueryObject is available.
|
||||
if procNtQueryObject.Find() != nil {
|
||||
procNtQueryObject = nil
|
||||
}
|
||||
}
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
@@ -43,6 +47,7 @@ func IsTerminal(fd uintptr) bool {
|
||||
// Check pipe name is used for cygwin/msys2 pty.
|
||||
// Cygwin/MSYS2 PTY has a name like:
|
||||
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
|
||||
// On Windows 7 a trailing suffix (e.g. "-nat") may be appended.
|
||||
func isCygwinPipeName(name string) bool {
|
||||
token := strings.Split(name, "-")
|
||||
if len(token) < 5 {
|
||||
@@ -72,13 +77,19 @@ func isCygwinPipeName(name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, t := range token[5:] {
|
||||
if t == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
|
||||
// getFileNameByHandle use the undocumented ntdll NtQueryObject to get file full name from file handler
|
||||
// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion
|
||||
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
|
||||
// Windows vista to 10
|
||||
// Windows Vista to 10
|
||||
// see https://stackoverflow.com/a/18792477 for details
|
||||
func getFileNameByHandle(fd uintptr) (string, error) {
|
||||
if procNtQueryObject == nil {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.26@sha256:68cb6d68bed024785b69195b89af7ac7a444f27791435f98647edff595aa0479
|
||||
FROM golang:1.26@sha256:b900de91b15b2e2953d930ece1d0ecff0a1590ab2006088d20dcf0f56f1e979f
|
||||
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=arm
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.26@sha256:68cb6d68bed024785b69195b89af7ac7a444f27791435f98647edff595aa0479
|
||||
FROM golang:1.26@sha256:b900de91b15b2e2953d930ece1d0ecff0a1590ab2006088d20dcf0f56f1e979f
|
||||
|
||||
ENV GOOS=linux
|
||||
ENV GOARCH=arm64
|
||||
|
||||
+89
-54
@@ -81,6 +81,8 @@ type config struct {
|
||||
colorCodes bool
|
||||
// custom colors to use for colorCodes
|
||||
customColors map[string]string
|
||||
// color to apply to spinner
|
||||
spinnerColorCode string
|
||||
|
||||
// show rate of change in kB/sec or MB/sec
|
||||
showBytes bool
|
||||
@@ -290,6 +292,13 @@ func OptionSetCustomColorCodes(customColors map[string]string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// OptionSetSpinnerColorCode sets color code for spinner
|
||||
func OptionSetSpinnerColorCode(colorCode string) Option {
|
||||
return func(p *ProgressBar) {
|
||||
p.config.spinnerColorCode = colorCode
|
||||
}
|
||||
}
|
||||
|
||||
// OptionSetElapsedTime will enable elapsed time. Always enabled if OptionSetPredictTime is true.
|
||||
func OptionSetElapsedTime(elapsedTime bool) Option {
|
||||
return func(p *ProgressBar) {
|
||||
@@ -1114,6 +1123,74 @@ func getStringWidth(c config, str string) int {
|
||||
return stringWidth
|
||||
}
|
||||
|
||||
func renderDeterminateProgressBar(c config, s *state, bar, stats, leftBrac, rightBrac string) string {
|
||||
if rightBrac == "" {
|
||||
str := fmt.Sprintf("%4d%% %s %s",
|
||||
s.currentPercent,
|
||||
bar,
|
||||
stats)
|
||||
if (s.currentPercent == 100 && c.showElapsedTimeOnFinish) || c.elapsedTime {
|
||||
str = fmt.Sprintf("%s [%s]", str, leftBrac)
|
||||
}
|
||||
|
||||
if c.showDescriptionAtLineEnd {
|
||||
return fmt.Sprintf("\r%s %s ", str, c.description)
|
||||
}
|
||||
return fmt.Sprintf("\r%s%s ", c.description, str)
|
||||
}
|
||||
|
||||
if s.currentPercent == 100 {
|
||||
str := fmt.Sprintf("%4d%% %s %s",
|
||||
s.currentPercent,
|
||||
bar,
|
||||
stats)
|
||||
|
||||
if c.showElapsedTimeOnFinish {
|
||||
str = fmt.Sprintf("%s [%s]", str, leftBrac)
|
||||
}
|
||||
|
||||
if c.showDescriptionAtLineEnd {
|
||||
return fmt.Sprintf("\r%s %s", str, c.description)
|
||||
}
|
||||
return fmt.Sprintf("\r%s%s", c.description, str)
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("%4d%% %s %s [%s:%s]",
|
||||
s.currentPercent,
|
||||
bar,
|
||||
stats,
|
||||
leftBrac,
|
||||
rightBrac)
|
||||
|
||||
if c.showDescriptionAtLineEnd {
|
||||
return fmt.Sprintf("\r%s %s", str, c.description)
|
||||
}
|
||||
return fmt.Sprintf("\r%s%s", c.description, str)
|
||||
}
|
||||
|
||||
func fitProgressBarWidth(c config, s *state, barStart, barEnd, stats, leftBrac, rightBrac string) int {
|
||||
if c.fullWidth || c.ignoreLength || c.width <= 0 {
|
||||
return c.width
|
||||
}
|
||||
|
||||
terminalWidth, err := termWidth(c.writer)
|
||||
if err != nil || terminalWidth <= 0 {
|
||||
return c.width
|
||||
}
|
||||
|
||||
bar := barStart + strings.Repeat(c.theme.SaucerPadding, c.width) + barEnd
|
||||
lineWidth := getStringWidth(c, renderDeterminateProgressBar(c, s, bar, stats, leftBrac, rightBrac))
|
||||
if lineWidth <= terminalWidth {
|
||||
return c.width
|
||||
}
|
||||
|
||||
fitWidth := c.width - (lineWidth - terminalWidth)
|
||||
if fitWidth < 0 {
|
||||
return 0
|
||||
}
|
||||
return fitWidth
|
||||
}
|
||||
|
||||
func renderProgressBar(c config, s *state) (int, error) {
|
||||
var sb strings.Builder
|
||||
|
||||
@@ -1243,6 +1320,10 @@ func renderProgressBar(c config, s *state) (int, error) {
|
||||
if (s.currentSaucerSize > 0 || s.currentPercent > 0) && c.theme.BarStartFilled != "" {
|
||||
barStart = c.theme.BarStartFilled
|
||||
}
|
||||
c.width = fitProgressBarWidth(c, s, barStart, barEnd, sb.String(), leftBrac, rightBrac)
|
||||
if !c.ignoreLength {
|
||||
s.currentSaucerSize = int(float64(s.currentPercent) / 100.0 * float64(c.width))
|
||||
}
|
||||
if s.currentSaucerSize > 0 {
|
||||
if c.ignoreLength {
|
||||
saucer = strings.Repeat(c.theme.SaucerPadding, s.currentSaucerSize-1)
|
||||
@@ -1294,6 +1375,10 @@ func renderProgressBar(c config, s *state) (int, error) {
|
||||
spinner = selectedSpinner[s.spinnerIdx]
|
||||
s.spinnerIdx = (s.spinnerIdx + 1) % len(selectedSpinner)
|
||||
}
|
||||
// if set add spinner color code
|
||||
if c.spinnerColorCode != "" && c.colorCodes {
|
||||
spinner = "[" + c.spinnerColorCode + "]" + spinner + "[reset]"
|
||||
}
|
||||
if c.elapsedTime {
|
||||
if c.showDescriptionAtLineEnd {
|
||||
str = fmt.Sprintf("\r%s %s [%s] %s ",
|
||||
@@ -1322,61 +1407,11 @@ func renderProgressBar(c config, s *state) (int, error) {
|
||||
}
|
||||
}
|
||||
} else if rightBrac == "" {
|
||||
str = fmt.Sprintf("%4d%% %s%s%s%s%s %s",
|
||||
s.currentPercent,
|
||||
barStart,
|
||||
saucer,
|
||||
saucerHead,
|
||||
strings.Repeat(c.theme.SaucerPadding, repeatAmount),
|
||||
barEnd,
|
||||
sb.String())
|
||||
if (s.currentPercent == 100 && c.showElapsedTimeOnFinish) || c.elapsedTime {
|
||||
str = fmt.Sprintf("%s [%s]", str, leftBrac)
|
||||
}
|
||||
|
||||
if c.showDescriptionAtLineEnd {
|
||||
str = fmt.Sprintf("\r%s %s ", str, c.description)
|
||||
} else {
|
||||
str = fmt.Sprintf("\r%s%s ", c.description, str)
|
||||
}
|
||||
bar := barStart + saucer + saucerHead + strings.Repeat(c.theme.SaucerPadding, repeatAmount) + barEnd
|
||||
str = renderDeterminateProgressBar(c, s, bar, sb.String(), leftBrac, rightBrac)
|
||||
} else {
|
||||
if s.currentPercent == 100 {
|
||||
str = fmt.Sprintf("%4d%% %s%s%s%s%s %s",
|
||||
s.currentPercent,
|
||||
barStart,
|
||||
saucer,
|
||||
saucerHead,
|
||||
strings.Repeat(c.theme.SaucerPadding, repeatAmount),
|
||||
barEnd,
|
||||
sb.String())
|
||||
|
||||
if c.showElapsedTimeOnFinish {
|
||||
str = fmt.Sprintf("%s [%s]", str, leftBrac)
|
||||
}
|
||||
|
||||
if c.showDescriptionAtLineEnd {
|
||||
str = fmt.Sprintf("\r%s %s", str, c.description)
|
||||
} else {
|
||||
str = fmt.Sprintf("\r%s%s", c.description, str)
|
||||
}
|
||||
} else {
|
||||
str = fmt.Sprintf("%4d%% %s%s%s%s%s %s [%s:%s]",
|
||||
s.currentPercent,
|
||||
barStart,
|
||||
saucer,
|
||||
saucerHead,
|
||||
strings.Repeat(c.theme.SaucerPadding, repeatAmount),
|
||||
barEnd,
|
||||
sb.String(),
|
||||
leftBrac,
|
||||
rightBrac)
|
||||
|
||||
if c.showDescriptionAtLineEnd {
|
||||
str = fmt.Sprintf("\r%s %s", str, c.description)
|
||||
} else {
|
||||
str = fmt.Sprintf("\r%s%s", c.description, str)
|
||||
}
|
||||
}
|
||||
bar := barStart + saucer + saucerHead + strings.Repeat(c.theme.SaucerPadding, repeatAmount) + barEnd
|
||||
str = renderDeterminateProgressBar(c, s, bar, sb.String(), leftBrac, rightBrac)
|
||||
}
|
||||
|
||||
if c.colorCodes {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
codespell==2.4.2
|
||||
codespell==2.4.3
|
||||
|
||||
+37
-25
@@ -6,38 +6,50 @@ package cpu
|
||||
|
||||
import "strconv"
|
||||
|
||||
// parseRelease parses a dot-separated version number. It follows the semver
|
||||
// syntax, but allows the minor and patch versions to be elided.
|
||||
// parseRelease parses a dot-separated version number from the prefix
|
||||
// of rel. It returns ok=true only if at least the major and minor
|
||||
// components were successfully parsed; the patch component is
|
||||
// best-effort. Trailing vendor or build suffixes such as
|
||||
// "-generic", "+", "_hi3535", or "-rc1" are ignored.
|
||||
//
|
||||
// This is a copy of the Go runtime's parseRelease from
|
||||
// https://golang.org/cl/209597.
|
||||
// https://golang.org/cl/209597, updated in https://golang.org/cl/781800.
|
||||
func parseRelease(rel string) (major, minor, patch int, ok bool) {
|
||||
// Strip anything after a dash or plus.
|
||||
for i := range len(rel) {
|
||||
if rel[i] == '-' || rel[i] == '+' {
|
||||
rel = rel[:i]
|
||||
break
|
||||
// next consumes a run of decimal digits from the front of rel,
|
||||
// returning the parsed value. If the digits are followed by a
|
||||
// '.', it is consumed and more is set so the caller knows to
|
||||
// parse another component; otherwise scanning terminates and
|
||||
// the rest of rel is discarded.
|
||||
next := func() (n int, more, ok bool) {
|
||||
i := 0
|
||||
for i < len(rel) && rel[i] >= '0' && rel[i] <= '9' {
|
||||
i++
|
||||
}
|
||||
if i == 0 {
|
||||
return 0, false, false
|
||||
}
|
||||
n, err := strconv.Atoi(rel[:i])
|
||||
if err != nil {
|
||||
return 0, false, false
|
||||
}
|
||||
if i < len(rel) && rel[i] == '.' {
|
||||
rel = rel[i+1:]
|
||||
return n, true, true
|
||||
}
|
||||
rel = ""
|
||||
return n, false, true
|
||||
}
|
||||
|
||||
next := func() (int, bool) {
|
||||
for i := range len(rel) {
|
||||
if rel[i] == '.' {
|
||||
ver, err := strconv.Atoi(rel[:i])
|
||||
rel = rel[i+1:]
|
||||
return ver, err == nil
|
||||
}
|
||||
}
|
||||
ver, err := strconv.Atoi(rel)
|
||||
rel = ""
|
||||
return ver, err == nil
|
||||
var more bool
|
||||
if major, more, ok = next(); !ok || !more {
|
||||
return 0, 0, 0, false
|
||||
}
|
||||
if major, ok = next(); !ok || rel == "" {
|
||||
return
|
||||
if minor, more, ok = next(); !ok {
|
||||
return 0, 0, 0, false
|
||||
}
|
||||
if minor, ok = next(); !ok || rel == "" {
|
||||
return
|
||||
if !more {
|
||||
return major, minor, 0, true
|
||||
}
|
||||
patch, ok = next()
|
||||
return
|
||||
patch, _, _ = next()
|
||||
return major, minor, patch, true
|
||||
}
|
||||
|
||||
+1
@@ -1874,6 +1874,7 @@ func Dup2(oldfd, newfd int) error {
|
||||
//sys Dup3(oldfd int, newfd int, flags int) (err error)
|
||||
//sysnb EpollCreate1(flag int) (fd int, err error)
|
||||
//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error)
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
|
||||
//sys Eventfd(initval uint, flags int) (fd int, err error) = SYS_EVENTFD2
|
||||
//sys Exit(code int) = SYS_EXIT_GROUP
|
||||
//sys Fallocate(fd int, mode uint32, off int64, len int64) (err error)
|
||||
|
||||
-1
@@ -20,7 +20,6 @@ func setTimeval(sec, usec int64) Timeval {
|
||||
|
||||
// 64-bit file system and 32-bit uid calls
|
||||
// (386 default is 32-bit file system and 16-bit uid).
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64_64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
|
||||
|
||||
-1
@@ -6,7 +6,6 @@
|
||||
|
||||
package unix
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
-1
@@ -44,7 +44,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
|
||||
|
||||
// 64-bit file system and 32-bit uid calls
|
||||
// (16-bit uid calls are not always supported in newer kernels)
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fchown(fd int, uid int, gid int) (err error) = SYS_FCHOWN32
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
|
||||
//sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64
|
||||
|
||||
-1
@@ -8,7 +8,6 @@ package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
-1
@@ -8,7 +8,6 @@ package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstatfs(fd int, buf *Statfs_t) (err error)
|
||||
|
||||
-1
@@ -6,7 +6,6 @@
|
||||
|
||||
package unix
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstatfs(fd int, buf *Statfs_t) (err error)
|
||||
|
||||
-1
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Ftruncate(fd int, length int64) (err error) = SYS_FTRUNCATE64
|
||||
|
||||
-1
@@ -11,7 +11,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error) = SYS_FSTAT64
|
||||
//sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_FSTATAT64
|
||||
|
||||
-1
@@ -6,7 +6,6 @@
|
||||
|
||||
package unix
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
-1
@@ -8,7 +8,6 @@ package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) = SYS_EPOLL_PWAIT
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
-1
@@ -10,7 +10,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
-1
@@ -6,7 +6,6 @@
|
||||
|
||||
package unix
|
||||
|
||||
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
|
||||
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
|
||||
//sys Fchown(fd int, uid int, gid int) (err error)
|
||||
//sys Fstat(fd int, stat *Stat_t) (err error)
|
||||
|
||||
+7
-1
@@ -1359,6 +1359,7 @@ const (
|
||||
FAN_UNLIMITED_MARKS = 0x20
|
||||
FAN_UNLIMITED_QUEUE = 0x10
|
||||
FD_CLOEXEC = 0x1
|
||||
FD_PIDFS_ROOT = -0x2712
|
||||
FD_SETSIZE = 0x400
|
||||
FF0 = 0x0
|
||||
FIB_RULE_DEV_DETACHED = 0x8
|
||||
@@ -1970,6 +1971,8 @@ const (
|
||||
MADV_DONTNEED = 0x4
|
||||
MADV_DONTNEED_LOCKED = 0x18
|
||||
MADV_FREE = 0x8
|
||||
MADV_GUARD_INSTALL = 0x66
|
||||
MADV_GUARD_REMOVE = 0x67
|
||||
MADV_HUGEPAGE = 0xe
|
||||
MADV_HWPOISON = 0x64
|
||||
MADV_KEEPONFORK = 0x13
|
||||
@@ -2114,7 +2117,7 @@ const (
|
||||
MS_NOSEC = 0x10000000
|
||||
MS_NOSUID = 0x2
|
||||
MS_NOSYMFOLLOW = 0x100
|
||||
MS_NOUSER = -0x80000000
|
||||
MS_NOUSER = 0x80000000
|
||||
MS_POSIXACL = 0x10000
|
||||
MS_PRIVATE = 0x40000
|
||||
MS_RDONLY = 0x1
|
||||
@@ -3786,6 +3789,9 @@ const (
|
||||
TCPOPT_TIMESTAMP = 0x8
|
||||
TCPOPT_TSTAMP_HDR = 0x101080a
|
||||
TCPOPT_WINDOW = 0x3
|
||||
TCP_AO_KEYF_EXCLUDE_OPT = 0x2
|
||||
TCP_AO_KEYF_IFINDEX = 0x1
|
||||
TCP_AO_MAXKEYLEN = 0x50
|
||||
TCP_CC_INFO = 0x1a
|
||||
TCP_CM_INQ = 0x24
|
||||
TCP_CONGESTION = 0xd
|
||||
|
||||
+17
@@ -700,6 +700,23 @@ func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Eventfd(initval uint, flags int) (fd int, err error) {
|
||||
r0, _, e1 := Syscall(SYS_EVENTFD2, uintptr(initval), uintptr(flags), 0)
|
||||
fd = int(r0)
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64_64, uintptr(fd), uintptr(offset), uintptr(offset>>32), uintptr(length), uintptr(length>>32), uintptr(advice))
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -213,23 +213,6 @@ func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fchown(fd int, uid int, gid int) (err error) {
|
||||
_, _, e1 := Syscall(SYS_FCHOWN32, uintptr(fd), uintptr(uid), uintptr(gid))
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall9(SYS_FADVISE64, uintptr(fd), 0, uintptr(offset>>32), uintptr(offset), uintptr(length>>32), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall9(SYS_FADVISE64, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(length), uintptr(length>>32), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fchown(fd int, uid int, gid int) (err error) {
|
||||
_, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid))
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_PWAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
-17
@@ -45,23 +45,6 @@ func Tee(rfd int, wfd int, len int, flags int) (n int64, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(events) > 0 {
|
||||
_p0 = unsafe.Pointer(&events[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_EPOLL_WAIT, uintptr(epfd), uintptr(_p0), uintptr(len(events)), uintptr(msec), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fadvise(fd int, offset int64, length int64, advice int) (err error) {
|
||||
_, _, e1 := Syscall6(SYS_FADVISE64, uintptr(fd), uintptr(offset), uintptr(length), uintptr(advice), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
+36
@@ -1109,17 +1109,53 @@ const (
|
||||
)
|
||||
|
||||
// This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions.
|
||||
//
|
||||
// Go pointers stored in a TrusteeValue must be pinned using [runtime.Pinner]
|
||||
// for the lifetime of the TrusteeValue.
|
||||
type TrusteeValue uintptr
|
||||
|
||||
// TrusteeValueFromString is unsafe and should not be used.
|
||||
//
|
||||
// It returns a uintptr containing a reference to newly-allocated memory
|
||||
// which will be freed by the garbage collector.
|
||||
// There is no way for the caller to safely reference this memory.
|
||||
//
|
||||
// To create a [TrusteeValue] from a string, use:
|
||||
//
|
||||
// p, err := windows.UTF16PtrFromString(s)
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
//
|
||||
// // Pin the string for as long as it is used.
|
||||
// var pinner runtime.Pinner
|
||||
// pinner.Pin(p)
|
||||
// defer pinner.Unpin()
|
||||
//
|
||||
// tv := TrusteeValue(unsafe.Pointer(p))
|
||||
//
|
||||
// Deprecated: TrusteeValueFromString is unsafe and should not be used.
|
||||
func TrusteeValueFromString(str string) TrusteeValue {
|
||||
return TrusteeValue(unsafe.Pointer(StringToUTF16Ptr(str)))
|
||||
}
|
||||
|
||||
// TrusteeValueFromSID returns a [TrusteeValue] referencing sid.
|
||||
//
|
||||
// The caller must pin sid using a [runtime.Pinner] for the lifetime of the TrusteeValue.
|
||||
func TrusteeValueFromSID(sid *SID) TrusteeValue {
|
||||
return TrusteeValue(unsafe.Pointer(sid))
|
||||
}
|
||||
|
||||
// TrusteeValueFromObjectsAndSid returns a [TrusteeValue] referencing objectsAndSid.
|
||||
//
|
||||
// The caller must pin objectsAndSid using a [runtime.Pinner] for the lifetime of the TrusteeValue.
|
||||
func TrusteeValueFromObjectsAndSid(objectsAndSid *OBJECTS_AND_SID) TrusteeValue {
|
||||
return TrusteeValue(unsafe.Pointer(objectsAndSid))
|
||||
}
|
||||
|
||||
// TrusteeValueFromObjectsAndName returns a [TrusteeValue] referencing objectsAndName.
|
||||
//
|
||||
// The caller must pin objectsAndName using a [runtime.Pinner] for the lifetime of the TrusteeValue.
|
||||
func TrusteeValueFromObjectsAndName(objectsAndName *OBJECTS_AND_NAME) TrusteeValue {
|
||||
return TrusteeValue(unsafe.Pointer(objectsAndName))
|
||||
}
|
||||
|
||||
+6
-2
@@ -1728,11 +1728,15 @@ func (s *NTUnicodeString) String() string {
|
||||
// the more common *uint16 string type.
|
||||
func NewNTString(s string) (*NTString, error) {
|
||||
var nts NTString
|
||||
s8, err := BytePtrFromString(s)
|
||||
s8, err := ByteSliceFromString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
RtlInitString(&nts, s8)
|
||||
// The source string plus its terminating NUL must fit within MAX_USHORT.
|
||||
if len(s8) > MAX_USHORT {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
RtlInitString(&nts, &s8[0])
|
||||
return &nts, nil
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -169,6 +169,7 @@ const (
|
||||
FORMAT_MESSAGE_ARGUMENT_ARRAY = 8192
|
||||
FORMAT_MESSAGE_MAX_WIDTH_MASK = 255
|
||||
|
||||
MAX_USHORT = 0xffff
|
||||
MAX_PATH = 260
|
||||
MAX_LONG_PATH = 32768
|
||||
|
||||
|
||||
Vendored
+6
-6
@@ -419,8 +419,8 @@ github.com/lucasb-eyer/go-colorful
|
||||
# github.com/mattn/go-colorable v0.1.14
|
||||
## explicit; go 1.18
|
||||
github.com/mattn/go-colorable
|
||||
# github.com/mattn/go-isatty v0.0.20
|
||||
## explicit; go 1.15
|
||||
# github.com/mattn/go-isatty v0.0.22
|
||||
## explicit; go 1.21
|
||||
github.com/mattn/go-isatty
|
||||
# github.com/mattn/go-localereader v0.0.1
|
||||
## explicit
|
||||
@@ -530,8 +530,8 @@ github.com/rivo/uniseg
|
||||
# github.com/russross/blackfriday/v2 v2.1.0
|
||||
## explicit
|
||||
github.com/russross/blackfriday/v2
|
||||
# github.com/schollz/progressbar/v3 v3.19.0
|
||||
## explicit; go 1.22
|
||||
# github.com/schollz/progressbar/v3 v3.19.1
|
||||
## explicit; go 1.25.0
|
||||
github.com/schollz/progressbar/v3
|
||||
# github.com/sergi/go-diff v1.4.0
|
||||
## explicit; go 1.13
|
||||
@@ -700,7 +700,7 @@ golang.org/x/net/internal/socks
|
||||
golang.org/x/net/internal/timeseries
|
||||
golang.org/x/net/proxy
|
||||
golang.org/x/net/trace
|
||||
# golang.org/x/sys v0.46.0
|
||||
# golang.org/x/sys v0.47.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/sys/cpu
|
||||
golang.org/x/sys/execabs
|
||||
@@ -708,7 +708,7 @@ golang.org/x/sys/plan9
|
||||
golang.org/x/sys/unix
|
||||
golang.org/x/sys/windows
|
||||
golang.org/x/sys/windows/registry
|
||||
# golang.org/x/term v0.44.0
|
||||
# golang.org/x/term v0.45.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/term
|
||||
# golang.org/x/text v0.36.0
|
||||
|
||||
Reference in New Issue
Block a user