Compare commits

...

46 Commits

Author SHA1 Message Date
Kevin Franklin Kim
1ef8e1066c
feat: update schema id
Some checks failed
Test Branch / test (push) Has been cancelled
2025-09-19 15:04:55 +02:00
Kevin Franklin Kim
aca5eb72ab
Merge pull request #130 from foomo/feature/schema
feat: add schema
2025-09-19 11:44:22 +02:00
Kevin Franklin Kim
09dec1320f
docs: update README 2025-09-19 11:38:10 +02:00
Kevin Franklin Kim
4743cc08cc
fix: welcome config 2025-09-19 11:37:16 +02:00
Kevin Franklin Kim
6bd3e40e5d
refactor: rename schema 2025-09-19 11:37:06 +02:00
Kevin Franklin Kim
3d66e427c9
chore: add -trimpath 2025-09-19 11:36:36 +02:00
Kevin Franklin Kim
ef3e93d992
chore: add schema 2025-09-19 11:36:25 +02:00
Kevin Franklin Kim
d2619ef68b
chore: add tidy 2025-09-19 11:36:14 +02:00
Kevin Franklin Kim
68b1491a6d
docs: update README 2025-09-18 12:35:14 +02:00
Kevin Franklin Kim
cb0cf44db0
feat: add schema 2025-09-18 12:29:38 +02:00
Kevin Franklin Kim
5f2ba5432b
chore: update makefile
Some checks failed
Test Branch / test (push) Has been cancelled
2025-09-17 11:32:30 +02:00
Kevin Franklin Kim
dce2b4054d
chore: update makefile 2025-09-17 11:29:46 +02:00
Kevin Franklin Kim
fcfbf199e0
fix: docker login 2025-09-17 11:17:26 +02:00
Kevin Franklin Kim
56a9436152
Merge pull request #129 from foomo/feature/mise
feat: add mise
2025-09-17 11:11:19 +02:00
Kevin Franklin Kim
d35a41bccb
chore: remove coveralls 2025-09-17 11:08:32 +02:00
Kevin Franklin Kim
2acbe3d983
chore: update dependabot 2025-09-17 11:03:44 +02:00
Kevin Franklin Kim
854e0c6d25
feat: add mise 2025-09-17 10:13:47 +02:00
Kevin Franklin Kim
d844a94cd8
Merge pull request #126 from foomo/dependabot/github_actions/github-actions-3ffaa8ed61
Some checks failed
Test Branch / test (push) Has been cancelled
chore(deps): bump the github-actions group across 1 directory with 2 updates
2025-09-15 09:30:37 +02:00
dependabot[bot]
fa583ff5d0
chore(deps): bump the github-actions group across 1 directory with 2 updates
Bumps the github-actions group with 2 updates in the / directory: [actions/checkout](https://github.com/actions/checkout) and [actions/setup-go](https://github.com/actions/setup-go).


Updates `actions/checkout` from 4 to 5
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

Updates `actions/setup-go` from 5 to 6
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-07 12:01:41 +00:00
Kevin Franklin Kim
f2942c095d
chore: add permissions 2025-08-01 07:50:11 +02:00
Kevin Franklin Kim
b990cd1113
Merge pull request #121 from foomo/feature/improved-suggestion-filtering
feat: add combined filter for improved suggestions in autocomplete dropdowns
2025-07-31 16:31:08 +02:00
Kevin Franklin Kim
2b6ebfdc9b
Merge pull request #115 from foomo/dependabot/github_actions/github-actions-afd9e15987
chore(deps): bump golangci/golangci-lint-action from 7 to 8 in the github-actions group
2025-07-31 16:20:39 +02:00
Kevin Franklin Kim
c837de02fb
Merge branch 'main' of github.com:foomo/posh into feature/improved-suggestion-filtering 2025-07-31 16:19:32 +02:00
Kevin Franklin Kim
ac6310cc10
Merge pull request #123 from foomo/feature/git-prompt
feat: improve git prompt
2025-07-31 16:18:34 +02:00
Kevin Franklin Kim
96c1e8d187
chore: add .husky 2025-07-31 16:14:42 +02:00
Kevin Franklin Kim
daf9191393
chore: add .husky 2025-07-31 16:12:09 +02:00
Kevin Franklin Kim
26e10c2473
feat: bump deps 2025-07-31 16:11:49 +02:00
Kevin Franklin Kim
2d63c8cf95
chore: switch to golangci-lint fmt 2025-07-31 16:10:43 +02:00
Kevin Franklin Kim
5518e8ed59
feat: improve git prompt 2025-07-31 16:10:23 +02:00
Wlad Meixner
c9b0bae9bb fix: rename file 2025-07-07 09:04:50 +02:00
Wlad Meixner
cfa5544df7 feat: add combined filter for improved suggestions 2025-07-07 08:57:35 +02:00
dependabot[bot]
76d0f35471
chore(deps): bump golangci/golangci-lint-action
Bumps the github-actions group with 1 update: [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action).


Updates `golangci/golangci-lint-action` from 7 to 8
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v7...v8)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-11 12:08:50 +00:00
Kevin Franklin Kim
1d6bfa9ea2
Merge pull request #112 from foomo/feature/multi-checkers
feat: allow multiple checkers
2025-04-07 16:05:28 +02:00
Kevin Franklin Kim
21acd3af35
fix: template 2025-04-07 16:03:14 +02:00
Kevin Franklin Kim
dc4d206b29
feat: allow multiple checkers 2025-04-07 15:55:21 +02:00
Kevin Franklin Kim
8a61d79a9d
Merge pull request #110 from foomo/feature/v0.11.0
feat: v0.11.0
2025-04-04 16:41:17 +02:00
Kevin Franklin Kim
bb5510f213
feat: bump golangci-lint 2025-04-04 16:37:23 +02:00
Kevin Franklin Kim
3a81a2482f
feat: use sync map 2025-04-04 16:32:53 +02:00
Kevin Franklin Kim
f2fece8fe3
fix: env list 2025-04-04 16:32:38 +02:00
Kevin Franklin Kim
e6ac04284d
fix: arg validation 2025-04-04 16:32:28 +02:00
Kevin Franklin Kim
a442caf9b8
feat: add git prefix 2025-04-04 16:32:11 +02:00
Kevin Franklin Kim
4650137564
feat: add debug 2025-04-04 16:31:43 +02:00
Kevin Franklin Kim
569a62a82b
feat: bump deps 2025-04-04 16:31:29 +02:00
Kevin Franklin Kim
a5f21ff78f
Merge pull request #108 from foomo/feature/cowsay
feat: add cowsay
2025-03-20 16:02:03 +01:00
Kevin Franklin Kim
773ac0e272
fix: lint error 2025-03-20 15:58:04 +01:00
Kevin Franklin Kim
9ac84164d4
feat: add cowsay 2025-03-20 15:18:38 +01:00
55 changed files with 1290 additions and 516 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
posh
dist

View File

@ -1,33 +1,43 @@
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/refs/heads/master/src/schemas/json/dependabot-2.0.json
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference
version: 2
updates:
- package-ecosystem: 'github-actions'
- package-ecosystem: github-actions
open-pull-requests-limit: 1
commit-message: { prefix: chore(deps) }
directory: '/'
schedule:
day: 'sunday'
interval: 'weekly'
groups:
github-actions:
patterns: ['*']
day: sunday
interval: weekly
timezone: Europe/Berlin
cooldown:
default-days: 1
include: [ '*' ]
- package-ecosystem: 'gomod'
- package-ecosystem: gomod
open-pull-requests-limit: 1
directory: '/'
commit-message: { prefix: chore(deps) }
directories: [ '**/*' ]
schedule:
day: 'sunday'
interval: 'weekly'
day: sunday
interval: weekly
timezone: Europe/Berlin
groups:
gomod-security:
security:
applies-to: security-updates
update-types: ['minor', 'patch']
patterns: ['*']
gomod-update:
update-types: [ minor, patch ]
patterns: [ '*' ]
updates:
applies-to: version-updates
update-types: ['minor', 'patch']
patterns: ['*']
update-types: [ minor, patch ]
patterns: [ '*' ]
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
- dependency-name: '*'
update-types: [ 'version-update:semver-major' ]
cooldown:
default-days: 1
semver-major-days: 14
semver-minor-days: 7
semver-patch-days: 1
include: [ '*' ]

View File

@ -13,23 +13,33 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: actions/setup-go@v5
- uses: jdx/mise-action@v3
- uses: actions/setup-go@v6
with:
check-latest: true
go-version-file: go.mod
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- id: app_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- name: Login to docker.io
run: docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }}
- uses: goreleaser/goreleaser-action@v6
with:
version: latest
version: '~> v2'
args: release --clean
env:
GITHUB_TOKEN: ${{ steps.app_token.outputs.token }}

