From 57619c58eda5fac9217563599e4e4571e8b6dd18 Mon Sep 17 00:00:00 2001 From: Patrick Joyce Date: Wed, 5 Aug 2026 10:37:22 -0400 Subject: [PATCH] gorelease 0.4.0 --- .dockerignore | 2 +- .github/workflows/pull-request.yml | 23 ++++++++ .github/workflows/release.yml | 28 +++++++--- .gitignore | 1 + .goreleaser.yaml | 87 ++++++++++++++++++++++++++++++ CHANGELOG.md | 20 +++++++ Dockerfile | 8 ++- Makefile | 32 ++++++----- Makefile_Windows | 8 ++- README.md | 67 +++++++++++++++-------- go.sum | 4 -- scripts/_awsd | 14 ----- scripts/_awsd_autocomplete | 45 ---------------- scripts/powershell/awsd.ps1 | 12 ----- src/cmd/version.go | 8 ++- src/utils/shell.go | 46 +++++++++------- src/utils/shell_test.go | 8 +-- 17 files changed, 256 insertions(+), 157 deletions(-) create mode 100644 .goreleaser.yaml delete mode 100755 scripts/_awsd delete mode 100755 scripts/_awsd_autocomplete delete mode 100644 scripts/powershell/awsd.ps1 diff --git a/.dockerignore b/.dockerignore index b721977..bb55e11 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,4 +7,4 @@ LICENSE assets docs bin -_awsd_prompt +awsd diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 16f2074..7829872 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -31,3 +31,26 @@ jobs: run: | go test ./... -coverprofile=coverage.out go tool cover -func=coverage.out + + goreleaser: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - uses: actions/setup-go@v7 + with: + go-version: '1.26' + # Catch a broken release config here rather than halfway through a tag. + - name: Validate release config + uses: goreleaser/goreleaser-action@v6 + with: + version: '~> v2' + args: check + - name: Build snapshot + uses: goreleaser/goreleaser-action@v6 + with: + version: '~> v2' + args: release --snapshot --clean --skip=publish + env: + HOMEBREW_TAP_GITHUB_TOKEN: '' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bd4b6a6..6040273 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,9 @@ on: push: tags: [ '*' ] +permissions: + contents: write + jobs: lint: runs-on: ubuntu-24.04 @@ -21,18 +24,27 @@ jobs: steps: - name: Check out code 🛒 uses: actions/checkout@v7 + with: + # GoReleaser needs full history and tags to resolve the version. + fetch-depth: 0 + + - name: Set up Go 🐹 + uses: actions/setup-go@v7 + with: + go-version: '1.26' - name: Generate Release Notes 📝 - if: github.ref_type == 'tag' run: | CHANGE=$(sed "/${GITHUB_REF_NAME%%-*}/,/##/!d;//d" CHANGELOG.md | awk 'NF') echo "$CHANGE" > changelog.txt - RELEASE_TYPE=$(if [[ "$GITHUB_REF_NAME" =~ "beta" ]]; then echo "true"; else echo "false"; fi) - echo "RELEASE_TYPE=${RELEASE_TYPE}" >> $GITHUB_ENV - - name: Create Release 🎉 - uses: ncipollo/release-action@v1 - if: github.ref_type == 'tag' + - name: Run GoReleaser 🚀 + uses: goreleaser/goreleaser-action@v6 with: - bodyFile: "changelog.txt" - prerelease: ${{ env.RELEASE_TYPE }} + version: '~> v2' + args: release --clean --release-notes=changelog.txt + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Needs write access to radiusmethod/homebrew-awsd. Without it the + # release still publishes; only the tap update is skipped. + HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 5e16162..e35c01f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ coverage.* # vendor/ bin/ tools/gendocs/gendocs +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..8c23cc4 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,87 @@ +version: 2 + +project_name: awsd + +before: + hooks: + - go mod tidy + +builds: + - id: awsd + binary: awsd + main: . + env: + - CGO_ENABLED=0 + goos: + - darwin + - linux + - windows + goarch: + - amd64 + - arm64 + ldflags: + # version is otherwise "dev"; the tag is the source of truth for releases. + - -s -w -X github.com/radiusmethod/awsd/src/cmd.version=v{{ .Version }} + +archives: + - id: awsd + ids: + - awsd + name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + files: + - README.md + - LICENSE + - CHANGELOG.md + format_overrides: + - goos: windows + formats: + - zip + +checksum: + name_template: "checksums.txt" + +changelog: + # Release notes come from CHANGELOG.md via --release-notes in the workflow. + disable: true + +release: + prerelease: auto + +homebrew_casks: + - name: awsd + ids: + - awsd + repository: + owner: radiusmethod + name: homebrew-awsd + branch: main + token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" + directory: Casks + homepage: "https://github.com/radiusmethod/awsd" + description: "AWS Profile Switcher in Go" + license: "MIT" + # Ship binaries even when the tap token is not configured, rather than + # failing the whole release. The workflow always defines the variable, so + # this is empty rather than missing when the secret is absent. + skip_upload: '{{ if .Env.HOMEBREW_TAP_GITHUB_TOKEN }}false{{ else }}true{{ end }}' + hooks: + post: + # The binaries are unsigned, so macOS quarantines them on download and + # Gatekeeper refuses to run them. Strip the attribute on install. + install: | + if system_command("/usr/bin/xattr", args: ["-h"]).exit_status == 0 + system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/awsd"] + end + caveats: | + To finish setting up awsd, add the line for your shell to its startup file: + + zsh eval "$(awsd init zsh)" + bash eval "$(awsd init bash)" + fish awsd init fish | source + PowerShell awsd init powershell | Out-String | Invoke-Expression + + Then open a new terminal, or source that file. + + Upgrading from before v0.4.0? Remove any 'alias awsd="source _awsd"' line + from your shell config, and delete leftover _awsd_prompt, _awsd, and + _awsd_autocomplete files from earlier manual installs. diff --git a/CHANGELOG.md b/CHANGELOG.md index b7501a2..744e5ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## v0.4.0 (August 5, 2026) + +**Breaking.** awsd is now a single binary named `awsd`. Pre-1.0, so no compatibility shims. + +* The binary is installed as `awsd`, not `_awsd_prompt`. The generated integration calls it through `command awsd`, which skips the shell function of the same name. +* Removed `scripts/_awsd`, `scripts/_awsd_autocomplete`, and `scripts/powershell/awsd.ps1`. `make install` now installs one file. +* Removed the `alias awsd="source _awsd"` setup path. Use `eval "$(awsd init )"`; see "Upgrading from pre-v0.4.0" in the README. +* The PowerShell integration resolves the binary with `Get-Command` at load time, since `&` does not bypass a same-named function the way POSIX `command` does. +* `Makefile_Windows` builds `awsd.exe` and no longer references the deleted PowerShell script. +* Docker image ships and runs `awsd`. + +Releases are now built by GoReleaser: + +* Releases ship prebuilt binaries for macOS, Linux, and Windows on amd64 and arm64, with `checksums.txt`. Installing no longer compiles from source. +* Homebrew distribution moves from a formula to a **cask**, generated on each tag. Reinstall with `brew uninstall awsd && brew install radiusmethod/awsd/awsd` if brew complains about the change. +* `awsd version` now reports the git tag, injected at build time. `make install` derives it from `git describe`, and a plain `go build` reports `dev`. +* Prerelease tags must now be SemVer-hyphenated (`v0.5.0-beta1`), not `v0.5.0beta`. + +To upgrade: replace your shell config line, then delete any leftover `_awsd_prompt`, `_awsd`, and `_awsd_autocomplete` files from earlier manual installs. Your `~/.awsd` file is unchanged and carries over. + ## v0.3.0 (August 5, 2026) * Added `awsd init ` — one line in your rc file (`eval "$(awsd init zsh)"`) now replaces the `awsd` alias, the completion `source`, and the persistence snippet from the README. [#53] * Added fish support, and PowerShell tab completion. diff --git a/Dockerfile b/Dockerfile index b67887d..0e18485 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,18 +7,16 @@ RUN go mod download COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o _awsd_prompt +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o awsd FROM alpine:3.24 RUN adduser -D -u 1000 awsd -COPY --from=builder /app/_awsd_prompt /usr/local/bin/_awsd_prompt -COPY scripts/_awsd /usr/local/bin/_awsd -COPY scripts/_awsd_autocomplete /usr/local/bin/_awsd_autocomplete +COPY --from=builder /app/awsd /usr/local/bin/awsd USER awsd WORKDIR /home/awsd -ENTRYPOINT ["_awsd_prompt"] +ENTRYPOINT ["awsd"] # docker run -it -v ~/.aws:/home/awsd/.aws:ro -v ~/.awsd:/home/awsd/.awsd awsd diff --git a/Makefile b/Makefile index d28c737..3d3441e 100644 --- a/Makefile +++ b/Makefile @@ -1,28 +1,26 @@ BINDIR = /usr/local/bin +VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) +LDFLAGS = -s -w -X github.com/radiusmethod/awsd/src/cmd.version=$(VERSION) help: ## Show this help @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//' install: ## Install Target - GOOS= GOARCH= GOARM= GOFLAGS= go build -o ${BINDIR}/_awsd_prompt - cp scripts/_awsd ${BINDIR}/_awsd - cp scripts/_awsd_autocomplete ${BINDIR}/_awsd_autocomplete - @echo " -=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- " - @echo " " - @echo " To Finish Installation add " - @echo " " - @echo " eval \"\$$(_awsd_prompt init zsh)\" " - @echo " " - @echo " to your zshrc (or bash profile, " - @echo " with 'init bash') then open a new " - @echo " terminal or source that file " - @echo " " - @echo " -=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- " + GOOS= GOARCH= GOARM= GOFLAGS= go build -ldflags="$(LDFLAGS)" -o ${BINDIR}/awsd + @echo " -=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=- " + @echo " " + @echo " To Finish Installation add " + @echo " " + @echo " eval \"\$$(awsd init zsh)\" " + @echo " " + @echo " to your zshrc (or bash profile, " + @echo " with 'init bash') then open a new " + @echo " terminal or source that file " + @echo " " + @echo " -=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=- " uninstall: ## Uninstall Target - rm -f ${BINDIR}/_awsd - rm -f ${BINDIR}/_awsd_autocomplete - rm -f ${BINDIR}/_awsd_prompt + rm -f ${BINDIR}/awsd .PHONY: test test-coverage docs test: ## Run tests diff --git a/Makefile_Windows b/Makefile_Windows index ce9c730..9728e84 100644 --- a/Makefile_Windows +++ b/Makefile_Windows @@ -1,9 +1,7 @@ -BINDIR = C:\tools +BINDIR = C:\tools\awsd install: ## Install Target - go build -o ${BINDIR}/awsd/awsd_prompt - cp scripts/powrshell/awsd.ps1 ${BINDIR}/awsd/awsd.ps1 - New-Item ~/.awsd + go build -o ${BINDIR}\awsd.exe uninstall: ## Uninstall Target - rm -Force ${BINDIR}/awsd + rm -Force ${BINDIR}\awsd.exe diff --git a/README.md b/README.md index e239849..1965930 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,11 @@ awsd is a command-line utility that allows you to easily switch between AWS Prof - [Installation](#installation) - [Homebrew](#homebrew) + - [Prebuilt binary](#prebuilt-binary) - [Makefile](#makefile) - [To Finish Installation](#to-finish-installation) - [Upgrading](#upgrading) - - [Upgrading from pre-v0.3.0](#upgrading-from-pre-v030) + - [Upgrading from pre-v0.4.0](#upgrading-from-pre-v040) - [Usage](#usage) - [Switching AWS Profiles](#switching-aws-profiles) - [Switching AWS Regions](#switching-aws-regions) @@ -27,8 +28,6 @@ awsd is a command-line utility that allows you to easily switch between AWS Prof ## Installation -Make sure you have Go installed. You can download it from [here](https://golang.org/dl/). - ### Homebrew ```sh @@ -36,8 +35,16 @@ brew tap radiusmethod/awsd brew install awsd ``` +### Prebuilt binary + +Grab the archive for your platform from the +[latest release](https://github.com/radiusmethod/awsd/releases/latest), then put `awsd` somewhere +on your `PATH`. macOS, Linux, and Windows on amd64 and arm64. + ### Makefile +Builds from source, so this one needs [Go](https://golang.org/dl/) installed. + ```sh make install ``` @@ -67,11 +74,8 @@ awsd init powershell | Out-String | Invoke-Expression Ex. `echo 'eval "$(awsd init zsh)"' >> ~/.zshrc` -That one line defines the `awsd` command, sets up tab completion, and applies the profile and -region you last selected to every new shell. Nothing else to configure. - -If `awsd` isn't on your `PATH` yet (the binary installs as `_awsd_prompt`), use -`eval "$(_awsd_prompt init zsh)"` instead. +That one line defines the `awsd` shell function, sets up tab completion, and applies the profile +and region you last selected to every new shell. Nothing else to configure. ### Upgrading Upgrading consists of just doing a brew update and brew upgrade. @@ -80,29 +84,42 @@ Upgrading consists of just doing a brew update and brew upgrade. brew update && brew upgrade radiusmethod/awsd/awsd ``` -### Upgrading from pre-v0.3.0 -Before v0.3.0 you needed a hand-written alias, a separate completion `source`, and a block of -shell copied out of this README to persist your profile across shells: +### Upgrading from pre-v0.4.0 +**v0.4.0 is a breaking change.** awsd now installs one binary named `awsd`. The old +`_awsd_prompt` binary, the `_awsd` wrapper script, and the `_awsd_autocomplete` completion script +are all gone, and so is the alias-based setup. + +Delete whatever of this you have in your shell config: ```sh -alias awsd="source _awsd" # no longer needed -source _awsd_autocomplete # no longer needed -if [ -f ~/.awsd ]; then ... # no longer needed +alias awsd="source _awsd" # removed in v0.4.0 +source _awsd_autocomplete # removed in v0.4.0 +if [ -f ~/.awsd ]; then ... # removed in v0.3.0 ``` -Replace all of it with `eval "$(awsd init zsh)"`. The old alias still works for now, but it is -deprecated and will be removed in a future release. +and replace it with the single line for your shell from +[To Finish Installation](#to-finish-installation) above. -Two things to check when you upgrade: +Then clear out the old files, which a package manager will not remove for you if you ever ran +`make install` by hand: + +```sh +rm -f /usr/local/bin/_awsd_prompt /usr/local/bin/_awsd /usr/local/bin/_awsd_autocomplete +type -a _awsd_prompt # should print nothing +``` + +Two things that bite during the upgrade: - **Remove the old alias.** In zsh an alias shadows a function of the same name, so leaving `alias awsd="source _awsd"` in place means the new `awsd` function never gets used. If the alias is defined *before* the `eval` line, the eval fails outright with `defining function based on alias 'awsd'`. -- **Put the `eval` line after any `PATH` changes** that point at your awsd install. It runs - `_awsd_prompt` at startup, so if an older copy is earlier in `PATH` at that moment you get - `(eval):1: bad pattern: ^[[0`. That is a pre-v0.3.0 binary printing `Profile init does not - exist` and zsh trying to eval the color codes. `type -a _awsd_prompt` shows you every copy. +- **Put the `eval` line after any `PATH` changes** that point at your awsd install. It runs `awsd` + at startup, so if an older copy is earlier in `PATH` at that moment you can get + `(eval):1: bad pattern: ^[[0`, which is an old binary printing a colored warning that your shell + then tries to eval. `type -a awsd` shows you every copy. + +Your `~/.awsd` file carries over untouched. Profile and region selections survive the upgrade. ## Usage @@ -178,11 +195,15 @@ asks it for the matching shell code and evals that: ```sh awsd() { - command _awsd_prompt "$@" || return - eval "$(command _awsd_prompt shellenv bash)" + command awsd "$@" || return + eval "$(command awsd shellenv bash)" } ``` +The function and the binary share the name `awsd`. That works because `command` skips functions +and aliases and runs the executable from `PATH`. PowerShell's `&` operator does not do this, so +the generated PowerShell integration resolves the binary path up front with `Get-Command` instead. + You can see exactly what gets eval'd at any time: ```sh diff --git a/go.sum b/go.sum index fd59e8f..c05bdd9 100644 --- a/go.sum +++ b/go.sum @@ -34,10 +34,6 @@ golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= -gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= -gopkg.in/ini.v1 v1.67.2 h1:JtOSMb9OuaCZKr7h5D/h6iii14sK0hLbplTc6frx4Ss= -gopkg.in/ini.v1 v1.67.2/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/ini.v1 v1.67.3 h1:iM9Lhz5MRSGhHVGGwCuzG9KO8PoirCXj/m/qTmOJJQw= gopkg.in/ini.v1 v1.67.3/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/scripts/_awsd b/scripts/_awsd deleted file mode 100755 index 3ddec23..0000000 --- a/scripts/_awsd +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -# -# Deprecated. Prefer the generated integration, which also wires up completion -# and picks up the active profile in new shells: -# -# eval "$(awsd init bash)" # or: awsd init zsh -# -# This file is kept so existing `alias awsd="source _awsd"` setups keep -# working. It must be sourced, not executed. The ~/.awsd parsing that used to -# live here now lives in the binary, behind `_awsd_prompt shellenv`. - -if _awsd_prompt "$@"; then - eval "$(_awsd_prompt shellenv bash)" -fi diff --git a/scripts/_awsd_autocomplete b/scripts/_awsd_autocomplete deleted file mode 100755 index 54f9f53..0000000 --- a/scripts/_awsd_autocomplete +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -[ "$BASH_VERSION" ] && AWSD_CMD="awsd" || AWSD_CMD="_awsd" -_awsd_completion() { - local cur=${COMP_WORDS[COMP_CWORD]} - local prev=${COMP_WORDS[COMP_CWORD-1]} - local subcmd=${COMP_WORDS[1]} - local suggestions - - case "$COMP_CWORD" in - 1) - # awsd — profiles + top-level subcommands - suggestions="$(awsd list profiles) list set unset" - ;; - 2) - case "$subcmd" in - set|unset) suggestions="profile region" ;; - list|l) suggestions="profiles regions" ;; - *) suggestions="" ;; - esac - ;; - 3) - case "$subcmd $prev" in - "set profile") suggestions="$(awsd list profiles)" ;; - "set region") suggestions="$(awsd list regions)" ;; - *) suggestions="" ;; - esac - ;; - *) - suggestions="" - ;; - esac - - COMPREPLY=($(compgen -W "$suggestions" -- $cur)) - return 0 -} - -# complete is a bash builtin, but recent versions of ZSH come with a function -# called bashcompinit that will create a complete in ZSH. If the user is in -# ZSH, load and run bashcompinit before calling the complete function. -if [[ -n ${ZSH_VERSION-} ]]; then - autoload -U +X bashcompinit && bashcompinit -fi - -complete -o nospace -F _awsd_completion "${AWSD_CMD}" diff --git a/scripts/powershell/awsd.ps1 b/scripts/powershell/awsd.ps1 deleted file mode 100644 index 43ee55b..0000000 --- a/scripts/powershell/awsd.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -# Deprecated. Prefer the generated integration, which also wires up completion -# and picks up the active profile in new shells. Add to your $PROFILE: -# -# awsd init powershell | Out-String | Invoke-Expression -# -# This file is kept so existing setups keep working. The ~/.awsd parsing that -# used to live here now lives in the binary, behind `awsd_prompt shellenv`. - -& awsd_prompt @args -if ($LASTEXITCODE -eq 0) { - & awsd_prompt shellenv powershell | Out-String | Invoke-Expression -} diff --git a/src/cmd/version.go b/src/cmd/version.go index 7becc24..1934c9a 100644 --- a/src/cmd/version.go +++ b/src/cmd/version.go @@ -5,7 +5,13 @@ import ( "github.com/spf13/cobra" ) -var version string = "v0.3.0" +// version is injected at build time via ldflags: +// +// -X github.com/radiusmethod/awsd/src/cmd.version=v1.2.3 +// +// GoReleaser sets it from the git tag, and `make install` sets it from +// `git describe`. Plain `go build` leaves it as "dev". +var version = "dev" var versionCmd = &cobra.Command{ Use: "version", diff --git a/src/utils/shell.go b/src/utils/shell.go index 1487fa2..cd42424 100644 --- a/src/utils/shell.go +++ b/src/utils/shell.go @@ -149,8 +149,8 @@ const posixInitTemplate = `# awsd shell integration (%[1]s). Generated by: awsd # Add to your rc file with: eval "$(awsd init %[1]s)" awsd() { - command _awsd_prompt "$@" || return - eval "$(command _awsd_prompt shellenv %[1]s)" + command awsd "$@" || return + eval "$(command awsd shellenv %[1]s)" } _awsd_completion() { @@ -161,7 +161,7 @@ _awsd_completion() { case "$COMP_CWORD" in 1) - suggestions="$(command _awsd_prompt list profiles) list set unset" + suggestions="$(command awsd list profiles) list set unset" ;; 2) case "$subcmd" in @@ -172,8 +172,8 @@ _awsd_completion() { ;; 3) case "$subcmd $prev" in - "set profile") suggestions="$(command _awsd_prompt list profiles)" ;; - "set region") suggestions="$(command _awsd_prompt list regions)" ;; + "set profile") suggestions="$(command awsd list profiles)" ;; + "set region") suggestions="$(command awsd list regions)" ;; *) suggestions="" ;; esac ;; @@ -189,37 +189,47 @@ _awsd_completion() { %[2]scomplete -o nospace -F _awsd_completion awsd # Apply the profile/region already in ~/.awsd to this shell. -eval "$(command _awsd_prompt shellenv %[1]s)" +eval "$(command awsd shellenv %[1]s)" ` const fishInit = `# awsd shell integration (fish). Generated by: awsd init fish # Add to ~/.config/fish/config.fish with: awsd init fish | source function awsd - command _awsd_prompt $argv; or return - command _awsd_prompt shellenv fish | source + command awsd $argv; or return + command awsd shellenv fish | source end complete -c awsd -f -complete -c awsd -n __fish_use_subcommand -a '(command _awsd_prompt list profiles)' +complete -c awsd -n __fish_use_subcommand -a '(command awsd list profiles)' complete -c awsd -n __fish_use_subcommand -a 'list set unset' complete -c awsd -n '__fish_seen_subcommand_from set unset' -a 'profile region' complete -c awsd -n '__fish_seen_subcommand_from list l' -a 'profiles regions' -complete -c awsd -n '__fish_seen_subcommand_from profile' -a '(command _awsd_prompt list profiles)' -complete -c awsd -n '__fish_seen_subcommand_from region' -a '(command _awsd_prompt list regions)' +complete -c awsd -n '__fish_seen_subcommand_from profile' -a '(command awsd list profiles)' +complete -c awsd -n '__fish_seen_subcommand_from region' -a '(command awsd list regions)' # Apply the profile/region already in ~/.awsd to this shell. -command _awsd_prompt shellenv fish | source +command awsd shellenv fish | source ` const powershellInit = `# awsd shell integration (PowerShell). Generated by: awsd init powershell # Add to your $PROFILE with: awsd init powershell | Out-String | Invoke-Expression +# Resolve the binary once, before the function below shadows the name. Unlike +# POSIX "command", PowerShell's & operator does not skip functions, so calling +# "& awsd" inside function awsd would recurse forever. +$global:AwsdBin = (Get-Command -CommandType Application awsd -ErrorAction SilentlyContinue | + Select-Object -First 1).Source +if (-not $global:AwsdBin) { + Write-Error "awsd: could not find the awsd binary on PATH" + return +} + function awsd { param([Parameter(ValueFromRemainingArguments = $true)] [string[]] $Arguments) - & _awsd_prompt @Arguments + & $global:AwsdBin @Arguments if ($LASTEXITCODE -ne 0) { return } - & _awsd_prompt shellenv powershell | Out-String | Invoke-Expression + & $global:AwsdBin shellenv powershell | Out-String | Invoke-Expression } Register-ArgumentCompleter -CommandName awsd -ParameterName Arguments -ScriptBlock { @@ -230,13 +240,13 @@ Register-ArgumentCompleter -CommandName awsd -ParameterName Arguments -ScriptBlo $suggestions = @() if ($words.Count -eq 0) { - $suggestions = @(& _awsd_prompt list profiles) + @('list', 'set', 'unset') + $suggestions = @(& $global:AwsdBin list profiles) + @('list', 'set', 'unset') } elseif ($words.Count -eq 1) { if ($words[0] -in @('set', 'unset')) { $suggestions = @('profile', 'region') } elseif ($words[0] -in @('list', 'l')) { $suggestions = @('profiles', 'regions') } } elseif ($words.Count -eq 2 -and $words[0] -eq 'set') { - if ($words[1] -eq 'profile') { $suggestions = @(& _awsd_prompt list profiles) } - elseif ($words[1] -eq 'region') { $suggestions = @(& _awsd_prompt list regions) } + if ($words[1] -eq 'profile') { $suggestions = @(& $global:AwsdBin list profiles) } + elseif ($words[1] -eq 'region') { $suggestions = @(& $global:AwsdBin list regions) } } $suggestions | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { @@ -245,5 +255,5 @@ Register-ArgumentCompleter -CommandName awsd -ParameterName Arguments -ScriptBlo } # Apply the profile/region already in ~/.awsd to this shell. -& _awsd_prompt shellenv powershell | Out-String | Invoke-Expression +& $global:AwsdBin shellenv powershell | Out-String | Invoke-Expression ` diff --git a/src/utils/shell_test.go b/src/utils/shell_test.go index 6ad2389..06e318c 100644 --- a/src/utils/shell_test.go +++ b/src/utils/shell_test.go @@ -166,22 +166,22 @@ func TestInitScript(t *testing.T) { { name: "bash", shell: Bash, - contains: []string{"awsd() {", `eval "$(command _awsd_prompt shellenv bash)"`, "complete -o nospace -F _awsd_completion awsd"}, + contains: []string{"awsd() {", `eval "$(command awsd shellenv bash)"`, "complete -o nospace -F _awsd_completion awsd"}, }, { name: "zsh", shell: Zsh, - contains: []string{"awsd() {", `eval "$(command _awsd_prompt shellenv zsh)"`, "bashcompinit"}, + contains: []string{"awsd() {", `eval "$(command awsd shellenv zsh)"`, "bashcompinit"}, }, { name: "fish", shell: Fish, - contains: []string{"function awsd", "command _awsd_prompt shellenv fish | source", "complete -c awsd"}, + contains: []string{"function awsd", "command awsd shellenv fish | source", "complete -c awsd"}, }, { name: "powershell", shell: PowerShell, - contains: []string{"function awsd", "& _awsd_prompt shellenv powershell | Out-String | Invoke-Expression", "Register-ArgumentCompleter"}, + contains: []string{"function awsd", "& $global:AwsdBin shellenv powershell | Out-String | Invoke-Expression", "Register-ArgumentCompleter"}, }, }