Compare commits

...
Author SHA1 Message Date
devydave a28cc7afce docs: more formatting and typo
continuous-integration/drone/push Build is passing
2026-08-13 20:41:04 +02:00
devydave 21dccc895d docs: formatting
continuous-integration/drone/push Build is passing
2026-08-13 20:38:20 +02:00
devydave bb3c7576f8 docs: add setup documentation
continuous-integration/drone/push Build is passing
2026-08-13 20:34:14 +02:00
devydave 02cbf7c014 refactor: make path editable without nix and impure 2026-08-13 19:57:51 +02:00
devydave a8c8df8604 fix: remove personal terminal choice
continuous-integration/drone/push Build is passing
2026-08-12 23:01:12 +02:00
devydave 5766a25144 feat: adds host module
continuous-integration/drone/push Build is passing
2026-08-12 22:43:16 +02:00
devydave 1ccbb31b70 refactor: simplify config and remove hom-manager dependency
continuous-integration/drone/push Build is passing
2026-08-07 15:15:04 +02:00
devydave 5e0dac6508 refactor: adjusting hosts generation
continuous-integration/drone/push Build is passing
2026-08-07 14:11:00 +02:00
devydave 38d13eae73 refactor: better variable name 2026-08-07 13:42:22 +02:00
devydave b5e22790be refactor: better name for inspect function 2026-08-07 13:36:25 +02:00
devydave beee7bd3f2 refactor: only export ssh auth socket when not existing 2026-08-07 11:09:30 +02:00
devydave 0c0e16914a refactor: remove traefik deployment because it is not needed 2026-08-07 10:36:23 +02:00
devydave 9bd8f4962a feat: adds running integrations test suite
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2026-08-06 22:00:29 +02:00
devydave 4e42d4fb9e feat: adds local vm setup that can reach each other
continuous-integration/drone/push Build is passing
2026-07-30 16:53:34 +02:00
renovate-bot b13b10be9a chore(deps): update golang docker tag
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-06-30 21:00:55 +00:00
renovate-bot 8137e56d1e chore(deps): update module github.com/schollz/progressbar/v3 to v3.19.1
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-06-30 19:01:51 +00:00
renovate-bot f3943d25d1 chore(deps): update otel/weaver docker tag to v0.24.2
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-06-23 18:00:57 +00:00
renovate-bot 9859ff46f5 chore(deps): update otel/weaver docker tag to v0.24.1
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-06-21 21:01:00 +00:00
renovate-bot 3109a0da82 chore(deps): update otel/weaver docker tag to v0.24.0
continuous-integration/drone/push Build is passing
2026-06-20 00:01:07 +00:00
renovate-bot b1146a707e chore(deps): update nginx docker tag to v1.31.2
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-06-17 23:01:19 +00:00
Weblate 6d13d14d75 chore: update translation files
continuous-integration/drone/push Build is passing
Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate.

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/
2026-06-16 17:41:06 +00:00
Linus Gasser 427ec22cab Running i18n
continuous-integration/drone/push Build is passing
2026-06-16 17:40:58 +00:00
Linus Gasser 1111b69f12 Use docker login credentials from host
I had a lot of failures for pulling the docker images lately,
so I was looking for a way to connect using docker login.
This PR sends the docker login credentials from the host to
the swarm server.
2026-06-16 17:40:58 +00:00
Linus Gasser 1541e6aa6a Also use xgettext-go@latest for .drone.yml
continuous-integration/drone/push Build is passing
2026-06-14 18:37:34 +02:00
Linus Gasser 14ee80582a Use go run for xgettext-go
continuous-integration/drone/pr Build is passing
Before: on a mac, the Makefile downloaded a linux file, which did not work
for updating the i18n.

Now: use 'go run' to run the xgettext-go file. go caches it, so it compiles only
once.
2026-06-14 18:28:45 +02:00
renovate-bot e5c65b8fa0 chore(deps): update alpine docker tag to v3.24
continuous-integration/drone/push Build is passing
2026-06-09 21:00:57 +00:00
Weblate e1b10f6020 chore: update translation files
continuous-integration/drone/push Build is passing
Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate.

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/
2026-06-09 14:36:32 +00:00
moritz 8e4ed7b689 chore: make i18n
continuous-integration/drone/push Build is passing
2026-06-09 16:30:08 +02:00
moritz a44fde2df2 fix(new): checkout given recipeVersion before generating env closes #862
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2026-06-09 16:09:58 +02:00
renovate-bot e623f55852 chore(deps): update golang docker tag
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-06-08 17:01:04 +00:00
renovate-bot 11e6f28a60 chore(deps): update module golang.org/x/term to v0.44.0
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-06-08 16:01:46 +00:00
renovate-bot acec067d76 chore(deps): update golang docker tag
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-06-08 14:01:12 +00:00
renovate-bot f50a57af7a chore(deps): update module golang.org/x/sys to v0.46.0
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-06-08 13:01:32 +00:00
renovate-bot 776693acc0 chore(deps): update golang docker tag
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-22 23:01:07 +00:00
renovate-bot 5dea5f7746 chore(deps): update module golang.org/x/sys to v0.45.0
continuous-integration/drone/push Build is passing
2026-05-22 22:01:49 +00:00
renovate-bot 1d9a289888 chore(deps): update nginx docker tag to v1.31.1
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-22 21:00:50 +00:00
devydave c7bd55e371 feat: adds nix flake
continuous-integration/drone/push Build is passing
2026-05-20 22:29:47 +00:00
renovate-bot 4276337b0f chore(deps): update golang docker tag
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-18 22:01:06 +00:00
renovate-bot 90ca856b64 chore(deps): update module github.com/go-git/go-git/v5 to v5.19.1
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-18 21:01:33 +00:00
renovate-bot f2dd65491d chore(deps): update golang docker tag
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-15 16:01:27 +00:00
renovate-bot 0e902ed897 chore(deps): update coopcloud.tech/tagcmp digest to c26951b
continuous-integration/drone/push Build is passing
2026-05-15 14:01:42 +00:00
renovate-bot db001c1ba4 chore(deps): update tonistiigi/xx docker tag to v1.9.0
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-15 00:00:59 +00:00
renovate-bot e4215c09aa chore(deps): update nginx docker tag to v1.31.0
continuous-integration/drone/push Build is passing
2026-05-14 23:04:17 +00:00
renovate-bot e0e6dcb710 chore(deps): update otel/weaver docker tag to v0.23.0
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-14 23:00:58 +00:00
renovate-bot e7ddb74a08 chore(deps): update module golang.org/x/term to v0.43.0
continuous-integration/drone/push Build is passing
2026-05-14 22:04:37 +00:00
renovate-bot 24a5e6334f chore(deps): update golang docker tag
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-14 22:00:58 +00:00
renovate-bot 9d8eb2317e chore(deps): update module golang.org/x/sys to v0.44.0
continuous-integration/drone/push Build is passing
2026-05-14 21:01:45 +00:00
renovate-bot 5945ea8e1b chore(deps): update module github.com/go-git/go-git/v5 to v5.19.0
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-14 20:01:30 +00:00
renovate-bot e170d1c971 chore(deps): update alpine docker tag to v3.23
continuous-integration/drone/push Build is passing
2026-05-14 19:04:12 +00:00
renovate-bot 5eba3abb1b chore(deps): update golang docker tag
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-14 19:01:12 +00:00
renovate-bot df5a38e887 chore(deps): update module github.com/decentral1se/cobra to v1.10.2
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-05-14 18:01:50 +00:00
decentral1se 1e80d111e6 test: less flaky test
continuous-integration/drone/push Build is passing
2026-05-02 09:56:40 +02:00
Weblate 7a079b78de chore: update translation files
continuous-integration/drone/push Build is passing
Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate.

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/
2026-04-11 13:54:52 +00:00
iexos 7524a785ce chore: make i18n
continuous-integration/drone/push Build is failing
2026-04-11 11:36:14 +02:00
iexos 1013f669bb fix: i18n & clarify no release notes msg
continuous-integration/drone/pr Build is failing
2026-04-10 23:22:40 +02:00
decentral1se aae20f07cc fix: general golang path [ci skip] 2026-04-03 22:59:21 +02:00
decentral1se 6ef8e1ff52 test: temporarily skip flaky test
continuous-integration/drone/push Build is passing
See #814
2026-04-01 11:37:07 +02:00
decentral1se 7fb9675b1e chore: golang 1.26 && make deps
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2026-04-01 11:25:57 +02:00
decentral1se d88b478503 test: unit tests clean up themselves
continuous-integration/drone/push Build is passing
See #792
2026-04-01 10:47:32 +02:00
decentral1se 7a735043cd fix: unbork json [ci skip] 2026-03-30 09:40:13 +02:00
decentral1se e610f32c35 fix: ignore new forked deps [ci skip] 2026-03-29 18:15:47 +02:00
p4u1 e04a1e15c4 feat: add filename to error message when yaml file is invalid
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Before:
FATA <internal/validate.go:84> unable to validate recipe: yaml: line 3: did not find expected key

After:
FATA <internal/validate.go:84> unable to validate recipe: <redacted>/recipes/monitoring-ng/compose.grafana-oidc.yml: yaml: line 3: did not find expected key
2026-03-20 14:42:14 +01:00
decentral1se 9d401202b4 test: ensure "release, fail, release" works
continuous-integration/drone/push Build is passing
See #794
2026-03-05 11:28:34 +01:00
decentral1se 6504be6403 test: ensure reproducible version 2026-03-05 11:28:33 +01:00
Weblate d4944dbf35 chore: update translation files
continuous-integration/drone/push Build is passing
Updated by "Update PO files to match POT (msgmerge)" add-on in Weblate.

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/
2026-03-03 00:03:00 +00:00
decentral1se 8d8d4f799d chore: i18n
continuous-integration/drone/push Build is passing
2026-03-03 01:02:49 +01:00
iexos 0633f24d1b feat: recipe release reverts completely on failure
continuous-integration/drone/push Build is failing
2026-03-03 00:02:15 +00:00
decentral1se 2e062899c7 feat: release make target
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2026-03-01 09:42:59 +01:00
decentral1se fbd7275f03 build: remove broken goreleaser automagic
continuous-integration/drone/push Build is passing
See #780
2026-03-01 09:40:29 +01:00
ChasquiLabo cedf185e97 chore: translation using Weblate (Spanish)
continuous-integration/drone/push Build is passing
Currently translated at 91.1% (1048 of 1150 strings)