View File

@ -4,35 +4,42 @@ on:
push:
branches: [ main ]
pull_request:
merge_group:
workflow_dispatch:
permissions:
contents: read
pull-requests: write
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
- uses: jdx/mise-action@v3
- uses: actions/setup-go@v6
with:
check-latest: true
go-version-file: go.mod
- uses: gotesttools/gotestfmt-action@v2
- uses: golangci/golangci-lint-action@v8
with:
token: ${{ secrets.GITHUB_TOKEN }}
install-mode: none
- uses: golangci/golangci-lint-action@v6
with:
version: latest
- name: Run tidy
run: make tidy && git diff --exit-code
- run: make test
- name: Run lint
run: make lint
- run: make test.demo
- name: Run tests
run: make test
- uses: coverallsapp/github-action@v2
with:
file: coverage.out
- name: Run demo test
run: make test.demo

6
.gitignore vendored
View File

@ -16,6 +16,9 @@
## Editorconfig
!.editorconfig
## Docker
!.dockerignore
## Husky
!.husky/
!.husky.yaml
@ -29,3 +32,6 @@ go.work.sum
## Posh
!.posh/
!.posh.yml
## Mise
!.mise.toml

View File

@ -1,124 +1,15 @@
# yaml-language-server: $schema=https://golangci-lint.run/jsonschema/golangci.jsonschema.json
# https://golangci-lint.run/usage/configuration/
version: "2"
run:
go: 1.24.1
build-tags: [safe]
build-tags: [ safe ]
modules-download-mode: readonly
issues:
exclude-dirs:
- 'tmp'
exclude-rules:
- path: _test\.go
linters:
- forbidigo
- forcetypeassert
linters-settings:
misspell:
mode: restricted
asasalint:
ignore-test: true
# https://golangci-lint.run/usage/linters/#exhaustive
exhaustive:
default-signifies-exhaustive: true
# https://golangci-lint.run/usage/linters/#predeclared
predeclared:
ignore: "new,error"
# https://golangci-lint.run/usage/linters/#gocritic
gocritic:
disabled-checks:
- ifElseChain
- singleCaseSwitch
- commentFormatting
# https://golangci-lint.run/usage/linters/#testifylint
testifylint:
disable:
- float-compare
# https://golangci-lint.run/usage/linters/#gosec
gosec:
confidence: medium
# https://golangci-lint.run/usage/linters/#importas
importas:
no-unaliased: true
# https://golangci-lint.run/usage/linters/#gomoddirectives
gomoddirectives:
replace-local: true
replace-allow-list:
- github.com/c-bata/go-prompt
# https://golangci-lint.run/usage/linters/#staticcheck
staticcheck:
checks: ["all", "-SA1029"]
# https://golangci-lint.run/usage/linters/#revive
revive:
ignore-generated-header: true
enable-all-rules: true
rules:
- name: line-length-limit
disabled: true
- name: cognitive-complexity
disabled: true
- name: unused-parameter
disabled: true
- name: add-constant
disabled: true
- name: cyclomatic
disabled: true
- name: function-length
disabled: true
- name: function-result-limit
disabled: true
- name: flag-parameter
disabled: true
- name: unused-receiver
disabled: true
- name: argument-limit
disabled: true
- name: max-control-nesting
disabled: true
- name: comment-spacings
disabled: true
- name: struct-tag
arguments:
- "json,inline"
- name: unhandled-error
arguments:
- "fmt.Println"
# TODO remove
- name: use-any
disabled: true
- name: if-return
disabled: true
- name: deep-exit
disabled: true
- name: empty-block
disabled: true
- name: context-keys-type
disabled: true
- name: nested-structs
disabled: true
- name: unnecessary-stmt
disabled: true
- name: max-public-structs
disabled: true
- name: import-shadowing
disabled: true
- name: confusing-naming
disabled: true
- name: increment-decrement
disabled: true
- name: indent-error-flow
disabled: true
- name: unchecked-type-assertion
disabled: true
linters:
disable-all: true
default: none
enable:
## Default linters
- errcheck # errcheck is a program for checking for unchecked errors in Go code. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]
- gosimple # (megacheck) Linter for Go source code that specializes in simplifying code [fast: false, auto-fix: false]
- govet # (vet, vetshadow) Vet examines Go source code and reports suspicious constructs. It is roughly the same as 'go vet' and uses its passes. [fast: false, auto-fix: false]
- ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
- staticcheck # (megacheck) It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary. The author of staticcheck doesn't support or approve the use of staticcheck as a library inside golangci-lint. [fast: false, auto-fix: false]
@ -129,7 +20,7 @@ linters:
- asciicheck # checks that all code identifiers does not have non-ASCII symbols in the name [fast: true, auto-fix: false]
- bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false]
- bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
- canonicalheader # canonicalheader checks whether net/http.Header uses canonical header [fast: false, auto-fix: false]
- canonicalheader # checks whether net/http.Header uses canonical header [fast: false, auto-fix: false]
#- containedctx # containedctx is a linter that detects struct contained context.Context field [fast: false, auto-fix: false]
- contextcheck # check whether the function uses a non-inherited context [fast: false, auto-fix: false]
#- copyloopvar # (go >= 1.22) copyloopvar is a linter detects places where loop variables are copied [fast: true, auto-fix: false]
@ -147,9 +38,7 @@ linters:
- gochecksumtype # Run exhaustiveness checks on Go "sum types" [fast: false, auto-fix: false]
- goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
- gocritic # Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: true]
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
- goheader # Checks is file header matches to pattern [fast: true, auto-fix: true]
- goimports # Check import statements are formatted according to the 'goimport' command. Reformat imports in autofix mode. [fast: true, auto-fix: true]
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false]
- goprintffuncname # Checks that printf-like functions are named with `f` at the end. [fast: true, auto-fix: false]
@ -181,7 +70,6 @@ linters:
- rowserrcheck # checks whether Rows.Err of rows is checked successfully [fast: false, auto-fix: false]
- spancheck # Checks for mistakes with OpenTelemetry/Census spans. [fast: false, auto-fix: false]
- sqlclosecheck # Checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed. [fast: false, auto-fix: false]
- stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false]
- testableexamples # linter checks if examples are testable (have an expected output) [fast: true, auto-fix: false]
- testifylint # Checks usage of github.com/stretchr/testify. [fast: false, auto-fix: false]
- testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
@ -199,10 +87,10 @@ linters:
#- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
#- dupl # Tool for code clone detection [fast: true, auto-fix: false]
#- dupword # checks for duplicate words in the source code [fast: true, auto-fix: false]
#- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
#- err113 # Go linter to check the errors handling expressions [fast: false, auto-fix: false]
#- exhaustruct # Checks if all structure fields are initialized [fast: false, auto-fix: false]
#- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
#- gci # Gci controls Go package import order and makes it always deterministic. [fast: true, auto-fix: true]
#- ginkgolinter # enforces standards of using ginkgo and gomega [fast: false, auto-fix: false]
#- gochecknoglobals # Check that no global variables exist. [fast: false, auto-fix: false]
#- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
@ -210,7 +98,6 @@ linters:
#- gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
#- godot # Check if comments end in a period [fast: true, auto-fix: true]
#- godox # Tool for detection of comment keywords [fast: true, auto-fix: false]
#- gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
#- interfacebloat # A linter that checks the number of methods inside an interface. [fast: true, auto-fix: false]
#- intrange # (go >= 1.22) intrange is a linter to find places where for loops could make use of an integer range. [fast: true, auto-fix: false]
#- ireturn # Accept Interfaces, Return Concrete Types [fast: false, auto-fix: false]
@ -230,3 +117,126 @@ linters:
#- wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
#- wsl # add or remove empty lines [fast: true, auto-fix: false]
#- zerologlint # Detects the wrong usage of `zerolog` that a user forgets to dispatch with `Send` or `Msg` [fast: false, auto-fix: false]
settings:
exhaustive:
default-signifies-exhaustive: true
gocritic:
disabled-checks:
- ifElseChain
- singleCaseSwitch
- commentFormatting
gomoddirectives:
replace-allow-list:
- github.com/c-bata/go-prompt
replace-local: true
gosec:
confidence: medium
importas:
no-unaliased: true
misspell:
mode: restricted
predeclared:
ignore:
- new
- error
revive:
enable-all-rules: true
rules:
- name: line-length-limit
disabled: true
- name: cognitive-complexity
disabled: true
- name: unused-parameter
disabled: true
- name: add-constant
disabled: true
- name: cyclomatic
disabled: true
- name: function-length
disabled: true
- name: function-result-limit
disabled: true
- name: flag-parameter
disabled: true
- name: unused-receiver
disabled: true
- name: argument-limit
disabled: true
- name: max-control-nesting
disabled: true
- name: comment-spacings
disabled: true
- name: struct-tag
arguments:
- json,inline
- name: unhandled-error
arguments:
- fmt.Println
- name: use-any
disabled: true
- name: if-return
disabled: true
- name: deep-exit
disabled: true
- name: empty-block
disabled: true
- name: context-keys-type
disabled: true
- name: nested-structs
disabled: true
- name: unnecessary-stmt
disabled: true
- name: max-public-structs
disabled: true
- name: import-shadowing
disabled: true
- name: confusing-naming
disabled: true
- name: increment-decrement
disabled: true
- name: indent-error-flow
disabled: true
- name: unchecked-type-assertion
disabled: true
- name: var-naming
disabled: true
- name: enforce-switch-style
disabled: true
staticcheck:
checks:
- -SA1029
- all
testifylint:
disable:
- float-compare
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- forbidigo
- forcetypeassert
path: _test\.go
- linters:
- asasalint
path: (.+)_test\.go
paths:
- tmp
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- tmp
- third_party$
- builtin$
- examples$

View File

@ -1,5 +1,7 @@
version: 2
project_name: posh
before:
hooks:
- go mod download
@ -10,6 +12,7 @@ builds:
env:
- CGO_ENABLED=0
flags:
- -trimpath
- -tags=safe
ldflags:
- -s -w
@ -25,10 +28,10 @@ builds:
- arm64
archives:
- format: tar.gz
- formats: [ tar.gz ]
format_overrides:
- goos: windows
format: zip
formats: [ zip ]
release:
prerelease: auto
@ -43,3 +46,74 @@ brews:
caveats: "posh help"
homepage: "https://github.com/foomo/posh"
description: "Project Oriented SHELL"
dockers:
- use: buildx
goos: linux
goarch: amd64
dockerfile: Dockerfile
image_templates:
- '{{ if eq .Prerelease "" }}foomo/posh:latest-amd64{{ end }}'
- 'foomo/posh:{{ .Version }}-amd64'
- '{{ if eq .Prerelease "" }}foomo/posh:{{ .Major }}-amd64{{ end }}'
- '{{ if eq .Prerelease "" }}foomo/posh:{{ .Major }}.{{ .Minor }}-amd64{{ end }}'
build_flag_templates:
- '--pull'
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
- '--label=org.opencontainers.image.title={{.ProjectName}}'
- '--label=org.opencontainers.image.description=Project Oriented SHELL (posh)'
- '--label=org.opencontainers.image.source={{.GitURL}}'
- '--label=org.opencontainers.image.url={{.GitURL}}'
- '--label=org.opencontainers.image.documentation={{.GitURL}}'
- '--label=org.opencontainers.image.created={{.Date}}'
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
- '--label=org.opencontainers.image.version={{.Version}}'
- '--platform=linux/amd64'
- use: buildx
goos: linux
goarch: arm64
dockerfile: Dockerfile
image_templates:
- '{{ if eq .Prerelease "" }}foomo/posh:latest-arm64{{ end }}'
- 'foomo/posh:{{ .Version }}-arm64'
- '{{ if eq .Prerelease "" }}foomo/posh:{{ .Major }}-arm64{{ end }}'
- '{{ if eq .Prerelease "" }}foomo/posh:{{ .Major }}.{{ .Minor }}-arm64{{ end }}'
build_flag_templates:
- '--pull'
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
- '--label=org.opencontainers.image.title={{.ProjectName}}'
- '--label=org.opencontainers.image.description=Project Oriented SHELL'
- '--label=org.opencontainers.image.source={{.GitURL}}'
- '--label=org.opencontainers.image.url={{.GitURL}}'
- '--label=org.opencontainers.image.documentation={{.GitURL}}'
- '--label=org.opencontainers.image.created={{.Date}}'
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
- '--label=org.opencontainers.image.version={{.Version}}'
- '--platform=linux/arm64'
docker_manifests:
# basic
- name_template: 'foomo/posh:latest'
image_templates:
- 'foomo/posh:latest-amd64'
- 'foomo/posh:latest-arm64'
skip_push: auto
- name_template: 'foomo/posh:{{ .Version }}'
image_templates:
- 'foomo/posh:{{ .Version }}-amd64'
- 'foomo/posh:{{ .Version }}-arm64'
skip_push: auto
- name_template: 'foomo/posh:{{ .Major }}'
image_templates:
- 'foomo/posh:{{ .Major }}-amd64'
- 'foomo/posh:{{ .Major }}-arm64'
skip_push: auto
- name_template: 'foomo/posh:{{ .Major }}.{{ .Minor }}'
image_templates:
- 'foomo/posh:{{ .Major }}.{{ .Minor }}-amd64'
- 'foomo/posh:{{ .Major }}.{{ .Minor }}-arm64'
skip_push: auto

View File

@ -1,6 +1,6 @@
hooks:
pre-commit:
- golangci-lint run --fast
- golangci-lint run --fast-only
- husky lint-staged
commit-msg:
# only execute if not in a merge
@ -8,10 +8,8 @@ hooks:
lint-staged:
'*.go':
- goimports -l -w
- gofmt -l -w
- golangci-lint fmt
lint-commit:
email: '^(.+@bestbytes.com)$'
types: '^(feat|fix|build|chore|docs|perf|refactor|revert|style|test|wip)$'
header: '^(?P<type>\w+)(\((?P<scope>[\w/.-]+)\))?(?P<breaking>!)?:( +)?(?P<header>.+)'

3
.husky/applypatch-msg Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/commit-msg Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/fsmonitor-watchman Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/post-update Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/pre-applypatch Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/pre-commit Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/pre-merge-commit Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/pre-push Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/pre-rebase Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/pre-receive Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/prepare-commit-msg Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/push-to-checkout Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/sendemail-validate Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

3
.husky/update Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
husky hook $(basename "$0") $*

5
.mise.toml Normal file
View File

@ -0,0 +1,5 @@
[tools]
# https://github.com/golangci/golangci-lint/releases
golangci-lint = "2.4.0"
# https://github.com/go-courier/husky/releases
"github:go-courier/husky" = "1.8.1"

12
Dockerfile Normal file
View File

@ -0,0 +1,12 @@
FROM alpine:latest
RUN apk --no-cache add ca-certificates
RUN adduser -D -u 1001 -g 1001 posh
COPY posh /usr/bin/
USER posh
WORKDIR /home/posh
ENTRYPOINT ["posh"]

125
Makefile
View File

@ -1,25 +1,59 @@
.DEFAULT_GOAL:=help
-include .makerc
## === Tasks ===
# --- Config -----------------------------------------------------------------
# Newline hack for error output
define br
endef
# --- Targets -----------------------------------------------------------------
# This allows us to accept extra arguments
%: .mise .husky
@:
.PHONY: .mise
# Install dependencies
.mise: msg := $(br)$(br)Please ensure you have 'mise' installed and activated!$(br)$(br)$$ brew update$(br)$$ brew install mise$(br)$(br)See the documentation: https://mise.jdx.dev/getting-started.html$(br)$(br)
.mise:
ifeq (, $(shell command -v mise))
$(error ${msg})
endif
@mise install
.PHONY: .husky
# Configure git hooks for husky
.husky:
@git config core.hooksPath .husky
### Tasks
.PHONY: check
## Run tests and linters
check: tidy lint test
## Run lint & test
check: tidy lint test test.demo
.PHONY: tidy
## Run go mod tidy
tidy:
@go mod tidy
.PHONY: outdated
## Show outdated direct dependencies
outdated:
@go list -u -m -json all | go-mod-outdated -update -direct
.PHONY: lint
## Run linter
lint:
@golangci-lint run
.PHONY: lint.fix
## Run linter and fix
lint.fix:
@golangci-lint run --fix
.PHONY: test
## Run tests
test:
@go test -coverprofile=coverage.out -race -json ./... | gotestfmt
@GO_TEST_TAGS=-skip go test -coverprofile=coverage.out --tags=safe -race ./...
.PHONY: test.demo
## Run tests
@ -34,27 +68,17 @@ test.demo: install
make shell.build && \
bin/posh execute welcome demo
.PHONY: lint
## Run linter
lint:
@golangci-lint run
.PHONY: build
## Build binary
build:
@rm -f bin/posh
@go build -o bin/posh main.go
.PHONY: lint.fix
## Fix lint violations
lint.fix:
@golangci-lint run --fix
.PHONY: lint.super
## Run super linter
lint.super:
docker run --rm -it \
-e 'RUN_LOCAL=true' \
-e 'DEFAULT_BRANCH=main' \
-e 'IGNORE_GITIGNORED_FILES=true' \
-e 'VALIDATE_JSCPD=false' \
-e 'VALIDATE_GO=false' \
-v $(PWD):/tmp/lint \
github/super-linter
.PHONY: build.debug
## Build binary in debug mode
build.debug:
@rm -f bin/posh
@go build -gcflags "all=-N -l" -o bin/posh main.go
.PHONY: install
## Run go install
@ -68,36 +92,23 @@ install:
install.debug:
@go install -a -gcflags "all=-N -l" main.go
## === Utils ===
### Utils
.PHONY: help
## Show help text
help:
@echo "Project Oriented SHELL (posh)\n"
@echo "Usage:\n make [task]"
@awk '{ \
if ($$0 ~ /^.PHONY: [a-zA-Z\-\_0-9]+$$/) { \
helpCommand = substr($$0, index($$0, ":") + 2); \
if (helpMessage) { \
printf "\033[36m%-23s\033[0m %s\n", \
helpCommand, helpMessage; \
helpMessage = ""; \
} \
} else if ($$0 ~ /^[a-zA-Z\-\_0-9.]+:/) { \
helpCommand = substr($$0, 0, index($$0, ":")); \
if (helpMessage) { \
printf "\033[36m%-23s\033[0m %s\n", \
helpCommand, helpMessage"\n"; \
helpMessage = ""; \
} \
} else if ($$0 ~ /^##/) { \
if (helpMessage) { \
helpMessage = helpMessage"\n "substr($$0, 3); \
} else { \
helpMessage = substr($$0, 3); \
} \
} else { \
if (helpMessage) { \
print "\n "helpMessage"\n" \
} \
helpMessage = ""; \
} \
}' \
$(MAKEFILE_LIST)
if($$0 ~ /^### /){ \
if(help) printf "%-23s %s\n\n", cmd, help; help=""; \
printf "\n%s:\n", substr($$0,5); \
} else if($$0 ~ /^[a-zA-Z0-9._-]+:/){ \
cmd = substr($$0, 1, index($$0, ":")-1); \
if(help) printf " %-23s %s\n", cmd, help; help=""; \
} else if($$0 ~ /^##/){ \
help = help ? help "\n " substr($$0,3) : substr($$0,3); \
} else if(help){ \
print "\n " help "\n"; help=""; \
} \
}' $(MAKEFILE_LIST)