Translation: Co-op Cloud/abra
Translate-URL: https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/
2026-02-28 13:52:56 +00:00
812 changed files with 55513 additions and 80628 deletions
+15 -38
View File
@@ -3,17 +3,22 @@ kind: pipeline
name: coopcloud.tech/abra
steps:
- name: make check
image: golang:1.24
image: golang:1.26
commands:
- 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:
@@ -22,7 +27,7 @@ steps:
- tag
- name: xgettext-go status
image: golang:1.24-alpine3.22
image: golang:1.26-alpine3.22
commands:
- apk add patchutils git make
- cd /drone/src
@@ -38,42 +43,14 @@ steps:
- tag
- name: make test
image: golang:1.24
image: golang:1.26
environment:
ABRA_DIR: $HOME/.abra
CATL_URL: https://git.coopcloud.tech/toolshed/recipes-catalogue-json.git
ABRA_DIR: /root/.abra_test
commands:
- mkdir -p $HOME/.abra
- git clone $CATL_URL $HOME/.abra/catalogue
- make test
depends_on:
- make check
- name: fetch
image: docker:git
commands:
- git fetch --tags
depends_on:
- make check
- make test
when:
event: tag
- name: release
image: goreleaser/goreleaser:v2.5.1
environment:
GITEA_TOKEN:
from_secret: goreleaser_gitea_token
volumes:
- name: deps
path: /go
commands:
- goreleaser release
depends_on:
- fetch
when:
event: tag
- name: publish image
image: plugins/docker
settings:
+2 -3
View File
@@ -1,5 +1,5 @@
# Build image
FROM golang:1.24-alpine AS build
FROM golang:1.26-alpine AS build
ENV GOPRIVATE=coopcloud.tech
@@ -15,8 +15,7 @@ WORKDIR /app
RUN CGO_ENABLED=0 make build
# Release image ("slim")
FROM alpine:3.19.1
FROM alpine:3.24
RUN apk add --no-cache \
ca-certificates \
+50 -10
View File
@@ -1,8 +1,8 @@
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.24
GOVERSION := 1.26
LDFLAGS := "-X 'main.Commit=$(COMMIT)'"
DIST_LDFLAGS := $(LDFLAGS)" -s -w"
BFLAGS := -v -trimpath
@@ -40,7 +40,7 @@ check:
(echo "gofmt: formatting issue - run 'make format' to resolve" && exit 1)
test:
@go test ./... -cover -v
@go test ./... -cover -v -p 1
find-tests:
@find . -name "*_test.go"
@@ -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
@@ -86,3 +81,48 @@ build-mo:
for lang in $(POFILES); do \
msgfmt $$lang -o $$(echo $$lang | sed 's/.po/.mo/g') --statistics; \
done
release:
@goreleaser release --clean
CLIENT_VM := abra-client
vm-client-status:
sudo systemctl status microvm@$(CLIENT_VM).service
vm-client-create:test-integration-hosts
sudo microvm -f git+file://$$(pwd) -c $(CLIENT_VM)
vm-client-start:
sudo systemctl start microvm@$(CLIENT_VM).service
vm-client-update:test-integration-hosts
sudo microvm -R -f git+file://$$(pwd) -u $(CLIENT_VM)
vm-client-run:
sudo microvm -f git+file://$$(pwd) -r $(CLIENT_VM)
vm-client-stop:
sudo systemctl stop microvm@$(CLIENT_VM)
vm-client-delete: vm-client-stop
sudo rm -rf /var/lib/microvms/$(CLIENT_VM)
vm-client-connect:
ssh abra@10.0.0.2 -i ./tests/resources/local_integration_ssh
SERVER_VM := abra-server
vm-server-status:
sudo systemctl status microvm@$(SERVER_VM).service
vm-server-create:
sudo microvm -f git+file://$$(pwd) -c $(SERVER_VM)
vm-server-start:
sudo systemctl start microvm@$(SERVER_VM).service
vm-server-update:
sudo microvm -R -f git+file://$$(pwd) -u $(SERVER_VM)
vm-server-run:
sudo microvm -f git+file://$$(pwd) -r $(SERVER_VM)
vm-server-stop:
sudo systemctl stop microvm@$(SERVER_VM)
vm-server-delete: vm-server-stop
sudo rm -rf /var/lib/microvms/$(SERVER_VM)
vm-server-connect:
ssh abra@10.0.0.3 -i ./tests/resources/local_integration_ssh
test-integration-hosts:
./scripts/tests/extra_hosts
test-integration:
ssh-add ./tests/resources/local_integration_ssh
@bats -Tp tests/integration --verbose-run
+6 -5
View File
@@ -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 {
+5 -1
View File
@@ -112,7 +112,11 @@ var AppNewCommand = &cobra.Command{
}
}
if len(recipeVersions) > 0 {
if recipeVersion != "" {
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
log.Fatal(err)
}
} else if len(recipeVersions) > 0 {
latest := recipeVersions[len(recipeVersions)-1]
for tag := range latest {
recipeVersion = tag
+6 -5
View File
@@ -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)
+7 -6
View File
@@ -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)
@@ -238,7 +239,7 @@ beforehand. See "abra app backup" for more.`),
if upgradeReleaseNotes == "" {
upgradeWarnMessages = append(
upgradeWarnMessages,
fmt.Sprintf("no release notes available for %s", chosenUpgrade),
i18n.G("no release notes for upgrading from %s to %s", deployMeta.Version, chosenUpgrade),
)
}
+30 -52
View File
@@ -97,6 +97,16 @@ your private key and enter your passphrase beforehand.
log.Fatal(i18n.G("main app service version for %s is empty?", recipe.Name))
}
repo, err := git.PlainOpen(recipe.Dir)
if err != nil {
log.Fatal(err)
}
preCommitHead, err := repo.Head()
if err != nil {
log.Fatal(err)
}
isClean, err := gitPkg.IsClean(recipe.Dir)
if err != nil {
log.Fatal(err)
@@ -201,11 +211,6 @@ likely to change.
}
if tagString == "" {
repo, err := git.PlainOpen(recipe.Dir)
if err != nil {
log.Fatal(err)
}
var lastGitTag tagcmp.Tag
iter, err := repo.Tags()
if err != nil {
@@ -282,16 +287,6 @@ likely to change.
log.Fatal(i18n.G("invalid version %s specified", tagString))
}
mainService := "app"
label := i18n.G("coop-cloud.${STACK_NAME}.version=%s", tagString)
if !internal.Dry {
if err := recipe.UpdateLabel("compose.y*ml", mainService, label); err != nil {
log.Fatal(err)
}
} else {
log.Info(i18n.G("dry run: not syncing label %s for recipe %s", tagString, recipe.Name))
}
for _, tag := range tags {
previousTagLeftHand := strings.Split(tag, "+")[0]
newTagStringLeftHand := strings.Split(tagString, "+")[0]
@@ -300,24 +295,15 @@ likely to change.
}
}
repo, err := git.PlainOpen(recipe.Dir)
if err != nil {
log.Fatal(err)
}
preCommitHead, err := repo.Head()
if err != nil {
log.Fatal(err)
}
if err := createReleaseFromTag(recipe, tagString, mainAppVersion); err != nil {
if cleanErr := cleanTag(recipe, tagString); cleanErr != nil {
log.Fatal(cleanErr)
log.Fatal(i18n.G("unable to clean up tag after failed release attempt: %s", cleanErr))
}
if cleanErr := cleanCommit(recipe, preCommitHead); cleanErr != nil {
log.Fatal(cleanErr)
if resetErr := resetCommit(recipe, preCommitHead); resetErr != nil {
log.Fatal(i18n.G("unable to reset commit after failed release attempt: %s", resetErr))
}
log.Fatal(err)
log.Error(err)
log.Fatal(i18n.G("release failed. any changes made have been reverted"))
}
},
}
@@ -375,23 +361,14 @@ func createReleaseFromTag(recipe recipePkg.Recipe, tagString, mainAppVersion str
return err
}
tag, err := tagcmp.Parse(tagString)
if err != nil {
return err
}
if tag.MissingMinor {
tag.Minor = "0"
tag.MissingMinor = false
}
if tag.MissingPatch {
tag.Patch = "0"
tag.MissingPatch = false
}
if tagString == "" {
tagString = fmt.Sprintf("%s+%s", tag.String(), mainAppVersion)
mainService := "app"
label := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", tagString)
if !internal.Dry {
if err := recipe.UpdateLabel("compose.y*ml", mainService, label); err != nil {
log.Fatal(err)
}
} else {
log.Info(i18n.G("dry run: not syncing label %s for recipe %s", tagString, recipe.Name))
}
if err := addReleaseNotes(recipe, tagString); err != nil {
@@ -587,9 +564,10 @@ func pushRelease(recipe recipePkg.Recipe, tagString string) error {
return nil
}
// cleanCommit soft removes the latest release commit. No change are lost the
// the commit itself is removed. This is the equivalent of `git reset HEAD~1`.
func cleanCommit(recipe recipePkg.Recipe, head *plumbing.Reference) error {
// resetCommit hard resets to the state before release was started.
// This will only remove changes made by the release process due to requiring
// a clean working directory.
func resetCommit(recipe recipePkg.Recipe, head *plumbing.Reference) error {
repo, err := git.PlainOpen(recipe.Dir)
if err != nil {
return errors.New(i18n.G("unable to open repo in %s: %s", recipe.Dir, err))
@@ -600,12 +578,12 @@ func cleanCommit(recipe recipePkg.Recipe, head *plumbing.Reference) error {
return errors.New(i18n.G("unable to open work tree in %s: %s", recipe.Dir, err))
}
opts := &git.ResetOptions{Commit: head.Hash(), Mode: git.MixedReset}
opts := &git.ResetOptions{Commit: head.Hash(), Mode: git.HardReset}
if err := worktree.Reset(opts); err != nil {
return errors.New(i18n.G("unable to soft reset %s: %s", recipe.Dir, err))
return errors.New(i18n.G("unable to hard reset %s: %s", recipe.Dir, err))
}
log.Debug(i18n.G("removed freshly created commit"))
log.Debug(i18n.G("reset commit to pre-release state"))
return nil
}
Generated
+99
View File
@@ -0,0 +1,99 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"microvm": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"spectrum": "spectrum"
},
"locked": {
"lastModified": 1784666190,
"narHash": "sha256-xgfS6slV7J3baMooNN1UuBi51RIgg9y0DbCxfSA0668=",
"owner": "astro",
"repo": "microvm.nix",
"rev": "fa5340ac684cdce8a22b6d4a0bcebb0cc999275e",
"type": "github"
},
"original": {
"owner": "astro",
"repo": "microvm.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1778443072,
"narHash": "sha256-zi7/fsqM/kFdNuED//4WOCUtezGtKKqRNORjMvfwjnA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "da5ad661ba4e5ef59ba743f0d112cbc30e474f32",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"microvm": "microvm",
"nixpkgs": "nixpkgs"
}
},
"spectrum": {
"flake": false,
"locked": {
"lastModified": 1783694892,
"narHash": "sha256-xO8f7Qng+18FK2UlB9vcrkxCaQMCt5WjCH24aW/11eg=",
"ref": "refs/heads/main",
"rev": "24c4346e30fdea8d8e80f34aec3554a15a667d24",
"revCount": 1410,
"type": "git",
"url": "https://spectrum-os.org/git/spectrum"
},
"original": {
"type": "git",
"url": "https://spectrum-os.org/git/spectrum"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+106
View File
@@ -0,0 +1,106 @@
{
description = "The Co-op Cloud utility belt";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
microvm = {
url = "github:astro/microvm.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
microvm,
flake-utils,
}:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
publicKey = builtins.readFile ./tests/resources/local_integration_ssh.pub;
privateKey = builtins.readFile ./tests/resources/local_integration_ssh;
pathToRepo = builtins.readFile ./tests/resources/path_to_repo;
# local DNS aliases for the server host
extraHosts = builtins.readFile ./tests/resources/extra_hosts;
username = "abra";
password = "abra";
defaultDNS = [
# Quad9.net
"9.9.9.9"
"149.112.112.112"
"2620:fe::fe"
"2620:fe::9"
];
in
{
nixosConfigurations = {
abra-client = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
microvm.nixosModules.microvm
./nix/hosts/client/configuration.nix
];
specialArgs = {
inherit
publicKey
privateKey
username
password
defaultDNS
extraHosts
pathToRepo
;
};
};
abra-server = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
microvm.nixosModules.microvm
./nix/hosts/server/configuration.nix
];
specialArgs = {
inherit
publicKey
privateKey
username
password
defaultDNS
;
};
};
};
nixosModules = rec {
host = {
imports = [
microvm.nixosModules.host
./nix/modules/host.nix
];
};
default = host;
};
packages = rec {
abra = pkgs.callPackage ./nix/package.nix { };
default = abra;
};
apps = rec {
abra = flake-utils.lib.mkApp { drv = self.packages.${system}.abra; };
default = abra;
};
devShells.${system}.default = pkgs.mkShell {
# testing env variables
BATS_LIB_PATH = "~/.local/share/bats/";
TEST_SERVER = "abra.local";
ABRA_DIR = "$HOME/.abra_test";
packages = with pkgs; [
go_1_26
gnumake
gopls
];
};
};
}
+55 -56
View File
@@ -1,28 +1,26 @@
module coopcloud.tech/abra
go 1.24.0
toolchain go1.24.1
go 1.26.0
require (
coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca
coopcloud.tech/tagcmp v0.0.0-20260515102403-c26951b55977
git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/charmbracelet/log v0.4.2
github.com/charmbracelet/log v1.0.0
github.com/distribution/reference v0.6.0
github.com/docker/cli v28.4.0+incompatible
github.com/docker/docker v28.5.2+incompatible
github.com/docker/go-units v0.5.0
github.com/go-git/go-git/v5 v5.16.2
github.com/go-git/go-git/v5 v5.19.1
github.com/google/go-cmp v0.7.0
github.com/leonelquinteros/gotext v1.7.2
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.18.0
golang.org/x/term v0.35.0
github.com/schollz/progressbar/v3 v3.19.1
golang.org/x/term v0.44.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.2
)
@@ -30,27 +28,28 @@ require (
require (
dario.cat/mergo v1.0.2 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/ProtonMail/go-crypto v1.4.1 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/colorprofile v0.3.2 // indirect
github.com/charmbracelet/x/ansi v0.10.2 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
github.com/charmbracelet/x/ansi v0.11.6 // indirect
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/cyphar/filepath-securejoin v0.5.0 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/go-connections v0.6.0 // indirect
@@ -61,26 +60,26 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-git/go-billy/v5 v5.9.0 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/kevinburke/ssh_config v1.6.0 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/lucasb-eyer/go-colorful v1.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.19 // indirect
github.com/mattn/go-runewidth v0.0.21 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
@@ -89,7 +88,7 @@ require (
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/morikuni/aec v1.1.0 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
@@ -97,43 +96,43 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/runc v1.1.13 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/pjbgf/sha1cd v0.5.0 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.20.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/skeema/knownhosts v1.3.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
go.opentelemetry.io/otel v1.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.opentelemetry.io/otel/metric v1.42.0 // indirect
go.opentelemetry.io/otel/sdk v1.42.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.42.0 // indirect
go.opentelemetry.io/otel/trace v1.42.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/time v0.13.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect
google.golang.org/grpc v1.75.1 // indirect
google.golang.org/protobuf v1.36.9 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/time v0.15.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/grpc v1.80.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
@@ -142,12 +141,12 @@ require (
github.com/containers/image v3.0.2+incompatible
github.com/containers/storage v1.38.2 // indirect
github.com/decentral1se/passgen v1.0.1
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/docker-credential-helpers v0.9.5 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.8
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/patternmatcher v0.6.1 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
@@ -156,9 +155,9 @@ 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.36.0
golang.org/x/sys v0.46.0
)
replace github.com/docker/cli v28.4.0+incompatible => git.coopcloud.tech/toolshed/docker-cli v28.5.3-0.20260202112816-30df2d0b3a00+incompatible
replace github.com/spf13/cobra => github.com/decentral1se/cobra v1.10.2-i18n
replace github.com/spf13/cobra => github.com/decentral1se/cobra v1.10.2
+110 -108
View File
@@ -22,8 +22,8 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca h1:gSD53tBAsbIGq4SnFfq+mEep6foekQ2a5ea7b38qkm0=
coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
coopcloud.tech/tagcmp v0.0.0-20260515102403-c26951b55977 h1:J7I0HFjwVAj/kkX6lwSTHmlXDRjQRsdIFNUUqu55ADY=
coopcloud.tech/tagcmp v0.0.0-20260515102403-c26951b55977/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
@@ -51,8 +51,8 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
@@ -81,8 +81,8 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
@@ -136,20 +136,20 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=
github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
github.com/charmbracelet/x/ansi v0.10.2 h1:ith2ArZS0CJG30cIUfID1LXN7ZFXRCww6RUvAPA+Pzw=
github.com/charmbracelet/x/ansi v0.10.2/go.mod h1:HbLdJjQH4UH4AqA2HpRWuWNluRE6zxJH/yteYEYCFa8=
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/log v1.0.0 h1:HVVVMmfOorfj3BA9i8X8UL69Hoz9lI0PYwXfJvOdRc4=
github.com/charmbracelet/log v1.0.0/go.mod h1:uYgY3SmLpwJWxmlrPwXvzVYujxis1vAKRV/0VQB7yWA=
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
@@ -165,11 +165,13 @@ github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -297,8 +299,8 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/cyphar/filepath-securejoin v0.5.0 h1:hIAhkRBMQ8nIeuVwcAoymp7MY4oherZdAxD+m0u9zaw=
github.com/cyphar/filepath-securejoin v0.5.0/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
@@ -306,8 +308,8 @@ github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjI
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decentral1se/cobra v1.10.2-i18n h1:XR+6AHHfnf4k5NM9f09oLMrEVwz3rkQIAIcqgL8R08g=
github.com/decentral1se/cobra v1.10.2-i18n/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/decentral1se/cobra v1.10.2 h1:MZ8Ifi/jRels9sZrpSccDbUlK++3b2HlBODfv0Bh6x0=
github.com/decentral1se/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/decentral1se/passgen v1.0.1 h1:j2AxK/kHKxDHWZZfkJj8Wgae9+O+DYEqR5sjKthIYKA=
github.com/decentral1se/passgen v1.0.1/go.mod h1:530V+lNoPhKtkrX2fIVsIfLhkl47CuiOM7HRgi7C+SU=
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
@@ -327,8 +329,8 @@ github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
@@ -387,12 +389,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00=
github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -401,8 +403,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -423,8 +425,8 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
@@ -524,8 +526,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -573,8 +575,8 @@ github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVE
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -582,8 +584,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@@ -607,8 +609,8 @@ github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -621,13 +623,13 @@ 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=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
@@ -653,8 +655,8 @@ github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6U
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
@@ -679,8 +681,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
@@ -750,8 +753,8 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -779,8 +782,8 @@ github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7q
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -791,8 +794,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -805,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.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA=
github.com/schollz/progressbar/v3 v3.18.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=
@@ -821,10 +824,10 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -912,37 +915,37 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 h1:MdKucPl/HbzckWWEisiNqMPhRrAOQX8r4jTuGr636gk=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0/go.mod h1:RolT8tWtfHcjajEH5wFIZ4Dgh5jpPdFXYV9pTAk/qjc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE=
go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -963,8 +966,8 @@ golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -975,8 +978,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1040,8 +1043,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1136,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.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
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/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.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
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=
@@ -1153,16 +1155,16 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1212,8 +1214,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -1258,10 +1260,10 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 h1:8XJ4pajGwOlasW+L13MnEGA8W4115jJySQtVfS2/IBU=
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 h1:i8QOKZfYg6AbGVZzUAY3LrNWCKF8O6zFisU9Wl9RER4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1281,8 +1283,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1296,8 +1298,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU=
+72
View File
@@ -0,0 +1,72 @@
# Hosts
These hosts are VMs used for running the integration tests on your local machine.
The hosts contain a client where abra with the tests will be run and a server
to simulate a remote machine.
## Prepare the host
For this setup to work you need a machine with NixOS and flakes enabled.
1. Import the abra flake
The install example is based on the [using nix flakes wiki page](https://nixos.wiki/wiki/flakes#Using_nix_flakes_with_NixOS).
```nix
inputs = {
abra = {
url = "git+https://git.coopcloud.tech/toolshed/abra.git";
};
};
```
2. Add the host module to your configuration
Now add the host module to your configuration. At the toplevel of a flake it could look like this:
```nix
desktop = inputs.nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
abra.nixosModules.host
./configuration.nix
];
specialArgs = inputs;
};
```
3. Adjust the required config
```nix
abra.testing = {
enable = true; # loads the required config, when set true
externalInterface = "eth1"; # adjust this to your network interface that has access to the internet
};
```
You can look up the interface with `ip link`. After that rebuild your system and you are ready to go.
## Get started
All commands are run on the repository root path.
1. Set path to repo
The VM will create a shared volume from the content of the file `tests/resources/path_to_repo`.
Set the content of the file to the path of your abra repository.
This will share the repository from your machine with the VM and you can edit files while testing without rebuiling
or restarting the VM.
2. Create the VMs
```sh
make vm-client-create
make vm-server-create
```
3. Start the VMs
```sh
make vm-client-start
make vm-server-start
```
After running that command you should be able to ping the machines.
The client runs on 10.0.0.2 and server 10.0.0.3
4. Connect to the client
Running the following command will connect to the client VM via SSH.
```sh
make vm-client-connect
```
5. Run the tests
```sh
make test-integration
```
+145
View File
@@ -0,0 +1,145 @@
{
lib,
pkgs,
publicKey,
username,
pathToRepo,
defaultDNS,
extraHosts,
...
}:
let
index = 2;
mac = "00:00:00:00:00:01";
serverIp = "10.0.0.3";
in
{
imports = [
../../modules/ssh.nix
../../modules/user.nix
../../modules/docker.nix
];
microvm = {
vcpu = 4;
mem = 2049; # see issue related in microvm repo with 2048, so add 1MB
interfaces = [
{
id = "vm${toString index}";
type = "tap";
inherit mac;
}
];
shares = [
{
proto = "virtiofs";
tag = "repo";
# Source path can be absolute or relative
# to /var/lib/microvms/$hostName
source = "${lib.trim pathToRepo}";
mountPoint = "/home/${username}/abra";
}
];
};
boot.tmp = {
useTmpfs = true;
tmpfsSize = "2G";
};
networking = {
hostName = "abra-client";
useNetworkd = true;
};
systemd.network.networks."10-eth" = {
matchConfig.MACAddress = mac;
# Static IP configuration
address = [
"10.0.0.${toString index}/32"
"fec0::${lib.toHexString index}/128"
];
routes = [
{
# A route to the host
Destination = "10.0.0.0/32";
GatewayOnLink = true;
}
{
# Route to server
Destination = "${serverIp}/32";
Gateway = "10.0.0.0";
GatewayOnLink = true;
}
{
# Default route
Destination = "0.0.0.0/0";
Gateway = "10.0.0.0";
GatewayOnLink = true;
}
{
# Default route
Destination = "::/0";
Gateway = "fec0::";
GatewayOnLink = true;
}
];
networkConfig = {
# DNS servers no longer come from DHCP nor Router
# Advertisements. Perhaps you want to change the defaults:
DNS = defaultDNS;
};
};
# open the ports to allow ssh access from the host
networking.firewall.allowedTCPPorts = [ 22 ];
networking.firewall.allowedUDPPorts = [ 22 ];
networking.extraHosts = extraHosts;
environment.variables = {
CGO_ENABLED = 0;
TEST_SERVER = "abra.local";
ABRA_DIR = "$HOME/.abra_test";
};
programs.ssh = {
startAgent = true;
knownHostsFiles = [
(pkgs.writeText "local.keys" ''
abra.local ${publicKey}
'')
];
extraConfig = ''
Host abra.local
HostName abra.local
Port 22
User ${username}
'';
};
programs.git = {
enable = true;
# many recipe commands need a preset git user
config = [
{
user = {
name = "abra dev";
email = "helo@coopcloud.tech";
};
}
];
};
environment.systemPackages = with pkgs; [
# build dependencies, copied from flake.nix
go_1_26
gnumake
# testing dependencies
(bats.withLibraries (p: [
p.bats-assert
p.bats-file
p.bats-support
]))
jq
wget
];
}
+42
View File
@@ -0,0 +1,42 @@
{
serverIp,
username,
...
}:
{
# Home Manager needs a bit of information about you and the
# paths it should manage.
home.username = username;
home.homeDirectory = "/home/${username}";
# This value determines the Home Manager release that your
# configuration is compatible with. This helps avoid breakage
# when a new Home Manager release introduces backwards
# incompatible changes.
#
# You can update Home Manager without changing this value. See
# the Home Manager release notes for a list of state version
# changes in each release.
home.stateVersion = "26.05";
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
programs.ssh = {
enable = true;
enableDefaultConfig = false;
settings = {
"abra.local" = {
HostName = serverIp;
User = username;
Port = 22;
IdentityFile = "/home/${username}/abra/tests/resources/local_integration_ssh";
};
"*.abra.local" = {
HostName = serverIp;
User = username;
Port = 22;
IdentityFile = "/home/${username}/abra/tests/resources/local_integration_sshn";
};
};
};
}
+91
View File
@@ -0,0 +1,91 @@
{
lib,
defaultDNS,
...
}:
let
index = 3;
mac = "00:00:00:00:00:02";
clientIp = "10.0.0.2";
in
{
imports = [
../../modules/ssh.nix
../../modules/user.nix
../../modules/docker.nix
];
microvm = {
vcpu = 4;
mem = 8097; # see issue related in microvm repo with 2048, so add 1MB
interfaces = [
{
id = "vm${toString index}";
type = "tap";
inherit mac;
}
];
};
boot.tmp = {
useTmpfs = true;
tmpfsSize = "8G";
};
networking = {
hostName = "abra-server";
useNetworkd = true;
};
systemd.network.networks."11-eth" = {
matchConfig.MACAddress = mac;
# Static IP configuration
address = [
"10.0.0.${toString index}/32"
"fec0::${lib.toHexString index}/128"
];
routes = [
{
# A route to the host
Destination = "10.0.0.0/32";
GatewayOnLink = true;
}
{
# Route to server
Destination = "${clientIp}/32";
Gateway = "10.0.0.0";
GatewayOnLink = true;
}
{
# Default route
Destination = "0.0.0.0/0";
Gateway = "10.0.0.0";
GatewayOnLink = true;
}
{
# Default route
Destination = "::/0";
Gateway = "fec0::";
GatewayOnLink = true;
}
];
networkConfig = {
# DNS servers no longer come from DHCP nor Router
# Advertisements. Perhaps you want to change the defaults:
DNS = defaultDNS;
};
};
# open the ports to allow ssh access from the host
networking.firewall.allowedTCPPorts = [
22
80
443
1312 # deploy with udp and tcp on same port test
];
networking.firewall.allowedUDPPorts = [
22
80
443
1312 # deploy with udp and tcp on same port test
];
}
+7
View File
@@ -0,0 +1,7 @@
{ ... }:
{
virtualisation.docker = {
enable = true;
liveRestore = false;
};
}
+59
View File
@@ -0,0 +1,59 @@
{
lib,
config,
...
}:
let
maxVMs = 3;
in
{
options = {
abra.testing.enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Enable the abra integration testing framework";
};
abra.testing.externalInterface = lib.mkOption {
type = lib.types.str;
default = null;
description = "Change this to the interface with upstream Internet access";
};
};
config = lib.mkIf config.abra.testing.enable {
networking.useNetworkd = true;
systemd.network.wait-online.enable = false;
systemd.network.networks = builtins.listToAttrs (
map (index: {
name = "30-vm${toString index}";
value = {
matchConfig.Name = "vm${toString index}";
# Host's addresses
address = [
"10.0.0.0/32"
"fec0::/128"
];
# Setup routes to the VM
routes = [
{
Destination = "10.0.0.${toString index}/32";
}
{
Destination = "fec0::${lib.toHexString index}/128";
}
];
# Enable routing
networkConfig = {
IPv4Forwarding = true;
IPv6Forwarding = true;
};
};
}) (lib.genList (i: i + 1) maxVMs)
);
networking.nat = {
enable = true;
internalIPs = [ "10.0.0.0/24" ];
externalInterface = config.abra.testing.externalInterface;
};
};
}
+33
View File
@@ -0,0 +1,33 @@
{
privateKey,
publicKey,
username,
...
}:
{
# default host key location
environment.etc."ssh/ssh_host_ed25519_key" = {
text = privateKey;
mode = "0600";
};
environment.etc."ssh/ssh_host_ed25519_key.pub" = {
text = publicKey;
mode = "0644";
};
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "no";
AllowUsers = [ "${username}" ];
};
generateHostKeys = false;
hostKeys = [
{
path = "/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
];
};
}
+22
View File
@@ -0,0 +1,22 @@
{
username,
password,
publicKey,
...
}:
{
users.users."${username}" = {
isNormalUser = true;
password = password;
home = "/home/${username}";
description = "Abra user";
extraGroups = [
"wheel"
"networkmanager"
"docker"
];
openssh.authorizedKeys.keys = [
publicKey
];
};
}
+54
View File
@@ -0,0 +1,54 @@
{
buildGo126Module,
fetchgit,
lib,
installShellFiles,
}:
buildGo126Module rec {
pname = "abra";
version = "0.13.0-beta";
rev = "06a57ded025a43c80f94d4e65299add8a31830dc";
src = fetchgit {
url = "https://git.coopcloud.tech/toolshed/abra.git";
tag = version;
hash = "sha256-rgoK0TY0WLSQ39lPvVM80zW/qJF40VFBSxYDOaKXZQo=";
};
vendorHash = null;
nativeBuildInputs = [
installShellFiles
];
env.CGO_ENABLED = 0;
buildPhase = ''
runHook preBuild
go build -ldflags="-s -w -X 'main.Commit=${rev}' -X 'main.Version=${version}'" ./cmd/abra
runHook postBuild
'';
installPhase = ''
runHook preInstall
install -D abra $out/bin/abra
runHook postInstall
'';
postInstall = ''
export ABRA_DIR="$out"
$out/bin/abra autocomplete bash >abra.bash
$out/bin/abra autocomplete fish >abra.fish
$out/bin/abra autocomplete zsh >abra.zsh
installShellCompletion abra.{bash,fish,zsh}
'';
meta = with lib; {
description = "The Co-op Cloud utility belt";
homepage = "https://docs.coopcloud.tech/abra";
changelog = "https://git.coopcloud.tech/toolshed/abra/releases/tag/${version}";
mainProgram = "abra";
license = licenses.gpl3Plus;
maintainers = "devydave";
};
}
+67 -18
View File
@@ -10,46 +10,83 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/envfile"
"coopcloud.tech/abra/pkg/recipe"
testPkg "coopcloud.tech/abra/pkg/test"
recipePkg "coopcloud.tech/abra/pkg/recipe"
"coopcloud.tech/abra/pkg/test"
"github.com/docker/docker/api/types/filters"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
)
var (
expectedAppEnv = envfile.AppEnv{
"DOMAIN": test.AppName,
"RECIPE": test.RecipeName,
}
expectedApp = appPkg.App{
Name: test.AppName,
Recipe: recipePkg.Get(expectedAppEnv["RECIPE"]),
Domain: expectedAppEnv["DOMAIN"],
Env: expectedAppEnv,
Path: expectedAppFile.Path,
Server: expectedAppFile.Server,
}
expectedAppFile = appPkg.AppFile{
Path: test.AppEnvPath,
Server: test.ServerName,
}
expectedAppFiles = map[string]appPkg.AppFile{
test.AppName: expectedAppFile,
}
)
func TestNewApp(t *testing.T) {
app, err := appPkg.NewApp(testPkg.ExpectedAppEnv, testPkg.AppName, testPkg.ExpectedAppFile)
app, err := appPkg.NewApp(expectedAppEnv, test.AppName, expectedAppFile)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(app, testPkg.ExpectedApp) {
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, testPkg.ExpectedApp)
if !reflect.DeepEqual(app, expectedApp) {
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, expectedApp)
}
}
func TestReadAppEnvFile(t *testing.T) {
app, err := appPkg.ReadAppEnvFile(testPkg.ExpectedAppFile, testPkg.AppName)
test.Setup()
t.Cleanup(func() { test.Teardown() })
app, err := appPkg.ReadAppEnvFile(expectedAppFile, test.AppName)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(app, testPkg.ExpectedApp) {
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, testPkg.ExpectedApp)
if !reflect.DeepEqual(app, expectedApp) {
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, expectedApp)
}
}
func TestGetApp(t *testing.T) {
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
test.Setup()
t.Cleanup(func() { test.Teardown() })
app, err := appPkg.GetApp(expectedAppFiles, test.AppName)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(app, testPkg.ExpectedApp) {
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, testPkg.ExpectedApp)
if !reflect.DeepEqual(app, expectedApp) {
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, expectedApp)
}
}
func TestGetComposeFiles(t *testing.T) {
r := recipe.Get("abra-test-recipe")
err := r.EnsureExists()
if err != nil {
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := recipe.Get(test.AbraTestRecipe)
if err := r.EnsureExists(); err != nil {
t.Fatal(err)
}
@@ -94,7 +131,10 @@ func TestGetComposeFiles(t *testing.T) {
}
func TestGetComposeFilesError(t *testing.T) {
r := recipe.Get("abra-test-recipe")
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := recipe.Get(test.AbraTestRecipe)
err := r.EnsureExists()
if err != nil {
t.Fatal(err)
@@ -186,26 +226,32 @@ func TestFilters(t *testing.T) {
func compareFilter(t *testing.T, f1 filters.Args, f2 map[string]map[string]bool) {
t.Helper()
j1, err := f1.MarshalJSON()
if err != nil {
t.Error(err)
}
j2, err := json.Marshal(f2)
if err != nil {
t.Error(err)
}
if diff := cmp.Diff(string(j2), string(j1)); diff != "" {
t.Errorf("filters mismatch (-want +got):\n%s", diff)
}
}
func TestWriteRecipeVersionOverwrite(t *testing.T) {
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
test.Setup()
t.Cleanup(func() { test.Teardown() })
app, err := appPkg.GetApp(expectedAppFiles, test.AppName)
if err != nil {
t.Fatal(err)
}
defer t.Cleanup(func() {
t.Cleanup(func() {
if err := app.WipeRecipeVersion(); err != nil {
t.Fatal(err)
}
@@ -217,7 +263,7 @@ func TestWriteRecipeVersionOverwrite(t *testing.T) {
t.Fatal(err)
}
app, err = appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
app, err = appPkg.GetApp(expectedAppFiles, test.AppName)
if err != nil {
t.Fatal(err)
}
@@ -226,7 +272,10 @@ func TestWriteRecipeVersionOverwrite(t *testing.T) {
}
func TestWriteRecipeVersionUnknown(t *testing.T) {
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
test.Setup()
t.Cleanup(func() { test.Teardown() })
app, err := appPkg.GetApp(expectedAppFiles, test.AppName)
if err != nil {
t.Fatal(err)
}
+4 -3
View File
@@ -4,6 +4,7 @@ import (
"testing"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/test"
testPkg "coopcloud.tech/abra/pkg/test"
stack "coopcloud.tech/abra/pkg/upstream/stack"
@@ -11,8 +12,8 @@ import (
)
func TestGetTimeoutFromLabel(t *testing.T) {
testPkg.MkServerAppRecipe()
defer testPkg.RmServerAppRecipe()
test.Setup()
t.Cleanup(func() { test.Teardown() })
tests := []struct {
configuredTimeout string
@@ -25,7 +26,7 @@ func TestGetTimeoutFromLabel(t *testing.T) {
}
for _, test := range tests {
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
app, err := appPkg.GetApp(expectedAppFiles, testPkg.AppName)
if err != nil {
t.Fatal(err)
}
+9 -6
View File
@@ -10,8 +10,9 @@ import (
func TestFindAbraConfig(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
t.Fatal(err)
}
tests := []struct {
Dir string
Config string
@@ -51,8 +52,9 @@ func TestFindAbraConfig(t *testing.T) {
func TestLoadAbraConfigGetAbraDir(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
t.Fatal(err)
}
t.Setenv("ABRA_DIR", "")
t.Run("default", func(t *testing.T) {
@@ -67,7 +69,7 @@ func TestLoadAbraConfigGetAbraDir(t *testing.T) {
t.Cleanup(func() { os.Chdir(wd) })
err = os.Chdir(filepath.Join(wd, "testdata/abraconfig1"))
if err != nil {
log.Fatal(err)
t.Fatal(err)
}
cfg := LoadAbraConfig()
@@ -81,7 +83,7 @@ func TestLoadAbraConfigGetAbraDir(t *testing.T) {
t.Cleanup(func() { os.Chdir(wd) })
err := os.Chdir(filepath.Join(wd, "testdata/abraconfig2"))
if err != nil {
log.Fatal(err)
t.Fatal(err)
}
cfg := LoadAbraConfig()
@@ -104,8 +106,9 @@ func TestLoadAbraConfigGetAbraDir(t *testing.T) {
func TestLoadAbraConfigServersDir(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
t.Fatal(err)
}
t.Setenv("ABRA_DIR", "")
t.Run("default", func(t *testing.T) {
@@ -120,7 +123,7 @@ func TestLoadAbraConfigServersDir(t *testing.T) {
t.Cleanup(func() { os.Chdir(wd) })
err = os.Chdir(filepath.Join(wd, "testdata/abraconfig1"))
if err != nil {
log.Fatal(err)
t.Fatal(err)
}
cfg := LoadAbraConfig()
+67 -29
View File
@@ -10,48 +10,73 @@ import (
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/envfile"
"coopcloud.tech/abra/pkg/recipe"
testPkg "coopcloud.tech/abra/pkg/test"
"coopcloud.tech/abra/pkg/test"
"github.com/stretchr/testify/assert"
)
var (
expectedAppEnv = envfile.AppEnv{
"DOMAIN": test.AppName,
"RECIPE": test.RecipeName,
}
expectedAppFile = appPkg.AppFile{
Path: test.AppEnvPath,
Server: test.ServerName,
}
expectedAppFiles = map[string]appPkg.AppFile{
test.AppName: expectedAppFile,
}
)
func TestGetAllFoldersInDirectory(t *testing.T) {
folders, err := config.GetAllFoldersInDirectory(testPkg.TestDir)
folders, err := config.GetAllFoldersInDirectory(test.TestDir)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(folders, testPkg.TFolders) {
t.Fatalf("did not get expected folders. Expected: (%s), Got: (%s)", strings.Join(testPkg.TFolders, ","), strings.Join(folders, ","))
if !reflect.DeepEqual(folders, test.TFolders) {
t.Fatalf("did not get expected folders. Expected: (%s), Got: (%s)", strings.Join(test.TFolders, ","), strings.Join(folders, ","))
}
}
func TestGetAllFilesInDirectory(t *testing.T) {
files, err := config.GetAllFilesInDirectory(testPkg.TestDir)
files, err := config.GetAllFilesInDirectory(test.TestDir)
if err != nil {
t.Fatal(err)
}
var fileNames []string
for _, file := range files {
fileNames = append(fileNames, file.Name())
}
if !reflect.DeepEqual(fileNames, testPkg.TFiles) {
t.Fatalf("did not get expected files. Expected: (%s), Got: (%s)", strings.Join(testPkg.TFiles, ","), strings.Join(fileNames, ","))
if !reflect.DeepEqual(fileNames, test.TFiles) {
t.Fatalf("did not get expected files. Expected: (%s), Got: (%s)", strings.Join(test.TFiles, ","), strings.Join(fileNames, ","))
}
}
func TestReadEnv(t *testing.T) {
env, err := envfile.ReadEnv(testPkg.ExpectedAppFile.Path)
test.Setup()
t.Cleanup(func() { test.Teardown() })
env, err := envfile.ReadEnv(expectedAppFile.Path)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(env, testPkg.ExpectedAppEnv) {
if !reflect.DeepEqual(env, expectedAppEnv) {
t.Fatal("did not get expected application settings")
}
}
func TestReadAbraShEnvVars(t *testing.T) {
r := recipe.Get("abra-test-recipe")
err := r.EnsureExists()
if err != nil {
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := recipe.Get(test.AbraTestRecipe)
if err := r.EnsureExists(); err != nil {
t.Fatal(err)
}
@@ -78,9 +103,11 @@ func TestReadAbraShEnvVars(t *testing.T) {
}
func TestReadAbraShCmdNames(t *testing.T) {
r := recipe.Get("abra-test-recipe")
err := r.EnsureExists()
if err != nil {
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := recipe.Get(test.AbraTestRecipe)
if err := r.EnsureExists(); err != nil {
t.Fatal(err)
}
@@ -102,9 +129,11 @@ func TestReadAbraShCmdNames(t *testing.T) {
}
func TestCheckEnv(t *testing.T) {
r := recipe.Get("abra-test-recipe")
err := r.EnsureExists()
if err != nil {
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := recipe.Get(test.AbraTestRecipe)
if err := r.EnsureExists(); err != nil {
t.Fatal(err)
}
@@ -135,9 +164,11 @@ func TestCheckEnv(t *testing.T) {
}
func TestCheckEnvError(t *testing.T) {
r := recipe.Get("abra-test-recipe")
err := r.EnsureExists()
if err != nil {
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := recipe.Get(test.AbraTestRecipe)
if err := r.EnsureExists(); err != nil {
t.Fatal(err)
}
@@ -170,9 +201,11 @@ func TestCheckEnvError(t *testing.T) {
}
func TestEnvVarCommentsRemoved(t *testing.T) {
r := recipe.Get("abra-test-recipe")
err := r.EnsureExists()
if err != nil {
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := recipe.Get(test.AbraTestRecipe)
if err := r.EnsureExists(); err != nil {
t.Fatal(err)
}
@@ -201,9 +234,11 @@ func TestEnvVarCommentsRemoved(t *testing.T) {
}
func TestEnvVarModifiersIncluded(t *testing.T) {
r := recipe.Get("abra-test-recipe")
err := r.EnsureExists()
if err != nil {
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := recipe.Get(test.AbraTestRecipe)
if err := r.EnsureExists(); err != nil {
t.Fatal(err)
}
@@ -225,7 +260,10 @@ func TestEnvVarModifiersIncluded(t *testing.T) {
}
func TestNoOverwriteNonVersionEnvVars(t *testing.T) {
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
test.Setup()
t.Cleanup(func() { test.Teardown() })
app, err := appPkg.GetApp(expectedAppFiles, test.AppName)
if err != nil {
t.Fatal(err)
}
@@ -234,7 +272,7 @@ func TestNoOverwriteNonVersionEnvVars(t *testing.T) {
t.Fatal(err)
}
app, err = appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
app, err = appPkg.GetApp(expectedAppFiles, test.AppName)
if err != nil {
t.Fatal(err)
}
+39 -1
View File
@@ -8,11 +8,44 @@ import (
"testing"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/log"
)
func setup() {
teardown()
if err := os.Mkdir(os.ExpandEnv("$ABRA_DIR"), 0764); err != nil {
if !os.IsExist(err) {
log.Fatal(err)
}
}
if err := os.Mkdir(os.ExpandEnv("$ABRA_DIR/recipes"), 0764); err != nil {
if !os.IsExist(err) {
log.Fatal(err)
}
}
}
func teardown() {
abraDir := os.ExpandEnv("$ABRA_DIR")
if abraDir == fmt.Sprintf("%s/.abra", os.ExpandEnv("$HOME")) {
log.Fatal("set $ABRA_DIR before running the test suite")
}
if err := os.RemoveAll(abraDir); err != nil {
log.Fatal(err)
}
}
func TestClone(t *testing.T) {
setup()
t.Cleanup(func() { teardown() })
dir := path.Join(config.RECIPES_DIR, "gitea")
os.RemoveAll(dir)
if err := os.RemoveAll(dir); err != nil {
t.Fatal(err)
}
gitURL := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, "gitea")
if err := Clone(dir, gitURL); err != nil {
@@ -25,6 +58,11 @@ func TestClone(t *testing.T) {
}
func TestCancelGitClone(t *testing.T) {
t.Skip("https://git.coopcloud.tech/toolshed/abra/issues/814")
setup()
t.Cleanup(func() { teardown() })
dir := path.Join(config.RECIPES_DIR, "gitea")
os.RemoveAll(dir)
+156 -137
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+274 -172
View File
File diff suppressed because it is too large Load Diff
+6 -4
View File
@@ -5,12 +5,15 @@ import (
"path/filepath"
"testing"
"coopcloud.tech/abra/pkg/test"
"github.com/stretchr/testify/assert"
)
func TestIsDirty(t *testing.T) {
r := Get("abra-test-recipe")
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := Get(test.RecipeName)
if err := r.EnsureExists(); err != nil {
t.Fatal(err)
}
@@ -22,10 +25,9 @@ func TestIsDirty(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer f.Close()
defer t.Cleanup(func() {
os.Remove(fpath)
})
t.Cleanup(func() { os.Remove(fpath) })
dirty, err := r.IsDirty()
if err != nil {
+12 -11
View File
@@ -5,6 +5,7 @@ import (
"testing"
"coopcloud.tech/abra/pkg/config"
"coopcloud.tech/abra/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
@@ -101,24 +102,24 @@ func TestGet(t *testing.T) {
}
func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) {
r := Get("traefik")
test.Setup()
t.Cleanup(func() { test.Teardown() })
r := Get(test.RecipeName)
if err := r.EnsureExists(); err != nil {
t.Fatal(err)
}
for i := 1; i < 50; i++ {
timeout := "120"
if err := test.AddEnv("TIMEOUT", timeout); err != nil {
t.Fatal(err)
}
for i := 1; i < 3; i++ {
label, err := r.GetVersionLabelLocal()
if err != nil {
t.Fatal(err)
}
// NOTE(d1): this is potentially quite a brittle unit test as it needs to
// hardcode the default timeout label to ensure that the label parser never
// returns it. hopefully this won't fail too often! if you're here because
// of a failure, just update the `defaultTimeoutLabel` value & permalink
// below
// https://git.coopcloud.tech/coop-cloud/traefik/src/commit/ac3a47fe8ca3ef92db84f64cfedfbb348000faee/.env.sample#L2
defaultTimeoutLabel := "300"
assert.NotEqual(t, label, defaultTimeoutLabel)
assert.NotEqual(t, label, timeout)
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ version: "3.8"
services:
app:
image: nginx:1.21.0
image: nginx:1.31.2
secrets:
- test_pass_one
- test_pass_two
+54 -40
View File
@@ -2,57 +2,50 @@ package test
import (
"fmt"
"log"
"os"
"path"
appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/envfile"
"coopcloud.tech/abra/pkg/log"
"coopcloud.tech/abra/pkg/recipe"
gitPkg "coopcloud.tech/abra/pkg/git"
"git.coopcloud.tech/toolshed/godotenv"
)
var (
AppName = "test_app.example.com"
ServerName = "test_server"
TFiles = []string{"bar.env", "foo.env"}
TFolders = []string{"dir1", "dir2"}
TestServer = os.ExpandEnv("$PWD/../../tests/resources/test_server")
TestDir = os.ExpandEnv("$PWD/../../tests/resources/test_dir")
RecipeName = "test_recipe"
ExpectedAppEnv = envfile.AppEnv{
"DOMAIN": "test_app.example.com",
"RECIPE": "test_recipe",
}
TFiles = []string{"bar.env", "foo.env"}
TFolders = []string{"dir1", "dir2"}
ExpectedApp = appPkg.App{
Name: AppName,
Recipe: recipe.Get(ExpectedAppEnv["RECIPE"]),
Domain: ExpectedAppEnv["DOMAIN"],
Env: ExpectedAppEnv,
Path: ExpectedAppFile.Path,
Server: ExpectedAppFile.Server,
}
ServerDir = os.ExpandEnv("$ABRA_DIR/servers/test_server")
RecipeDir = os.ExpandEnv("$ABRA_DIR/recipes/test_recipe")
TestDir = os.ExpandEnv("$PWD/../../tests/resources/test_dir")
ExpectedAppFile = appPkg.AppFile{
Path: path.Join(TestServer, fmt.Sprintf("%s.env", AppName)),
Server: ServerName,
}
AppEnvPath = path.Join(ServerDir, fmt.Sprintf("%s.env", AppName))
ExpectedAppFiles = map[string]appPkg.AppFile{
AppName: ExpectedAppFile,
}
AbraTestRecipe = "abra-test-recipe"
)
func RmServerAppRecipe() {
testAppLink := os.ExpandEnv("$ABRA_DIR/servers/test_server")
os.Remove(testAppLink)
func Teardown() {
abraDir := os.ExpandEnv("$ABRA_DIR")
if abraDir == fmt.Sprintf("%s/.abra", os.ExpandEnv("$HOME")) {
log.Fatal("set $ABRA_DIR before running the test suite")
}
testRecipeLink := os.ExpandEnv("$ABRA_DIR/recipes/test_recipe")
os.Remove(testRecipeLink)
if err := os.RemoveAll(abraDir); err != nil {
log.Fatal(err)
}
}
func MkServerAppRecipe() {
RmServerAppRecipe()
func Setup() {
Teardown()
if err := os.Mkdir(os.ExpandEnv("$ABRA_DIR"), 0764); err != nil {
if !os.IsExist(err) {
log.Fatal(err)
}
}
if err := os.Mkdir(os.ExpandEnv("$ABRA_DIR/servers"), 0700); err != nil {
if !os.IsExist(err) {
@@ -66,15 +59,36 @@ func MkServerAppRecipe() {
}
}
testAppDir := os.ExpandEnv("$PWD/../../tests/resources/test_server")
testAppLink := os.ExpandEnv("$ABRA_DIR/servers/test_server")
if err := os.Symlink(testAppDir, testAppLink); err != nil {
serverSrcDir := os.ExpandEnv("$PWD/../../tests/resources/test_server")
serverDestDir := os.ExpandEnv("$ABRA_DIR/servers/test_server")
if err := os.CopyFS(serverDestDir, os.DirFS(serverSrcDir)); err != nil {
log.Fatal(err)
}
testRecipeDir := os.ExpandEnv("$PWD/../../tests/resources/test_recipe")
testRecipeLink := os.ExpandEnv("$ABRA_DIR/recipes/test_recipe")
if err := os.Symlink(testRecipeDir, testRecipeLink); err != nil {
recipeSrcDir := os.ExpandEnv("$PWD/../../tests/resources/test_recipe")
recipeDestDir := os.ExpandEnv("$ABRA_DIR/recipes/test_recipe")
if err := os.CopyFS(recipeDestDir, os.DirFS(recipeSrcDir)); err != nil {
log.Fatal(err)
}
if err := gitPkg.Init(recipeDestDir, true, "tester", "helo@coopcloud.tech"); err != nil {
log.Fatal(err)
}
}
func AddEnv(envKey, envValue string) error {
filePath := os.ExpandEnv(fmt.Sprintf("$ABRA_DIR/servers/%s/%s.env", ServerName, AppName))
envVars, _, err := godotenv.Read(filePath)
if err != nil {
return err
}
envVars[envKey] = envValue
if err := godotenv.Write(envVars, filePath); err != nil {
return err
}
return nil
}
+1 -1
View File
@@ -138,7 +138,7 @@ func loadConfigFile(filename string) (*composetypes.ConfigFile, error) {
config, err := loader.ParseYAML(bytes)
if err != nil {
return nil, err
return nil, fmt.Errorf("%s: %s", filename, err)
}
return &composetypes.ConfigFile{
+2 -2
View File
@@ -8,7 +8,7 @@
"gomodTidy"
],
"ignoreDeps": [
"github.com/urfave/cli",
"goreleaser/goreleaser"
"github.com/docker/cli",
"github.com/spf13/cobra"
]
}
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# This file generates local DNS aliases for the client VM for integration testing
echo "generating hosts file..."
hosts_file_path="./tests/resources/extra_hosts"
echo "removing old file..."
rm -f $hosts_file_path
# the server domain
server_ip="10.0.0.3"
domain="abra.local"
echo "$server_ip $domain" >> $hosts_file_path
# static subdomains used in integration tests
subdomains=("gitea" "zammad" "custom-html" "foo" "foobar")
for subdomain in "${subdomains[@]}"
do
echo "$server_ip $subdomain.$domain" >> $hosts_file_path
done
search_dir=./tests/integration
for entry in "$search_dir"/*.bats
do
file_name=$(basename "${entry}")
# converting file names to testing subdomains
subdomain=$(echo "$file_name" | tr . _)
echo "$server_ip $subdomain.$domain" >> $hosts_file_path
done
echo "writing extra hosts success"
+1 -1
View File
@@ -50,7 +50,7 @@ echo "========================================================================"
echo "========================================================================"
echo "BUILDING ABRA"
echo "========================================================================"
export PATH="/usr/lib/go-1.21/bin:$PATH"
export PATH="$PATH:/usr/local/go/bin"
make build
echo "========================================================================"
+2 -1
View File
@@ -7,4 +7,5 @@
* Integration tests are in `./tests/integration`. Please see [these
docs](https://docs.coopcloud.tech/abra/hack/#integration-tests) for
instructions and tips on how to run them.
instructions and tips on how to run them. If you want to run it locally via nix,
read the [nix testing docs](../nix/hosts/README.md).
+9 -5
View File
@@ -598,8 +598,7 @@ teardown(){
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
assert_output --partial "WITH_COMMENT=foo"
@@ -610,8 +609,7 @@ teardown(){
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input --force
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
refute_output --partial "WITH_COMMENT=foo"
assert_output --partial "WITH_COMMENT=bar"
@@ -629,8 +627,14 @@ teardown(){
run $ABRA app deploy "$TEST_APP_DOMAIN" --no-input
assert_success
run docker service inspect --format '{{ range .Endpoint.Ports }}{{ .Protocol }}={{ .PublishedPort }}{{ end }}' \
if _is_local;
then
run docker service inspect --format "{{ range .Endpoint.Ports }}{{ .Protocol }}={{ .PublishedPort }}{{ end }}" \
"${TEST_APP_DOMAIN//./_}_app"
else
run ssh "$TEST_SERVER" "docker service inspect --format '{{ range .Endpoint.Ports }}{{ .Protocol }}={{ .PublishedPort }}{{ end }}' ${TEST_APP_DOMAIN//./_}_app"
fi
assert_success
assert_output --partial "tcp=1312"
assert_output --partial "udp=1312"
@@ -133,8 +133,7 @@ teardown(){
run $ABRA app deploy "$TEST_APP_DOMAIN" "0.1.0+1.20.0" --no-input
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
assert_output --partial "$TEST_RECIIPE:0.1.0+1.20.0"
}
+20
View File
@@ -61,6 +61,16 @@ teardown(){
run grep -q "TYPE=$TEST_RECIPE:0.3.0+1.21.0" \
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
assert_success
# 0.3.0-only
run grep -q "NETWORK_WITH_COMMENT=BAZ" \
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
assert_success
# latest-only
run grep -q "TIMEOUT=120" \
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
assert_failure
}
@test "ensure recipe is up-to-date" {
@@ -100,6 +110,16 @@ teardown(){
run grep -q "TYPE=$TEST_RECIPE:${tagHash}" \
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
assert_success
# 0.3.0-only
run grep -q "NETWORK_WITH_COMMENT=BAZ" \
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
assert_success
# latest-only
run grep -q "TIMEOUT=120" \
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
assert_failure
}
@test "does not overwrite existing env files" {
+11 -4
View File
@@ -140,15 +140,22 @@ teardown(){
_undeploy_app
sanitisedDomainName="${TEST_APP_DOMAIN//./_}"
run docker config create "${sanitisedDomainName}_test_conf_v99" "$ABRA_DIR/recipes/abra-test-recipe/abra.sh"
assert_success
remote_ssh_command=""
if _is_local;
then
run docker config create "${sanitisedDomainName}_test_conf_v99" "$ABRA_DIR/recipes/abra-test-recipe/abra.sh"
else
remote_ssh_command="ssh '$TEST_SERVER'"
run cat "$ABRA_DIR/recipes/abra-test-recipe/abra.sh" | ssh "$TEST_SERVER" docker config create "${sanitisedDomainName}_test_conf_v99 -"
assert_success
fi
assert bash -c "docker config ls | grep -q test_conf_v99"
assert bash -c "$remote_ssh_command docker config ls | grep -q test_conf_v99"
run $ABRA app rm "$TEST_APP_DOMAIN" --no-input
assert_success
refute bash -c "docker config ls | grep -q test_conf_v99"
refute bash -c "$remote_ssh_command docker config ls | grep -q test_conf_v99"
}
@test "remove .env file" {
@@ -54,8 +54,7 @@ teardown(){
--no-input
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
assert_output --partial "$TEST_RECIIPE:0.1.0+1.20.0"
}
@@ -53,8 +53,7 @@ teardown(){
run $ABRA app upgrade "$TEST_APP_DOMAIN" "0.2.0+1.21.0" --no-input
assert_success
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' \
$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)
_inspect_env_test_app
assert_success
assert_output --partial "$TEST_RECIIPE:0.2.0+1.21.0"
assert_output --partial "$TEST_RECIPE:0.2.0+1.21.0"
}
+11 -2
View File
@@ -31,9 +31,18 @@ _ensure_ssh_agent() {
exit 1
fi
export SSH_AUTH_SOCK="$HOME/.ssh/ssh_auth_sock"
if [ ! -S ~/.ssh/ssh_auth_sock ]; then
if [[ ! -v SSH_AUTH_SOCK ]]; then
export SSH_AUTH_SOCK="$HOME/.ssh/ssh_auth_sock"
eval `ssh-agent`
ln -sf "$SSH_AUTH_SOCK" ~/.ssh/ssh_auth_sock
fi
}
_is_local() {
if [[ "$TEST_SERVER" == "default" ]];
then
return 0
else
return 1
fi
}
+30 -6
View File
@@ -1,13 +1,37 @@
#!/usr/bin/env bash
_ensure_swarm() {
if [ "$(docker info | grep Swarm | sed 's/Swarm: //g' | tr -d ' ')" == "inactive" ]; then
run docker swarm init --advertise-addr 127.0.0.1:2377
assert_success
if [ "$(docker info | grep Swarm | sed 's/Swarm: //g' | tr -d ' ')" == "inactive" ]; then
run docker swarm init --advertise-addr 127.0.0.1:2377
assert_success
fi
if ! $(docker network ls | grep -q 'proxy'); then
run docker network create -d overlay proxy
assert_success
if ! docker network ls | grep -q 'proxy'; then
run docker network create -d overlay proxy
assert_success
fi
if ! _is_local;
then
if [ "$(ssh "$TEST_SERVER" docker info | grep Swarm | sed 's/Swarm: //g' | tr -d ' ')" == "inactive" ]; then
run ssh "$TEST_SERVER" docker swarm init --advertise-addr 127.0.0.1:2377
assert_success
fi
if ! ssh "$TEST_SERVER" docker network ls | grep -q 'proxy'; then
run ssh "$TEST_SERVER" docker network create -d overlay proxy
assert_success
fi
fi
}
_inspect_env_test_app() {
if _is_local;
then
containerId="$(docker ps -f name="$TEST_APP_DOMAIN_$TEST_SERVER" -q)"
run docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' "$containerId"
else
containerId="$(ssh "$TEST_SERVER" docker ps -f name="$TEST_APP_DOMAIN" -q)"
run ssh "$TEST_SERVER" "docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' '$containerId'"
fi
}
+55 -8
View File
@@ -63,26 +63,23 @@ teardown() {
}
@test "release minor bump" {
run $ABRA recipe upgrade "$TEST_RECIPE" --no-input --minor --commit
assert_success
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" show
assert_success
assert_output --regexp 'image: nginx:1.2.*'
# NOTE(d1): ensure the latest tag is the one we expect
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout 0.3.0+1.21.0
_remove_tags
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag \
-a "0.3.0+1.21.0" -m "fake: 0.3.0+1.21.0"
assert_success
run $ABRA recipe upgrade "$TEST_RECIPE" --no-input --minor --commit
assert_success
run $ABRA recipe release "$TEST_RECIPE" --no-input --minor
assert_success
assert_output --partial 'INFO new release published:'
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag --list
assert_success
assert_output --regexp '0\.4\.0\+1\.2.*'
assert_output --regexp '0\.4\.0\+1\.(.*)'
}
@test "release with unstaged changes" {
@@ -217,3 +214,53 @@ teardown() {
assert_failure
assert_output --partial "automagic insertion not supported yet"
}
@test "push during release fails" {
tagHash=$(_get_tag_hash "0.2.0+1.21.0")
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$tagHash"
assert_success
run $ABRA recipe upgrade "$TEST_RECIPE" --no-input --patch --commit
assert_success
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" show
assert_success
assert_output --partial 'image: nginx:1.21.6'
wantHash="$(_get_current_hash)"
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" remote set-url origin-ssh "$ABRA_DIR/does/not/exist"
assert_success
run $ABRA recipe release "$TEST_RECIPE" --no-input --patch
assert_failure
assert_output --partial 'failed to publish new release:'
assert_output --partial 'any changes made have been reverted'
assert_equal "$wantHash" "$(_get_current_hash)"
assert_equal "$(_git_status)" ""
}
@test "release, fail, release: works" {
tagHash=$(_get_tag_hash "0.2.0+1.21.0")
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout "$tagHash"
assert_success
run $ABRA recipe upgrade "$TEST_RECIPE" --no-input --patch --commit
assert_success
# NOTE(d1): fake broken remote so the release fails
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" remote set-url origin-ssh "$ABRA_DIR/does/not/exist"
assert_success
run $ABRA recipe release "$TEST_RECIPE" --no-input --patch
assert_failure
# NOTE(d1): correct remote so release can proceed
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" remote set-url origin-ssh "$ABRA_DIR/origin-recipes/$TEST_RECIPE.git"
assert_success
run $ABRA recipe release "$TEST_RECIPE" --no-input --patch
assert_success
}
-2
View File
@@ -38,8 +38,6 @@ teardown(){
run $ABRA recipe upgrade "custom-html" --no-input
assert_success
assert_output --partial 'can upgrade service: app'
assert_exists "$ABRA_DIR/recipes/custom-html"
}
+3 -3
View File
@@ -27,7 +27,7 @@ teardown(){
assert bash -c "docker context ls | grep -q $TEST_SERVER"
server_dir_perms=$(stat -c "%a" "$ABRA_DIR/servers/$TEST_SERVER")
assert_equal $server_dir_perms "700"
assert_equal "$server_dir_perms" "700"
}
@test "error if using name and --local together" {
@@ -43,8 +43,8 @@ teardown(){
assert bash -c "docker context ls | grep -q default"
assert_output --partial 'local server successfully added'
server_dir_perms=$(stat -c "%a" "$ABRA_DIR/servers/$TEST_SERVER")
assert_equal $server_dir_perms "700"
server_dir_perms=$(stat -c "%a" "$ABRA_DIR/servers/default")
assert_equal "$server_dir_perms" "700"
}
@test "create local server fails when no docker swarm" {
+57
View File
@@ -0,0 +1,57 @@
10.0.0.3 abra.local
10.0.0.3 gitea.abra.local
10.0.0.3 zammad.abra.local
10.0.0.3 custom-html.abra.local
10.0.0.3 foo.abra.local
10.0.0.3 foobar.abra.local
10.0.0.3 app_check_bats.abra.local
10.0.0.3 app_cmd_bats.abra.local
10.0.0.3 app_config_bats.abra.local
10.0.0.3 app_cp_bats.abra.local
10.0.0.3 app_deploy_bats.abra.local
10.0.0.3 app_deploy_env_version_bats.abra.local
10.0.0.3 app_deploy_overview_bats.abra.local
10.0.0.3 app_deploy_remote_recipes_bats.abra.local
10.0.0.3 app_env_bats.abra.local
10.0.0.3 app_env_version_bats.abra.local
10.0.0.3 app_labels_bats.abra.local
10.0.0.3 app_list_bats.abra.local
10.0.0.3 app_logs_bats.abra.local
10.0.0.3 app_move_bats.abra.local
10.0.0.3 app_new_bats.abra.local
10.0.0.3 app_ps_bats.abra.local
10.0.0.3 app_remove_bats.abra.local
10.0.0.3 app_restart_bats.abra.local
10.0.0.3 app_rollback_bats.abra.local
10.0.0.3 app_rollback_env_version_bats.abra.local
10.0.0.3 app_rollback_overview_bats.abra.local
10.0.0.3 app_run_bats.abra.local
10.0.0.3 app_secret_bats.abra.local
10.0.0.3 app_secret_env_version_bats.abra.local
10.0.0.3 app_services_bats.abra.local
10.0.0.3 app_undeploy_bats.abra.local
10.0.0.3 app_undeploy_env_version_bats.abra.local
10.0.0.3 app_undeploy_overview_bats.abra.local
10.0.0.3 app_upgrade_bats.abra.local
10.0.0.3 app_upgrade_env_version_bats.abra.local
10.0.0.3 app_upgrade_overview_bats.abra.local
10.0.0.3 app_volume_bats.abra.local
10.0.0.3 autocomplete_bats.abra.local
10.0.0.3 catalogue_bats.abra.local
10.0.0.3 dirs_bats.abra.local
10.0.0.3 install_bats.abra.local
10.0.0.3 recipe_diff_bats.abra.local
10.0.0.3 recipe_fetch_bats.abra.local
10.0.0.3 recipe_lint_bats.abra.local
10.0.0.3 recipe_list_bats.abra.local
10.0.0.3 recipe_new_bats.abra.local
10.0.0.3 recipe_release_bats.abra.local
10.0.0.3 recipe_reset_bats.abra.local
10.0.0.3 recipe_upgrade_bats.abra.local
10.0.0.3 recipe_version_bats.abra.local
10.0.0.3 server_add_bats.abra.local
10.0.0.3 server_list_bats.abra.local
10.0.0.3 server_prune_bats.abra.local
10.0.0.3 server_remove_bats.abra.local
10.0.0.3 upgrade_bats.abra.local
10.0.0.3 version_bats.abra.local
+7
View File
@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBQ967UhxmFkq71WXvVRkEmtehCGsEnBVppAXxgtHHm7wAAAJhFGwTnRRsE
5wAAAAtzc2gtZWQyNTUxOQAAACBQ967UhxmFkq71WXvVRkEmtehCGsEnBVppAXxgtHHm7w
AAAEAnHEZcf2NeRQEcJC/aVgUWsdOz+vQgEG9ZY+3ErCeaKFD3rtSHGYWSrvVZe9VGQSa1
6EIawScFWmkBfGC0cebvAAAAFWFicmEgaW50ZWdyYXRpb24gdGVzdA==
-----END OPENSSH PRIVATE KEY-----
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFD3rtSHGYWSrvVZe9VGQSa16EIawScFWmkBfGC0cebv abra integration test
+1
View File
@@ -0,0 +1 @@
/path/to/this/repo
+6 -3
View File
@@ -1,6 +1,9 @@
RECIPE=test_recipe
DOMAIN=test_app.example.com
# NOTE(d1): ensure commented out TIMEOUT doesn't get included
# see TestReadEnv in ./pkg/envfile
# TIMEOUT=120
#
# NOTE(d1): for new changes, you *MUST* also update ../test_server/test_app.example.com.env
#
# NOTE(d1): TestReadEnv
# FOO=BAR
+2 -1
View File
@@ -3,11 +3,12 @@ version: "3.8"
services:
app:
image: nginx:1.29.0
image: nginx:1.31.2
networks:
- proxy
deploy:
labels:
- "coop-cloud.${STACK_NAME}.version=0.1.0+0.1.0"
- "coop-cloud.${STACK_NAME}.timeout=${TIMEOUT}"
networks:
@@ -1,6 +1,9 @@
RECIPE=test_recipe
DOMAIN=test_app.example.com
# NOTE(d1): ensure commented out TIMEOUT doesn't get included
# see TestReadEnv in ./pkg/envfile
# TIMEOUT=120
#
# NOTE(d1): for new changes, you *MUST* also update ../test_recipe/.env.sample
#
# NOTE(d1): TestReadEnv
# FOO=BAR
+3 -3
View File
@@ -3,16 +3,16 @@ kind: pipeline
name: coopcloud.tech/tagcmp
steps:
- name: gofmt
image: golang:1.21
image: golang:1.26
commands:
- test -z "$(gofmt -l .)"
- name: go build
image: golang:1.21
image: golang:1.26
commands:
- go build -v .
- name: go test
image: golang:1.21
image: golang:1.26
commands:
- go test . -cover
+6
View File
@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
]
}
+1 -1
View File
@@ -1,7 +1,7 @@
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
reflection interface similar to Go's standard library `json` and `xml` packages.
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
Compatible with TOML version [v1.1.0](https://toml.io/en/v1.1.0).
Documentation: https://pkg.go.dev/github.com/BurntSushi/toml
+8 -1
View File
@@ -206,6 +206,13 @@ func markDecodedRecursive(md *MetaData, tmap map[string]any) {
markDecodedRecursive(md, tmap)
md.context = md.context[0 : len(md.context)-1]
}
if tarr, ok := tmap[key].([]map[string]any); ok {
for _, elm := range tarr {
md.context = append(md.context, key)
markDecodedRecursive(md, elm)
md.context = md.context[0 : len(md.context)-1]
}
}
}
}
@@ -423,7 +430,7 @@ func (md *MetaData) unifyString(data any, rv reflect.Value) error {
if i, ok := data.(int64); ok {
rv.SetString(strconv.FormatInt(i, 10))
} else if f, ok := data.(float64); ok {
rv.SetString(strconv.FormatFloat(f, 'f', -1, 64))
rv.SetString(strconv.FormatFloat(f, 'g', -1, 64))
} else {
return md.badtype("string", data)
}
+46 -33
View File
@@ -228,9 +228,9 @@ func (enc *Encoder) eElement(rv reflect.Value) {
}
switch v.Location() {
default:
enc.wf(v.Format(format))
enc.write(v.Format(format))
case internal.LocalDatetime, internal.LocalDate, internal.LocalTime:
enc.wf(v.In(time.UTC).Format(format))
enc.write(v.In(time.UTC).Format(format))
}
return
case Marshaler:
@@ -279,40 +279,40 @@ func (enc *Encoder) eElement(rv reflect.Value) {
case reflect.String:
enc.writeQuoted(rv.String())
case reflect.Bool:
enc.wf(strconv.FormatBool(rv.Bool()))
enc.write(strconv.FormatBool(rv.Bool()))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
enc.wf(strconv.FormatInt(rv.Int(), 10))
enc.write(strconv.FormatInt(rv.Int(), 10))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
enc.wf(strconv.FormatUint(rv.Uint(), 10))
enc.write(strconv.FormatUint(rv.Uint(), 10))
case reflect.Float32:
f := rv.Float()
if math.IsNaN(f) {
if math.Signbit(f) {
enc.wf("-")
enc.write("-")
}
enc.wf("nan")
enc.write("nan")
} else if math.IsInf(f, 0) {
if math.Signbit(f) {
enc.wf("-")
enc.write("-")
}
enc.wf("inf")
enc.write("inf")
} else {
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 32)))
enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 32)))
}
case reflect.Float64:
f := rv.Float()
if math.IsNaN(f) {
if math.Signbit(f) {
enc.wf("-")
enc.write("-")
}
enc.wf("nan")
enc.write("nan")
} else if math.IsInf(f, 0) {
if math.Signbit(f) {
enc.wf("-")
enc.write("-")
}
enc.wf("inf")
enc.write("inf")
} else {
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 64)))
enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 64)))
}
case reflect.Array, reflect.Slice:
enc.eArrayOrSliceElement(rv)
@@ -330,27 +330,32 @@ func (enc *Encoder) eElement(rv reflect.Value) {
// By the TOML spec, all floats must have a decimal with at least one number on
// either side.
func floatAddDecimal(fstr string) string {
if !strings.Contains(fstr, ".") {
return fstr + ".0"
for _, c := range fstr {
if c == 'e' { // Exponent syntax
return fstr
}
if c == '.' {
return fstr
}
}
return fstr
return fstr + ".0"
}
func (enc *Encoder) writeQuoted(s string) {
enc.wf("\"%s\"", dblQuotedReplacer.Replace(s))
enc.write(`"` + dblQuotedReplacer.Replace(s) + `"`)
}
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
length := rv.Len()
enc.wf("[")
enc.write("[")
for i := 0; i < length; i++ {
elem := eindirect(rv.Index(i))
enc.eElement(elem)
if i != length-1 {
enc.wf(", ")
enc.write(", ")
}
}
enc.wf("]")
enc.write("]")
}
func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
@@ -363,7 +368,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
continue
}
enc.newline()
enc.wf("%s[[%s]]", enc.indentStr(key), key)
enc.writef("%s[[%s]]", enc.indentStr(key), key)
enc.newline()
enc.eMapOrStruct(key, trv, false)
}
@@ -376,7 +381,7 @@ func (enc *Encoder) eTable(key Key, rv reflect.Value) {
enc.newline()
}
if len(key) > 0 {
enc.wf("%s[%s]", enc.indentStr(key), key)
enc.writef("%s[%s]", enc.indentStr(key), key)
enc.newline()
}
enc.eMapOrStruct(key, rv, false)
@@ -422,7 +427,7 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
if inline {
enc.writeKeyValue(Key{mapKey.String()}, val, true)
if trailC || i != len(mapKeys)-1 {
enc.wf(", ")
enc.write(", ")
}
} else {
enc.encode(key.add(mapKey.String()), val)
@@ -431,12 +436,12 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
}
if inline {
enc.wf("{")
enc.write("{")
}
writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0)
writeMapKeys(mapKeysSub, false)
if inline {
enc.wf("}")
enc.write("}")
}
}
@@ -534,7 +539,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
if inline {
enc.writeKeyValue(Key{keyName}, fieldVal, true)
if fieldIndex[0] != totalFields-1 {
enc.wf(", ")
enc.write(", ")
}
} else {
enc.encode(key.add(keyName), fieldVal)
@@ -543,14 +548,14 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
}
if inline {
enc.wf("{")
enc.write("{")
}
l := len(fieldsDirect) + len(fieldsSub)
writeFields(fieldsDirect, l)
writeFields(fieldsSub, l)
if inline {
enc.wf("}")
enc.write("}")
}
}
@@ -700,7 +705,7 @@ func isEmpty(rv reflect.Value) bool {
func (enc *Encoder) newline() {
if enc.hasWritten {
enc.wf("\n")
enc.write("\n")
}
}
@@ -722,14 +727,22 @@ func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
enc.eElement(val)
return
}
enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
enc.writef("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
enc.eElement(val)
if !inline {
enc.newline()
}
}
func (enc *Encoder) wf(format string, v ...any) {
func (enc *Encoder) write(s string) {
_, err := enc.w.WriteString(s)
if err != nil {
encPanic(err)
}
enc.hasWritten = true
}
func (enc *Encoder) writef(format string, v ...any) {
_, err := fmt.Fprintf(enc.w, format, v...)
if err != nil {
encPanic(err)
+53 -77
View File
@@ -13,7 +13,6 @@ type itemType int
const (
itemError itemType = iota
itemNIL // used in the parser to indicate no type
itemEOF
itemText
itemString
@@ -47,14 +46,13 @@ func (p Position) String() string {
}
type lexer struct {
input string
start int
pos int
line int
state stateFn
items chan item
tomlNext bool
esc bool
input string
start int
pos int
line int
state stateFn
items chan item
esc bool
// Allow for backing up up to 4 runes. This is necessary because TOML
// contains 3-rune tokens (""" and ''').
@@ -90,14 +88,13 @@ func (lx *lexer) nextItem() item {
}
}
func lex(input string, tomlNext bool) *lexer {
func lex(input string) *lexer {
lx := &lexer{
input: input,
state: lexTop,
items: make(chan item, 10),
stack: make([]stateFn, 0, 10),
line: 1,
tomlNext: tomlNext,
input: input,
state: lexTop,
items: make(chan item, 10),
stack: make([]stateFn, 0, 10),
line: 1,
}
return lx
}
@@ -108,7 +105,7 @@ func (lx *lexer) push(state stateFn) {
func (lx *lexer) pop() stateFn {
if len(lx.stack) == 0 {
return lx.errorf("BUG in lexer: no states to pop")
panic("BUG in lexer: no states to pop")
}
last := lx.stack[len(lx.stack)-1]
lx.stack = lx.stack[0 : len(lx.stack)-1]
@@ -305,6 +302,8 @@ func lexTop(lx *lexer) stateFn {
return lexTableStart
case eof:
if lx.pos > lx.start {
// TODO: never reached? I think this can only occur on a bug in the
// lexer(?)
return lx.errorf("unexpected EOF")
}
lx.emit(itemEOF)
@@ -392,8 +391,6 @@ func lexTableNameStart(lx *lexer) stateFn {
func lexTableNameEnd(lx *lexer) stateFn {
lx.skip(isWhitespace)
switch r := lx.next(); {
case isWhitespace(r):
return lexTableNameEnd
case r == '.':
lx.ignore()
return lexTableNameStart
@@ -412,7 +409,7 @@ func lexTableNameEnd(lx *lexer) stateFn {
// Lexes only one part, e.g. only 'a' inside 'a.b'.
func lexBareName(lx *lexer) stateFn {
r := lx.next()
if isBareKeyChar(r, lx.tomlNext) {
if isBareKeyChar(r) {
return lexBareName
}
lx.backup()
@@ -420,23 +417,23 @@ func lexBareName(lx *lexer) stateFn {
return lx.pop()
}
// lexBareName lexes one part of a key or table.
//
// It assumes that at least one valid character for the table has already been
// read.
// lexQuotedName lexes one part of a quoted key or table name. It assumes that
// it starts lexing at the quote itself (" or ').
//
// Lexes only one part, e.g. only '"a"' inside '"a".b'.
func lexQuotedName(lx *lexer) stateFn {
r := lx.next()
switch {
case isWhitespace(r):
return lexSkip(lx, lexValue)
case r == '"':
lx.ignore() // ignore the '"'
return lexString
case r == '\'':
lx.ignore() // ignore the "'"
return lexRawString
// TODO: I don't think any of the below conditions can ever be reached?
case isWhitespace(r):
return lexSkip(lx, lexValue)
case r == eof:
return lx.errorf("unexpected EOF; expected value")
default:
@@ -464,17 +461,19 @@ func lexKeyStart(lx *lexer) stateFn {
func lexKeyNameStart(lx *lexer) stateFn {
lx.skip(isWhitespace)
switch r := lx.peek(); {
case r == '=' || r == eof:
return lx.errorf("unexpected '='")
case r == '.':
return lx.errorf("unexpected '.'")
default:
lx.push(lexKeyEnd)
return lexBareName
case r == '"' || r == '\'':
lx.ignore()
lx.push(lexKeyEnd)
return lexQuotedName
default:
lx.push(lexKeyEnd)
return lexBareName
// TODO: I think these can never be reached?
case r == '=' || r == eof:
return lx.errorf("unexpected '='")
case r == '.':
return lx.errorf("unexpected '.'")
}
}
@@ -485,7 +484,7 @@ func lexKeyEnd(lx *lexer) stateFn {
switch r := lx.next(); {
case isWhitespace(r):
return lexSkip(lx, lexKeyEnd)
case r == eof:
case r == eof: // TODO: never reached
return lx.errorf("unexpected EOF; expected key separator '='")
case r == '.':
lx.ignore()
@@ -628,10 +627,7 @@ func lexInlineTableValue(lx *lexer) stateFn {
case isWhitespace(r):
return lexSkip(lx, lexInlineTableValue)
case isNL(r):
if lx.tomlNext {
return lexSkip(lx, lexInlineTableValue)
}
return lx.errorPrevLine(errLexInlineTableNL{})
return lexSkip(lx, lexInlineTableValue)
case r == '#':
lx.push(lexInlineTableValue)
return lexCommentStart
@@ -653,10 +649,7 @@ func lexInlineTableValueEnd(lx *lexer) stateFn {
case isWhitespace(r):
return lexSkip(lx, lexInlineTableValueEnd)
case isNL(r):
if lx.tomlNext {
return lexSkip(lx, lexInlineTableValueEnd)
}
return lx.errorPrevLine(errLexInlineTableNL{})
return lexSkip(lx, lexInlineTableValueEnd)
case r == '#':
lx.push(lexInlineTableValueEnd)
return lexCommentStart
@@ -664,10 +657,7 @@ func lexInlineTableValueEnd(lx *lexer) stateFn {
lx.ignore()
lx.skip(isWhitespace)
if lx.peek() == '}' {
if lx.tomlNext {
return lexInlineTableValueEnd
}
return lx.errorf("trailing comma not allowed in inline tables")
return lexInlineTableValueEnd
}
return lexInlineTableValue
case r == '}':
@@ -855,9 +845,6 @@ func lexStringEscape(lx *lexer) stateFn {
r := lx.next()
switch r {
case 'e':
if !lx.tomlNext {
return lx.error(errLexEscape{r})
}
fallthrough
case 'b':
fallthrough
@@ -878,9 +865,6 @@ func lexStringEscape(lx *lexer) stateFn {
case '\\':
return lx.pop()
case 'x':
if !lx.tomlNext {
return lx.error(errLexEscape{r})
}
return lexHexEscape
case 'u':
return lexShortUnicodeEscape
@@ -928,19 +912,9 @@ func lexLongUnicodeEscape(lx *lexer) stateFn {
// lexBaseNumberOrDate can differentiate base prefixed integers from other
// types.
func lexNumberOrDateStart(lx *lexer) stateFn {
r := lx.next()
switch r {
case '0':
if lx.next() == '0' {
return lexBaseNumberOrDate
}
if !isDigit(r) {
// The only way to reach this state is if the value starts
// with a digit, so specifically treat anything else as an
// error.
return lx.errorf("expected a digit but got %q", r)
}
return lexNumberOrDate
}
@@ -1196,13 +1170,13 @@ func lexSkip(lx *lexer, nextState stateFn) stateFn {
}
func (s stateFn) String() string {
if s == nil {
return "<nil>"
}
name := runtime.FuncForPC(reflect.ValueOf(s).Pointer()).Name()
if i := strings.LastIndexByte(name, '.'); i > -1 {
name = name[i+1:]
}
if s == nil {
name = "<nil>"
}
return name + "()"
}
@@ -1210,8 +1184,6 @@ func (itype itemType) String() string {
switch itype {
case itemError:
return "Error"
case itemNIL:
return "NIL"
case itemEOF:
return "EOF"
case itemText:
@@ -1226,18 +1198,22 @@ func (itype itemType) String() string {
return "Float"
case itemDatetime:
return "DateTime"
case itemTableStart:
return "TableStart"
case itemTableEnd:
return "TableEnd"
case itemKeyStart:
return "KeyStart"
case itemKeyEnd:
return "KeyEnd"
case itemArray:
return "Array"
case itemArrayEnd:
return "ArrayEnd"
case itemTableStart:
return "TableStart"
case itemTableEnd:
return "TableEnd"
case itemArrayTableStart:
return "ArrayTableStart"
case itemArrayTableEnd:
return "ArrayTableEnd"
case itemKeyStart:
return "KeyStart"
case itemKeyEnd:
return "KeyEnd"
case itemCommentStart:
return "CommentStart"
case itemInlineTableStart:
@@ -1266,7 +1242,7 @@ func isDigit(r rune) bool { return r >= '0' && r <= '9' }
func isBinary(r rune) bool { return r == '0' || r == '1' }
func isOctal(r rune) bool { return r >= '0' && r <= '7' }
func isHex(r rune) bool { return (r >= '0' && r <= '9') || (r|0x20 >= 'a' && r|0x20 <= 'f') }
func isBareKeyChar(r rune, tomlNext bool) bool {
func isBareKeyChar(r rune) bool {
return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') ||
(r >= '0' && r <= '9') || r == '_' || r == '-'
}
+18 -28
View File
@@ -3,7 +3,6 @@ package toml
import (
"fmt"
"math"
"os"
"strconv"
"strings"
"time"
@@ -17,7 +16,6 @@ type parser struct {
context Key // Full key for the current hash in scope.
currentKey string // Base key name for everything except hashes.
pos Position // Current position in the TOML file.
tomlNext bool
ordered []Key // List of keys in the order that they appear in the TOML data.
@@ -32,8 +30,6 @@ type keyInfo struct {
}
func parse(data string) (p *parser, err error) {
_, tomlNext := os.LookupEnv("BURNTSUSHI_TOML_110")
defer func() {
if r := recover(); r != nil {
if pErr, ok := r.(ParseError); ok {
@@ -73,10 +69,9 @@ func parse(data string) (p *parser, err error) {
p = &parser{
keyInfo: make(map[string]keyInfo),
mapping: make(map[string]any),
lx: lex(data, tomlNext),
lx: lex(data),
ordered: make([]Key, 0),
implicits: make(map[string]struct{}),
tomlNext: tomlNext,
}
for {
item := p.next()
@@ -350,17 +345,14 @@ func (p *parser) valueFloat(it item) (any, tomlType) {
var dtTypes = []struct {
fmt string
zone *time.Location
next bool
}{
{time.RFC3339Nano, time.Local, false},
{"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false},
{"2006-01-02", internal.LocalDate, false},
{"15:04:05.999999999", internal.LocalTime, false},
// tomlNext
{"2006-01-02T15:04Z07:00", time.Local, true},
{"2006-01-02T15:04", internal.LocalDatetime, true},
{"15:04", internal.LocalTime, true},
{time.RFC3339Nano, time.Local},
{"2006-01-02T15:04:05.999999999", internal.LocalDatetime},
{"2006-01-02", internal.LocalDate},
{"15:04:05.999999999", internal.LocalTime},
{"2006-01-02T15:04Z07:00", time.Local},
{"2006-01-02T15:04", internal.LocalDatetime},
{"15:04", internal.LocalTime},
}
func (p *parser) valueDatetime(it item) (any, tomlType) {
@@ -371,9 +363,6 @@ func (p *parser) valueDatetime(it item) (any, tomlType) {
err error
)
for _, dt := range dtTypes {
if dt.next && !p.tomlNext {
continue
}
t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone)
if err == nil {
if missingLeadingZero(it.val, dt.fmt) {
@@ -644,6 +633,11 @@ func (p *parser) setValue(key string, value any) {
// Note that since it has already been defined (as a hash), we don't
// want to overwrite it. So our business is done.
if p.isArray(keyContext) {
if !p.isImplicit(keyContext) {
if _, ok := hash[key]; ok {
p.panicf("Key '%s' has already been defined.", keyContext)
}
}
p.removeImplicit(keyContext)
hash[key] = value
return
@@ -802,10 +796,8 @@ func (p *parser) replaceEscapes(it item, str string) string {
b.WriteByte(0x0d)
skip = 1
case 'e':
if p.tomlNext {
b.WriteByte(0x1b)
skip = 1
}
b.WriteByte(0x1b)
skip = 1
case '"':
b.WriteByte(0x22)
skip = 1
@@ -815,11 +807,9 @@ func (p *parser) replaceEscapes(it item, str string) string {
// The lexer guarantees the correct number of characters are present;
// don't need to check here.
case 'x':
if p.tomlNext {
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
b.WriteRune(escaped)
skip = 3
}
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
b.WriteRune(escaped)
skip = 3
case 'u':
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+6])
b.WriteRune(escaped)
+2
View File
@@ -69,6 +69,8 @@ func (l *lineReader) Read(p []byte) (n int, err error) {
if isPrefix {
return 0, ArmorCorrupt
}
// Trim the line to remove any whitespace
line = bytes.TrimSpace(line)
if bytes.HasPrefix(line, armorEnd) {
l.eof = true
+8 -2
View File
@@ -125,7 +125,10 @@ func (c *curve25519) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecr
// "VB = convert point V to the octet string"
// sharedPoint corresponds to `VB`.
var sharedPoint x25519lib.Key
x25519lib.Shared(&sharedPoint, &ephemeralPrivate, &pubKey)
ok := x25519lib.Shared(&sharedPoint, &ephemeralPrivate, &pubKey)
if !ok {
return nil, nil, errors.KeyInvalidError("ecc: the public key is a low order point")
}
return ephemeralPublic[:], sharedPoint[:], nil
}
@@ -146,7 +149,10 @@ func (c *curve25519) Decaps(vsG, secret []byte) (sharedSecret []byte, err error)
// RFC6637 §8: "Note that the recipient obtains the shared secret by calculating
// S = rV = rvG, where (r,R) is the recipient's key pair."
// sharedPoint corresponds to `S`.
x25519lib.Shared(&sharedPoint, &decodedPrivate, &ephemeralPublic)
ok := x25519lib.Shared(&sharedPoint, &decodedPrivate, &ephemeralPublic)
if !ok {
return nil, errors.KeyInvalidError("ecc: the public key is a low order point")
}
return sharedPoint[:], nil
}
+4 -1
View File
@@ -78,7 +78,7 @@ func (c *genericCurve) GenerateECDSA(rand io.Reader) (x, y, secret *big.Int, err
func (c *genericCurve) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) {
xP, yP := elliptic.Unmarshal(c.Curve, point)
if xP == nil {
panic("invalid point")
return nil, nil, errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", c.Curve.Params().Name))
}
d, x, y, err := elliptic.GenerateKey(c.Curve, rand)
@@ -99,6 +99,9 @@ func (c *genericCurve) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSe
func (c *genericCurve) Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error) {
x, y := elliptic.Unmarshal(c.Curve, ephemeral)
if x == nil {
return nil, errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", c.Curve.Params().Name))
}
zbBig, _ := c.Curve.ScalarMult(x, y, secret)
byteLen := (c.Curve.Params().BitSize + 7) >> 3
zb := make([]byte, byteLen)
+26
View File
@@ -178,6 +178,18 @@ type Config struct {
// When set to true, a key without flags is treated as if all flags are enabled.
// This behavior is consistent with GPG.
InsecureAllowAllKeyFlagsWhenMissing bool
// InsecureGenerateNonCriticalKeyFlags causes the "Key Flags" signature subpacket
// to be non-critical in newly generated signatures.
// This may be needed for keys to be accepted by older clients who do not recognize
// the subpacket.
// For example, rpm 4.14.3-150400.59.3.1 in OpenSUSE Leap 15.4 does not recognize it.
InsecureGenerateNonCriticalKeyFlags bool
// InsecureGenerateNonCriticalSignatureCreationTime causes the "Signature Creation Time" signature subpacket
// to be non-critical in newly generated signatures.
// This may be needed for keys to be accepted by older clients who do not recognize
// the subpacket.
// For example, yum 3.4.3-168 in CentOS 7 and yum 3.4.3-158 in Amazon Linux 2 do not recognize it.
InsecureGenerateNonCriticalSignatureCreationTime bool
// MaxDecompressedMessageSize specifies the maximum number of bytes that can be
// read from a compressed packet. This serves as an upper limit to prevent
@@ -420,6 +432,20 @@ func (c *Config) AllowAllKeyFlagsWhenMissing() bool {
return c.InsecureAllowAllKeyFlagsWhenMissing
}
func (c *Config) GenerateNonCriticalKeyFlags() bool {
if c == nil {
return false
}
return c.InsecureGenerateNonCriticalKeyFlags
}
func (c *Config) GenerateNonCriticalSignatureCreationTime() bool {
if c == nil {
return false
}
return c.InsecureGenerateNonCriticalSignatureCreationTime
}
func (c *Config) DecompressedMessageSizeLimit() *int64 {
if c == nil {
return nil
+4 -4
View File
@@ -933,7 +933,7 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
}
sig.Notations = append(sig.Notations, &notation)
}
sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey)
sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey, config)
if err != nil {
return err
}
@@ -1254,11 +1254,11 @@ type outputSubpacket struct {
contents []byte
}
func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubpacket, err error) {
func (sig *Signature) buildSubpackets(issuer PublicKey, config *Config) (subpackets []outputSubpacket, err error) {
creationTime := make([]byte, 4)
binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix()))
// Signature Creation Time
subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, true, creationTime})
subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, !config.GenerateNonCriticalSignatureCreationTime(), creationTime})
// Signature Expiration Time
if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 {
sigLifetime := make([]byte, 4)
@@ -1357,7 +1357,7 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
if sig.FlagGroupKey {
flags |= KeyFlagGroupKey
}
subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, true, []byte{flags}})
subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, !config.GenerateNonCriticalKeyFlags(), []byte{flags}})
}
// Signer's User ID
if sig.SignerUserId != nil {
+3
View File
@@ -33,6 +33,9 @@ linters:
generated: lax
presets:
- common-false-positives
settings:
exhaustive:
default-signifies-exhaustive: true
issues:
max-issues-per-linter: 0
max-same-issues: 0
+24 -23
View File
@@ -2,6 +2,7 @@ package colorprofile
import (
"bytes"
"context"
"io"
"os/exec"
"runtime"
@@ -83,8 +84,8 @@ func colorProfile(isatty bool, env environ) (p Profile) {
}
if envNoColor(env) && isatty {
if p > Ascii {
p = Ascii
if p > ASCII {
p = ASCII
}
return //nolint:nakedret
}
@@ -153,29 +154,29 @@ func envColorProfile(env environ) (p Profile) {
p = ANSI
}
parts := strings.Split(term, "-")
switch parts[0] {
case "alacritty",
"contour",
"foot",
"ghostty",
"kitty",
"rio",
"st",
"wezterm":
switch {
case strings.Contains(term, "alacritty"),
strings.Contains(term, "contour"),
strings.Contains(term, "foot"),
strings.Contains(term, "ghostty"),
strings.Contains(term, "kitty"),
strings.Contains(term, "rio"),
strings.Contains(term, "st"),
strings.Contains(term, "wezterm"):
return TrueColor
case "xterm":
if len(parts) > 1 {
switch parts[1] {
case "ghostty", "kitty":
// These terminals can be defined as xterm-TERMNAME
return TrueColor
}
}
case "tmux", "screen":
case strings.HasPrefix(term, "tmux"), strings.HasPrefix(term, "screen"):
if p < ANSI256 {
p = ANSI256
}
case strings.HasPrefix(term, "xterm"):
if p < ANSI {
p = ANSI
}
}
if len(env["WT_SESSION"]) > 0 {
// Windows Terminal supports TrueColor
return TrueColor
}
if isCloudShell, _ := strconv.ParseBool(env.get("GOOGLE_CLOUD_SHELL")); isCloudShell {
@@ -244,13 +245,13 @@ func tmux(env environ) (p Profile) {
// Check if tmux has either Tc or RGB capabilities. Otherwise, return
// ANSI256.
p = ANSI256
cmd := exec.Command("tmux", "info")
cmd := exec.CommandContext(context.Background(), "tmux", "info")
out, err := cmd.Output()
if err != nil {
return
}
for _, line := range bytes.Split(out, []byte("\n")) {
for line := range bytes.SplitSeq(out, []byte("\n")) {
if (bytes.Contains(line, []byte("Tc")) || bytes.Contains(line, []byte("RGB"))) &&
bytes.Contains(line, []byte("true")) {
return TrueColor
-5
View File
@@ -14,11 +14,6 @@ func windowsColorProfile(env map[string]string) (Profile, bool) {
return TrueColor, true
}
if len(env["WT_SESSION"]) > 0 {
// Windows Terminal supports TrueColor
return TrueColor, true
}
major, _, build := windows.RtlGetNtVersionNumbers()
if build < 10586 || major < 10 {
// No ANSI support before WindowsNT 10 build 10586
+17 -9
View File
@@ -11,10 +11,12 @@ import (
type Profile byte
const (
// Unknown is a profile that represents the absence of a profile.
Unknown Profile = iota
// NoTTY is a profile with no terminal support.
NoTTY Profile = iota
// Ascii is a profile with no color support.
Ascii //nolint:revive
NoTTY
// ASCII is a profile with no color support.
ASCII
// ANSI is a profile with 16 colors (4-bit).
ANSI
// ANSI256 is a profile with 256 colors (8-bit).
@@ -23,6 +25,9 @@ const (
TrueColor
)
// Ascii is an alias for the [ASCII] profile for backwards compatibility.
const Ascii = ASCII //nolint:revive
// String returns the string representation of a Profile.
func (p Profile) String() string {
switch p {
@@ -32,12 +37,13 @@ func (p Profile) String() string {
return "ANSI256"
case ANSI:
return "ANSI"
case Ascii:
case ASCII:
return "Ascii"
case NoTTY:
return "NoTTY"
default:
return "Unknown"
}
return "Unknown"
}
var (
@@ -50,7 +56,7 @@ var (
// Convert transforms a given Color to a Color supported within the Profile.
func (p Profile) Convert(c color.Color) (cc color.Color) {
if p <= Ascii {
if p <= ASCII {
return nil
}
if p == TrueColor {
@@ -90,11 +96,13 @@ func (p Profile) Convert(c color.Color) (cc color.Color) {
return c
default:
if p == ANSI256 {
switch p {
case ANSI256:
return ansi.Convert256(c)
} else if p == ANSI {
case ANSI:
return ansi.Convert16(c)
default:
return c
}
return c
}
}
+11 -9
View File
@@ -36,13 +36,15 @@ type Writer struct {
// Write writes the given text to the underlying writer.
func (w *Writer) Write(p []byte) (int, error) {
switch w.Profile {
case TrueColor:
switch {
case w.Profile == TrueColor:
return w.Forward.Write(p) //nolint:wrapcheck
case NoTTY:
return io.WriteString(w.Forward, ansi.Strip(string(p))) //nolint:wrapcheck
case Ascii, ANSI, ANSI256:
return w.downsample(p)
case w.Profile <= NoTTY:
_, err := io.WriteString(w.Forward, ansi.Strip(string(p)))
return len(p), err
case w.Profile == ASCII, w.Profile == ANSI, w.Profile == ANSI256:
_, err := w.downsample(p)
return len(p), err
default:
return 0, fmt.Errorf("invalid profile: %v", w.Profile)
}
@@ -112,7 +114,7 @@ func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
if w.Profile < ANSI {
continue
}
style = style.DefaultForegroundColor()
style = style.ForegroundColor(nil)
case 40, 41, 42, 43, 44, 45, 46, 47: // 8-bit background color
if w.Profile < ANSI {
continue
@@ -132,7 +134,7 @@ func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
if w.Profile < ANSI {
continue
}
style = style.DefaultBackgroundColor()
style = style.BackgroundColor(nil)
case 58: // 16 or 24-bit underline color
var c color.Color
if n := ansi.ReadStyleColor(params[i:], &c); n > 0 {
@@ -146,7 +148,7 @@ func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
if w.Profile < ANSI {
continue
}
style = style.DefaultUnderlineColor()
style = style.UnderlineColor(nil)
case 90, 91, 92, 93, 94, 95, 96, 97: // 8-bit bright foreground color
if w.Profile < ANSI {
continue
+7 -1
View File
@@ -7,7 +7,6 @@ linters:
- exhaustive
- goconst
- godot
- godox
- gomoddirectives
- goprintffuncname
- gosec
@@ -27,9 +26,16 @@ linters:
- whitespace
- wrapcheck
exclusions:
rules:
- text: '(slog|log)\.\w+'
linters:
- noctx
generated: lax
presets:
- common-false-positives
settings:
exhaustive:
default-signifies-exhaustive: true
issues:
max-issues-per-linter: 0
max-same-issues: 0
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"time"
)
func (l *Logger) jsonFormatter(keyvals ...interface{}) {
func (l *Logger) jsonFormatter(keyvals ...any) {
jw := &jsonWriter{w: &l.b}
jw.start()
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/go-logfmt/logfmt"
)
func (l *Logger) logfmtFormatter(keyvals ...interface{}) {
func (l *Logger) logfmtFormatter(keyvals ...any) {
e := logfmt.NewEncoder(&l.b)
for i := 0; i < len(keyvals); i += 2 {
+19 -19
View File
@@ -41,19 +41,19 @@ type Logger struct {
reportCaller bool
reportTimestamp bool
fields []interface{}
fields []any
helpers *sync.Map
styles *Styles
}
// Logf logs a message with formatting.
func (l *Logger) Logf(level Level, format string, args ...interface{}) {
func (l *Logger) Logf(level Level, format string, args ...any) {
l.Log(level, fmt.Sprintf(format, args...))
}
// Log logs the given message with the given keyvals for the given level.
func (l *Logger) Log(level Level, msg interface{}, keyvals ...interface{}) {
func (l *Logger) Log(level Level, msg any, keyvals ...any) {
if atomic.LoadUint32(&l.isDiscard) != 0 {
return
}
@@ -81,8 +81,8 @@ func (l *Logger) Log(level Level, msg interface{}, keyvals ...interface{}) {
l.handle(level, l.timeFunc(time.Now()), []runtime.Frame{frame}, msg, keyvals...)
}
func (l *Logger) handle(level Level, ts time.Time, frames []runtime.Frame, msg interface{}, keyvals ...interface{}) {
var kvs []interface{}
func (l *Logger) handle(level Level, ts time.Time, frames []runtime.Frame, msg any, keyvals ...any) {
var kvs []any
if l.reportTimestamp && !ts.IsZero() {
kvs = append(kvs, TimestampKey, ts)
}
@@ -327,7 +327,7 @@ func (l *Logger) SetStyles(s *Styles) {
}
// With returns a new logger with the given keyvals added.
func (l *Logger) With(keyvals ...interface{}) *Logger {
func (l *Logger) With(keyvals ...any) *Logger {
var st Styles
l.mu.Lock()
sl := *l
@@ -336,7 +336,7 @@ func (l *Logger) With(keyvals ...interface{}) *Logger {
sl.b = bytes.Buffer{}
sl.mu = &sync.RWMutex{}
sl.helpers = &sync.Map{}
sl.fields = append(make([]interface{}, 0, len(l.fields)+len(keyvals)), l.fields...)
sl.fields = append(make([]any, 0, len(l.fields)+len(keyvals)), l.fields...)
sl.fields = append(sl.fields, keyvals...)
sl.styles = &st
return &sl
@@ -350,63 +350,63 @@ func (l *Logger) WithPrefix(prefix string) *Logger {
}
// Debug prints a debug message.
func (l *Logger) Debug(msg interface{}, keyvals ...interface{}) {
func (l *Logger) Debug(msg any, keyvals ...any) {
l.Log(DebugLevel, msg, keyvals...)
}
// Info prints an info message.
func (l *Logger) Info(msg interface{}, keyvals ...interface{}) {
func (l *Logger) Info(msg any, keyvals ...any) {
l.Log(InfoLevel, msg, keyvals...)
}
// Warn prints a warning message.
func (l *Logger) Warn(msg interface{}, keyvals ...interface{}) {
func (l *Logger) Warn(msg any, keyvals ...any) {
l.Log(WarnLevel, msg, keyvals...)
}
// Error prints an error message.
func (l *Logger) Error(msg interface{}, keyvals ...interface{}) {
func (l *Logger) Error(msg any, keyvals ...any) {
l.Log(ErrorLevel, msg, keyvals...)
}
// Fatal prints a fatal message and exits.
func (l *Logger) Fatal(msg interface{}, keyvals ...interface{}) {
func (l *Logger) Fatal(msg any, keyvals ...any) {
l.Log(FatalLevel, msg, keyvals...)
os.Exit(1)
}
// Print prints a message with no level.
func (l *Logger) Print(msg interface{}, keyvals ...interface{}) {
func (l *Logger) Print(msg any, keyvals ...any) {
l.Log(noLevel, msg, keyvals...)
}
// Debugf prints a debug message with formatting.
func (l *Logger) Debugf(format string, args ...interface{}) {
func (l *Logger) Debugf(format string, args ...any) {
l.Log(DebugLevel, fmt.Sprintf(format, args...))
}
// Infof prints an info message with formatting.
func (l *Logger) Infof(format string, args ...interface{}) {
func (l *Logger) Infof(format string, args ...any) {
l.Log(InfoLevel, fmt.Sprintf(format, args...))
}
// Warnf prints a warning message with formatting.
func (l *Logger) Warnf(format string, args ...interface{}) {
func (l *Logger) Warnf(format string, args ...any) {
l.Log(WarnLevel, fmt.Sprintf(format, args...))
}
// Errorf prints an error message with formatting.
func (l *Logger) Errorf(format string, args ...interface{}) {
func (l *Logger) Errorf(format string, args ...any) {
l.Log(ErrorLevel, fmt.Sprintf(format, args...))
}
// Fatalf prints a fatal message with formatting and exits.
func (l *Logger) Fatalf(format string, args ...interface{}) {
func (l *Logger) Fatalf(format string, args ...any) {
l.Log(FatalLevel, fmt.Sprintf(format, args...))
os.Exit(1)
}
// Printf prints a message with no level and formatting.
func (l *Logger) Printf(format string, args ...interface{}) {
func (l *Logger) Printf(format string, args ...any) {
l.Log(noLevel, fmt.Sprintf(format, args...))
}
+2 -2
View File
@@ -36,7 +36,7 @@ func (l *Logger) Handle(ctx context.Context, record slog.Record) error {
return nil
}
fields := make([]interface{}, 0, record.NumAttrs()*2)
fields := make([]any, 0, record.NumAttrs()*2)
record.Attrs(func(a slog.Attr) bool {
fields = append(fields, a.Key, a.Value)
return true
@@ -52,7 +52,7 @@ func (l *Logger) Handle(ctx context.Context, record slog.Record) error {
//
// Implements slog.Handler.
func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler {
fields := make([]interface{}, 0, len(attrs)*2)
fields := make([]any, 0, len(attrs)*2)
for _, attr := range attrs {
fields = append(fields, attr.Key, attr.Value)
}
+2 -2
View File
@@ -33,7 +33,7 @@ func (l *Logger) Enabled(_ context.Context, level slog.Level) bool {
//
// Implements slog.Handler.
func (l *Logger) Handle(_ context.Context, record slog.Record) error {
fields := make([]interface{}, 0, record.NumAttrs()*2)
fields := make([]any, 0, record.NumAttrs()*2)
record.Attrs(func(a slog.Attr) bool {
fields = append(fields, a.Key, a.Value)
return true
@@ -49,7 +49,7 @@ func (l *Logger) Handle(_ context.Context, record slog.Record) error {
//
// Implements slog.Handler.
func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler {
fields := make([]interface{}, 0, len(attrs)*2)
fields := make([]any, 0, len(attrs)*2)
for _, attr := range attrs {
fields = append(fields, attr.Key, attr.Value)
}
+1 -1
View File
@@ -55,7 +55,7 @@ type Options struct {
// CallerOffset is the caller format for the logger. The default is 0.
CallerOffset int
// Fields is the fields for the logger. The default is no fields.
Fields []interface{}
Fields []any
// Formatter is the formatter for the logger. The default is TextFormatter.
Formatter Formatter
}
+15 -15
View File
@@ -155,7 +155,7 @@ func GetPrefix() string {
}
// With returns a new logger with the given keyvals.
func With(keyvals ...interface{}) *Logger {
func With(keyvals ...any) *Logger {
return Default().With(keyvals...)
}
@@ -172,74 +172,74 @@ func Helper() {
}
// Log logs a message with the given level.
func Log(level Level, msg interface{}, keyvals ...interface{}) {
func Log(level Level, msg any, keyvals ...any) {
Default().Log(level, msg, keyvals...)
}
// Debug logs a debug message.
func Debug(msg interface{}, keyvals ...interface{}) {
func Debug(msg any, keyvals ...any) {
Default().Log(DebugLevel, msg, keyvals...)
}
// Info logs an info message.
func Info(msg interface{}, keyvals ...interface{}) {
func Info(msg any, keyvals ...any) {
Default().Log(InfoLevel, msg, keyvals...)
}
// Warn logs a warning message.
func Warn(msg interface{}, keyvals ...interface{}) {
func Warn(msg any, keyvals ...any) {
Default().Log(WarnLevel, msg, keyvals...)
}
// Error logs an error message.
func Error(msg interface{}, keyvals ...interface{}) {
func Error(msg any, keyvals ...any) {
Default().Log(ErrorLevel, msg, keyvals...)
}
// Fatal logs a fatal message and exit.
func Fatal(msg interface{}, keyvals ...interface{}) {
func Fatal(msg any, keyvals ...any) {
Default().Log(FatalLevel, msg, keyvals...)
os.Exit(1)
}
// Print logs a message with no level.
func Print(msg interface{}, keyvals ...interface{}) {
func Print(msg any, keyvals ...any) {
Default().Log(noLevel, msg, keyvals...)
}
// Logf logs a message with formatting and level.
func Logf(level Level, format string, args ...interface{}) {
func Logf(level Level, format string, args ...any) {
Default().Logf(level, format, args...)
}
// Debugf logs a debug message with formatting.
func Debugf(format string, args ...interface{}) {
func Debugf(format string, args ...any) {
Default().Log(DebugLevel, fmt.Sprintf(format, args...))
}
// Infof logs an info message with formatting.
func Infof(format string, args ...interface{}) {
func Infof(format string, args ...any) {
Default().Log(InfoLevel, fmt.Sprintf(format, args...))
}
// Warnf logs a warning message with formatting.
func Warnf(format string, args ...interface{}) {
func Warnf(format string, args ...any) {
Default().Log(WarnLevel, fmt.Sprintf(format, args...))
}
// Errorf logs an error message with formatting.
func Errorf(format string, args ...interface{}) {
func Errorf(format string, args ...any) {
Default().Log(ErrorLevel, fmt.Sprintf(format, args...))
}
// Fatalf logs a fatal message with formatting and exit.
func Fatalf(format string, args ...interface{}) {
func Fatalf(format string, args ...any) {
Default().Log(FatalLevel, fmt.Sprintf(format, args...))
os.Exit(1)
}
// Printf logs a message with formatting and no level.
func Printf(format string, args ...interface{}) {
func Printf(format string, args ...any) {
Default().Log(noLevel, fmt.Sprintf(format, args...))
}
+2 -2
View File
@@ -62,7 +62,7 @@ const (
)
var bufPool = sync.Pool{
New: func() interface{} {
New: func() any {
return new(strings.Builder)
},
}
@@ -164,7 +164,7 @@ func writeSpace(w io.Writer, first bool) {
}
}
func (l *Logger) textFormatter(keyvals ...interface{}) {
func (l *Logger) textFormatter(keyvals ...any) {
st := l.styles
lenKeyvals := len(keyvals)
+4 -2
View File
@@ -261,7 +261,7 @@ func CHA(col int) string {
//
// See: https://vt100.net/docs/vt510-rm/CUP.html
func CursorPosition(col, row int) string {
if row <= 0 && col <= 0 {
if row <= 1 && col <= 1 {
return CursorHomePosition
}
@@ -281,7 +281,9 @@ func CUP(col, row int) string {
}
// CursorHomePosition is a sequence for moving the cursor to the upper left
// corner of the scrolling region. This is equivalent to `CursorPosition(1, 1)`.
// corner of the scrolling region.
//
// This is equivalent to [CursorPosition](1, 1).
const CursorHomePosition = "\x1b[H"
// SetCursorPosition (CUP) returns a sequence for setting the cursor to the
+24
View File
@@ -1,5 +1,29 @@
package ansi
import (
"os"
"strconv"
"github.com/clipperhouse/displaywidth"
"github.com/mattn/go-runewidth"
)
var wcOptions = &runewidth.Condition{
EastAsianWidth: false,
StrictEmojiNeutral: true,
}
var dwOptions = &displaywidth.Options{
EastAsianWidth: false,
}
func init() {
if ea, err := strconv.ParseBool(os.Getenv("RUNEWIDTH_EASTASIAN")); err == nil && ea {
wcOptions.EastAsianWidth = true
dwOptions.EastAsianWidth = true
}
}
// Method is a type that represents the how the renderer should calculate the
// display width of cells.
type Method uint8
+154 -311
View File
@@ -108,7 +108,7 @@ func DECRST(modes ...Mode) string {
func setMode(reset bool, modes ...Mode) (s string) {
if len(modes) == 0 {
return //nolint:nakedret
return s
}
cmd := "h"
@@ -142,7 +142,7 @@ func setMode(reset bool, modes ...Mode) (s string) {
if len(dec) > 0 {
s += seq + "?" + strings.Join(dec, ";") + cmd
}
return //nolint:nakedret
return s
}
// RequestMode (DECRQM) returns a sequence to request a mode from the terminal.
@@ -228,12 +228,12 @@ func (m DECMode) Mode() int {
//
// See: https://vt100.net/docs/vt510-rm/KAM.html
const (
KeyboardActionMode = ANSIMode(2)
KAM = KeyboardActionMode
ModeKeyboardAction = ANSIMode(2)
KAM = ModeKeyboardAction
SetKeyboardActionMode = "\x1b[2h"
ResetKeyboardActionMode = "\x1b[2l"
RequestKeyboardActionMode = "\x1b[2$p"
SetModeKeyboardAction = "\x1b[2h"
ResetModeKeyboardAction = "\x1b[2l"
RequestModeKeyboardAction = "\x1b[2$p"
)
// Insert/Replace Mode (IRM) is a mode that determines whether characters are
@@ -245,12 +245,12 @@ const (
//
// See: https://vt100.net/docs/vt510-rm/IRM.html
const (
InsertReplaceMode = ANSIMode(4)
IRM = InsertReplaceMode
ModeInsertReplace = ANSIMode(4)
IRM = ModeInsertReplace
SetInsertReplaceMode = "\x1b[4h"
ResetInsertReplaceMode = "\x1b[4l"
RequestInsertReplaceMode = "\x1b[4$p"
SetModeInsertReplace = "\x1b[4h"
ResetModeInsertReplace = "\x1b[4l"
RequestModeInsertReplace = "\x1b[4$p"
)
// BiDirectional Support Mode (BDSM) is a mode that determines whether the
@@ -260,12 +260,12 @@ const (
//
// See ECMA-48 7.2.1.
const (
BiDirectionalSupportMode = ANSIMode(8)
BDSM = BiDirectionalSupportMode
ModeBiDirectionalSupport = ANSIMode(8)
BDSM = ModeBiDirectionalSupport
SetBiDirectionalSupportMode = "\x1b[8h"
ResetBiDirectionalSupportMode = "\x1b[8l"
RequestBiDirectionalSupportMode = "\x1b[8$p"
SetModeBiDirectionalSupport = "\x1b[8h"
ResetModeBiDirectionalSupport = "\x1b[8l"
RequestModeBiDirectionalSupport = "\x1b[8$p"
)
// Send Receive Mode (SRM) or Local Echo Mode is a mode that determines whether
@@ -274,17 +274,17 @@ const (
//
// See: https://vt100.net/docs/vt510-rm/SRM.html
const (
SendReceiveMode = ANSIMode(12)
LocalEchoMode = SendReceiveMode
SRM = SendReceiveMode
ModeSendReceive = ANSIMode(12)
ModeLocalEcho = ModeSendReceive
SRM = ModeSendReceive
SetSendReceiveMode = "\x1b[12h"
ResetSendReceiveMode = "\x1b[12l"
RequestSendReceiveMode = "\x1b[12$p"
SetModeSendReceive = "\x1b[12h"
ResetModeSendReceive = "\x1b[12l"
RequestModeSendReceive = "\x1b[12$p"
SetLocalEchoMode = "\x1b[12h"
ResetLocalEchoMode = "\x1b[12l"
RequestLocalEchoMode = "\x1b[12$p"
SetModeLocalEcho = "\x1b[12h"
ResetModeLocalEcho = "\x1b[12l"
RequestModeLocalEcho = "\x1b[12$p"
)
// Line Feed/New Line Mode (LNM) is a mode that determines whether the terminal
@@ -299,12 +299,12 @@ const (
//
// See: https://vt100.net/docs/vt510-rm/LNM.html
const (
LineFeedNewLineMode = ANSIMode(20)
LNM = LineFeedNewLineMode
ModeLineFeedNewLine = ANSIMode(20)
LNM = ModeLineFeedNewLine
SetLineFeedNewLineMode = "\x1b[20h"
ResetLineFeedNewLineMode = "\x1b[20l"
RequestLineFeedNewLineMode = "\x1b[20$p"
SetModeLineFeedNewLine = "\x1b[20h"
ResetModeLineFeedNewLine = "\x1b[20l"
RequestModeLineFeedNewLine = "\x1b[20$p"
)
// Cursor Keys Mode (DECCKM) is a mode that determines whether the cursor keys
@@ -312,18 +312,12 @@ const (
//
// See: https://vt100.net/docs/vt510-rm/DECCKM.html
const (
CursorKeysMode = DECMode(1)
DECCKM = CursorKeysMode
ModeCursorKeys = DECMode(1)
DECCKM = ModeCursorKeys
SetCursorKeysMode = "\x1b[?1h"
ResetCursorKeysMode = "\x1b[?1l"
RequestCursorKeysMode = "\x1b[?1$p"
)
// Deprecated: use [SetCursorKeysMode] and [ResetCursorKeysMode] instead.
const (
EnableCursorKeys = "\x1b[?1h" //nolint:revive // grouped constants
DisableCursorKeys = "\x1b[?1l"
SetModeCursorKeys = "\x1b[?1h"
ResetModeCursorKeys = "\x1b[?1l"
RequestModeCursorKeys = "\x1b[?1$p"
)
// Origin Mode (DECOM) is a mode that determines whether the cursor moves to the
@@ -331,12 +325,12 @@ const (
//
// See: https://vt100.net/docs/vt510-rm/DECOM.html
const (
OriginMode = DECMode(6)
DECOM = OriginMode
ModeOrigin = DECMode(6)
DECOM = ModeOrigin
SetOriginMode = "\x1b[?6h"
ResetOriginMode = "\x1b[?6l"
RequestOriginMode = "\x1b[?6$p"
SetModeOrigin = "\x1b[?6h"
ResetModeOrigin = "\x1b[?6l"
RequestModeOrigin = "\x1b[?6$p"
)
// Auto Wrap Mode (DECAWM) is a mode that determines whether the cursor wraps
@@ -344,12 +338,12 @@ const (
//
// See: https://vt100.net/docs/vt510-rm/DECAWM.html
const (
AutoWrapMode = DECMode(7)
DECAWM = AutoWrapMode
ModeAutoWrap = DECMode(7)
DECAWM = ModeAutoWrap
SetAutoWrapMode = "\x1b[?7h"
ResetAutoWrapMode = "\x1b[?7l"
RequestAutoWrapMode = "\x1b[?7$p"
SetModeAutoWrap = "\x1b[?7h"
ResetModeAutoWrap = "\x1b[?7l"
RequestModeAutoWrap = "\x1b[?7$p"
)
// X10 Mouse Mode is a mode that determines whether the mouse reports on button
@@ -364,39 +358,29 @@ const (
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
X10MouseMode = DECMode(9)
ModeMouseX10 = DECMode(9)
SetX10MouseMode = "\x1b[?9h"
ResetX10MouseMode = "\x1b[?9l"
RequestX10MouseMode = "\x1b[?9$p"
SetModeMouseX10 = "\x1b[?9h"
ResetModeMouseX10 = "\x1b[?9l"
RequestModeMouseX10 = "\x1b[?9$p"
)
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
//
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
const (
TextCursorEnableMode = DECMode(25)
DECTCEM = TextCursorEnableMode
ModeTextCursorEnable = DECMode(25)
DECTCEM = ModeTextCursorEnable
SetTextCursorEnableMode = "\x1b[?25h"
ResetTextCursorEnableMode = "\x1b[?25l"
RequestTextCursorEnableMode = "\x1b[?25$p"
SetModeTextCursorEnable = "\x1b[?25h"
ResetModeTextCursorEnable = "\x1b[?25l"
RequestModeTextCursorEnable = "\x1b[?25$p"
)
// These are aliases for [SetTextCursorEnableMode] and [ResetTextCursorEnableMode].
// These are aliases for [SetModeTextCursorEnable] and [ResetModeTextCursorEnable].
const (
ShowCursor = SetTextCursorEnableMode
HideCursor = ResetTextCursorEnableMode
)
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
//
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
//
// Deprecated: use [SetTextCursorEnableMode] and [ResetTextCursorEnableMode] instead.
const (
CursorEnableMode = DECMode(25)
RequestCursorVisibility = "\x1b[?25$p"
ShowCursor = SetModeTextCursorEnable
HideCursor = ResetModeTextCursorEnable
)
// Numeric Keypad Mode (DECNKM) is a mode that determines whether the keypad
@@ -406,12 +390,12 @@ const (
//
// See: https://vt100.net/docs/vt510-rm/DECNKM.html
const (
NumericKeypadMode = DECMode(66)
DECNKM = NumericKeypadMode
ModeNumericKeypad = DECMode(66)
DECNKM = ModeNumericKeypad
SetNumericKeypadMode = "\x1b[?66h"
ResetNumericKeypadMode = "\x1b[?66l"
RequestNumericKeypadMode = "\x1b[?66$p"
SetModeNumericKeypad = "\x1b[?66h"
ResetModeNumericKeypad = "\x1b[?66l"
RequestModeNumericKeypad = "\x1b[?66$p"
)
// Backarrow Key Mode (DECBKM) is a mode that determines whether the backspace
@@ -419,12 +403,12 @@ const (
//
// See: https://vt100.net/docs/vt510-rm/DECBKM.html
const (
BackarrowKeyMode = DECMode(67)
DECBKM = BackarrowKeyMode
ModeBackarrowKey = DECMode(67)
DECBKM = ModeBackarrowKey
SetBackarrowKeyMode = "\x1b[?67h"
ResetBackarrowKeyMode = "\x1b[?67l"
RequestBackarrowKeyMode = "\x1b[?67$p"
SetModeBackarrowKey = "\x1b[?67h"
ResetModeBackarrowKey = "\x1b[?67l"
RequestModeBackarrowKey = "\x1b[?67$p"
)
// Left Right Margin Mode (DECLRMM) is a mode that determines whether the left
@@ -432,47 +416,33 @@ const (
//
// See: https://vt100.net/docs/vt510-rm/DECLRMM.html
const (
LeftRightMarginMode = DECMode(69)
DECLRMM = LeftRightMarginMode
ModeLeftRightMargin = DECMode(69)
DECLRMM = ModeLeftRightMargin
SetLeftRightMarginMode = "\x1b[?69h"
ResetLeftRightMarginMode = "\x1b[?69l"
RequestLeftRightMarginMode = "\x1b[?69$p"
SetModeLeftRightMargin = "\x1b[?69h"
ResetModeLeftRightMargin = "\x1b[?69l"
RequestModeLeftRightMargin = "\x1b[?69$p"
)
// Normal Mouse Mode is a mode that determines whether the mouse reports on
// button presses and releases. It will also report modifier keys, wheel
// events, and extra buttons.
//
// It uses the same encoding as [X10MouseMode] with a few differences:
// It uses the same encoding as [ModeMouseX10] with a few differences:
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
NormalMouseMode = DECMode(1000)
ModeMouseNormal = DECMode(1000)
SetNormalMouseMode = "\x1b[?1000h"
ResetNormalMouseMode = "\x1b[?1000l"
RequestNormalMouseMode = "\x1b[?1000$p"
)
// VT Mouse Tracking is a mode that determines whether the mouse reports on
// button press and release.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
//
// Deprecated: use [NormalMouseMode] instead.
const (
MouseMode = DECMode(1000)
EnableMouse = "\x1b[?1000h"
DisableMouse = "\x1b[?1000l"
RequestMouse = "\x1b[?1000$p"
SetModeMouseNormal = "\x1b[?1000h"
ResetModeMouseNormal = "\x1b[?1000l"
RequestModeMouseNormal = "\x1b[?1000$p"
)
// Highlight Mouse Tracking is a mode that determines whether the mouse reports
// on button presses, releases, and highlighted cells.
//
// It uses the same encoding as [NormalMouseMode] with a few differences:
// It uses the same encoding as [ModeMouseNormal] with a few differences:
//
// On highlight events, the terminal responds with the following encoding:
//
@@ -481,11 +451,11 @@ const (
//
// Where the parameters are startx, starty, endx, endy, mousex, and mousey.
const (
HighlightMouseMode = DECMode(1001)
ModeMouseHighlight = DECMode(1001)
SetHighlightMouseMode = "\x1b[?1001h"
ResetHighlightMouseMode = "\x1b[?1001l"
RequestHighlightMouseMode = "\x1b[?1001$p"
SetModeMouseHighlight = "\x1b[?1001h"
ResetModeMouseHighlight = "\x1b[?1001l"
RequestModeMouseHighlight = "\x1b[?1001$p"
)
// VT Hilite Mouse Tracking is a mode that determines whether the mouse reports on
@@ -493,65 +463,29 @@ const (
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
//
// Deprecated: use [HighlightMouseMode] instead.
const (
MouseHiliteMode = DECMode(1001)
EnableMouseHilite = "\x1b[?1001h"
DisableMouseHilite = "\x1b[?1001l"
RequestMouseHilite = "\x1b[?1001$p"
)
// Button Event Mouse Tracking is essentially the same as [NormalMouseMode],
// Button Event Mouse Tracking is essentially the same as [ModeMouseNormal],
// but it also reports button-motion events when a button is pressed.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
ButtonEventMouseMode = DECMode(1002)
ModeMouseButtonEvent = DECMode(1002)
SetButtonEventMouseMode = "\x1b[?1002h"
ResetButtonEventMouseMode = "\x1b[?1002l"
RequestButtonEventMouseMode = "\x1b[?1002$p"
SetModeMouseButtonEvent = "\x1b[?1002h"
ResetModeMouseButtonEvent = "\x1b[?1002l"
RequestModeMouseButtonEvent = "\x1b[?1002$p"
)
// Cell Motion Mouse Tracking is a mode that determines whether the mouse
// reports on button press, release, and motion events.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
//
// Deprecated: use [ButtonEventMouseMode] instead.
const (
MouseCellMotionMode = DECMode(1002)
EnableMouseCellMotion = "\x1b[?1002h"
DisableMouseCellMotion = "\x1b[?1002l"
RequestMouseCellMotion = "\x1b[?1002$p"
)
// Any Event Mouse Tracking is the same as [ButtonEventMouseMode], except that
// Any Event Mouse Tracking is the same as [ModeMouseButtonEvent], except that
// all motion events are reported even if no mouse buttons are pressed.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
AnyEventMouseMode = DECMode(1003)
ModeMouseAnyEvent = DECMode(1003)
SetAnyEventMouseMode = "\x1b[?1003h"
ResetAnyEventMouseMode = "\x1b[?1003l"
RequestAnyEventMouseMode = "\x1b[?1003$p"
)
// All Mouse Tracking is a mode that determines whether the mouse reports on
// button press, release, motion, and highlight events.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
//
// Deprecated: use [AnyEventMouseMode] instead.
const (
MouseAllMotionMode = DECMode(1003)
EnableMouseAllMotion = "\x1b[?1003h"
DisableMouseAllMotion = "\x1b[?1003l"
RequestMouseAllMotion = "\x1b[?1003$p"
SetModeMouseAnyEvent = "\x1b[?1003h"
ResetModeMouseAnyEvent = "\x1b[?1003l"
RequestModeMouseAnyEvent = "\x1b[?1003$p"
)
// Focus Event Mode is a mode that determines whether the terminal reports focus
@@ -564,22 +498,11 @@ const (
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Focus-Tracking
const (
FocusEventMode = DECMode(1004)
ModeFocusEvent = DECMode(1004)
SetFocusEventMode = "\x1b[?1004h"
ResetFocusEventMode = "\x1b[?1004l"
RequestFocusEventMode = "\x1b[?1004$p"
)
// Deprecated: use [SetFocusEventMode], [ResetFocusEventMode], and
// [RequestFocusEventMode] instead.
// Focus reporting mode constants.
const (
ReportFocusMode = DECMode(1004) //nolint:revive // grouped constants
EnableReportFocus = "\x1b[?1004h"
DisableReportFocus = "\x1b[?1004l"
RequestReportFocus = "\x1b[?1004$p"
SetModeFocusEvent = "\x1b[?1004h"
ResetModeFocusEvent = "\x1b[?1004l"
RequestModeFocusEvent = "\x1b[?1004$p"
)
// SGR Extended Mouse Mode is a mode that changes the mouse tracking encoding
@@ -589,24 +512,15 @@ const (
//
// CSI < Cb ; Cx ; Cy M
//
// Where Cb is the same as [NormalMouseMode], and Cx and Cy are the x and y.
// Where Cb is the same as [ModeMouseNormal], and Cx and Cy are the x and y.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
SgrExtMouseMode = DECMode(1006)
ModeMouseExtSgr = DECMode(1006)
SetSgrExtMouseMode = "\x1b[?1006h"
ResetSgrExtMouseMode = "\x1b[?1006l"
RequestSgrExtMouseMode = "\x1b[?1006$p"
)
// Deprecated: use [SgrExtMouseMode] [SetSgrExtMouseMode],
// [ResetSgrExtMouseMode], and [RequestSgrExtMouseMode] instead.
const (
MouseSgrExtMode = DECMode(1006) //nolint:revive // grouped constants
EnableMouseSgrExt = "\x1b[?1006h"
DisableMouseSgrExt = "\x1b[?1006l"
RequestMouseSgrExt = "\x1b[?1006$p"
SetModeMouseExtSgr = "\x1b[?1006h"
ResetModeMouseExtSgr = "\x1b[?1006l"
RequestModeMouseExtSgr = "\x1b[?1006$p"
)
// UTF-8 Extended Mouse Mode is a mode that changes the mouse tracking encoding
@@ -614,11 +528,11 @@ const (
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
Utf8ExtMouseMode = DECMode(1005)
ModeMouseExtUtf8 = DECMode(1005)
SetUtf8ExtMouseMode = "\x1b[?1005h"
ResetUtf8ExtMouseMode = "\x1b[?1005l"
RequestUtf8ExtMouseMode = "\x1b[?1005$p"
SetModeMouseExtUtf8 = "\x1b[?1005h"
ResetModeMouseExtUtf8 = "\x1b[?1005l"
RequestModeMouseExtUtf8 = "\x1b[?1005$p"
)
// URXVT Extended Mouse Mode is a mode that changes the mouse tracking encoding
@@ -626,25 +540,25 @@ const (
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
UrxvtExtMouseMode = DECMode(1015)
ModeMouseExtUrxvt = DECMode(1015)
SetUrxvtExtMouseMode = "\x1b[?1015h"
ResetUrxvtExtMouseMode = "\x1b[?1015l"
RequestUrxvtExtMouseMode = "\x1b[?1015$p"
SetModeMouseExtUrxvt = "\x1b[?1015h"
ResetModeMouseExtUrxvt = "\x1b[?1015l"
RequestModeMouseExtUrxvt = "\x1b[?1015$p"
)
// SGR Pixel Extended Mouse Mode is a mode that changes the mouse tracking
// encoding to use SGR parameters with pixel coordinates.
//
// This is similar to [SgrExtMouseMode], but also reports pixel coordinates.
// This is similar to [ModeMouseExtSgr], but also reports pixel coordinates.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
const (
SgrPixelExtMouseMode = DECMode(1016)
ModeMouseExtSgrPixel = DECMode(1016)
SetSgrPixelExtMouseMode = "\x1b[?1016h"
ResetSgrPixelExtMouseMode = "\x1b[?1016l"
RequestSgrPixelExtMouseMode = "\x1b[?1016$p"
SetModeMouseExtSgrPixel = "\x1b[?1016h"
ResetModeMouseExtSgrPixel = "\x1b[?1016l"
RequestModeMouseExtSgrPixel = "\x1b[?1016$p"
)
// Alternate Screen Mode is a mode that determines whether the alternate screen
@@ -653,11 +567,11 @@ const (
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
const (
AltScreenMode = DECMode(1047)
ModeAltScreen = DECMode(1047)
SetAltScreenMode = "\x1b[?1047h"
ResetAltScreenMode = "\x1b[?1047l"
RequestAltScreenMode = "\x1b[?1047$p"
SetModeAltScreen = "\x1b[?1047h"
ResetModeAltScreen = "\x1b[?1047l"
RequestModeAltScreen = "\x1b[?1047$p"
)
// Save Cursor Mode is a mode that saves the cursor position.
@@ -665,42 +579,24 @@ const (
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
const (
SaveCursorMode = DECMode(1048)
ModeSaveCursor = DECMode(1048)
SetSaveCursorMode = "\x1b[?1048h"
ResetSaveCursorMode = "\x1b[?1048l"
RequestSaveCursorMode = "\x1b[?1048$p"
SetModeSaveCursor = "\x1b[?1048h"
ResetModeSaveCursor = "\x1b[?1048l"
RequestModeSaveCursor = "\x1b[?1048$p"
)
// Alternate Screen Save Cursor Mode is a mode that saves the cursor position as in
// [SaveCursorMode], switches to the alternate screen buffer as in [AltScreenMode],
// [ModeSaveCursor], switches to the alternate screen buffer as in [ModeAltScreen],
// and clears the screen on switch.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
const (
AltScreenSaveCursorMode = DECMode(1049)
ModeAltScreenSaveCursor = DECMode(1049)
SetAltScreenSaveCursorMode = "\x1b[?1049h"
ResetAltScreenSaveCursorMode = "\x1b[?1049l"
RequestAltScreenSaveCursorMode = "\x1b[?1049$p"
)
// Alternate Screen Buffer is a mode that determines whether the alternate screen
// buffer is active.
//
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
//
// Deprecated: use [AltScreenSaveCursorMode] instead.
const (
AltScreenBufferMode = DECMode(1049)
SetAltScreenBufferMode = "\x1b[?1049h"
ResetAltScreenBufferMode = "\x1b[?1049l"
RequestAltScreenBufferMode = "\x1b[?1049$p"
EnableAltScreenBuffer = "\x1b[?1049h"
DisableAltScreenBuffer = "\x1b[?1049l"
RequestAltScreenBuffer = "\x1b[?1049$p"
SetModeAltScreenSaveCursor = "\x1b[?1049h"
ResetModeAltScreenSaveCursor = "\x1b[?1049l"
RequestModeAltScreenSaveCursor = "\x1b[?1049$p"
)
// Bracketed Paste Mode is a mode that determines whether pasted text is
@@ -709,19 +605,11 @@ const (
// See: https://cirw.in/blog/bracketed-paste
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
const (
BracketedPasteMode = DECMode(2004)
ModeBracketedPaste = DECMode(2004)
SetBracketedPasteMode = "\x1b[?2004h"
ResetBracketedPasteMode = "\x1b[?2004l"
RequestBracketedPasteMode = "\x1b[?2004$p"
)
// Deprecated: use [SetBracketedPasteMode], [ResetBracketedPasteMode], and
// [RequestBracketedPasteMode] instead.
const (
EnableBracketedPaste = "\x1b[?2004h" //nolint:revive // grouped constants
DisableBracketedPaste = "\x1b[?2004l"
RequestBracketedPaste = "\x1b[?2004$p"
SetModeBracketedPaste = "\x1b[?2004h"
ResetModeBracketedPaste = "\x1b[?2004l"
RequestModeBracketedPaste = "\x1b[?2004$p"
)
// Synchronized Output Mode is a mode that determines whether output is
@@ -729,23 +617,11 @@ const (
//
// See: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
const (
SynchronizedOutputMode = DECMode(2026)
ModeSynchronizedOutput = DECMode(2026)
SetSynchronizedOutputMode = "\x1b[?2026h"
ResetSynchronizedOutputMode = "\x1b[?2026l"
RequestSynchronizedOutputMode = "\x1b[?2026$p"
)
// Synchronized Output Mode. See [SynchronizedOutputMode].
//
// Deprecated: use [SynchronizedOutputMode], [SetSynchronizedOutputMode], and
// [ResetSynchronizedOutputMode], and [RequestSynchronizedOutputMode] instead.
const (
SyncdOutputMode = DECMode(2026)
EnableSyncdOutput = "\x1b[?2026h"
DisableSyncdOutput = "\x1b[?2026l"
RequestSyncdOutput = "\x1b[?2026$p"
SetModeSynchronizedOutput = "\x1b[?2026h"
ResetModeSynchronizedOutput = "\x1b[?2026l"
RequestModeSynchronizedOutput = "\x1b[?2026$p"
)
// Unicode Core Mode is a mode that determines whether the terminal should use
@@ -754,41 +630,16 @@ const (
//
// See: https://github.com/contour-terminal/terminal-unicode-core
const (
UnicodeCoreMode = DECMode(2027)
ModeUnicodeCore = DECMode(2027)
SetUnicodeCoreMode = "\x1b[?2027h"
ResetUnicodeCoreMode = "\x1b[?2027l"
RequestUnicodeCoreMode = "\x1b[?2027$p"
SetModeUnicodeCore = "\x1b[?2027h"
ResetModeUnicodeCore = "\x1b[?2027l"
RequestModeUnicodeCore = "\x1b[?2027$p"
)
// Grapheme Clustering Mode is a mode that determines whether the terminal
// should look for grapheme clusters instead of single runes in the rendered
// text. This makes the terminal properly render combining characters such as
// emojis.
//
// See: https://github.com/contour-terminal/terminal-unicode-core
//
// Deprecated: use [GraphemeClusteringMode], [SetUnicodeCoreMode],
// [ResetUnicodeCoreMode], and [RequestUnicodeCoreMode] instead.
const (
GraphemeClusteringMode = DECMode(2027)
SetGraphemeClusteringMode = "\x1b[?2027h"
ResetGraphemeClusteringMode = "\x1b[?2027l"
RequestGraphemeClusteringMode = "\x1b[?2027$p"
)
// Grapheme Clustering Mode. See [GraphemeClusteringMode].
//
// Deprecated: use [SetUnicodeCoreMode], [ResetUnicodeCoreMode], and
// [RequestUnicodeCoreMode] instead.
const (
EnableGraphemeClustering = "\x1b[?2027h"
DisableGraphemeClustering = "\x1b[?2027l"
RequestGraphemeClustering = "\x1b[?2027$p"
)
// LightDarkMode is a mode that enables reporting the operating system's color
// ModeLightDark is a mode that enables reporting the operating system's color
// scheme (light or dark) preference. It reports the color scheme as a [DSR]
// and [LightDarkReport] escape sequences encoded as follows:
//
@@ -802,14 +653,14 @@ const (
//
// See: https://contour-terminal.org/vt-extensions/color-palette-update-notifications/
const (
LightDarkMode = DECMode(2031)
ModeLightDark = DECMode(2031)
SetLightDarkMode = "\x1b[?2031h"
ResetLightDarkMode = "\x1b[?2031l"
RequestLightDarkMode = "\x1b[?2031$p"
SetModeLightDark = "\x1b[?2031h"
ResetModeLightDark = "\x1b[?2031l"
RequestModeLightDark = "\x1b[?2031$p"
)
// InBandResizeMode is a mode that reports terminal resize events as escape
// ModeInBandResize is a mode that reports terminal resize events as escape
// sequences. This is useful for systems that do not support [SIGWINCH] like
// Windows.
//
@@ -819,11 +670,11 @@ const (
//
// See: https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3704bd83
const (
InBandResizeMode = DECMode(2048)
ModeInBandResize = DECMode(2048)
SetInBandResizeMode = "\x1b[?2048h"
ResetInBandResizeMode = "\x1b[?2048l"
RequestInBandResizeMode = "\x1b[?2048$p"
SetModeInBandResize = "\x1b[?2048h"
ResetModeInBandResize = "\x1b[?2048l"
RequestModeInBandResize = "\x1b[?2048$p"
)
// Win32Input is a mode that determines whether input is processed by the
@@ -831,17 +682,9 @@ const (
//
// See: https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md
const (
Win32InputMode = DECMode(9001)
ModeWin32Input = DECMode(9001)
SetWin32InputMode = "\x1b[?9001h"
ResetWin32InputMode = "\x1b[?9001l"
RequestWin32InputMode = "\x1b[?9001$p"
)
// Deprecated: use [SetWin32InputMode], [ResetWin32InputMode], and
// [RequestWin32InputMode] instead.
const (
EnableWin32Input = "\x1b[?9001h" //nolint:revive // grouped constants
DisableWin32Input = "\x1b[?9001l"
RequestWin32Input = "\x1b[?9001$p"
SetModeWin32Input = "\x1b[?9001h"
ResetModeWin32Input = "\x1b[?9001l"
RequestModeWin32Input = "\x1b[?9001$p"
)
+495
View File
@@ -0,0 +1,495 @@
package ansi
// Keyboard Action Mode (KAM) controls locking of the keyboard.
//
// Deprecated: use [ModeKeyboardAction] instead.
const (
KeyboardActionMode = ANSIMode(2)
SetKeyboardActionMode = "\x1b[2h"
ResetKeyboardActionMode = "\x1b[2l"
RequestKeyboardActionMode = "\x1b[2$p"
)
// Insert/Replace Mode (IRM) determines whether characters are inserted or replaced.
//
// Deprecated: use [ModeInsertReplace] instead.
const (
InsertReplaceMode = ANSIMode(4)
SetInsertReplaceMode = "\x1b[4h"
ResetInsertReplaceMode = "\x1b[4l"
RequestInsertReplaceMode = "\x1b[4$p"
)
// BiDirectional Support Mode (BDSM) determines whether the terminal supports bidirectional text.
//
// Deprecated: use [ModeBiDirectionalSupport] instead.
const (
BiDirectionalSupportMode = ANSIMode(8)
SetBiDirectionalSupportMode = "\x1b[8h"
ResetBiDirectionalSupportMode = "\x1b[8l"
RequestBiDirectionalSupportMode = "\x1b[8$p"
)
// Send Receive Mode (SRM) or Local Echo Mode determines whether the terminal echoes characters.
//
// Deprecated: use [ModeSendReceive] instead.
const (
SendReceiveMode = ANSIMode(12)
LocalEchoMode = SendReceiveMode
SetSendReceiveMode = "\x1b[12h"
ResetSendReceiveMode = "\x1b[12l"
RequestSendReceiveMode = "\x1b[12$p"
SetLocalEchoMode = "\x1b[12h"
ResetLocalEchoMode = "\x1b[12l"
RequestLocalEchoMode = "\x1b[12$p"
)
// Line Feed/New Line Mode (LNM) determines whether the terminal interprets line feed as new line.
//
// Deprecated: use [ModeLineFeedNewLine] instead.
const (
LineFeedNewLineMode = ANSIMode(20)
SetLineFeedNewLineMode = "\x1b[20h"
ResetLineFeedNewLineMode = "\x1b[20l"
RequestLineFeedNewLineMode = "\x1b[20$p"
)
// Cursor Keys Mode (DECCKM) determines whether cursor keys send ANSI or application sequences.
//
// Deprecated: use [ModeCursorKeys] instead.
const (
CursorKeysMode = DECMode(1)
SetCursorKeysMode = "\x1b[?1h"
ResetCursorKeysMode = "\x1b[?1l"
RequestCursorKeysMode = "\x1b[?1$p"
)
// Cursor Keys mode.
//
// Deprecated: use [SetModeCursorKeys] and [ResetModeCursorKeys] instead.
const (
EnableCursorKeys = "\x1b[?1h"
DisableCursorKeys = "\x1b[?1l"
)
// Origin Mode (DECOM) determines whether the cursor moves to home or margin position.
//
// Deprecated: use [ModeOrigin] instead.
const (
OriginMode = DECMode(6)
SetOriginMode = "\x1b[?6h"
ResetOriginMode = "\x1b[?6l"
RequestOriginMode = "\x1b[?6$p"
)
// Auto Wrap Mode (DECAWM) determines whether the cursor wraps to the next line.
//
// Deprecated: use [ModeAutoWrap] instead.
const (
AutoWrapMode = DECMode(7)
SetAutoWrapMode = "\x1b[?7h"
ResetAutoWrapMode = "\x1b[?7l"
RequestAutoWrapMode = "\x1b[?7$p"
)
// X10 Mouse Mode determines whether the mouse reports on button presses.
//
// Deprecated: use [ModeMouseX10] instead.
const (
X10MouseMode = DECMode(9)
SetX10MouseMode = "\x1b[?9h"
ResetX10MouseMode = "\x1b[?9l"
RequestX10MouseMode = "\x1b[?9$p"
)
// Text Cursor Enable Mode (DECTCEM) shows/hides the cursor.
//
// Deprecated: use [ModeTextCursorEnable] instead.
const (
TextCursorEnableMode = DECMode(25)
SetTextCursorEnableMode = "\x1b[?25h"
ResetTextCursorEnableMode = "\x1b[?25l"
RequestTextCursorEnableMode = "\x1b[?25$p"
)
// Text Cursor Enable mode.
//
// Deprecated: use [SetModeTextCursorEnable] and [ResetModeTextCursorEnable] instead.
const (
CursorEnableMode = DECMode(25)
RequestCursorVisibility = "\x1b[?25$p"
)
// Numeric Keypad Mode (DECNKM) determines whether the keypad sends application or numeric sequences.
//
// Deprecated: use [ModeNumericKeypad] instead.
const (
NumericKeypadMode = DECMode(66)
SetNumericKeypadMode = "\x1b[?66h"
ResetNumericKeypadMode = "\x1b[?66l"
RequestNumericKeypadMode = "\x1b[?66$p"
)
// Backarrow Key Mode (DECBKM) determines whether the backspace key sends backspace or delete.
//
// Deprecated: use [ModeBackarrowKey] instead.
const (
BackarrowKeyMode = DECMode(67)
SetBackarrowKeyMode = "\x1b[?67h"
ResetBackarrowKeyMode = "\x1b[?67l"
RequestBackarrowKeyMode = "\x1b[?67$p"
)
// Left Right Margin Mode (DECLRMM) determines whether left and right margins can be set.
//
// Deprecated: use [ModeLeftRightMargin] instead.
const (
LeftRightMarginMode = DECMode(69)
SetLeftRightMarginMode = "\x1b[?69h"
ResetLeftRightMarginMode = "\x1b[?69l"
RequestLeftRightMarginMode = "\x1b[?69$p"
)
// Normal Mouse Mode determines whether the mouse reports on button presses and releases.
//
// Deprecated: use [ModeMouseNormal] instead.
const (
NormalMouseMode = DECMode(1000)
SetNormalMouseMode = "\x1b[?1000h"
ResetNormalMouseMode = "\x1b[?1000l"
RequestNormalMouseMode = "\x1b[?1000$p"
)
// VT Mouse Tracking mode.
//
// Deprecated: use [ModeMouseNormal] instead.
const (
MouseMode = DECMode(1000)
EnableMouse = "\x1b[?1000h"
DisableMouse = "\x1b[?1000l"
RequestMouse = "\x1b[?1000$p"
)
// Highlight Mouse Tracking determines whether the mouse reports on button presses and highlighted cells.
//
// Deprecated: use [ModeMouseHighlight] instead.
const (
HighlightMouseMode = DECMode(1001)
SetHighlightMouseMode = "\x1b[?1001h"
ResetHighlightMouseMode = "\x1b[?1001l"
RequestHighlightMouseMode = "\x1b[?1001$p"
)
// VT Hilite Mouse Tracking mode.
//
// Deprecated: use [ModeMouseHighlight] instead.
const (
MouseHiliteMode = DECMode(1001)
EnableMouseHilite = "\x1b[?1001h"
DisableMouseHilite = "\x1b[?1001l"
RequestMouseHilite = "\x1b[?1001$p"
)
// Button Event Mouse Tracking reports button-motion events when a button is pressed.
//
// Deprecated: use [ModeMouseButtonEvent] instead.
const (
ButtonEventMouseMode = DECMode(1002)
SetButtonEventMouseMode = "\x1b[?1002h"
ResetButtonEventMouseMode = "\x1b[?1002l"
RequestButtonEventMouseMode = "\x1b[?1002$p"
)
// Cell Motion Mouse Tracking mode.
//
// Deprecated: use [ModeMouseButtonEvent] instead.
const (
MouseCellMotionMode = DECMode(1002)
EnableMouseCellMotion = "\x1b[?1002h"
DisableMouseCellMotion = "\x1b[?1002l"
RequestMouseCellMotion = "\x1b[?1002$p"
)
// Any Event Mouse Tracking reports all motion events.
//
// Deprecated: use [ModeMouseAnyEvent] instead.
const (
AnyEventMouseMode = DECMode(1003)
SetAnyEventMouseMode = "\x1b[?1003h"
ResetAnyEventMouseMode = "\x1b[?1003l"
RequestAnyEventMouseMode = "\x1b[?1003$p"
)
// All Mouse Tracking mode.
//
// Deprecated: use [ModeMouseAnyEvent] instead.
const (
MouseAllMotionMode = DECMode(1003)
EnableMouseAllMotion = "\x1b[?1003h"
DisableMouseAllMotion = "\x1b[?1003l"
RequestMouseAllMotion = "\x1b[?1003$p"
)
// Focus Event Mode determines whether the terminal reports focus and blur events.
//
// Deprecated: use [ModeFocusEvent] instead.
const (
FocusEventMode = DECMode(1004)
SetFocusEventMode = "\x1b[?1004h"
ResetFocusEventMode = "\x1b[?1004l"
RequestFocusEventMode = "\x1b[?1004$p"
)
// Focus reporting mode.
//
// Deprecated: use [SetModeFocusEvent], [ResetModeFocusEvent], and
// [RequestModeFocusEvent] instead.
const (
ReportFocusMode = DECMode(1004)
EnableReportFocus = "\x1b[?1004h"
DisableReportFocus = "\x1b[?1004l"
RequestReportFocus = "\x1b[?1004$p"
)
// UTF-8 Extended Mouse Mode changes the mouse tracking encoding to use UTF-8 parameters.
//
// Deprecated: use [ModeMouseExtUtf8] instead.
const (
Utf8ExtMouseMode = DECMode(1005)
SetUtf8ExtMouseMode = "\x1b[?1005h"
ResetUtf8ExtMouseMode = "\x1b[?1005l"
RequestUtf8ExtMouseMode = "\x1b[?1005$p"
)
// SGR Extended Mouse Mode changes the mouse tracking encoding to use SGR parameters.
//
// Deprecated: use [ModeMouseExtSgr] instead.
const (
SgrExtMouseMode = DECMode(1006)
SetSgrExtMouseMode = "\x1b[?1006h"
ResetSgrExtMouseMode = "\x1b[?1006l"
RequestSgrExtMouseMode = "\x1b[?1006$p"
)
// Mouse SGR Extended mode.
//
// Deprecated: use [ModeMouseExtSgr], [SetModeMouseExtSgr],
// [ResetModeMouseExtSgr], and [RequestModeMouseExtSgr] instead.
const (
MouseSgrExtMode = DECMode(1006)
EnableMouseSgrExt = "\x1b[?1006h"
DisableMouseSgrExt = "\x1b[?1006l"
RequestMouseSgrExt = "\x1b[?1006$p"
)
// URXVT Extended Mouse Mode changes the mouse tracking encoding to use an alternate encoding.
//
// Deprecated: use [ModeMouseUrxvtExt] instead.
const (
UrxvtExtMouseMode = DECMode(1015)
SetUrxvtExtMouseMode = "\x1b[?1015h"
ResetUrxvtExtMouseMode = "\x1b[?1015l"
RequestUrxvtExtMouseMode = "\x1b[?1015$p"
)
// SGR Pixel Extended Mouse Mode changes the mouse tracking encoding to use SGR parameters with pixel coordinates.
//
// Deprecated: use [ModeMouseExtSgrPixel] instead.
const (
SgrPixelExtMouseMode = DECMode(1016)
SetSgrPixelExtMouseMode = "\x1b[?1016h"
ResetSgrPixelExtMouseMode = "\x1b[?1016l"
RequestSgrPixelExtMouseMode = "\x1b[?1016$p"
)
// Alternate Screen Mode determines whether the alternate screen buffer is active.
//
// Deprecated: use [ModeAltScreen] instead.
const (
AltScreenMode = DECMode(1047)
SetAltScreenMode = "\x1b[?1047h"
ResetAltScreenMode = "\x1b[?1047l"
RequestAltScreenMode = "\x1b[?1047$p"
)
// Save Cursor Mode saves the cursor position.
//
// Deprecated: use [ModeSaveCursor] instead.
const (
SaveCursorMode = DECMode(1048)
SetSaveCursorMode = "\x1b[?1048h"
ResetSaveCursorMode = "\x1b[?1048l"
RequestSaveCursorMode = "\x1b[?1048$p"
)
// Alternate Screen Save Cursor Mode saves the cursor position and switches to alternate screen.
//
// Deprecated: use [ModeAltScreenSaveCursor] instead.
const (
AltScreenSaveCursorMode = DECMode(1049)
SetAltScreenSaveCursorMode = "\x1b[?1049h"
ResetAltScreenSaveCursorMode = "\x1b[?1049l"
RequestAltScreenSaveCursorMode = "\x1b[?1049$p"
)
// Alternate Screen Buffer mode.
//
// Deprecated: use [ModeAltScreenSaveCursor] instead.
const (
AltScreenBufferMode = DECMode(1049)
SetAltScreenBufferMode = "\x1b[?1049h"
ResetAltScreenBufferMode = "\x1b[?1049l"
RequestAltScreenBufferMode = "\x1b[?1049$p"
EnableAltScreenBuffer = "\x1b[?1049h"
DisableAltScreenBuffer = "\x1b[?1049l"
RequestAltScreenBuffer = "\x1b[?1049$p"
)
// Bracketed Paste Mode determines whether pasted text is bracketed with escape sequences.
//
// Deprecated: use [ModeBracketedPaste] instead.
const (
BracketedPasteMode = DECMode(2004)
SetBracketedPasteMode = "\x1b[?2004h"
ResetBracketedPasteMode = "\x1b[?2004l"
RequestBracketedPasteMode = "\x1b[?2004$p"
)
// Deprecated: use [SetModeBracketedPaste], [ResetModeBracketedPaste], and
// [RequestModeBracketedPaste] instead.
const (
EnableBracketedPaste = "\x1b[?2004h" //nolint:revive
DisableBracketedPaste = "\x1b[?2004l"
RequestBracketedPaste = "\x1b[?2004$p"
)
// Synchronized Output Mode determines whether output is synchronized with the terminal.
//
// Deprecated: use [ModeSynchronizedOutput] instead.
const (
SynchronizedOutputMode = DECMode(2026)
SetSynchronizedOutputMode = "\x1b[?2026h"
ResetSynchronizedOutputMode = "\x1b[?2026l"
RequestSynchronizedOutputMode = "\x1b[?2026$p"
)
// Synchronized output mode.
//
// Deprecated: use [ModeSynchronizedOutput], [SetModeSynchronizedOutput],
// [ResetModeSynchronizedOutput], and [RequestModeSynchronizedOutput] instead.
const (
SyncdOutputMode = DECMode(2026)
EnableSyncdOutput = "\x1b[?2026h"
DisableSyncdOutput = "\x1b[?2026l"
RequestSyncdOutput = "\x1b[?2026$p"
)
// Unicode Core Mode determines whether the terminal uses Unicode grapheme clustering.
//
// Deprecated: use [ModeUnicodeCore] instead.
const (
UnicodeCoreMode = DECMode(2027)
SetUnicodeCoreMode = "\x1b[?2027h"
ResetUnicodeCoreMode = "\x1b[?2027l"
RequestUnicodeCoreMode = "\x1b[?2027$p"
)
// Grapheme Clustering Mode determines whether the terminal looks for grapheme clusters.
//
// Deprecated: use [ModeUnicodeCore], [SetModeUnicodeCore],
// [ResetModeUnicodeCore], and [RequestModeUnicodeCore] instead.
const (
GraphemeClusteringMode = DECMode(2027)
SetGraphemeClusteringMode = "\x1b[?2027h"
ResetGraphemeClusteringMode = "\x1b[?2027l"
RequestGraphemeClusteringMode = "\x1b[?2027$p"
)
// Unicode Core mode.
//
// Deprecated: use [SetModeUnicodeCore], [ResetModeUnicodeCore], and
// [RequestModeUnicodeCore] instead.
const (
EnableGraphemeClustering = "\x1b[?2027h"
DisableGraphemeClustering = "\x1b[?2027l"
RequestGraphemeClustering = "\x1b[?2027$p"
)
// Light Dark Mode enables reporting the operating system's color scheme preference.
//
// Deprecated: use [ModeLightDark] instead.
const (
LightDarkMode = DECMode(2031)
SetLightDarkMode = "\x1b[?2031h"
ResetLightDarkMode = "\x1b[?2031l"
RequestLightDarkMode = "\x1b[?2031$p"
)
// In Band Resize Mode reports terminal resize events as escape sequences.
//
// Deprecated: use [ModeInBandResize] instead.
const (
InBandResizeMode = DECMode(2048)
SetInBandResizeMode = "\x1b[?2048h"
ResetInBandResizeMode = "\x1b[?2048l"
RequestInBandResizeMode = "\x1b[?2048$p"
)
// Win32Input determines whether input is processed by the Win32 console and Conpty.
//
// Deprecated: use [ModeWin32Input] instead.
const (
Win32InputMode = DECMode(9001)
SetWin32InputMode = "\x1b[?9001h"
ResetWin32InputMode = "\x1b[?9001l"
RequestWin32InputMode = "\x1b[?9001$p"
)
// Deprecated: use [SetModeWin32Input], [ResetModeWin32Input], and
// [RequestModeWin32Input] instead.
const (
EnableWin32Input = "\x1b[?9001h" //nolint:revive
DisableWin32Input = "\x1b[?9001l"
RequestWin32Input = "\x1b[?9001$p"
)
+1 -1
View File
@@ -134,7 +134,7 @@ func EncodeMouseButton(b MouseButton, motion, shift, alt, ctrl bool) (m byte) {
m |= bitMotion
}
return //nolint:nakedret
return m
}
// x10Offset is the offset for X10 mouse events.
+19
View File
@@ -1,5 +1,10 @@
package ansi
import (
"fmt"
"strings"
)
// Notify sends a desktop notification using iTerm's OSC 9.
//
// OSC 9 ; Mc ST
@@ -11,3 +16,17 @@ package ansi
func Notify(s string) string {
return "\x1b]9;" + s + "\x07"
}
// DesktopNotification sends a desktop notification based on the extensible OSC
// 99 escape code.
//
// OSC 99 ; <metadata> ; <payload> ST
// OSC 99 ; <metadata> ; <payload> BEL
//
// Where <metadata> is a colon-separated list of key-value pairs, and
// <payload> is the notification body.
//
// See: https://sw.kovidgoyal.net/kitty/desktop-notifications/
func DesktopNotification(payload string, metadata ...string) string {
return fmt.Sprintf("\x1b]99;%s;%s\x07", strings.Join(metadata, ":"), payload)
}
+17 -16
View File
@@ -4,8 +4,7 @@ import (
"unicode/utf8"
"github.com/charmbracelet/x/ansi/parser"
"github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
"github.com/clipperhouse/uax29/v2/graphemes"
)
// State represents the state of the ANSI escape sequence parser used by
@@ -176,10 +175,7 @@ func decodeSequence[T string | []byte](m Method, b T, state State, p *Parser) (s
}
if utf8.RuneStart(c) {
seq, _, width, _ = FirstGraphemeCluster(b, -1)
if m == WcWidth {
width = runewidth.StringWidth(string(seq))
}
seq, width = FirstGraphemeCluster(b, m)
i += len(seq)
return b[:i], width, i, NormalState
}
@@ -434,17 +430,22 @@ func HasEscPrefix[T string | []byte](b T) bool {
return len(b) > 0 && b[0] == ESC
}
// FirstGraphemeCluster returns the first grapheme cluster in the given string or byte slice.
// This is a syntactic sugar function that wraps
// uniseg.FirstGraphemeClusterInString and uniseg.FirstGraphemeCluster.
func FirstGraphemeCluster[T string | []byte](b T, state int) (T, T, int, int) {
// FirstGraphemeCluster returns the first grapheme cluster in the given string
// or byte slice, and its monospace display width.
func FirstGraphemeCluster[T string | []byte](b T, m Method) (T, int) {
switch b := any(b).(type) {
case string:
cluster, rest, width, newState := uniseg.FirstGraphemeClusterInString(b, state)
return T(cluster), T(rest), width, newState
cluster := graphemes.FromString(b).First()
if m == WcWidth {
return T(cluster), wcOptions.StringWidth(cluster)
}
return T(cluster), dwOptions.String(cluster)
case []byte:
cluster, rest, width, newState := uniseg.FirstGraphemeCluster(b, state)
return T(cluster), T(rest), width, newState
cluster := graphemes.FromBytes(b).First()
if m == WcWidth {
return T(cluster), wcOptions.StringWidth(string(cluster))
}
return T(cluster), dwOptions.Bytes(cluster)
}
panic("unreachable")
}
@@ -490,7 +491,7 @@ func Command(prefix, inter, final byte) (c int) {
c = int(final)
c |= int(prefix) << parser.PrefixShift
c |= int(inter) << parser.IntermedShift
return
return c
}
// Param represents a sequence parameter. Sequence parameters with
@@ -520,5 +521,5 @@ func Parameter(p int, hasMore bool) (s int) {
if hasMore {
s |= parser.HasMoreFlag
}
return
return s
}
+1 -1
View File
@@ -10,7 +10,7 @@ var parserPool = sync.Pool{
New: func() any {
p := NewParser()
p.SetParamsSize(parser.MaxParamsSize)
p.SetDataSize(1024 * 1024 * 4) // 4MB of data buffer
p.SetDataSize(1024 * 4) // 4KB of data buffer
return p
},
}
+55 -55
View File
@@ -21,59 +21,59 @@ func SGR(ps ...Attr) string {
}
var attrStrings = map[int]string{
ResetAttr: resetAttr,
BoldAttr: boldAttr,
FaintAttr: faintAttr,
ItalicAttr: italicAttr,
UnderlineAttr: underlineAttr,
SlowBlinkAttr: slowBlinkAttr,
RapidBlinkAttr: rapidBlinkAttr,
ReverseAttr: reverseAttr,
ConcealAttr: concealAttr,
StrikethroughAttr: strikethroughAttr,
NormalIntensityAttr: normalIntensityAttr,
NoItalicAttr: noItalicAttr,
NoUnderlineAttr: noUnderlineAttr,
NoBlinkAttr: noBlinkAttr,
NoReverseAttr: noReverseAttr,
NoConcealAttr: noConcealAttr,
NoStrikethroughAttr: noStrikethroughAttr,
BlackForegroundColorAttr: blackForegroundColorAttr,
RedForegroundColorAttr: redForegroundColorAttr,
GreenForegroundColorAttr: greenForegroundColorAttr,
YellowForegroundColorAttr: yellowForegroundColorAttr,
BlueForegroundColorAttr: blueForegroundColorAttr,
MagentaForegroundColorAttr: magentaForegroundColorAttr,
CyanForegroundColorAttr: cyanForegroundColorAttr,
WhiteForegroundColorAttr: whiteForegroundColorAttr,
ExtendedForegroundColorAttr: extendedForegroundColorAttr,
DefaultForegroundColorAttr: defaultForegroundColorAttr,
BlackBackgroundColorAttr: blackBackgroundColorAttr,
RedBackgroundColorAttr: redBackgroundColorAttr,
GreenBackgroundColorAttr: greenBackgroundColorAttr,
YellowBackgroundColorAttr: yellowBackgroundColorAttr,
BlueBackgroundColorAttr: blueBackgroundColorAttr,
MagentaBackgroundColorAttr: magentaBackgroundColorAttr,
CyanBackgroundColorAttr: cyanBackgroundColorAttr,
WhiteBackgroundColorAttr: whiteBackgroundColorAttr,
ExtendedBackgroundColorAttr: extendedBackgroundColorAttr,
DefaultBackgroundColorAttr: defaultBackgroundColorAttr,
ExtendedUnderlineColorAttr: extendedUnderlineColorAttr,
DefaultUnderlineColorAttr: defaultUnderlineColorAttr,
BrightBlackForegroundColorAttr: brightBlackForegroundColorAttr,
BrightRedForegroundColorAttr: brightRedForegroundColorAttr,
BrightGreenForegroundColorAttr: brightGreenForegroundColorAttr,
BrightYellowForegroundColorAttr: brightYellowForegroundColorAttr,
BrightBlueForegroundColorAttr: brightBlueForegroundColorAttr,
BrightMagentaForegroundColorAttr: brightMagentaForegroundColorAttr,
BrightCyanForegroundColorAttr: brightCyanForegroundColorAttr,
BrightWhiteForegroundColorAttr: brightWhiteForegroundColorAttr,
BrightBlackBackgroundColorAttr: brightBlackBackgroundColorAttr,
BrightRedBackgroundColorAttr: brightRedBackgroundColorAttr,
BrightGreenBackgroundColorAttr: brightGreenBackgroundColorAttr,
BrightYellowBackgroundColorAttr: brightYellowBackgroundColorAttr,
BrightBlueBackgroundColorAttr: brightBlueBackgroundColorAttr,
BrightMagentaBackgroundColorAttr: brightMagentaBackgroundColorAttr,
BrightCyanBackgroundColorAttr: brightCyanBackgroundColorAttr,
BrightWhiteBackgroundColorAttr: brightWhiteBackgroundColorAttr,
AttrReset: attrReset,
AttrBold: attrBold,
AttrFaint: attrFaint,
AttrItalic: attrItalic,
AttrUnderline: attrUnderline,
AttrBlink: attrBlink,
AttrRapidBlink: attrRapidBlink,
AttrReverse: attrReverse,
AttrConceal: attrConceal,
AttrStrikethrough: attrStrikethrough,
AttrNormalIntensity: attrNormalIntensity,
AttrNoItalic: attrNoItalic,
AttrNoUnderline: attrNoUnderline,
AttrNoBlink: attrNoBlink,
AttrNoReverse: attrNoReverse,
AttrNoConceal: attrNoConceal,
AttrNoStrikethrough: attrNoStrikethrough,
AttrBlackForegroundColor: attrBlackForegroundColor,
AttrRedForegroundColor: attrRedForegroundColor,
AttrGreenForegroundColor: attrGreenForegroundColor,
AttrYellowForegroundColor: attrYellowForegroundColor,
AttrBlueForegroundColor: attrBlueForegroundColor,
AttrMagentaForegroundColor: attrMagentaForegroundColor,
AttrCyanForegroundColor: attrCyanForegroundColor,
AttrWhiteForegroundColor: attrWhiteForegroundColor,
AttrExtendedForegroundColor: attrExtendedForegroundColor,
AttrDefaultForegroundColor: attrDefaultForegroundColor,
AttrBlackBackgroundColor: attrBlackBackgroundColor,
AttrRedBackgroundColor: attrRedBackgroundColor,
AttrGreenBackgroundColor: attrGreenBackgroundColor,
AttrYellowBackgroundColor: attrYellowBackgroundColor,
AttrBlueBackgroundColor: attrBlueBackgroundColor,
AttrMagentaBackgroundColor: attrMagentaBackgroundColor,
AttrCyanBackgroundColor: attrCyanBackgroundColor,
AttrWhiteBackgroundColor: attrWhiteBackgroundColor,
AttrExtendedBackgroundColor: attrExtendedBackgroundColor,
AttrDefaultBackgroundColor: attrDefaultBackgroundColor,
AttrExtendedUnderlineColor: attrExtendedUnderlineColor,
AttrDefaultUnderlineColor: attrDefaultUnderlineColor,
AttrBrightBlackForegroundColor: attrBrightBlackForegroundColor,
AttrBrightRedForegroundColor: attrBrightRedForegroundColor,
AttrBrightGreenForegroundColor: attrBrightGreenForegroundColor,
AttrBrightYellowForegroundColor: attrBrightYellowForegroundColor,
AttrBrightBlueForegroundColor: attrBrightBlueForegroundColor,
AttrBrightMagentaForegroundColor: attrBrightMagentaForegroundColor,
AttrBrightCyanForegroundColor: attrBrightCyanForegroundColor,
AttrBrightWhiteForegroundColor: attrBrightWhiteForegroundColor,
AttrBrightBlackBackgroundColor: attrBrightBlackBackgroundColor,
AttrBrightRedBackgroundColor: attrBrightRedBackgroundColor,
AttrBrightGreenBackgroundColor: attrBrightGreenBackgroundColor,
AttrBrightYellowBackgroundColor: attrBrightYellowBackgroundColor,
AttrBrightBlueBackgroundColor: attrBrightBlueBackgroundColor,
AttrBrightMagentaBackgroundColor: attrBrightMagentaBackgroundColor,
AttrBrightCyanBackgroundColor: attrBrightCyanBackgroundColor,
AttrBrightWhiteBackgroundColor: attrBrightWhiteBackgroundColor,
}
+388 -242
View File
@@ -17,7 +17,9 @@ type Attr = int
// Style represents an ANSI SGR (Select Graphic Rendition) style.
type Style []string
// NewStyle returns a new style with the given attributes.
// NewStyle returns a new style with the given attributes. Attributes are SGR
// (Select Graphic Rendition) codes that control text formatting like bold,
// italic, colors, etc.
func NewStyle(attrs ...Attr) Style {
if len(attrs) == 0 {
return Style{}
@@ -46,7 +48,8 @@ func (s Style) String() string {
return "\x1b[" + strings.Join(s, ";") + "m"
}
// Styled returns a styled string with the given style applied.
// Styled returns a styled string with the given style applied. The style is
// applied at the beginning and reset at the end of the string.
func (s Style) Styled(str string) string {
if len(s) == 0 {
return str
@@ -54,309 +57,446 @@ func (s Style) Styled(str string) string {
return s.String() + str + ResetStyle
}
// Reset appends the reset style attribute to the style.
// Reset appends the reset style attribute to the style. This resets all
// formatting attributes to their defaults.
func (s Style) Reset() Style {
return append(s, resetAttr)
return append(s, attrReset)
}
// Bold appends the bold style attribute to the style.
// Bold appends the bold or normal intensity style attribute to the style.
// You can use [Style.Normal] to reset to normal intensity.
func (s Style) Bold() Style {
return append(s, boldAttr)
return append(s, attrBold)
}
// Faint appends the faint style attribute to the style.
// Faint appends the faint or normal intensity style attribute to the style.
// You can use [Style.Normal] to reset to normal intensity.
func (s Style) Faint() Style {
return append(s, faintAttr)
return append(s, attrFaint)
}
// Italic appends the italic style attribute to the style.
func (s Style) Italic() Style {
return append(s, italicAttr)
// Italic appends the italic or no italic style attribute to the style.
// When v is true, text is rendered in italic. When false, italic is disabled.
func (s Style) Italic(v bool) Style {
if v {
return append(s, attrItalic)
}
return append(s, attrNoItalic)
}
// Underline appends the underline style attribute to the style.
func (s Style) Underline() Style {
return append(s, underlineAttr)
// Underline appends the underline or no underline style attribute to the style.
// When v is true, text is underlined. When false, underline is disabled.
func (s Style) Underline(v bool) Style {
if v {
return append(s, attrUnderline)
}
return append(s, attrNoUnderline)
}
// UnderlineStyle appends the underline style attribute to the style.
func (s Style) UnderlineStyle(u UnderlineStyle) Style {
// Supports various underline styles including single, double, curly, dotted,
// and dashed.
func (s Style) UnderlineStyle(u Underline) Style {
switch u {
case NoUnderlineStyle:
return s.NoUnderline()
case SingleUnderlineStyle:
return s.Underline()
case DoubleUnderlineStyle:
return append(s, doubleUnderlineStyle)
case CurlyUnderlineStyle:
return append(s, curlyUnderlineStyle)
case DottedUnderlineStyle:
return append(s, dottedUnderlineStyle)
case DashedUnderlineStyle:
return append(s, dashedUnderlineStyle)
case UnderlineNone:
return s.Underline(false)
case UnderlineSingle:
return s.Underline(true)
case UnderlineDouble:
return append(s, underlineDouble)
case UnderlineCurly:
return append(s, underlineCurly)
case UnderlineDotted:
return append(s, underlineDotted)
case UnderlineDashed:
return append(s, underlineDashed)
}
return s
}
// DoubleUnderline appends the double underline style attribute to the style.
// This is a convenience method for UnderlineStyle(DoubleUnderlineStyle).
func (s Style) DoubleUnderline() Style {
return s.UnderlineStyle(DoubleUnderlineStyle)
// Blink appends the slow blink or no blink style attribute to the style.
// When v is true, text blinks slowly (less than 150 per minute). When false,
// blinking is disabled.
func (s Style) Blink(v bool) Style {
if v {
return append(s, attrBlink)
}
return append(s, attrNoBlink)
}
// CurlyUnderline appends the curly underline style attribute to the style.
// This is a convenience method for UnderlineStyle(CurlyUnderlineStyle).
func (s Style) CurlyUnderline() Style {
return s.UnderlineStyle(CurlyUnderlineStyle)
// RapidBlink appends the rapid blink or no blink style attribute to the style.
// When v is true, text blinks rapidly (150+ per minute). When false, blinking
// is disabled.
//
// Note that this is not widely supported in terminal emulators.
func (s Style) RapidBlink(v bool) Style {
if v {
return append(s, attrRapidBlink)
}
return append(s, attrNoBlink)
}
// DottedUnderline appends the dotted underline style attribute to the style.
// This is a convenience method for UnderlineStyle(DottedUnderlineStyle).
func (s Style) DottedUnderline() Style {
return s.UnderlineStyle(DottedUnderlineStyle)
// Reverse appends the reverse or no reverse style attribute to the style.
// When v is true, foreground and background colors are swapped. When false,
// reverse video is disabled.
func (s Style) Reverse(v bool) Style {
if v {
return append(s, attrReverse)
}
return append(s, attrNoReverse)
}
// DashedUnderline appends the dashed underline style attribute to the style.
// This is a convenience method for UnderlineStyle(DashedUnderlineStyle).
func (s Style) DashedUnderline() Style {
return s.UnderlineStyle(DashedUnderlineStyle)
// Conceal appends the conceal or no conceal style attribute to the style.
// When v is true, text is hidden/concealed. When false, concealment is
// disabled.
func (s Style) Conceal(v bool) Style {
if v {
return append(s, attrConceal)
}
return append(s, attrNoConceal)
}
// SlowBlink appends the slow blink style attribute to the style.
func (s Style) SlowBlink() Style {
return append(s, slowBlinkAttr)
// Strikethrough appends the strikethrough or no strikethrough style attribute
// to the style. When v is true, text is rendered with a horizontal line through
// it. When false, strikethrough is disabled.
func (s Style) Strikethrough(v bool) Style {
if v {
return append(s, attrStrikethrough)
}
return append(s, attrNoStrikethrough)
}
// RapidBlink appends the rapid blink style attribute to the style.
func (s Style) RapidBlink() Style {
return append(s, rapidBlinkAttr)
}
// Reverse appends the reverse style attribute to the style.
func (s Style) Reverse() Style {
return append(s, reverseAttr)
}
// Conceal appends the conceal style attribute to the style.
func (s Style) Conceal() Style {
return append(s, concealAttr)
}
// Strikethrough appends the strikethrough style attribute to the style.
func (s Style) Strikethrough() Style {
return append(s, strikethroughAttr)
}
// NormalIntensity appends the normal intensity style attribute to the style.
func (s Style) NormalIntensity() Style {
return append(s, normalIntensityAttr)
// Normal appends the normal intensity style attribute to the style. This
// resets [Style.Bold] and [Style.Faint] attributes.
func (s Style) Normal() Style {
return append(s, attrNormalIntensity)
}
// NoItalic appends the no italic style attribute to the style.
//
// Deprecated: use [Style.Italic](false) instead.
func (s Style) NoItalic() Style {
return append(s, noItalicAttr)
return append(s, attrNoItalic)
}
// NoUnderline appends the no underline style attribute to the style.
//
// Deprecated: use [Style.Underline](false) instead.
func (s Style) NoUnderline() Style {
return append(s, noUnderlineAttr)
return append(s, attrNoUnderline)
}
// NoBlink appends the no blink style attribute to the style.
//
// Deprecated: use [Style.Blink](false) or [Style.RapidBlink](false) instead.
func (s Style) NoBlink() Style {
return append(s, noBlinkAttr)
return append(s, attrNoBlink)
}
// NoReverse appends the no reverse style attribute to the style.
//
// Deprecated: use [Style.Reverse](false) instead.
func (s Style) NoReverse() Style {
return append(s, noReverseAttr)
return append(s, attrNoReverse)
}
// NoConceal appends the no conceal style attribute to the style.
//
// Deprecated: use [Style.Conceal](false) instead.
func (s Style) NoConceal() Style {
return append(s, noConcealAttr)
return append(s, attrNoConceal)
}
// NoStrikethrough appends the no strikethrough style attribute to the style.
//
// Deprecated: use [Style.Strikethrough](false) instead.
func (s Style) NoStrikethrough() Style {
return append(s, noStrikethroughAttr)
return append(s, attrNoStrikethrough)
}
// DefaultForegroundColor appends the default foreground color style attribute to the style.
//
// Deprecated: use [Style.ForegroundColor](nil) instead.
func (s Style) DefaultForegroundColor() Style {
return append(s, defaultForegroundColorAttr)
return append(s, attrDefaultForegroundColor)
}
// DefaultBackgroundColor appends the default background color style attribute to the style.
//
// Deprecated: use [Style.BackgroundColor](nil) instead.
func (s Style) DefaultBackgroundColor() Style {
return append(s, defaultBackgroundColorAttr)
return append(s, attrDefaultBackgroundColor)
}
// DefaultUnderlineColor appends the default underline color style attribute to the style.
//
// Deprecated: use [Style.UnderlineColor](nil) instead.
func (s Style) DefaultUnderlineColor() Style {
return append(s, defaultUnderlineColorAttr)
return append(s, attrDefaultUnderlineColor)
}
// ForegroundColor appends the foreground color style attribute to the style.
// If c is nil, the default foreground color is used. Supports [BasicColor],
// [IndexedColor] (256-color), and [color.Color] (24-bit RGB).
func (s Style) ForegroundColor(c Color) Style {
if c == nil {
return append(s, attrDefaultForegroundColor)
}
return append(s, foregroundColorString(c))
}
// BackgroundColor appends the background color style attribute to the style.
// If c is nil, the default background color is used. Supports [BasicColor],
// [IndexedColor] (256-color), and [color.Color] (24-bit RGB).
func (s Style) BackgroundColor(c Color) Style {
if c == nil {
return append(s, attrDefaultBackgroundColor)
}
return append(s, backgroundColorString(c))
}
// UnderlineColor appends the underline color style attribute to the style.
// If c is nil, the default underline color is used. Supports [BasicColor],
// [IndexedColor] (256-color), and [color.Color] (24-bit RGB).
func (s Style) UnderlineColor(c Color) Style {
if c == nil {
return append(s, attrDefaultUnderlineColor)
}
return append(s, underlineColorString(c))
}
// Underline represents an ANSI SGR (Select Graphic Rendition) underline style.
type Underline = byte
// UnderlineStyle represents an ANSI SGR (Select Graphic Rendition) underline
// style.
//
// Deprecated: use [Underline] instead.
type UnderlineStyle = byte
const (
doubleUnderlineStyle = "4:2"
curlyUnderlineStyle = "4:3"
dottedUnderlineStyle = "4:4"
dashedUnderlineStyle = "4:5"
underlineDouble = "4:2"
underlineCurly = "4:3"
underlineDotted = "4:4"
underlineDashed = "4:5"
)
// Underline styles constants.
const (
// NoUnderlineStyle is the default underline style.
NoUnderlineStyle UnderlineStyle = iota
// SingleUnderlineStyle is a single underline style.
UnderlineNone Underline = iota
UnderlineSingle
UnderlineDouble
UnderlineCurly
UnderlineDotted
UnderlineDashed
)
// Underline styles constants.
//
// Deprecated: use [UnderlineNone], [UnderlineSingle], etc. instead.
const (
NoUnderlineStyle Underline = iota
SingleUnderlineStyle
// DoubleUnderlineStyle is a double underline style.
DoubleUnderlineStyle
// CurlyUnderlineStyle is a curly underline style.
CurlyUnderlineStyle
// DottedUnderlineStyle is a dotted underline style.
DottedUnderlineStyle
// DashedUnderlineStyle is a dashed underline style.
DashedUnderlineStyle
)
// Underline styles constants.
//
// Deprecated: use [UnderlineNone], [UnderlineSingle], etc. instead.
const (
UnderlineStyleNone Underline = iota
UnderlineStyleSingle
UnderlineStyleDouble
UnderlineStyleCurly
UnderlineStyleDotted
UnderlineStyleDashed
)
// SGR (Select Graphic Rendition) style attributes.
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
const (
ResetAttr Attr = 0
BoldAttr Attr = 1
FaintAttr Attr = 2
ItalicAttr Attr = 3
UnderlineAttr Attr = 4
SlowBlinkAttr Attr = 5
RapidBlinkAttr Attr = 6
ReverseAttr Attr = 7
ConcealAttr Attr = 8
StrikethroughAttr Attr = 9
NormalIntensityAttr Attr = 22
NoItalicAttr Attr = 23
NoUnderlineAttr Attr = 24
NoBlinkAttr Attr = 25
NoReverseAttr Attr = 27
NoConcealAttr Attr = 28
NoStrikethroughAttr Attr = 29
BlackForegroundColorAttr Attr = 30
RedForegroundColorAttr Attr = 31
GreenForegroundColorAttr Attr = 32
YellowForegroundColorAttr Attr = 33
BlueForegroundColorAttr Attr = 34
MagentaForegroundColorAttr Attr = 35
CyanForegroundColorAttr Attr = 36
WhiteForegroundColorAttr Attr = 37
ExtendedForegroundColorAttr Attr = 38
DefaultForegroundColorAttr Attr = 39
BlackBackgroundColorAttr Attr = 40
RedBackgroundColorAttr Attr = 41
GreenBackgroundColorAttr Attr = 42
YellowBackgroundColorAttr Attr = 43
BlueBackgroundColorAttr Attr = 44
MagentaBackgroundColorAttr Attr = 45
CyanBackgroundColorAttr Attr = 46
WhiteBackgroundColorAttr Attr = 47
ExtendedBackgroundColorAttr Attr = 48
DefaultBackgroundColorAttr Attr = 49
ExtendedUnderlineColorAttr Attr = 58
DefaultUnderlineColorAttr Attr = 59
BrightBlackForegroundColorAttr Attr = 90
BrightRedForegroundColorAttr Attr = 91
BrightGreenForegroundColorAttr Attr = 92
BrightYellowForegroundColorAttr Attr = 93
BrightBlueForegroundColorAttr Attr = 94
BrightMagentaForegroundColorAttr Attr = 95
BrightCyanForegroundColorAttr Attr = 96
BrightWhiteForegroundColorAttr Attr = 97
BrightBlackBackgroundColorAttr Attr = 100
BrightRedBackgroundColorAttr Attr = 101
BrightGreenBackgroundColorAttr Attr = 102
BrightYellowBackgroundColorAttr Attr = 103
BrightBlueBackgroundColorAttr Attr = 104
BrightMagentaBackgroundColorAttr Attr = 105
BrightCyanBackgroundColorAttr Attr = 106
BrightWhiteBackgroundColorAttr Attr = 107
AttrReset Attr = 0
AttrBold Attr = 1
AttrFaint Attr = 2
AttrItalic Attr = 3
AttrUnderline Attr = 4
AttrBlink Attr = 5
AttrRapidBlink Attr = 6
AttrReverse Attr = 7
AttrConceal Attr = 8
AttrStrikethrough Attr = 9
AttrNormalIntensity Attr = 22
AttrNoItalic Attr = 23
AttrNoUnderline Attr = 24
AttrNoBlink Attr = 25
AttrNoReverse Attr = 27
AttrNoConceal Attr = 28
AttrNoStrikethrough Attr = 29
AttrBlackForegroundColor Attr = 30
AttrRedForegroundColor Attr = 31
AttrGreenForegroundColor Attr = 32
AttrYellowForegroundColor Attr = 33
AttrBlueForegroundColor Attr = 34
AttrMagentaForegroundColor Attr = 35
AttrCyanForegroundColor Attr = 36
AttrWhiteForegroundColor Attr = 37
AttrExtendedForegroundColor Attr = 38
AttrDefaultForegroundColor Attr = 39
AttrBlackBackgroundColor Attr = 40
AttrRedBackgroundColor Attr = 41
AttrGreenBackgroundColor Attr = 42
AttrYellowBackgroundColor Attr = 43
AttrBlueBackgroundColor Attr = 44
AttrMagentaBackgroundColor Attr = 45
AttrCyanBackgroundColor Attr = 46
AttrWhiteBackgroundColor Attr = 47
AttrExtendedBackgroundColor Attr = 48
AttrDefaultBackgroundColor Attr = 49
AttrExtendedUnderlineColor Attr = 58
AttrDefaultUnderlineColor Attr = 59
AttrBrightBlackForegroundColor Attr = 90
AttrBrightRedForegroundColor Attr = 91
AttrBrightGreenForegroundColor Attr = 92
AttrBrightYellowForegroundColor Attr = 93
AttrBrightBlueForegroundColor Attr = 94
AttrBrightMagentaForegroundColor Attr = 95
AttrBrightCyanForegroundColor Attr = 96
AttrBrightWhiteForegroundColor Attr = 97
AttrBrightBlackBackgroundColor Attr = 100
AttrBrightRedBackgroundColor Attr = 101
AttrBrightGreenBackgroundColor Attr = 102
AttrBrightYellowBackgroundColor Attr = 103
AttrBrightBlueBackgroundColor Attr = 104
AttrBrightMagentaBackgroundColor Attr = 105
AttrBrightCyanBackgroundColor Attr = 106
AttrBrightWhiteBackgroundColor Attr = 107
RGBColorIntroducerAttr Attr = 2
ExtendedColorIntroducerAttr Attr = 5
AttrRGBColorIntroducer Attr = 2
AttrExtendedColorIntroducer Attr = 5
)
// SGR (Select Graphic Rendition) style attributes.
//
// Deprecated: use Attr* constants instead.
const (
ResetAttr = AttrReset
BoldAttr = AttrBold
FaintAttr = AttrFaint
ItalicAttr = AttrItalic
UnderlineAttr = AttrUnderline
SlowBlinkAttr = AttrBlink
RapidBlinkAttr = AttrRapidBlink
ReverseAttr = AttrReverse
ConcealAttr = AttrConceal
StrikethroughAttr = AttrStrikethrough
NormalIntensityAttr = AttrNormalIntensity
NoItalicAttr = AttrNoItalic
NoUnderlineAttr = AttrNoUnderline
NoBlinkAttr = AttrNoBlink
NoReverseAttr = AttrNoReverse
NoConcealAttr = AttrNoConceal
NoStrikethroughAttr = AttrNoStrikethrough
BlackForegroundColorAttr = AttrBlackForegroundColor
RedForegroundColorAttr = AttrRedForegroundColor
GreenForegroundColorAttr = AttrGreenForegroundColor
YellowForegroundColorAttr = AttrYellowForegroundColor
BlueForegroundColorAttr = AttrBlueForegroundColor
MagentaForegroundColorAttr = AttrMagentaForegroundColor
CyanForegroundColorAttr = AttrCyanForegroundColor
WhiteForegroundColorAttr = AttrWhiteForegroundColor
ExtendedForegroundColorAttr = AttrExtendedForegroundColor
DefaultForegroundColorAttr = AttrDefaultForegroundColor
BlackBackgroundColorAttr = AttrBlackBackgroundColor
RedBackgroundColorAttr = AttrRedBackgroundColor
GreenBackgroundColorAttr = AttrGreenBackgroundColor
YellowBackgroundColorAttr = AttrYellowBackgroundColor
BlueBackgroundColorAttr = AttrBlueBackgroundColor
MagentaBackgroundColorAttr = AttrMagentaBackgroundColor
CyanBackgroundColorAttr = AttrCyanBackgroundColor
WhiteBackgroundColorAttr = AttrWhiteBackgroundColor
ExtendedBackgroundColorAttr = AttrExtendedBackgroundColor
DefaultBackgroundColorAttr = AttrDefaultBackgroundColor
ExtendedUnderlineColorAttr = AttrExtendedUnderlineColor
DefaultUnderlineColorAttr = AttrDefaultUnderlineColor
BrightBlackForegroundColorAttr = AttrBrightBlackForegroundColor
BrightRedForegroundColorAttr = AttrBrightRedForegroundColor
BrightGreenForegroundColorAttr = AttrBrightGreenForegroundColor
BrightYellowForegroundColorAttr = AttrBrightYellowForegroundColor
BrightBlueForegroundColorAttr = AttrBrightBlueForegroundColor
BrightMagentaForegroundColorAttr = AttrBrightMagentaForegroundColor
BrightCyanForegroundColorAttr = AttrBrightCyanForegroundColor
BrightWhiteForegroundColorAttr = AttrBrightWhiteForegroundColor
BrightBlackBackgroundColorAttr = AttrBrightBlackBackgroundColor
BrightRedBackgroundColorAttr = AttrBrightRedBackgroundColor
BrightGreenBackgroundColorAttr = AttrBrightGreenBackgroundColor
BrightYellowBackgroundColorAttr = AttrBrightYellowBackgroundColor
BrightBlueBackgroundColorAttr = AttrBrightBlueBackgroundColor
BrightMagentaBackgroundColorAttr = AttrBrightMagentaBackgroundColor
BrightCyanBackgroundColorAttr = AttrBrightCyanBackgroundColor
BrightWhiteBackgroundColorAttr = AttrBrightWhiteBackgroundColor
RGBColorIntroducerAttr = AttrRGBColorIntroducer
ExtendedColorIntroducerAttr = AttrExtendedColorIntroducer
)
const (
resetAttr = "0"
boldAttr = "1"
faintAttr = "2"
italicAttr = "3"
underlineAttr = "4"
slowBlinkAttr = "5"
rapidBlinkAttr = "6"
reverseAttr = "7"
concealAttr = "8"
strikethroughAttr = "9"
normalIntensityAttr = "22"
noItalicAttr = "23"
noUnderlineAttr = "24"
noBlinkAttr = "25"
noReverseAttr = "27"
noConcealAttr = "28"
noStrikethroughAttr = "29"
blackForegroundColorAttr = "30"
redForegroundColorAttr = "31"
greenForegroundColorAttr = "32"
yellowForegroundColorAttr = "33"
blueForegroundColorAttr = "34"
magentaForegroundColorAttr = "35"
cyanForegroundColorAttr = "36"
whiteForegroundColorAttr = "37"
extendedForegroundColorAttr = "38"
defaultForegroundColorAttr = "39"
blackBackgroundColorAttr = "40"
redBackgroundColorAttr = "41"
greenBackgroundColorAttr = "42"
yellowBackgroundColorAttr = "43"
blueBackgroundColorAttr = "44"
magentaBackgroundColorAttr = "45"
cyanBackgroundColorAttr = "46"
whiteBackgroundColorAttr = "47"
extendedBackgroundColorAttr = "48"
defaultBackgroundColorAttr = "49"
extendedUnderlineColorAttr = "58"
defaultUnderlineColorAttr = "59"
brightBlackForegroundColorAttr = "90"
brightRedForegroundColorAttr = "91"
brightGreenForegroundColorAttr = "92"
brightYellowForegroundColorAttr = "93"
brightBlueForegroundColorAttr = "94"
brightMagentaForegroundColorAttr = "95"
brightCyanForegroundColorAttr = "96"
brightWhiteForegroundColorAttr = "97"
brightBlackBackgroundColorAttr = "100"
brightRedBackgroundColorAttr = "101"
brightGreenBackgroundColorAttr = "102"
brightYellowBackgroundColorAttr = "103"
brightBlueBackgroundColorAttr = "104"
brightMagentaBackgroundColorAttr = "105"
brightCyanBackgroundColorAttr = "106"
brightWhiteBackgroundColorAttr = "107"
attrReset = "0"
attrBold = "1"
attrFaint = "2"
attrItalic = "3"
attrUnderline = "4"
attrBlink = "5"
attrRapidBlink = "6"
attrReverse = "7"
attrConceal = "8"
attrStrikethrough = "9"
attrNormalIntensity = "22"
attrNoItalic = "23"
attrNoUnderline = "24"
attrNoBlink = "25"
attrNoReverse = "27"
attrNoConceal = "28"
attrNoStrikethrough = "29"
attrBlackForegroundColor = "30"
attrRedForegroundColor = "31"
attrGreenForegroundColor = "32"
attrYellowForegroundColor = "33"
attrBlueForegroundColor = "34"
attrMagentaForegroundColor = "35"
attrCyanForegroundColor = "36"
attrWhiteForegroundColor = "37"
attrExtendedForegroundColor = "38"
attrDefaultForegroundColor = "39"
attrBlackBackgroundColor = "40"
attrRedBackgroundColor = "41"
attrGreenBackgroundColor = "42"
attrYellowBackgroundColor = "43"
attrBlueBackgroundColor = "44"
attrMagentaBackgroundColor = "45"
attrCyanBackgroundColor = "46"
attrWhiteBackgroundColor = "47"
attrExtendedBackgroundColor = "48"
attrDefaultBackgroundColor = "49"
attrExtendedUnderlineColor = "58"
attrDefaultUnderlineColor = "59"
attrBrightBlackForegroundColor = "90"
attrBrightRedForegroundColor = "91"
attrBrightGreenForegroundColor = "92"
attrBrightYellowForegroundColor = "93"
attrBrightBlueForegroundColor = "94"
attrBrightMagentaForegroundColor = "95"
attrBrightCyanForegroundColor = "96"
attrBrightWhiteForegroundColor = "97"
attrBrightBlackBackgroundColor = "100"
attrBrightRedBackgroundColor = "101"
attrBrightGreenBackgroundColor = "102"
attrBrightYellowBackgroundColor = "103"
attrBrightBlueBackgroundColor = "104"
attrBrightMagentaBackgroundColor = "105"
attrBrightCyanBackgroundColor = "106"
attrBrightWhiteBackgroundColor = "107"
)
// foregroundColorString returns the style SGR attribute for the given
@@ -364,42 +504,44 @@ const (
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
func foregroundColorString(c Color) string {
switch c := c.(type) {
case nil:
return attrDefaultForegroundColor
case BasicColor:
// 3-bit or 4-bit ANSI foreground
// "3<n>" or "9<n>" where n is the color number from 0 to 7
switch c {
case Black:
return blackForegroundColorAttr
return attrBlackForegroundColor
case Red:
return redForegroundColorAttr
return attrRedForegroundColor
case Green:
return greenForegroundColorAttr
return attrGreenForegroundColor
case Yellow:
return yellowForegroundColorAttr
return attrYellowForegroundColor
case Blue:
return blueForegroundColorAttr
return attrBlueForegroundColor
case Magenta:
return magentaForegroundColorAttr
return attrMagentaForegroundColor
case Cyan:
return cyanForegroundColorAttr
return attrCyanForegroundColor
case White:
return whiteForegroundColorAttr
return attrWhiteForegroundColor
case BrightBlack:
return brightBlackForegroundColorAttr
return attrBrightBlackForegroundColor
case BrightRed:
return brightRedForegroundColorAttr
return attrBrightRedForegroundColor
case BrightGreen:
return brightGreenForegroundColorAttr
return attrBrightGreenForegroundColor
case BrightYellow:
return brightYellowForegroundColorAttr
return attrBrightYellowForegroundColor
case BrightBlue:
return brightBlueForegroundColorAttr
return attrBrightBlueForegroundColor
case BrightMagenta:
return brightMagentaForegroundColorAttr
return attrBrightMagentaForegroundColor
case BrightCyan:
return brightCyanForegroundColorAttr
return attrBrightCyanForegroundColor
case BrightWhite:
return brightWhiteForegroundColorAttr
return attrBrightWhiteForegroundColor
}
case ExtendedColor:
// 256-color ANSI foreground
@@ -414,7 +556,7 @@ func foregroundColorString(c Color) string {
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
strconv.FormatUint(uint64(shift(b)), 10)
}
return defaultForegroundColorAttr
return attrDefaultForegroundColor
}
// backgroundColorString returns the style SGR attribute for the given
@@ -422,42 +564,44 @@ func foregroundColorString(c Color) string {
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
func backgroundColorString(c Color) string {
switch c := c.(type) {
case nil:
return attrDefaultBackgroundColor
case BasicColor:
// 3-bit or 4-bit ANSI foreground
// "4<n>" or "10<n>" where n is the color number from 0 to 7
switch c {
case Black:
return blackBackgroundColorAttr
return attrBlackBackgroundColor
case Red:
return redBackgroundColorAttr
return attrRedBackgroundColor
case Green:
return greenBackgroundColorAttr
return attrGreenBackgroundColor
case Yellow:
return yellowBackgroundColorAttr
return attrYellowBackgroundColor
case Blue:
return blueBackgroundColorAttr
return attrBlueBackgroundColor
case Magenta:
return magentaBackgroundColorAttr
return attrMagentaBackgroundColor
case Cyan:
return cyanBackgroundColorAttr
return attrCyanBackgroundColor
case White:
return whiteBackgroundColorAttr
return attrWhiteBackgroundColor
case BrightBlack:
return brightBlackBackgroundColorAttr
return attrBrightBlackBackgroundColor
case BrightRed:
return brightRedBackgroundColorAttr
return attrBrightRedBackgroundColor
case BrightGreen:
return brightGreenBackgroundColorAttr
return attrBrightGreenBackgroundColor
case BrightYellow:
return brightYellowBackgroundColorAttr
return attrBrightYellowBackgroundColor
case BrightBlue:
return brightBlueBackgroundColorAttr
return attrBrightBlueBackgroundColor
case BrightMagenta:
return brightMagentaBackgroundColorAttr
return attrBrightMagentaBackgroundColor
case BrightCyan:
return brightCyanBackgroundColorAttr
return attrBrightCyanBackgroundColor
case BrightWhite:
return brightWhiteBackgroundColorAttr
return attrBrightWhiteBackgroundColor
}
case ExtendedColor:
// 256-color ANSI foreground
@@ -472,7 +616,7 @@ func backgroundColorString(c Color) string {
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
strconv.FormatUint(uint64(shift(b)), 10)
}
return defaultBackgroundColorAttr
return attrDefaultBackgroundColor
}
// underlineColorString returns the style SGR attribute for the given underline
@@ -480,6 +624,8 @@ func backgroundColorString(c Color) string {
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
func underlineColorString(c Color) string {
switch c := c.(type) {
case nil:
return attrDefaultUnderlineColor
// NOTE: we can't use 3-bit and 4-bit ANSI color codes with underline
// color, use 256-color instead.
//
@@ -498,7 +644,7 @@ func underlineColorString(c Color) string {
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
strconv.FormatUint(uint64(shift(b)), 10)
}
return defaultUnderlineColorAttr
return attrDefaultUnderlineColor
}
// ReadStyleColor decodes a color from a slice of parameters. It returns the
@@ -526,7 +672,7 @@ func underlineColorString(c Color) string {
// 2. Support ignoring and omitting the color space id (second parameter) with respect to RGB colors
// 3. Support ignoring and omitting the 6th parameter with respect to RGB and CMY colors
// 4. Support reading RGBA colors
func ReadStyleColor(params Params, co *color.Color) (n int) {
func ReadStyleColor(params Params, co *color.Color) int {
if len(params) < 2 { // Need at least SGR type and color type
return 0
}
@@ -535,7 +681,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
s := params[0]
p := params[1]
colorType := p.Param(0)
n = 2
n := 2
paramsfn := func() (p1, p2, p3, p4 int) {
// Where should we start reading the color?
@@ -594,7 +740,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
B: uint8(b), //nolint:gosec
A: 0xff,
}
return //nolint:nakedret
return n
case 3: // CMY direct color
if len(params) < 5 {
@@ -612,7 +758,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
Y: uint8(y), //nolint:gosec
K: 0,
}
return //nolint:nakedret
return n
case 4: // CMYK direct color
if len(params) < 6 {
@@ -630,7 +776,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
Y: uint8(y), //nolint:gosec
K: uint8(k), //nolint:gosec
}
return //nolint:nakedret
return n
case 5: // indexed color
if len(params) < 3 {
@@ -665,7 +811,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
B: uint8(b), //nolint:gosec
A: uint8(a), //nolint:gosec
}
return //nolint:nakedret
return n
default:
return 0
+24 -33
View File
@@ -1,11 +1,11 @@
package ansi
import (
"bytes"
"strings"
"github.com/charmbracelet/x/ansi/parser"
"github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
"github.com/clipperhouse/displaywidth"
"github.com/clipperhouse/uax29/v2/graphemes"
)
// Cut the string, without adding any prefix or tail strings. This function is
@@ -74,12 +74,11 @@ func truncate(m Method, s string, length int, tail string) string {
return ""
}
var cluster []byte
var buf bytes.Buffer
var cluster string
var buf strings.Builder
curWidth := 0
ignoring := false
pstate := parser.GroundState // initial state
b := []byte(s)
i := 0
// Here we iterate over the bytes of the string and collect printable
@@ -88,16 +87,12 @@ func truncate(m Method, s string, length int, tail string) string {
//
// Once we reach the given length, we start ignoring characters and only
// collect ANSI escape codes until we reach the end of string.
for i < len(b) {
state, action := parser.Table.Transition(pstate, b[i])
for i < len(s) {
state, action := parser.Table.Transition(pstate, s[i])
if state == parser.Utf8State {
// This action happens when we transition to the Utf8State.
var width int
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
if m == WcWidth {
width = runewidth.StringWidth(string(cluster))
}
cluster, width = FirstGraphemeCluster(s[i:], m)
// increment the index by the length of the cluster
i += len(cluster)
curWidth += width
@@ -118,7 +113,7 @@ func truncate(m Method, s string, length int, tail string) string {
continue
}
buf.Write(cluster)
buf.WriteString(cluster)
// Done collecting, now we're back in the ground state.
pstate = parser.GroundState
@@ -152,7 +147,7 @@ func truncate(m Method, s string, length int, tail string) string {
}
fallthrough
default:
buf.WriteByte(b[i])
buf.WriteByte(s[i])
i++
}
@@ -193,27 +188,23 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
return s
}
var cluster []byte
var buf bytes.Buffer
var cluster string
var buf strings.Builder
curWidth := 0
ignoring := true
pstate := parser.GroundState
b := []byte(s)
i := 0
for i < len(b) {
for i < len(s) {
if !ignoring {
buf.Write(b[i:])
buf.WriteString(s[i:])
break
}
state, action := parser.Table.Transition(pstate, b[i])
state, action := parser.Table.Transition(pstate, s[i])
if state == parser.Utf8State {
var width int
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
if m == WcWidth {
width = runewidth.StringWidth(string(cluster))
}
cluster, width = FirstGraphemeCluster(s[i:], m)
i += len(cluster)
curWidth += width
@@ -224,7 +215,7 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
}
if curWidth > n {
buf.Write(cluster)
buf.WriteString(cluster)
}
if ignoring {
@@ -259,7 +250,7 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
}
fallthrough
default:
buf.WriteByte(b[i])
buf.WriteByte(s[i])
i++
}
@@ -278,22 +269,22 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
// You can use this with [Truncate], [TruncateLeft], and [Cut].
func ByteToGraphemeRange(str string, byteStart, byteStop int) (charStart, charStop int) {
bytePos, charPos := 0, 0
gr := uniseg.NewGraphemes(str)
gr := graphemes.FromString(str)
for byteStart > bytePos {
if !gr.Next() {
break
}
bytePos += len(gr.Str())
charPos += max(1, gr.Width())
bytePos += len(gr.Value())
charPos += max(1, displaywidth.String(gr.Value()))
}
charStart = charPos
for byteStop > bytePos {
if !gr.Next() {
break
}
bytePos += len(gr.Str())
charPos += max(1, gr.Width())
bytePos += len(gr.Value())
charPos += max(1, displaywidth.String(gr.Value()))
}
charStop = charPos
return
return charStart, charStop
}
+17
View File
@@ -0,0 +1,17 @@
package ansi
import (
"fmt"
"strings"
)
// URxvtExt returns an escape sequence for calling a URxvt perl extension with
// the given name and parameters.
//
// OSC 777 ; extension_name ; param1 ; param2 ; ... ST
// OSC 777 ; extension_name ; param1 ; param2 ; ... BEL
//
// See: https://man.archlinux.org/man/extra/rxvt-unicode/urxvt.7.en#XTerm_Operating_System_Commands
func URxvtExt(extension string, params ...string) string {
return fmt.Sprintf("\x1b]777;%s;%s\x07", extension, strings.Join(params, ";"))
}
+4 -10
View File
@@ -4,8 +4,6 @@ import (
"bytes"
"github.com/charmbracelet/x/ansi/parser"
"github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
)
// Strip removes ANSI escape codes from a string.
@@ -83,20 +81,16 @@ func stringWidth(m Method, s string) int {
}
var (
pstate = parser.GroundState // initial state
cluster string
width int
pstate = parser.GroundState // initial state
width int
)
for i := 0; i < len(s); i++ {
state, action := parser.Table.Transition(pstate, s[i])
if state == parser.Utf8State {
var w int
cluster, _, w, _ = uniseg.FirstGraphemeClusterInString(s[i:], -1)
if m == WcWidth {
w = runewidth.StringWidth(cluster)
}
cluster, w := FirstGraphemeCluster(s[i:], m)
width += w
i += len(cluster) - 1
pstate = parser.GroundState
continue
+24 -26
View File
@@ -2,12 +2,11 @@ package ansi
import (
"bytes"
"strings"
"unicode"
"unicode/utf8"
"github.com/charmbracelet/x/ansi/parser"
"github.com/mattn/go-runewidth"
"github.com/rivo/uniseg"
)
// nbsp is a non-breaking space.
@@ -55,12 +54,9 @@ func hardwrap(m Method, s string, limit int, preserveSpace bool) string {
i := 0
for i < len(b) {
state, action := parser.Table.Transition(pstate, b[i])
if state == parser.Utf8State { //nolint:nestif
if state == parser.Utf8State {
var width int
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
if m == WcWidth {
width = runewidth.StringWidth(string(cluster))
}
cluster, width = FirstGraphemeCluster(b[i:], m)
i += len(cluster)
if curWidth+width > limit {
@@ -192,10 +188,7 @@ func wordwrap(m Method, s string, limit int, breakpoints string) string {
state, action := parser.Table.Transition(pstate, b[i])
if state == parser.Utf8State { //nolint:nestif
var width int
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
if m == WcWidth {
width = runewidth.StringWidth(string(cluster))
}
cluster, width = FirstGraphemeCluster(b[i:], m)
i += len(cluster)
r, _ := utf8.DecodeRune(cluster)
@@ -303,7 +296,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
}
var (
cluster []byte
cluster string
buf bytes.Buffer
word bytes.Buffer
space bytes.Buffer
@@ -311,10 +304,12 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
curWidth int // written width of the line
wordLen int // word buffer len without ANSI escape codes
pstate = parser.GroundState // initial state
b = []byte(s)
)
addSpace := func() {
if spaceWidth == 0 && space.Len() == 0 {
return
}
curWidth += spaceWidth
buf.Write(space.Bytes())
space.Reset()
@@ -341,30 +336,27 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
}
i := 0
for i < len(b) {
state, action := parser.Table.Transition(pstate, b[i])
for i < len(s) {
state, action := parser.Table.Transition(pstate, s[i])
if state == parser.Utf8State { //nolint:nestif
var width int
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
if m == WcWidth {
width = runewidth.StringWidth(string(cluster))
}
cluster, width = FirstGraphemeCluster(s[i:], m)
i += len(cluster)
r, _ := utf8.DecodeRune(cluster)
r, _ := utf8.DecodeRuneInString(cluster)
switch {
case r != utf8.RuneError && unicode.IsSpace(r) && r != nbsp: // nbsp is a non-breaking space
addWord()
space.WriteRune(r)
spaceWidth += width
case bytes.ContainsAny(cluster, breakpoints):
case strings.ContainsAny(cluster, breakpoints):
addSpace()
if curWidth+wordLen+width > limit {
word.Write(cluster)
word.WriteString(cluster)
wordLen += width
} else {
addWord()
buf.Write(cluster)
buf.WriteString(cluster)
curWidth += width
}
default:
@@ -373,12 +365,17 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
addWord()
}
word.Write(cluster)
word.WriteString(cluster)
wordLen += width
if curWidth+wordLen+spaceWidth > limit {
addNewline()
}
if wordLen == limit {
// Hardwrap the word if it's too long
addWord()
}
}
pstate = parser.GroundState
@@ -387,7 +384,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
switch action {
case parser.PrintAction, parser.ExecuteAction:
switch r := rune(b[i]); {
switch r := rune(s[i]); {
case r == '\n':
if wordLen == 0 {
if curWidth+spaceWidth > limit {
@@ -424,6 +421,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
if curWidth == limit {
addNewline()
}
word.WriteRune(r)
wordLen++
@@ -438,7 +436,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
}
default:
word.WriteByte(b[i])
word.WriteByte(s[i])
}
// We manage the UTF8 state separately manually above.
+8 -7
View File
@@ -1,3 +1,4 @@
// Package cellbuf provides terminal cell buffer functionality.
package cellbuf
import (
@@ -24,7 +25,7 @@ func NewCell(r rune, comb ...rune) (c *Cell) {
}
c.Comb = comb
c.Width = runewidth.StringWidth(string(append([]rune{r}, comb...)))
return
return c
}
// NewCellString returns a new cell with the given string content. This is a
@@ -46,7 +47,7 @@ func NewCellString(s string) (c *Cell) {
c.Comb = append(c.Comb, r)
}
}
return
return c
}
// NewGraphemeCell returns a new cell. This is a convenience function that
@@ -71,7 +72,7 @@ func newGraphemeCell(s string, w int) (c *Cell) {
c.Comb = append(c.Comb, r)
}
}
return
return c
}
// Line represents a line in the terminal.
@@ -104,7 +105,7 @@ func (l Line) String() (s string) {
}
}
s = strings.TrimRight(s, " ")
return
return s
}
// At returns the cell at the given x position.
@@ -150,7 +151,7 @@ func (l Line) set(x int, c *Cell, clone bool) bool {
for j := 1; j < maxCellWidth && x-j >= 0; j++ {
wide := l.At(x - j)
if wide != nil && wide.Width > 1 && j < wide.Width {
for k := 0; k < wide.Width; k++ {
for k := range wide.Width {
l[x-j+k] = wide.Clone().Blank()
}
break
@@ -206,7 +207,7 @@ func (b *Buffer) String() (s string) {
s += "\r\n"
}
}
return
return s
}
// Line returns a pointer to the line at the given y position.
@@ -296,7 +297,7 @@ func (b *Buffer) FillRect(c *Cell, rect Rectangle) {
}
for y := rect.Min.Y; y < rect.Max.Y; y++ {
for x := rect.Min.X; x < rect.Max.X; x += cellWidth {
b.setCell(x, y, c, false) //nolint:errcheck
b.setCell(x, y, c, false)
}
}
}
+31 -52
View File
@@ -96,7 +96,7 @@ func (c *Cell) Clear() bool {
func (c *Cell) Clone() (n *Cell) {
n = new(Cell)
*n = *c
return
return n
}
// Blank makes the cell a blank cell by setting the rune to a space, comb to
@@ -164,12 +164,12 @@ type UnderlineStyle = ansi.UnderlineStyle
// These are the available underline styles.
const (
NoUnderline = ansi.NoUnderlineStyle
SingleUnderline = ansi.SingleUnderlineStyle
DoubleUnderline = ansi.DoubleUnderlineStyle
CurlyUnderline = ansi.CurlyUnderlineStyle
DottedUnderline = ansi.DottedUnderlineStyle
DashedUnderline = ansi.DashedUnderlineStyle
NoUnderline = ansi.UnderlineStyleNone
SingleUnderline = ansi.UnderlineStyleSingle
DoubleUnderline = ansi.UnderlineStyleDouble
CurlyUnderline = ansi.UnderlineStyleCurly
DottedUnderline = ansi.UnderlineStyleDotted
DashedUnderline = ansi.UnderlineStyleDashed
)
// Style represents the Style of a cell.
@@ -189,7 +189,7 @@ func (s Style) Sequence() string {
var b ansi.Style
if s.Attrs != 0 {
if s.Attrs != 0 { //nolint:nestif
if s.Attrs&BoldAttr != 0 {
b = b.Bold()
}
@@ -197,36 +197,31 @@ func (s Style) Sequence() string {
b = b.Faint()
}
if s.Attrs&ItalicAttr != 0 {
b = b.Italic()
b = b.Italic(true)
}
if s.Attrs&SlowBlinkAttr != 0 {
b = b.SlowBlink()
b = b.Blink(true)
}
if s.Attrs&RapidBlinkAttr != 0 {
b = b.RapidBlink()
b = b.RapidBlink(true)
}
if s.Attrs&ReverseAttr != 0 {
b = b.Reverse()
b = b.Reverse(true)
}
if s.Attrs&ConcealAttr != 0 {
b = b.Conceal()
b = b.Conceal(true)
}
if s.Attrs&StrikethroughAttr != 0 {
b = b.Strikethrough()
b = b.Strikethrough(true)
}
}
if s.UlStyle != NoUnderline {
switch s.UlStyle {
case SingleUnderline:
b = b.Underline()
case DoubleUnderline:
b = b.DoubleUnderline()
case CurlyUnderline:
b = b.CurlyUnderline()
case DottedUnderline:
b = b.DottedUnderline()
case DashedUnderline:
b = b.DashedUnderline()
switch u := s.UlStyle; u {
case NoUnderline:
b = b.Underline(false)
default:
b = b.Underline(true)
b = b.UnderlineStyle(u)
}
}
if s.Fg != nil {
@@ -268,64 +263,48 @@ func (s Style) DiffSequence(o Style) string {
isNormal bool
)
if s.Attrs != o.Attrs {
if s.Attrs != o.Attrs { //nolint:nestif
if s.Attrs&BoldAttr != o.Attrs&BoldAttr {
if s.Attrs&BoldAttr != 0 {
b = b.Bold()
} else if !isNormal {
isNormal = true
b = b.NormalIntensity()
b = b.Normal()
}
}
if s.Attrs&FaintAttr != o.Attrs&FaintAttr {
if s.Attrs&FaintAttr != 0 {
b = b.Faint()
} else if !isNormal {
b = b.NormalIntensity()
b = b.Normal()
}
}
if s.Attrs&ItalicAttr != o.Attrs&ItalicAttr {
if s.Attrs&ItalicAttr != 0 {
b = b.Italic()
} else {
b = b.NoItalic()
}
b = b.Italic(s.Attrs&ItalicAttr != 0)
}
if s.Attrs&SlowBlinkAttr != o.Attrs&SlowBlinkAttr {
if s.Attrs&SlowBlinkAttr != 0 {
b = b.SlowBlink()
b = b.Blink(true)
} else if !noBlink {
noBlink = true
b = b.NoBlink()
b = b.Blink(false)
}
}
if s.Attrs&RapidBlinkAttr != o.Attrs&RapidBlinkAttr {
if s.Attrs&RapidBlinkAttr != 0 {
b = b.RapidBlink()
b = b.RapidBlink(true)
} else if !noBlink {
b = b.NoBlink()
b = b.Blink(false)
}
}
if s.Attrs&ReverseAttr != o.Attrs&ReverseAttr {
if s.Attrs&ReverseAttr != 0 {
b = b.Reverse()
} else {
b = b.NoReverse()
}
b = b.Reverse(s.Attrs&ReverseAttr != 0)
}
if s.Attrs&ConcealAttr != o.Attrs&ConcealAttr {
if s.Attrs&ConcealAttr != 0 {
b = b.Conceal()
} else {
b = b.NoConceal()
}
b = b.Conceal(s.Attrs&ConcealAttr != 0)
}
if s.Attrs&StrikethroughAttr != o.Attrs&StrikethroughAttr {
if s.Attrs&StrikethroughAttr != 0 {
b = b.Strikethrough()
} else {
b = b.NoStrikethrough()
}
b = b.Strikethrough(s.Attrs&StrikethroughAttr != 0)
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ func Pos(x, y int) Position {
return image.Pt(x, y)
}
// Rectange represents a rectangle.
// Rectangle represents a rectangle.
type Rectangle = image.Rectangle
// Rect is a shorthand for Rectangle.
+10 -9
View File
@@ -75,7 +75,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
)
blank := s.clearBlank()
if n > 0 {
if n > 0 { //nolint:nestif
// Scroll up (forward)
v = s.scrollUp(n, top, bot, 0, maxY, blank)
if !v {
@@ -99,7 +99,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
s.move(0, bot-n+1)
s.clearToBottom(nil)
} else {
for i := 0; i < n; i++ {
for i := range n {
s.move(0, bot-i)
s.clearToEnd(nil, false)
}
@@ -124,7 +124,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
// Clear newly shifted-in lines.
if v &&
(nonDestScrollRegion || (memoryBelow && top == 0)) {
for i := 0; i < -n; i++ {
for i := range -n {
s.move(0, top+i)
s.clearToEnd(nil, false)
}
@@ -133,7 +133,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
}
if !v {
return
return v
}
s.scrollBuffer(s.curbuf, n, top, bot, blank)
@@ -193,7 +193,7 @@ func (s *Screen) touchLine(width, height, y, n int, changed bool) {
// scrollUp scrolls the screen up by n lines.
func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
if n == 1 && top == minY && bot == maxY {
if n == 1 && top == minY && bot == maxY { //nolint:nestif
s.move(0, bot)
s.updatePen(blank)
s.buf.WriteByte('\n')
@@ -202,13 +202,14 @@ func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
s.updatePen(blank)
s.buf.WriteString(ansi.DeleteLine(1))
} else if top == minY && bot == maxY {
if s.xtermLike {
supportsSU := s.caps.Contains(capSU)
if supportsSU {
s.move(0, bot)
} else {
s.move(0, top)
}
s.updatePen(blank)
if s.xtermLike {
if supportsSU {
s.buf.WriteString(ansi.ScrollUp(n))
} else {
s.buf.WriteString(strings.Repeat("\n", n))
@@ -225,7 +226,7 @@ func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
// scrollDown scrolls the screen down by n lines.
func (s *Screen) scrollDown(n, top, bot, minY, maxY int, blank *Cell) bool {
if n == 1 && top == minY && bot == maxY {
if n == 1 && top == minY && bot == maxY { //nolint:nestif
s.move(0, top)
s.updatePen(blank)
s.buf.WriteString(ansi.ReverseIndex)
@@ -236,7 +237,7 @@ func (s *Screen) scrollDown(n, top, bot, minY, maxY int, blank *Cell) bool {
} else if top == minY && bot == maxY {
s.move(0, top)
s.updatePen(blank)
if s.xtermLike {
if s.caps.Contains(capSD) {
s.buf.WriteString(ansi.ScrollDown(n))
} else {
s.buf.WriteString(strings.Repeat(ansi.ReverseIndex, n))

Some files were not shown because too many files have changed in this diff Show More