145
README.md
View File

@ -1,6 +1,5 @@
[![Build Status](https://github.com/foomo/posh/actions/workflows/test.yml/badge.svg?branch=main&event=push)](https://github.com/foomo/posh/actions/workflows/test.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/foomo/posh)](https://goreportcard.com/report/github.com/foomo/posh)
[![Coverage Status](https://coveralls.io/repos/github/foomo/posh/badge.svg?branch=main&)](https://coveralls.io/github/foomo/posh?branch=main)
[![GoDoc](https://godoc.org/github.com/foomo/posh?status.svg)](https://godoc.org/github.com/foomo/posh)
<p align="center">
@ -9,16 +8,33 @@
# Project Oriented SHELL (posh)
> think of `posh` as an interactive and hackable Makefile
> Think of `posh` as an interactive, isolated and hackable Makefile
## Installing
## Installation
Install the latest release of the cli:
### Download binary
````bash
$ brew update
$ brew install foomo/tap/posh
````
Download a [binary release](https://github.com/foomo/posh/releases)
### Build from source
```
go install github.com/foomo/posh@latest
```
### Homebrew (Linux/macOS)
If you use [Homebrew](https://brew.sh), you can install like this:
```
brew install foomo/tap/posh
```
### Mise
If you use [mise](https://https://mise.jdx.dev), you can install like this:
```
mise use github.com:foomo/posh
```
## Usage
@ -46,16 +62,123 @@ Use "posh [command] --help" for more information about a command.
To start using posh, go into your project and run:
```bash
```shell
$ cd your/project
$ posh init
```
This will generate the standard layout for posh which can be changed as required through `.posh.yml`.
Once initialized you can start posh through:
```yaml
version: v1.0
```bash
## Prompt settings
prompt:
title: "Posh"
prefix: "posh >"
history:
limit: 100
filename: .posh/.history
lockFilename: .posh/.history.lock
## Environment variables
env:
- name: PATH
value: "${PROJECT_ROOT}/bin:${PATH}"
## Ownbrew settings
ownbrew:
binDir: "bin"
tapDir: ".posh/scripts/ownbrew"
tempDir: ".posh/tmp"
cellarDir: ".posh/bin"
packages: []
## Remote package
## See `https://github.com/foomo/ownbrew-tap`
#- name: gotsrpc
# tap: foomo/tap/foomo/gotsrpc
# version: 2.6.2
## Local package `.posh/scripts/ownbrew`
#- name: example
# version: 0.0.0
## Requirement settings
require:
## Required environment variables
envs: []
## Example: require VOLTA_HOME
#- name: VOLTA_HOME
# help: |
# Missing required $VOLTA_HOME env var.
#
# Please initialize volta and ensure $VOLTA_HOME is set:
#
# $ volta setup
## Required scripts that need to succeed
scripts: []
## Example: git
#- name: git
# command: |
# git status && exit 0 || exit 1
# help: |
# This is not a git repo. Please clone the repository
## Example: npm
#- name: npm
# command: npm whoami --registry=https://npm.pkg.github.com > /dev/null 2>&1
# help: |
# You're not yet logged into the github npm registry!
#
# $ npm login --scope=@<SCOPE> --registry=https://npm.pkg.github.com
# Username: [GITHUB_USERNAME]
# Password: [GITHUB_TOKEN]
# Email: [EMAIL]
## Required packages to be installed on the host
packages: []
## Example: git
#- name: git
# version: '~2'
# command: git version | awk '{print $3}'
# help: |
# Please ensure you have 'git' installed in the required version: %s!
#
# $ brew update
# $ brew install git
## Example: go
#- name: go
# version: '>=1.23'
# command: go env GOVERSION | cut -c3-
# help: |
# Please ensure you have 'go' installed in the required version: %s!
#
# $ brew update
# $ brew install go
#- name: volta
# version: '>=2'
# command: volta --version
# help: |
# Please ensure you have 'volta' installed in a recent version: %s!
#
# $ curl https://get.volta.sh | bash
#
# Or see the documentation: https://docs.volta.sh/guide/getting-started
## Integrations
## Example: Custom
welcome:
message: Hi, thanks for using POSH!
```
Once initialized, you can start posh through:
```shell
$ make shell
```

View File

@ -2,10 +2,13 @@ package cmd
import (
"context"
"fmt"
"os"
"os/signal"
"runtime/debug"
"strings"
cowsay "github.com/Code-Hex/Neo-cowsay/v2"
"github.com/foomo/posh/internal/cmd"
intenv "github.com/foomo/posh/internal/env"
"github.com/foomo/posh/pkg/plugin"
@ -46,11 +49,19 @@ func Execute() {
signal.Notify(osInterrupt, os.Interrupt)
ctx, cancel := context.WithCancel(context.Background())
say := func(msg string) string {
if say, err := cowsay.Say(msg, cowsay.BallonWidth(80)); err == nil {
msg = say
}
return msg
}
// handle defer
defer func() {
if err := recover(); err != nil {
debug.PrintStack()
l.Error(err)
if r := recover(); r != nil {
l.Error(say("It's time to panic"))
l.Error(fmt.Sprintf("%v", r))
l.Error(string(debug.Stack()))
code = 1
}
signal.Stop(osInterrupt)
@ -67,6 +78,7 @@ func Execute() {
if err := rootCmd.ExecuteContext(ctx); errors.Is(err, context.Canceled) {
l.Warn(err.Error())
} else if err != nil {
l.Error(say(strings.Split(errors.Cause(err).Error(), ":")[0]))
l.Error(err.Error())
code = 1
}

View File

@ -1,4 +1,5 @@
Version: v1.0
# yaml-language-server: $schema=https://raw.githubusercontent.com/foomo/posh/refs/heads/main/posh.schema.json
version: v1.0
## Prompt settings
prompt:
@ -105,5 +106,5 @@ require:
## Example: Custom
#welcome:
# message: Hi, thanks for using POSH!
welcome:
message: Hi, thanks for using POSH!

View File

@ -1,6 +1,6 @@
module {{ .module }}/posh
go 1.23.0
go 1.24
replace (
github.com/c-bata/go-prompt v0.2.6 => github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5

View File

@ -110,12 +110,12 @@ func (p *Plugin) Prompt(ctx context.Context, cfg config.Prompt) error {
prompt.WithAliases(cfg.Aliases),
prompt.WithCommands(p.commands),
prompt.WithCheckers(
func(ctx context.Context, l log.Logger) check.Info {
return check.Info{
func(ctx context.Context, l log.Logger) []check.Info {
return []check.Info{check.Info{
Name: "example",
Note: "all good",
Status: check.StatusSuccess,
}
}}
},
),
prompt.WithFileHistory(

85
go.mod
View File

@ -1,29 +1,32 @@
module github.com/foomo/posh
go 1.24.1
go 1.25
require (
dario.cat/mergo v1.0.1
github.com/Masterminds/semver/v3 v3.3.1
dario.cat/mergo v1.0.2
github.com/Code-Hex/Neo-cowsay/v2 v2.0.4
github.com/Masterminds/semver/v3 v3.4.0
github.com/Masterminds/sprig/v3 v3.3.0
github.com/alecthomas/chroma v0.10.0
github.com/c-bata/go-prompt v0.2.6
github.com/charlievieth/fastwalk v1.0.10
github.com/charlievieth/fastwalk v1.0.14
github.com/foomo/fender v1.0.1
github.com/foomo/go v0.0.3
github.com/foomo/ownbrew v0.2.3
github.com/go-git/go-git/v5 v5.14.0
github.com/go-git/go-git/v5 v5.16.2
github.com/gofrs/flock v0.12.1
github.com/invopop/jsonschema v0.12.0
github.com/joho/godotenv v1.5.1
github.com/kubescape/go-git-url v0.0.30
github.com/neilotoole/slogt v1.1.0
github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.80
github.com/samber/lo v1.49.1
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.10.0
golang.org/x/sync v0.12.0
github.com/pterm/pterm v0.12.81
github.com/samber/lo v1.51.0
github.com/spf13/cobra v1.10.1
github.com/spf13/pflag v1.0.10
github.com/spf13/viper v1.21.0
github.com/stretchr/testify v1.11.1
golang.org/x/sync v0.17.0
gopkg.in/yaml.v3 v3.0.1
)
@ -31,67 +34,69 @@ require (
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
github.com/Code-Hex/go-wordwrap v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/chainguard-dev/git-urls v1.0.2 // indirect
github.com/cloudflare/circl v1.6.0 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/containerd/console v1.0.5 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.10 // 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-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.25.0 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/gookit/color v1.6.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-tty v0.0.7 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pjbgf/sha1cd v0.5.0 // indirect
github.com/pkg/term v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
gopkg.in/ini.v1 v1.67.0 // 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/sys v0.36.0 // indirect
golang.org/x/term v0.35.0 // indirect
golang.org/x/text v0.29.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect
)
replace github.com/c-bata/go-prompt v0.2.6 => github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5

206
go.sum
View File

@ -6,8 +6,12 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
github.com/Code-Hex/Neo-cowsay/v2 v2.0.4 h1:y80Hd9hmB+rsEH/p4c5ti5PbO0PhBmxw4NgbpFZvoHg=
github.com/Code-Hex/Neo-cowsay/v2 v2.0.4/go.mod h1:6k40Pwrc2FazLf1BUbmAC36E9LvT+DErjZr30isbXhg=
github.com/Code-Hex/go-wordwrap v1.0.0 h1:yl5fLyZEz3+hPGbpTRlTQ8mQJ1HXWcTq1FCNR1ch6zM=
github.com/Code-Hex/go-wordwrap v1.0.0/go.mod h1:/SsbgkY2Q0aPQRyvXcyQwWYTQOIwSORKe6MPjRVGIWU=
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
@ -19,17 +23,15 @@ github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi
github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
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/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
@ -37,17 +39,19 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/chainguard-dev/git-urls v1.0.2 h1:pSpT7ifrpc5X55n4aTTm7FFUE+ZQHKiqpiwNkJrVcKQ=
github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4+An04HJY7Ol+Y/o=
github.com/charlievieth/fastwalk v1.0.9 h1:Odb92AfoReO3oFBfDGT5J+nwgzQPF/gWAw6E6/lkor0=
github.com/charlievieth/fastwalk v1.0.9/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
github.com/charlievieth/fastwalk v1.0.10 h1:0qUbvA2O+K+X+IrTfZTC0UH2DK5MOA+KjVfStAHUnGg=
github.com/charlievieth/fastwalk v1.0.10/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/charlievieth/fastwalk v1.0.14 h1:3Eh5uaFGwHZd8EGwTjJnSpBkfwfsak9h6ICgnWlhAyg=
github.com/charlievieth/fastwalk v1.0.14/go.mod h1:diVcUreiU1aQ4/Wu3NbxxH4/KYdKpLDojrQ1Bb2KgNY=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
@ -58,23 +62,24 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/foomo/fender v1.0.1 h1:sz+5IHWDgwAkYKau6NlgPHxa7v3kLyzScq8MrhKdybM=
github.com/foomo/fender v1.0.1/go.mod h1:Out1+dS5S8qJ4VqtxzCj/FDYi4Wtal2NK+8vhCUCrao=
github.com/foomo/go v0.0.3 h1:5pGzcPC78dImuBTT7nsZZnH+GIQUylbCtMkFEH26uZk=
github.com/foomo/go v0.0.3/go.mod h1:x6g64wiQusqaFElnh5rlk9unCgLKmfUWy0YFLejJxio=
github.com/foomo/ownbrew v0.2.3 h1:8ATDWLGH6+dscAoYc7be1lNPdxQAwwZf2xeCLE/4fhQ=
github.com/foomo/ownbrew v0.2.3/go.mod h1:lyUFvPABflzcI/cAmQU4vif1DjH8tZ6kogc+Ee44Sag=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5 h1:kXNtle4AoQnngdm+gwt4ku6Llbzw3EFHgZYpL618JaI=
github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5/go.mod h1:+syUfnvYJUO5A+6QMQYXAyzkxHMNlj9dH2LIeQfBSjc=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
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=
@ -83,47 +88,51 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
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.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
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/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
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/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@ -137,8 +146,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
@ -149,6 +158,7 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
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-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
@ -158,18 +168,16 @@ github.com/mattn/go-tty v0.0.7 h1:KJ486B6qI8+wBO7kQxYgmmEFDaFEE96JMBQ7h400N8Q=
github.com/mattn/go-tty v0.0.7/go.mod h1:f2i5ZOvXBU/tCABmLmOfzLz9azMo5wdAaElRNnJKr+k=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/neilotoole/slogt v1.1.0 h1:c7qE92sq+V0yvCuaxph+RQ2jOKL61c4hqS1Bv9W7FZE=
github.com/neilotoole/slogt v1.1.0/go.mod h1:RCrGXkPc/hYybNulqQrMHRtvlQ7F6NktNVLuLwk6V+w=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk=
@ -184,69 +192,65 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg=
github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo=
github.com/pterm/pterm v0.12.81 h1:ju+j5I2++FO1jBKMmscgh5h5DPFDFMB7epEjSoKehKA=
github.com/pterm/pterm v0.12.81/go.mod h1:TyuyrPjnxfwP+ccJdBTeWHtd/e0ybQHkOS/TakajZCw=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
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/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
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.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
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/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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -254,18 +258,14 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -288,40 +288,34 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@ -331,5 +325,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0=
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=

2
pkg/cache/cache.go vendored
View File

@ -2,6 +2,6 @@ package cache
type Cache interface {
Get(namespace string) Namespace
Clear(namespace string)
Clear(namespaces ...string)
List() map[string]Namespace
}

View File

@ -1,32 +1,43 @@
package cache
type MemoryCache map[string]MemoryNamespace
import (
"sync"
)
func NewMemoryCache() MemoryCache {
return MemoryCache{}
type MemoryCache struct {
store sync.Map
}
func (c MemoryCache) Clear(namespace string) {
if namespace == "" {
for _, value := range c {
value.Delete("")
}
} else {
c.Get(namespace).Delete("")
func NewMemoryCache() *MemoryCache {
return &MemoryCache{
store: sync.Map{},
}
}
func (c MemoryCache) Get(namespace string) Namespace {
if _, ok := c[namespace]; !ok {
c[namespace] = MemoryNamespace{}
func (c *MemoryCache) Clear(namespaces ...string) {
if len(namespaces) == 0 {
c.store.Range(func(key, value interface{}) bool {
namespaces = append(namespaces, key.(string)) //nolint:forcetypeassert
return true
})
}
for _, namespace := range namespaces {
c.Get(namespace).Delete()
}
return c[namespace]
}
func (c MemoryCache) List() map[string]Namespace {
func (c *MemoryCache) Get(namespace string) Namespace {
value, _ := c.store.LoadOrStore(namespace, &MemoryNamespace{
store: sync.Map{},
})
return value.(*MemoryNamespace) //nolint:forcetypeassert
}
func (c *MemoryCache) List() map[string]Namespace {
ret := map[string]Namespace{}
for s, namespace := range c {
ret[s] = namespace
}
c.store.Range(func(k, v interface{}) bool {
ret[k.(string)] = v.(*MemoryNamespace) //nolint:forcetypeassert
return true
})
return ret
}

View File

@ -1,38 +1,44 @@
package cache
import "github.com/c-bata/go-prompt"
import (
"sync"
type MemoryNamespace map[string]interface{}
"github.com/c-bata/go-prompt"
)
func (c MemoryNamespace) Delete(key string) {
if key == "" {
for key := range c {
delete(c, key)
}
type MemoryNamespace struct {
store sync.Map
}
func (c *MemoryNamespace) Delete(keys ...string) {
if len(keys) == 0 {
c.store.Clear()
} else {
delete(c, key)
}
}
func (c MemoryNamespace) Get(key string, cb func() interface{}) interface{} {
if _, ok := c[key]; !ok {
if cb == nil {
return nil
for _, key := range keys {
c.store.Delete(key)
}
c[key] = cb()
}
return c[key]
}
func (c MemoryNamespace) Keys() []string {
keys := make([]string, 0, len(c))
for k := range c {
keys = append(keys, k)
func (c *MemoryNamespace) Get(key string, cb func() any) any {
value, ok := c.store.Load(key)
if !ok && cb != nil {
value = cb()
c.store.Store(key, value)
}
return value
}
func (c *MemoryNamespace) Keys() []string {
var keys []string
c.store.Range(func(k, v interface{}) bool {
keys = append(keys, k.(string)) //nolint:forcetypeassert
return true
})
return keys
}
func (c MemoryNamespace) GetSuggests(key string, cb func() interface{}) []prompt.Suggest {
func (c *MemoryNamespace) GetSuggests(key string, cb func() any) []prompt.Suggest {
if v, ok := c.Get(key, cb).([]prompt.Suggest); ok {
return v
}

View File

@ -7,6 +7,6 @@ import (
type Namespace interface {
Get(key string, cb func() any) any
Keys() []string
Delete(key string)
Delete(keys ...string)
GetSuggests(key string, cb func() any) []prompt.Suggest
}

View File

@ -106,7 +106,7 @@ func (c *Cache) clear(ctx context.Context, r *readline.Readline) error {
}
} else {
c.l.Info("clearing all caches")
c.cache.Clear("")
c.cache.Clear()
}
return nil
}

View File

@ -107,8 +107,23 @@ func (c *Env) list(ctx context.Context, r *readline.Readline) error {
data := pterm.TableData{{"Name", "Value"}}
values := os.Environ()
sort.Strings(values)
var pairs [][]string
for _, s := range values {
data = append(data, strings.SplitN(s, "=", 2))
pairs = append(pairs, strings.SplitN(s, "=", 2))
}
return pterm.DefaultTable.WithHasHeader(true).WithData(data).Render()
var maxKeyLen int
for _, pair := range pairs {
maxKeyLen = max(maxKeyLen, len(pair[0]))
}
maxValueLen := pterm.GetTerminalWidth() - maxKeyLen - 5
for i, pair := range pairs {
var value string
for len(pair[1]) > maxValueLen {
value += pair[1][:maxValueLen] + "\n"
pair[1] = pair[1][maxValueLen:]
}
pairs[i][1] = value + pair[1]
}
data = append(data, pairs...)
return pterm.DefaultTable.WithHasHeader(true).WithHeaderRowSeparator("-").WithData(data).Render()
}

View File

@ -44,9 +44,10 @@ func (c *Node) setFlags(ctx context.Context, r *readline.Readline, parse bool) e
func (c *Node) completeArguments(ctx context.Context, p *root, r *readline.Readline, i int) []goprompt.Suggest {
var suggest []goprompt.Suggest
localArgs := r.Args()[i:]
localArgs := r.Args().From(i)
switch {
case len(c.Nodes) > 0 && len(localArgs) <= 1:
case len(c.Nodes) > 0:
for _, command := range c.Nodes {
if command.Values != nil {
suggest = command.Values(ctx, r)
@ -54,12 +55,12 @@ func (c *Node) completeArguments(ctx context.Context, p *root, r *readline.Readl
suggest = append(suggest, goprompt.Suggest{Text: command.Name, Description: command.Description})
}
}
case len(c.Args) > 0 && len(c.Args) >= len(localArgs):
j := len(localArgs)
if len(localArgs) > 0 && localArgs[j-1] != "" {
j -= 1
case len(c.Args) > 0 && len(localArgs) == 0:
if fn := c.Args[0].Suggest; fn != nil {
suggest = fn(ctx, p, r)
}
if fn := c.Args[j].Suggest; fn != nil {
case len(c.Args) > 0 && len(c.Args) >= len(localArgs):
if fn := c.Args[len(localArgs)-1].Suggest; fn != nil {
suggest = fn(ctx, p, r)
}
case len(c.Args) > 0 && c.Args.Last().Repeat && c.Args.Last().Suggest != nil:
@ -85,7 +86,7 @@ func (c *Node) completeFlags(r *readline.Readline) []goprompt.Suggest {
}
func (c *Node) execute(ctx context.Context, r *readline.Readline, i int) error {
localArgs := r.Args()[i:]
localArgs := r.Args().From(i)
// validate
switch {
@ -115,7 +116,7 @@ func (c *Node) find(ctx context.Context, r *readline.Readline, i int) (*Node, in
if subCmd, j := cmd.find(ctx, r, i+1); subCmd != nil {
return subCmd, j
}
return cmd, i
return cmd, i + 1
}
if cmd.Values != nil {
for _, name := range cmd.Values(ctx, r) {
@ -123,7 +124,7 @@ func (c *Node) find(ctx context.Context, r *readline.Readline, i int) (*Node, in
if subCmd, j := cmd.find(ctx, r, i+1); subCmd != nil {
return subCmd, j
}
return cmd, i
return cmd, i + 1
}
}
}

View File

@ -60,7 +60,7 @@ func (t *root) Complete(ctx context.Context, r *readline.Readline) []goprompt.Su
} else if err := cmd.setFlags(ctx, r, false); err != nil {
return nil
} else {
suggests = cmd.completeArguments(ctx, t, r, i+1)
suggests = cmd.completeArguments(ctx, t, r, i)
}
case readline.ModeFlags:
if cmd, _ := t.node.find(ctx, r, 0); cmd == nil && t.node != nil {

View File

@ -426,5 +426,5 @@ func T(ctx context.Context) *testing.T {
func SetT(ctx context.Context, t *testing.T) context.Context {
t.Helper()
return context.WithValue(ctx, "t", t)
return context.WithValue(ctx, "t", t) //nolint:staticcheck
}

View File

@ -2,10 +2,11 @@ package config
type (
Prompt struct {
Title string `json:"title" yaml:"title"`
Prefix string `json:"prefix" yaml:"prefix"`
History PromptHistory `json:"history" yaml:"history"`
Aliases map[string]string `json:"aliases" yaml:"aliases"`
Title string `json:"title" yaml:"title"`
Prefix string `json:"prefix" yaml:"prefix"`
PrefixGit bool `json:"prefixGit" yaml:"prefixGit"`
History PromptHistory `json:"history" yaml:"history"`
Aliases map[string]string `json:"aliases" yaml:"aliases"`
}
PromptHistory struct {
Limit int `json:"limit" yaml:"limit"`

View File

@ -4,6 +4,7 @@ import (
"fmt"
"log/slog"
ptermx "github.com/foomo/posh/pkg/pterm"
"github.com/pterm/pterm"
)
@ -56,13 +57,15 @@ func NewPTerm(opts ...PTermOption) *PTerm {
opt(inst)
}
}
pterm.Info = *pterm.Info.WithPrefix(pterm.Prefix{Text: "", Style: pterm.Info.Prefix.Style})
pterm.Debug = *pterm.Debug.WithPrefix(pterm.Prefix{Text: "⚒︎", Style: pterm.Debug.Prefix.Style})
pterm.Fatal = *pterm.Fatal.WithPrefix(pterm.Prefix{Text: "💀", Style: pterm.Fatal.Prefix.Style})
pterm.Error = *pterm.Error.WithPrefix(pterm.Prefix{Text: "⛌", Style: pterm.Error.Prefix.Style}) //nolint:reassign
pterm.Warning = *pterm.Info.WithPrefix(pterm.Prefix{Text: "⚠", Style: pterm.Warning.Prefix.Style})
pterm.Success = *pterm.Success.WithPrefix(pterm.Prefix{Text: "✓", Style: pterm.Success.Prefix.Style})
pterm.DefaultLogger = *pterm.DefaultLogger.WithTime(false)
pterm.Info.Prefix.Text = "⎈"
pterm.Debug.Prefix.Text = "⛏︎"
pterm.Fatal.Prefix.Text = "⛔︎"
pterm.Error.Prefix.Text = "⛌"
pterm.Warning.Prefix.Text = "⚠"
pterm.Success.Prefix.Text = "✓"
pterm.DefaultLogger.ShowTime = false
return inst
}
@ -165,7 +168,7 @@ func (l *PTerm) Must(err error) {
}
func (l *PTerm) SlogHandler() slog.Handler {
return pterm.NewSlogHandler(&pterm.DefaultLogger)
return ptermx.NewSlogHandler()
}
// ------------------------------------------------------------------------------------------------

View File

@ -6,4 +6,4 @@ import (
"github.com/foomo/posh/pkg/log"
)
type Checker func(ctx context.Context, l log.Logger) Info
type Checker func(ctx context.Context, l log.Logger) []Info

View File

@ -2,6 +2,9 @@ package check
import (
"context"
"slices"
"strings"
"sync"
"time"
"github.com/foomo/posh/pkg/log"
@ -10,29 +13,32 @@ import (
)
func DefaultCheck(ctx context.Context, l log.Logger, checkers []Checker) error {
var mu sync.Mutex
var wg errgroup.Group
data := make(pterm.TableData, len(checkers))
wg.SetLimit(3)
for i, checker := range checkers {
i := i
checker := checker
var data pterm.TableData
// wg.SetLimit(3)
for _, checker := range checkers {
wg.Go(func() error {
cancelCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
info := checker(cancelCtx, l)
var color pterm.Color
switch info.Status {
case StatusFailure:
color = pterm.FgRed
case StatusSuccess:
color = pterm.FgGreen
default:
color = pterm.FgGray
}
data[i] = []string{
info.Status.String(),
info.Name,
color.Sprint(info.Note),
infos := checker(cancelCtx, l)
mu.Lock()
defer mu.Unlock()
for _, info := range infos {
var color pterm.Color
switch info.Status {
case StatusFailure:
color = pterm.FgRed
case StatusSuccess:
color = pterm.FgGreen
default:
color = pterm.FgGray
}
data = append(data, []string{
info.Status.String(),
info.Name,
color.Sprint(info.Note),
})
}
return nil
})
@ -40,10 +46,10 @@ func DefaultCheck(ctx context.Context, l log.Logger, checkers []Checker) error {
if err := wg.Wait(); err != nil {
return err
}
table := pterm.DefaultTable
if err := table.WithData(data).Render(); err != nil {
return err
}
pterm.Println()
return nil
slices.SortFunc(data, func(a, b []string) int {
return strings.Compare(a[1], b[1])
})
return pterm.DefaultTable.WithData(data).Render()
}

View File

@ -0,0 +1,34 @@
package goprompt
// FilterRanked combines prefix, contains, and fuzzy filters in that order to create suggestions that
// consider the most relevant matches first.
func filterCombined(completions []Suggest, sub string, ignoreCase bool) []Suggest {
prefixMatches := FilterHasPrefix(completions, sub, ignoreCase)
containsMatches := FilterContains(completions, sub, ignoreCase)
fuzzyMatches := FilterFuzzy(completions, sub, ignoreCase)
// combine all matches, ensuring no duplicates
presenseSet := make(map[string]Suggest)
res := make([]Suggest, 0, len(prefixMatches)+len(containsMatches)+len(fuzzyMatches))
for _, match := range prefixMatches {
presenseSet[match.Text] = match
res = append(res, match)
}
for _, match := range containsMatches {
if _, exists := presenseSet[match.Text]; !exists {
presenseSet[match.Text] = match
res = append(res, match)
}
}
for _, match := range fuzzyMatches {
if _, exists := presenseSet[match.Text]; !exists {
res = append(res, match)
}
}
return res
}

View File

@ -16,4 +16,5 @@ var (
FilterContains = prompt.FilterContains
FilterHasPrefix = prompt.FilterHasPrefix
FilterHasSuffix = prompt.FilterHasSuffix
FilterCombined = filterCombined
)

View File

@ -7,6 +7,7 @@ import (
"github.com/c-bata/go-prompt"
"github.com/c-bata/go-prompt/completer"
"github.com/foomo/posh/pkg/command"
"github.com/foomo/posh/pkg/env"
"github.com/foomo/posh/pkg/log"
"github.com/foomo/posh/pkg/prompt/check"
"github.com/foomo/posh/pkg/prompt/flair"
@ -14,22 +15,26 @@ import (
"github.com/foomo/posh/pkg/prompt/history"
"github.com/foomo/posh/pkg/readline"
"github.com/foomo/posh/pkg/shell"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/pkg/errors"
)
type (
Prompt struct {
l log.Logger
ctx context.Context
title string
flair flair.Flair
prefix string
check check.Check
checkers []check.Checker
filter goprompt.Filter
readline *readline.Readline
history history.History
commands command.Commands
aliases map[string]string
l log.Logger
ctx context.Context
title string
flair flair.Flair
prefix string
prefixGit bool
check check.Check
checkers check.Checkers
filter goprompt.Filter
readline *readline.Readline
history history.History
commands command.Commands
aliases map[string]string
// inputRegex - split cmd into args
promptOptions []prompt.Option
}
@ -56,7 +61,14 @@ func WithFlair(v flair.Flair) Option {
func WithPrefix(v string) Option {
return func(o *Prompt) error {
o.prefix = v + " "
o.prefix = v + " "
return nil
}
}
func WithPrefixGit(v bool) Option {
return func(o *Prompt) error {
o.prefixGit = v
return nil
}
}
@ -134,15 +146,16 @@ func WithPromptOptions(v ...prompt.Option) Option {
func New(l log.Logger, opts ...Option) (*Prompt, error) {
inst := &Prompt{
l: l.Named("prompt"),
ctx: context.Background(),
title: "posh",
prefix: ">",
flair: flair.DefaultFlair,
filter: goprompt.FilterFuzzy,
check: check.DefaultCheck,
history: history.NewNoop(l),
commands: command.Commands{},
l: l.Named("prompt"),
ctx: context.Background(),
title: "posh",
prefix: " ",
prefixGit: false,
flair: flair.DefaultFlair,
filter: goprompt.FilterCombined,
check: check.DefaultCheck,
history: history.NewNoop(l),
commands: command.Commands{},
}
for _, opt := range opts {
if opt != nil {
@ -187,6 +200,30 @@ func (s *Prompt) Run() error {
[]prompt.Option{
prompt.OptionTitle(s.title),
prompt.OptionPrefix(s.prefix),
prompt.OptionLivePrefix(func() (string, bool) {
if s.prefixGit {
r, err := git.PlainOpen(env.ProjectRoot())
if err != nil {
s.l.Debug("failed to open git repository", "error", err)
}
ref, err := r.Head()
if err != nil {
s.l.Debug("failed to fetch HEAD", "error", err)
}
name := ref.Name().Short()
if t, err := r.Tags(); err == nil {
_ = t.ForEach(func(reference *plumbing.Reference) error {
if ref.Hash() == reference.Hash() {
name = "\uF412" + reference.Name().Short()
return errors.New("break")
}
return nil
})
}
return s.prefix[:len(s.prefix)-4] + "(" + name + ") ", true
}
return "", false
}),
prompt.OptionPrefixTextColor(prompt.Cyan),
prompt.OptionInputTextColor(prompt.DefaultColor),
prompt.OptionCompletionWordSeparator(completer.FilePathCompletionSeparator),
@ -253,7 +290,7 @@ func (s *Prompt) execute(input string) {
if cmd := s.Commands().Get(s.readline.Cmd()); cmd != nil {
s.l.Debugf(`executing command:
> %s
%s
%s
`, input, s.readline.String())
@ -263,11 +300,17 @@ func (s *Prompt) execute(input string) {
return
}
}
if err := cmd.Execute(ctx, s.readline); err != nil {
if err := cmd.Execute(ctx, s.readline); err != nil && err.Error() == "signal: interrupt" {
s.l.Debug(err.Error())
} else if err != nil {
s.l.Error(err.Error())
}
} else {
if err := shell.New(ctx, s.l, input).Run(); err != nil && err.Error() == "signal: interrupt" {
s.l.Debug(err.Error())
} else if err != nil {
s.l.Error(err.Error())
}
} else if err := shell.New(ctx, s.l, input).Run(); err != nil {
s.l.Error(err.Error())
}
}
@ -330,8 +373,8 @@ func (s *Prompt) complete(d prompt.Document) []prompt.Suggest {
// context returns and watches over a new context
func (s *Prompt) context() context.Context {
//ctx, cancel := context.WithCancel(context.Background())
//go func(ctx context.Context, cancel context.CancelFunc) {
// ctx, cancel := context.WithCancel(context.Background())
// go func(ctx context.Context, cancel context.CancelFunc) {
// sigChan := make(chan os.Signal, 1)
// signal.Notify(sigChan, os.Interrupt)
// select {
@ -344,7 +387,7 @@ func (s *Prompt) context() context.Context {
// case <-ctx.Done():
// return
// }
//}(ctx, cancel)
//return ctx
// }(ctx, cancel)
// return ctx
return s.ctx
}

20
pkg/pterm/ptermwriter.go Normal file
View File

@ -0,0 +1,20 @@
package pterm
import (
"github.com/pterm/pterm"
)
type Writer struct {
printer pterm.PrefixPrinter
}
func NewWriter(printer pterm.PrefixPrinter) *Writer {
return &Writer{
printer: printer,
}
}
func (p *Writer) Write(b []byte) (int, error) {
p.printer.Println(string(b))
return len(b), nil
}

85
pkg/pterm/sloghandler.go Normal file
View File

@ -0,0 +1,85 @@
package pterm
import (
"context"
"fmt"
"log/slog"
"github.com/pterm/pterm"
)
type SlogHandler struct {
attrs []slog.Attr
}
// NewSlogHandler returns a new logging handler that can be intrgrated with log/slog.
func NewSlogHandler() *SlogHandler {
return &SlogHandler{}
}
// Enabled returns true if the given level is enabled.
func (s *SlogHandler) Enabled(ctx context.Context, level slog.Level) bool {
switch level {
case slog.LevelDebug:
return pterm.PrintDebugMessages
default:
return true
}
}
// Handle handles the given record.
func (s *SlogHandler) Handle(ctx context.Context, record slog.Record) error {
level := record.Level
message := record.Message
// Convert slog Attrs to a map.
keyValsMap := make(map[string]any)
record.Attrs(func(attr slog.Attr) bool {
keyValsMap[attr.Key] = attr.Value
return true
})
for _, attr := range s.attrs {
keyValsMap[attr.Key] = attr.Value
}
args := pterm.DefaultLogger.ArgsFromMap(keyValsMap)
// Wrapping args inside another slice to match [][]LoggerArgument
argsWrapped := [][]pterm.LoggerArgument{args}
for _, arg := range argsWrapped {
for _, attr := range arg {
message += " " + attr.Key + ": " + fmt.Sprintf("%v", attr.Value)
}
}
switch level {
case slog.LevelDebug:
pterm.Debug.Println(message)
case slog.LevelInfo:
pterm.Info.Println(message)
case slog.LevelWarn:
pterm.Warning.Println(message)
case slog.LevelError:
pterm.Error.Println(message)
default:
pterm.Info.Println(message)
}
return nil
}
// WithAttrs returns a new handler with the given attributes.
func (s *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
newS := *s
newS.attrs = attrs
return &newS
}
// WithGroup is not yet supported.
func (s *SlogHandler) WithGroup(name string) slog.Handler {
// Grouping is not yet supported by pterm.
return s
}

View File

@ -10,7 +10,7 @@ type FlagSet struct {
func NewFlagSet(name string) *FlagSet {
fs := pflag.NewFlagSet(name, pflag.ContinueOnError)
fs.ParseErrorsWhitelist = pflag.ParseErrorsWhitelist{UnknownFlags: true}
fs.ParseErrorsAllowlist = pflag.ParseErrorsAllowlist{UnknownFlags: true}
return &FlagSet{
FlagSet: fs,
}
@ -29,7 +29,7 @@ func (s *FlagSet) SetValues(name string, values ...string) error {
}
func (s *FlagSet) GetValues(name string) []string {
if f := s.FlagSet.Lookup(name); f == nil {
if f := s.Lookup(name); f == nil {
return nil
} else if v, ok := f.Annotations["values"]; ok {
return v

View File

@ -16,6 +16,7 @@ type Shell struct {
l log.Logger
cmd *exec.Cmd
quiet bool
debug bool
args []string
stdin io.Reader
stdout io.Writer
@ -88,6 +89,11 @@ func (s *Shell) Stderr(v io.Writer) *Shell {
return s
}
func (s *Shell) Debug() *Shell {
s.debug = true
return s
}
func (s *Shell) Run() error {
args := s.args
s.cmd.Args = append(s.cmd.Args, strings.Join(args, " "))
@ -144,15 +150,19 @@ func (s *Shell) Wait() error {
// ------------------------------------------------------------------------------------------------
func (s *Shell) trace() {
s.l.Tracef(`"Executing:
if s.debug {
s.l.Info(s.cmd.String())
} else {
s.l.Tracef(`"Executing:
$ %s
Directory: %s
%s
`,
s.cmd.String(),
s.cmd.Dir,
strings.Join(s.cmd.Environ(), "\n"),
)
s.cmd.String(),
s.cmd.Dir,
strings.Join(s.cmd.Environ(), "\n"),
)
}
}

18
posh.example.yaml Normal file
View File

@ -0,0 +1,18 @@
# yaml-language-server: $schema=posh.schema.json
version: '1.0'
# Prompt settings
prompt:
title: My title
prefix: my-prefix
# Environment variables
env:
- name: FOO
value: bar
# Require settings
require:
envs: []
packages: []
scripts: []

144
posh.schema.json Normal file
View File

@ -0,0 +1,144 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://raw.githubusercontent.com/foomo/posh/refs/heads/main/posh.schema.json",
"$ref": "#/$defs/Config",
"$defs": {
"Config": {
"properties": {
"version": {
"type": "string",
"default": "v1.0"
},
"env": {
"$ref": "#/$defs/Env"
},
"prompt": {
"$ref": "#/$defs/Prompt"
},
"require": {
"$ref": "#/$defs/Require"
}
},
"type": "object",
"required": [
"version"
]
},
"Env": {
"items": {
"properties": {
"name": {
"type": "string"
},
"value": {
"type": "string"
}
},
"type": "object"
},
"type": "array"
},
"Prompt": {
"properties": {
"title": {
"type": "string"
},
"prefix": {
"type": "string"
},
"prefixGit": {
"type": "boolean"
},
"history": {
"$ref": "#/$defs/PromptHistory"
},
"aliases": {
"additionalProperties": {
"type": "string"
},
"type": "object"
}
},
"type": "object"
},
"PromptHistory": {
"properties": {
"limit": {
"type": "integer"
},
"filename": {
"type": "string"
},
"lockFilename": {
"type": "string"
}
},
"type": "object"
},
"Require": {
"properties": {
"envs": {
"items": {
"$ref": "#/$defs/RequireEnv"
},
"type": "array"
},
"scripts": {
"items": {
"$ref": "#/$defs/RequireScript"
},
"type": "array"
},
"packages": {
"items": {
"$ref": "#/$defs/RequirePackage"
},
"type": "array"
}
},
"type": "object"
},
"RequireEnv": {
"properties": {
"name": {
"type": "string"
},
"help": {
"type": "string"
}
},
"type": "object"
},
"RequirePackage": {
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
},
"command": {
"type": "string"
},
"help": {
"type": "string"
}
},
"type": "object"
},
"RequireScript": {
"properties": {
"name": {
"type": "string"
},
"command": {
"type": "string"
},
"help": {
"type": "string"
}
},
"type": "object"
}
}
}

54
schema_test.go Normal file
View File

@ -0,0 +1,54 @@
package main_test
import (
"encoding/json"
"os"
"path"
"testing"
testingx "github.com/foomo/go/testing"
tagx "github.com/foomo/go/testing/tag"
"github.com/foomo/posh/pkg/config"
"github.com/invopop/jsonschema"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type Config struct {
// Version of the config
Version string `json:"version" jsonschema:"required,default=v1.0"`
// Environment variables
Env config.Env `json:"env"`
// Prompt settings
Prompt config.Prompt `json:"prompt"`
// Require settings
Require config.Require `json:"require"`
}
func TestSchema(t *testing.T) {
t.Parallel()
testingx.Tags(t, tagx.Short)
cwd, err := os.Getwd()
require.NoError(t, err)
reflector := new(jsonschema.Reflector)
reflector.AllowAdditionalProperties = true
reflector.RequiredFromJSONSchemaTags = true
require.NoError(t, reflector.AddGoComments("github.com/foomo/posh", "./"))
schema := reflector.Reflect(&Config{})
schema.ID = "https://raw.githubusercontent.com/foomo/posh/refs/heads/main/posh.schema.json"
actual, err := json.MarshalIndent(schema, "", " ")
require.NoError(t, err)
filename := path.Join(cwd, "posh.schema.json")
expected, err := os.ReadFile(filename)
if !errors.Is(err, os.ErrNotExist) {
require.NoError(t, err)
}
if !assert.Equal(t, string(expected), string(actual)) {
require.NoError(t, os.WriteFile(filename, actual, 0600))
}
}