├── .editorconfig ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── build.yml │ ├── release.yml │ └── release_package.yml ├── .gitignore ├── .go-version ├── .goreleaser.yml ├── .mergify.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── bin ├── release └── shims │ ├── go │ └── gofmt ├── cache └── .gitignore ├── cmd ├── config.go ├── exec.go ├── global.go ├── info.go ├── init.go ├── install.go ├── license.go ├── local.go ├── rehash.go ├── root.go ├── satisfy.go ├── uninstall.go ├── version.go └── versions.go ├── config.toml ├── core ├── module │ ├── charm_select.go │ ├── charm_spinner.go │ ├── const.go │ ├── golang.go │ └── golang_test.go ├── service │ ├── file_system.go │ ├── file_system_test.go │ ├── installer.go │ └── installer_test.go └── util │ ├── helpers.go │ └── helpers_test.go ├── go.mod ├── go.sum ├── goenv.go ├── pkg └── loader.go ├── renovate.json ├── static ├── logo.png ├── logo1.png └── screenshot-1.png ├── techstack.md └── techstack.yml /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Docs: https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 2 | * @clivern 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: # clivern 2 | custom: clivern.com/sponsor/ 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | **Describe the bug** 7 | A clear and concise description of what the bug is. 8 | 9 | **Development or production environment** 10 | 11 | - OS: [e.g. Ubuntu 18.04] 12 | - Go 1.13 13 | 14 | **Additional context** 15 | Add any other context about the problem here. 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | --- 5 | 6 | **Is your feature request related to a problem? Please describe.** 7 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 8 | 9 | **Describe the solution you'd like** 10 | A clear and concise description of what you want to happen. 11 | 12 | **Additional context** 13 | Add any other context or screenshots about the feature request here. 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | go: ["1.18", "1.18.1", "1.19", "1.20", "1.21", "1.22", "1.23"] 14 | name: Go ${{ matrix.go }} run 15 | steps: 16 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 17 | - name: Setup go 18 | uses: actions/setup-go@v5 19 | with: 20 | go-version: ${{ matrix.go }} 21 | 22 | - name: Get dependencies 23 | run: | 24 | export PATH=${PATH}:`go env GOPATH`/bin 25 | make install_revive 26 | 27 | - name: Run make ci 28 | run: | 29 | export PATH=${PATH}:`go env GOPATH`/bin 30 | go get -t . 31 | make ci 32 | make integration 33 | git status 34 | git diff > diff.log 35 | cat diff.log 36 | git clean -fd 37 | git reset --hard 38 | make verify 39 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | with: 15 | fetch-depth: 0 16 | - name: Set up Go 17 | uses: actions/setup-go@v5 18 | with: 19 | go-version: 1.19 20 | - name: Run GoReleaser 21 | uses: goreleaser/goreleaser-action@v5 22 | with: 23 | version: latest 24 | args: release --rm-dist 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} 28 | -------------------------------------------------------------------------------- /.github/workflows/release_package.yml: -------------------------------------------------------------------------------- 1 | name: Release Package 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | with: 15 | fetch-depth: 0 16 | - name: Set up Go 17 | uses: actions/setup-go@v5 18 | with: 19 | go-version: 1.19 20 | 21 | - name: Update checksum database 22 | run: | 23 | ./bin/release 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # dist dir 15 | dist 16 | 17 | !web/dist 18 | 19 | sync 20 | 21 | *.db 22 | 23 | config.test.yml 24 | coverage.html -------------------------------------------------------------------------------- /.go-version: -------------------------------------------------------------------------------- 1 | 1.18 2 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example .goreleaser.yml file with some sensible defaults. 2 | # Make sure to check the documentation at https://goreleaser.com 3 | before: 4 | hooks: 5 | - "go mod download" 6 | - "go mod tidy" 7 | - "go generate ./..." 8 | builds: 9 | - env: 10 | - CGO_ENABLED=0 11 | goos: 12 | - linux 13 | - windows 14 | - darwin 15 | 16 | archives: 17 | - format: tar.gz 18 | # this name template makes the OS and Arch compatible with the results of uname. 19 | name_template: >- 20 | {{ .ProjectName }}_ 21 | {{- title .Os }}_ 22 | {{- if eq .Arch "amd64" }}x86_64 23 | {{- else if eq .Arch "386" }}i386 24 | {{- else }}{{ .Arch }}{{ end }} 25 | {{- if .Arm }}v{{ .Arm }}{{ end }} 26 | # use zip for windows archives 27 | format_overrides: 28 | - goos: windows 29 | format: zip 30 | files: 31 | - LICENSE 32 | - README.md 33 | checksum: 34 | name_template: 'checksums.txt' 35 | snapshot: 36 | name_template: "{{ incpatch .Version }}-next" 37 | changelog: 38 | sort: asc 39 | filters: 40 | exclude: 41 | - '^docs:' 42 | - '^test:' 43 | project_name: goenv 44 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | pull_request_rules: 3 | - actions: 4 | merge: 5 | method: squash 6 | conditions: 7 | - author!=Clivern 8 | - approved-reviews-by=Clivern 9 | - label=release 10 | name: "Automatic Approved Merge 🚀" 11 | - actions: 12 | merge: 13 | method: merge 14 | conditions: 15 | - author=Clivern 16 | - label=release 17 | name: "Automatic Merge 🚀" 18 | - actions: 19 | merge: 20 | method: squash 21 | conditions: 22 | - "author=renovate[bot]" 23 | - label=release 24 | name: "Automatic Merge for Renovate PRs 🚀" 25 | - actions: 26 | comment: 27 | message: "Nice! PR merged successfully." 28 | conditions: 29 | - merged 30 | name: "Merge Done 🚀" 31 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@clivern.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | - With issues: 4 | - Use the search tool before opening a new issue. 5 | - Please provide source code and commit sha if you found a bug. 6 | - Review existing issues and provide feedback or react to them. 7 | 8 | - With pull requests: 9 | - Open your pull request against `main` 10 | - Your pull request should have no more than two commits, if not you should squash them. 11 | - It should pass all tests in the available continuous integrations systems such as github actions. 12 | - You should add/modify tests to cover your proposed code changes. 13 | - If your pull request contains a new feature, please document it on the README. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Clivern 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | go ?= go 2 | gofmt ?= $(go)fmt 3 | pkgs = ./... 4 | 5 | 6 | help: Makefile 7 | @echo 8 | @echo " Choose a command run in Goenv:" 9 | @echo 10 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 11 | @echo 12 | 13 | 14 | ## install_revive: Install revive for linting. 15 | .PHONY: install_revive 16 | install_revive: 17 | @echo ">> ============= Install Revive ============= <<" 18 | $(go) install github.com/mgechev/revive@v1.3.9 19 | 20 | 21 | ## style: Check code style. 22 | .PHONY: style 23 | style: 24 | @echo ">> ============= Checking Code Style ============= <<" 25 | @fmtRes=$$($(gofmt) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \ 26 | if [ -n "$${fmtRes}" ]; then \ 27 | echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \ 28 | echo "Please ensure you are using $$($(go) version) for formatting code."; \ 29 | exit 1; \ 30 | fi 31 | 32 | 33 | ## check_license: Check if license header on all files. 34 | .PHONY: check_license 35 | check_license: 36 | @echo ">> ============= Checking License Header ============= <<" 37 | @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \ 38 | awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \ 39 | done); \ 40 | if [ -n "$${licRes}" ]; then \ 41 | echo "license header checking failed:"; echo "$${licRes}"; \ 42 | exit 1; \ 43 | fi 44 | 45 | 46 | ## test_short: Run test cases with short flag. 47 | .PHONY: test_short 48 | test_short: 49 | @echo ">> ============= Running Short Tests ============= <<" 50 | $(go) clean -testcache 51 | $(go) test -mod=readonly -short $(pkgs) 52 | 53 | 54 | ## test: Run test cases. 55 | .PHONY: test 56 | test: 57 | @echo ">> ============= Running All Tests ============= <<" 58 | $(go) clean -testcache 59 | $(go) test -mod=readonly -run=Unit -bench=. -benchmem -v -cover $(pkgs) 60 | 61 | 62 | ## integration: Run integration test cases (Requires etcd) 63 | .PHONY: integration 64 | integration: 65 | @echo ">> ============= Running All Tests ============= <<" 66 | $(go) clean -testcache 67 | $(go) test -mod=readonly -run=Integration -bench=. -benchmem -v -cover $(pkgs) 68 | 69 | 70 | ## lint: Lint the code. 71 | .PHONY: lint 72 | lint: 73 | @echo ">> ============= Lint All Files ============= <<" 74 | revive -config config.toml -exclude vendor/... -formatter friendly ./... 75 | 76 | 77 | ## verify: Verify dependencies 78 | .PHONY: verify 79 | verify: 80 | @echo ">> ============= List Dependencies ============= <<" 81 | $(go) list -m all 82 | @echo ">> ============= Verify Dependencies ============= <<" 83 | $(go) mod verify 84 | 85 | 86 | ## format: Format the code. 87 | .PHONY: format 88 | format: 89 | @echo ">> ============= Formatting Code ============= <<" 90 | $(go) fmt $(pkgs) 91 | 92 | 93 | ## vet: Examines source code and reports suspicious constructs. 94 | .PHONY: vet 95 | vet: 96 | @echo ">> ============= Vetting Code ============= <<" 97 | $(go) vet $(pkgs) 98 | 99 | 100 | ## coverage: Create HTML coverage report 101 | .PHONY: coverage 102 | coverage: 103 | @echo ">> ============= Coverage ============= <<" 104 | rm -f coverage.html cover.out 105 | $(go) test -mod=readonly -coverprofile=cover.out $(pkgs) 106 | go tool cover -html=cover.out -o coverage.html 107 | 108 | 109 | ## run: Run the Server 110 | .PHONY: run 111 | run: 112 | @echo ">> ============= Run API Server ============= <<" 113 | $(go) run goenv.go server -c config.dist.yml 114 | 115 | 116 | ## ci: Run all CI tests. 117 | .PHONY: ci 118 | ci: style check_license test vet lint 119 | @echo "\n==> All quality checks passed" 120 | 121 | 122 | .PHONY: help 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Goenv Logo 3 |

Goenv

4 |

Manage Your Applications Go Environment

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |

22 |

23 |
24 |

25 | 26 | 27 | 28 |

29 | 30 | Goenv helps you to work with multiple `golang` versions at the same time whether on mac or linux operating system. It supports both global and per-application version configuration. 31 | 32 | 33 | ## Usage 34 | 35 | Download [the latest `goenv` binary](https://github.com/clivern/Goenv/releases). Make it executable from everywhere. 36 | 37 | ```zsh 38 | $ export GOENV_LATEST_VERSION=$(curl --silent "https://api.github.com/repos/clivern/Goenv/releases/latest" | jq '.tag_name' | sed -E 's/.*"([^"]+)".*/\1/' | tr -d v) 39 | 40 | # For Linux 41 | $ curl -sL https://github.com/clivern/Goenv/releases/download/v{$GOENV_LATEST_VERSION}/goenv_Linux_x86_64.tar.gz | tar xz 42 | 43 | # For Mac (Arm M chips) 44 | $ curl -sL https://github.com/clivern/Goenv/releases/download/v{$GOENV_LATEST_VERSION}/goenv_Darwin_arm64.tar.gz | tar xz 45 | 46 | 47 | # For Mac (Intel) 48 | $ curl -sL https://github.com/clivern/Goenv/releases/download/v{$GOENV_LATEST_VERSION}/goenv_Darwin_x86_64.tar.gz | tar xz 49 | ``` 50 | 51 | Configure the goenv using the following command 52 | 53 | ```zsh 54 | $ goenv config 55 | ``` 56 | 57 | Add `goenv` shims to `PATH` using the following command. also append it to `~/.profile` file to make it permanent. 58 | 59 | ```zsh 60 | $ export PATH="$HOME/.goenv/shims:"$PATH 61 | 62 | # OR 63 | 64 | $ eval "$(goenv init)" 65 | ``` 66 | 67 | Install a new `go` version `1.18` and set as a global 68 | 69 | ```zsh 70 | $ goenv install 1.18 71 | $ goenv global 1.18 72 | ``` 73 | 74 | To configure a local version different from the global 75 | 76 | ```zsh 77 | $ goenv local 1.18 78 | ``` 79 | 80 | To Uninstall a version 81 | 82 | ```zsh 83 | $ goenv uninstall 1.18 84 | ``` 85 | 86 | Show the used version either from current directory or parent directories or the global version. 87 | 88 | ```zsh 89 | $ goenv version 90 | ``` 91 | 92 | To list all installed versions 93 | 94 | ```zsh 95 | $ goenv versions 96 | ``` 97 | 98 | for a list of all available commands 99 | 100 | ```zsh 101 | $ goenv --help 102 | 103 | 🐺 Manage Your Applications Go Environment. 104 | 105 | If you have any suggestions, bug reports, or annoyances please report 106 | them to our issue tracker at 107 | 108 | Usage: 109 | goenv [command] 110 | 111 | Available Commands: 112 | completion Generate the autocompletion script for the specified shell 113 | config Configure the goenv application. 114 | exec Show the current go version. 115 | global Set or show the global go version. 116 | help Help about any command 117 | info Print the goenv version 118 | init Init the import path for goenv shims. 119 | install Install a go version. 120 | license Print the license 121 | local Set or show the local application-specific go version. 122 | rehash Refresh binaries under goenv shim directory. 123 | satisfy Satisfy the current directry go version. 124 | uninstall Uninstall a specific go version. 125 | version Show the current go version. 126 | versions List installed go versions. 127 | 128 | Flags: 129 | -h, --help help for goenv 130 | 131 | Use "goenv [command] --help" for more information about a command. 132 | ``` 133 | 134 | 135 | ## Under The Hood 136 | 137 | Goenv is inspired by and works like `rbenv`. At a high level, `goenv` intercepts `Go` commands using `shim` executables injected into your `PATH`, determines which Go version has been specified by your application or globally, and passes your commands to the correct `Go` installation `bin` folder. 138 | 139 | **Understanding PATH** 140 | 141 | When you run a command like `go` or `gofmt`, your operating system searches through a list of directories to find an executable file with that name. This list of directories lives in an environment variable called `PATH`, with each directory in the list separated by a colon: 142 | 143 | ``` 144 | /usr/local/bin:/usr/bin:/bin 145 | ``` 146 | 147 | Directories in `PATH` are searched from left to right, so a matching executable in a directory at the beginning of the list takes precedence over another one at the end. In this example, the `/usr/local/bin` directory will be searched first, then `/usr/bin`, then `/bin`. 148 | 149 | **Understanding Shims** 150 | 151 | `goenv` works by inserting a directory of shims at the front of your `PATH`: 152 | 153 | ```zsh 154 | ~/.goenv/shims:/usr/local/bin:/usr/bin:/bin 155 | ``` 156 | 157 | Through a process called rehashing, `goenv` maintains shims in that directory to match every `Go` command across every installed version of `go` like `gofmt` and so on. 158 | 159 | `shims` are lightweight executables that simply pass your command to the right binary under the current go version, your operating system will do the following: 160 | 161 | 1. Search your `PATH` for an executable file named `gofmt`. 162 | 2. Find the goenv shim named `gofmt` at the beginning of your `PATH` 163 | 3. Run the shim named `gofmt`, which in turn fetch the target go version and use the `gofmt` inside `go/bin` directory. 164 | 165 | **Choosing the Go Version** 166 | 167 | When you execute a shim, `goenv` determines which Go version to use by reading it from the following sources, in this order: 168 | 169 | 1. The first `.go-version `file found by searching the current working directory and each of its parent directories until reaching the root of your filesystem. You can modify the `.go-version` file in the current working directory with the `goenv local x.x.x` command. 170 | 2. The global `$HOME/.goenv/.go-version` file. You can modify this file using the `goenv global x.x.x` command. 171 | 172 | 173 | ## Versioning 174 | 175 | For transparency into our release cycle and in striving to maintain backward compatibility, Goenv is maintained under the [Semantic Versioning guidelines](https://semver.org/) and release process is predictable and business-friendly. 176 | 177 | See the [Releases section of our GitHub project](https://github.com/clivern/goenv/releases) for changelogs for each release version of Goenv. It contains summaries of the most noteworthy changes made in each release. Also see the [Milestones section](https://github.com/clivern/goenv/milestones) for the future roadmap. 178 | 179 | 180 | ## Bug tracker 181 | 182 | If you have any suggestions, bug reports, or annoyances please report them to our issue tracker at https://github.com/clivern/goenv/issues 183 | 184 | 185 | ## Security Issues 186 | 187 | If you discover a security vulnerability within Goenv, please send an email to [hello@clivern.com](mailto:hello@clivern.com) 188 | 189 | 190 | ## Contributing 191 | 192 | We are an open source, community-driven project so please feel free to join us. see the [contributing guidelines](CONTRIBUTING.md) for more details. 193 | 194 | 195 | ## License 196 | 197 | © 2022, Clivern. Released under [MIT License](https://opensource.org/licenses/mit-license.php). 198 | 199 | **Goenv** is authored and maintained by [@clivern](http://github.com/clivern). 200 | -------------------------------------------------------------------------------- /bin/release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fetch latest version 4 | export LATEST_VERSION=$(curl --silent "https://api.github.com/repos/clivern/goenv/releases/latest" | jq '.tag_name' | sed -E 's/.*"([^"]+)".*/\1/') 5 | 6 | # Update go checksum database (sum.golang.org) immediately after release 7 | curl --silent https://sum.golang.org/lookup/github.com/clivern/goenv@{$LATEST_VERSION} 8 | -------------------------------------------------------------------------------- /bin/shims/go: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GO_BINARY_NAME=go 4 | 5 | GO_VERSION_PATH=$(goenv exec) 6 | 7 | GOENV_ROOT="$HOME/.goenv" \ 8 | GOPATH=$GO_VERSION_PATH \ 9 | GOBIN=$GO_VERSION_PATH/bin \ 10 | "$GO_VERSION_PATH/bin/$GO_BINARY_NAME" "$@" 11 | 12 | if [[ "$@" =~ 'get' ]] 13 | then 14 | goenv rehash 15 | fi 16 | 17 | if [[ "$@" =~ 'install' ]] 18 | then 19 | goenv rehash 20 | fi 21 | -------------------------------------------------------------------------------- /bin/shims/gofmt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GO_BINARY_NAME=gofmt 4 | 5 | GO_VERSION_PATH=$(goenv exec) 6 | 7 | GOENV_ROOT="$HOME/.goenv" \ 8 | GOPATH=$GO_VERSION_PATH \ 9 | GOBIN=$GO_VERSION_PATH/bin \ 10 | "$GO_VERSION_PATH/bin/$GO_BINARY_NAME" "$@" 11 | -------------------------------------------------------------------------------- /cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /cmd/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package cmd package 6 | package cmd 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | 12 | "github.com/clivern/goenv/core/module" 13 | 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | var configCmd = &cobra.Command{ 18 | Use: "config", 19 | Short: "Configure the goenv application.", 20 | Run: func(cmd *cobra.Command, args []string) { 21 | 22 | if HOME == "" { 23 | fmt.Println("Error! `HOME` environment variable is not set") 24 | os.Exit(1) 25 | } 26 | 27 | golang := module.NewGolangEnvironment(HOME) 28 | 29 | err := golang.Configure() 30 | 31 | if err != nil { 32 | fmt.Println(err.Error()) 33 | os.Exit(1) 34 | } 35 | 36 | err = golang.Rehash() 37 | 38 | if err != nil { 39 | fmt.Println(err.Error()) 40 | os.Exit(1) 41 | } 42 | 43 | fmt.Println("Goenv configured successfully!") 44 | }, 45 | } 46 | 47 | func init() { 48 | rootCmd.AddCommand(configCmd) 49 | } 50 | -------------------------------------------------------------------------------- /cmd/exec.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/clivern/goenv/core/module" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var execCmd = &cobra.Command{ 17 | Use: "exec", 18 | Short: "Show the current go version.", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | // Get local version path 22 | golang := module.NewGolangEnvironment(HOME) 23 | 24 | cdir, err := os.Getwd() 25 | 26 | if err != nil { 27 | fmt.Println(err.Error()) 28 | os.Exit(1) 29 | } 30 | 31 | version, err := golang.GetLocalVersion(cdir) 32 | 33 | if err == nil { 34 | fmt.Printf("%s/%s/%s", golang.RootPath, golang.VersionsDir, version) 35 | return 36 | } 37 | 38 | // Get global version path 39 | version, err = golang.GetGlobalVersion() 40 | 41 | if err == nil { 42 | fmt.Printf("%s/%s/%s", golang.RootPath, golang.VersionsDir, version) 43 | return 44 | } 45 | 46 | fmt.Println(err.Error()) 47 | os.Exit(1) 48 | }, 49 | } 50 | 51 | func init() { 52 | rootCmd.AddCommand(execCmd) 53 | } 54 | -------------------------------------------------------------------------------- /cmd/global.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/clivern/goenv/core/module" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var globalCmd = &cobra.Command{ 17 | Use: "global", 18 | Short: "Set or show the global go version.", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | golang := module.NewGolangEnvironment(HOME) 22 | 23 | if len(args) == 1 { 24 | if !golang.ValidateVersion(args[0]) { 25 | fmt.Printf("Error! Invalid version provided %s\n", args[0]) 26 | os.Exit(1) 27 | } 28 | 29 | // Set the global version 30 | err := golang.SetGlobalVersion(args[0]) 31 | 32 | if err != nil { 33 | fmt.Println(err.Error()) 34 | os.Exit(1) 35 | } 36 | 37 | fmt.Printf("Global version updated into %s\n", args[0]) 38 | return 39 | } 40 | 41 | // Get the global version 42 | version, err := golang.GetGlobalVersion() 43 | 44 | if err != nil { 45 | fmt.Println(err.Error()) 46 | os.Exit(1) 47 | } 48 | 49 | fmt.Println(version) 50 | }, 51 | } 52 | 53 | func init() { 54 | rootCmd.AddCommand(globalCmd) 55 | } 56 | -------------------------------------------------------------------------------- /cmd/info.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var ( 14 | // Version buildinfo item 15 | Version = "dev" 16 | // Commit buildinfo item 17 | Commit = "none" 18 | // Date buildinfo item 19 | Date = "unknown" 20 | // BuiltBy buildinfo item 21 | BuiltBy = "unknown" 22 | // HOME the home path 23 | HOME = "" 24 | ) 25 | 26 | var infoCmd = &cobra.Command{ 27 | Use: "info", 28 | Short: "Print the goenv version", 29 | Run: func(cmd *cobra.Command, args []string) { 30 | fmt.Println( 31 | fmt.Sprintf( 32 | `Current Goenv Version %v Commit %v, Built @%v By %v.`, 33 | Version, 34 | Commit, 35 | Date, 36 | BuiltBy, 37 | ), 38 | ) 39 | }, 40 | } 41 | 42 | func init() { 43 | rootCmd.AddCommand(infoCmd) 44 | } 45 | -------------------------------------------------------------------------------- /cmd/init.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/clivern/goenv/core/module" 11 | 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | var initCmd = &cobra.Command{ 16 | Use: "init", 17 | Short: "Init the import path for goenv shims.", 18 | Run: func(cmd *cobra.Command, args []string) { 19 | 20 | if HOME == "" { 21 | return 22 | } 23 | 24 | golang := module.NewGolangEnvironment(HOME) 25 | 26 | fmt.Printf("export PATH=\"%s/%s:${PATH}\"\n", golang.RootPath, golang.ShimsDir) 27 | }, 28 | } 29 | 30 | func init() { 31 | rootCmd.AddCommand(initCmd) 32 | } 33 | -------------------------------------------------------------------------------- /cmd/install.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/clivern/goenv/core/module" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var installCmd = &cobra.Command{ 17 | Use: "install", 18 | Short: "Install a go version.", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | golang := module.NewGolangEnvironment(HOME) 22 | 23 | if len(args) == 0 { 24 | // Show the go releases select box 25 | list := module.NewCharmSelect( 26 | "Which go version to install?", 27 | module.GolangReleases, 28 | ) 29 | 30 | if err := list.Start(); err != nil { 31 | fmt.Printf("Error showing releases list: %s\n", err.Error()) 32 | os.Exit(1) 33 | } 34 | } else { 35 | module.SelectedValue = args[0] 36 | } 37 | 38 | if !golang.ValidateVersion(module.SelectedValue) { 39 | fmt.Printf("Error! Invalid version provided %s\n", module.SelectedValue) 40 | os.Exit(1) 41 | } 42 | 43 | isInstalled, err := golang.ValidateInstalledVersion(module.SelectedValue) 44 | 45 | if err != nil { 46 | fmt.Println(err.Error()) 47 | os.Exit(1) 48 | } 49 | 50 | if isInstalled { 51 | fmt.Printf("Error! Version %s is already installed\n", module.SelectedValue) 52 | os.Exit(1) 53 | } 54 | 55 | // Show a loading spinner 56 | spinner := module.NewCharmSpinner(fmt.Sprintf( 57 | "Getting Go v%s Environment Ready", 58 | module.SelectedValue, 59 | )) 60 | 61 | go func() { 62 | // Download and install go selected version 63 | err = golang.Install(module.SelectedValue) 64 | 65 | if err != nil { 66 | fmt.Println(err.Error()) 67 | } 68 | 69 | spinner.Quit() 70 | }() 71 | 72 | // Start the spinner 73 | if err := spinner.Start(); err != nil { 74 | fmt.Printf("Error showing loading bar: %s\n", err.Error()) 75 | os.Exit(1) 76 | } 77 | }, 78 | } 79 | 80 | func init() { 81 | rootCmd.AddCommand(installCmd) 82 | } 83 | -------------------------------------------------------------------------------- /cmd/license.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | var licenseCmd = &cobra.Command{ 14 | Use: "license", 15 | Short: "Print the license", 16 | Run: func(cmd *cobra.Command, args []string) { 17 | fmt.Println(`MIT License 18 | 19 | Copyright (c) 2022 Clivern 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE.`) 38 | }, 39 | } 40 | 41 | func init() { 42 | rootCmd.AddCommand(licenseCmd) 43 | } 44 | -------------------------------------------------------------------------------- /cmd/local.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/clivern/goenv/core/module" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var localCmd = &cobra.Command{ 17 | Use: "local", 18 | Short: "Set or show the local application-specific go version.", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | golang := module.NewGolangEnvironment(HOME) 22 | 23 | if len(args) == 1 { 24 | 25 | if !golang.ValidateVersion(args[0]) { 26 | fmt.Printf("Error! Invalid version provided %s\n", args[0]) 27 | os.Exit(1) 28 | } 29 | 30 | // Set the local version 31 | err := golang.SetLocalVersion(args[0]) 32 | 33 | if err != nil { 34 | fmt.Println(err.Error()) 35 | os.Exit(1) 36 | } 37 | 38 | fmt.Printf("Local version updated into %s\n", args[0]) 39 | return 40 | } 41 | 42 | cdir, err := os.Getwd() 43 | 44 | if err != nil { 45 | fmt.Println(err.Error()) 46 | os.Exit(1) 47 | } 48 | 49 | version, err := golang.GetLocalVersion(cdir) 50 | 51 | if err == nil { 52 | fmt.Println(version) 53 | return 54 | } 55 | 56 | fmt.Println("Unable to find local version, fallback into global version") 57 | 58 | version, err = golang.GetGlobalVersion() 59 | 60 | if err == nil { 61 | fmt.Println(version) 62 | return 63 | } 64 | 65 | fmt.Println(err.Error()) 66 | os.Exit(1) 67 | }, 68 | } 69 | 70 | func init() { 71 | rootCmd.AddCommand(localCmd) 72 | } 73 | -------------------------------------------------------------------------------- /cmd/rehash.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/clivern/goenv/core/module" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var rehashCmd = &cobra.Command{ 17 | Use: "rehash", 18 | Short: "Refresh binaries under goenv shim directory.", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | if HOME == "" { 22 | fmt.Println("Error! `HOME` environment variable is not set") 23 | os.Exit(1) 24 | } 25 | 26 | golang := module.NewGolangEnvironment(HOME) 27 | 28 | err := golang.Configure() 29 | 30 | if err != nil { 31 | fmt.Println(err.Error()) 32 | os.Exit(1) 33 | } 34 | 35 | err = golang.Rehash() 36 | 37 | if err != nil { 38 | fmt.Println(err.Error()) 39 | os.Exit(1) 40 | } 41 | }, 42 | } 43 | 44 | func init() { 45 | rootCmd.AddCommand(rehashCmd) 46 | } 47 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var config string 15 | 16 | var rootCmd = &cobra.Command{ 17 | Use: "goenv", 18 | Short: `🐺 Manage Your Applications Go Environment. 19 | 20 | If you have any suggestions, bug reports, or annoyances please report 21 | them to our issue tracker at `, 22 | } 23 | 24 | // Execute runs cmd tool 25 | func Execute() { 26 | if err := rootCmd.Execute(); err != nil { 27 | fmt.Println(err) 28 | os.Exit(1) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cmd/satisfy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/clivern/goenv/core/module" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var satisfyCmd = &cobra.Command{ 17 | Use: "satisfy", 18 | Short: "Satisfy the current directry go version.", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | golang := module.NewGolangEnvironment(HOME) 22 | 23 | cdir, err := os.Getwd() 24 | 25 | if err != nil { 26 | fmt.Println(err.Error()) 27 | os.Exit(1) 28 | } 29 | 30 | version, err := golang.GetLocalVersion(cdir) 31 | 32 | if err == nil { 33 | // Validate version 34 | if !golang.ValidateVersion(version) { 35 | fmt.Printf("Error! Invalid version detected %s\n", version) 36 | os.Exit(1) 37 | } 38 | 39 | isInstalled, err := golang.ValidateInstalledVersion(version) 40 | 41 | if err != nil { 42 | fmt.Println(err.Error()) 43 | os.Exit(1) 44 | } 45 | 46 | if isInstalled { 47 | fmt.Printf("Go Version %s is already installed\n", version) 48 | return 49 | } 50 | 51 | // Show a loading spinner 52 | spinner := module.NewCharmSpinner(fmt.Sprintf( 53 | "Getting Go v%s Environment Ready", 54 | version, 55 | )) 56 | 57 | go func() { 58 | // Download and install the go version 59 | err = golang.Install(version) 60 | 61 | if err != nil { 62 | fmt.Println(err.Error()) 63 | } 64 | 65 | spinner.Quit() 66 | }() 67 | 68 | // Start the spinner 69 | if err := spinner.Start(); err != nil { 70 | fmt.Printf("Error showing loading bar: %s\n", err.Error()) 71 | os.Exit(1) 72 | } 73 | 74 | return 75 | } 76 | 77 | version, err = golang.GetGlobalVersion() 78 | 79 | if err == nil { 80 | // Validate version 81 | if !golang.ValidateVersion(version) { 82 | fmt.Printf("Error! Invalid version detected %s\n", version) 83 | os.Exit(1) 84 | } 85 | 86 | isInstalled, err := golang.ValidateInstalledVersion(version) 87 | 88 | if err != nil { 89 | fmt.Println(err.Error()) 90 | os.Exit(1) 91 | } 92 | 93 | if isInstalled { 94 | fmt.Printf("Go Version %s is already installed\n", version) 95 | return 96 | } 97 | 98 | // Show a loading spinner 99 | spinner := module.NewCharmSpinner(fmt.Sprintf( 100 | "Getting Go v%s Environment Ready", 101 | version, 102 | )) 103 | 104 | go func() { 105 | // Download and install the go version 106 | err = golang.Install(version) 107 | 108 | if err != nil { 109 | fmt.Println(err.Error()) 110 | } 111 | 112 | spinner.Quit() 113 | }() 114 | 115 | // Start the spinner 116 | if err := spinner.Start(); err != nil { 117 | fmt.Printf("Error showing loading bar: %s\n", err.Error()) 118 | os.Exit(1) 119 | } 120 | 121 | return 122 | } 123 | 124 | fmt.Println(err.Error()) 125 | os.Exit(1) 126 | }, 127 | } 128 | 129 | func init() { 130 | rootCmd.AddCommand(satisfyCmd) 131 | } 132 | -------------------------------------------------------------------------------- /cmd/uninstall.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/clivern/goenv/core/module" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var uninstallCmd = &cobra.Command{ 17 | Use: "uninstall", 18 | Short: "Uninstall a specific go version.", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | golang := module.NewGolangEnvironment(HOME) 22 | 23 | if len(args) == 0 { 24 | // Show the go releases select box 25 | versions, err := golang.GetInstalledVersions() 26 | 27 | if err != nil { 28 | fmt.Println(err.Error()) 29 | os.Exit(1) 30 | } 31 | 32 | list := module.NewCharmSelect( 33 | "Which go version to uninstall?", 34 | versions, 35 | ) 36 | 37 | if err := list.Start(); err != nil { 38 | fmt.Printf("Error showing releases list: %s\n", err.Error()) 39 | os.Exit(1) 40 | } 41 | } else { 42 | module.SelectedValue = args[0] 43 | } 44 | 45 | if !golang.ValidateVersion(module.SelectedValue) { 46 | fmt.Printf("Error! Invalid version provided %s\n", module.SelectedValue) 47 | os.Exit(1) 48 | } 49 | 50 | isInstalled, err := golang.ValidateInstalledVersion(module.SelectedValue) 51 | 52 | if err != nil { 53 | fmt.Println(err.Error()) 54 | os.Exit(1) 55 | } 56 | 57 | if !isInstalled { 58 | fmt.Printf("Error! Version %s is not installed\n", module.SelectedValue) 59 | os.Exit(1) 60 | } 61 | 62 | // Show a loading spinner 63 | spinner := module.NewCharmSpinner(fmt.Sprintf( 64 | "Removing Go v%s Environment", 65 | module.SelectedValue, 66 | )) 67 | 68 | go func() { 69 | // Download and install go selected version 70 | err = golang.Uninstall(module.SelectedValue) 71 | 72 | if err != nil { 73 | fmt.Println(err.Error()) 74 | } 75 | 76 | spinner.Quit() 77 | }() 78 | 79 | // Start the spinner 80 | if err := spinner.Start(); err != nil { 81 | fmt.Printf("Error showing loading bar: %s\n", err.Error()) 82 | os.Exit(1) 83 | } 84 | }, 85 | } 86 | 87 | func init() { 88 | rootCmd.AddCommand(uninstallCmd) 89 | } 90 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/clivern/goenv/core/module" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var versionCmd = &cobra.Command{ 17 | Use: "version", 18 | Short: "Show the current go version.", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | golang := module.NewGolangEnvironment(HOME) 22 | 23 | cdir, err := os.Getwd() 24 | 25 | if err != nil { 26 | fmt.Println(err.Error()) 27 | os.Exit(1) 28 | } 29 | 30 | version, err := golang.GetLocalVersion(cdir) 31 | 32 | if err == nil { 33 | fmt.Println(version) 34 | return 35 | } 36 | 37 | fmt.Println("Unable to find local version, fallback into global version") 38 | 39 | version, err = golang.GetGlobalVersion() 40 | 41 | if err == nil { 42 | fmt.Println(version) 43 | return 44 | } 45 | 46 | fmt.Println(err.Error()) 47 | os.Exit(1) 48 | }, 49 | } 50 | 51 | func init() { 52 | rootCmd.AddCommand(versionCmd) 53 | } 54 | -------------------------------------------------------------------------------- /cmd/versions.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmd 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/clivern/goenv/core/module" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var versionsCmd = &cobra.Command{ 17 | Use: "versions", 18 | Short: "List installed go versions.", 19 | Run: func(cmd *cobra.Command, args []string) { 20 | 21 | golang := module.NewGolangEnvironment(HOME) 22 | 23 | versions, err := golang.GetInstalledVersions() 24 | 25 | if err != nil { 26 | fmt.Println(err.Error()) 27 | os.Exit(1) 28 | } 29 | 30 | for i := 0; i < len(versions); i++ { 31 | fmt.Println(versions[i]) 32 | } 33 | }, 34 | } 35 | 36 | func init() { 37 | rootCmd.AddCommand(versionsCmd) 38 | } 39 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | ignoreGeneratedHeader = false 2 | severity = "warning" 3 | confidence = 0.8 4 | errorCode = 0 5 | warningCode = 0 6 | 7 | [rule.blank-imports] 8 | [rule.context-as-argument] 9 | [rule.context-keys-type] 10 | [rule.dot-imports] 11 | [rule.error-return] 12 | [rule.error-strings] 13 | [rule.error-naming] 14 | [rule.exported] 15 | [rule.if-return] 16 | [rule.increment-decrement] 17 | [rule.var-naming] 18 | [rule.var-declaration] 19 | [rule.package-comments] 20 | [rule.range] 21 | [rule.receiver-naming] 22 | [rule.time-naming] 23 | [rule.unexported-return] 24 | [rule.indent-error-flow] 25 | [rule.errorf] 26 | [rule.empty-block] 27 | [rule.superfluous-else] 28 | [rule.unused-parameter] 29 | [rule.unreachable-code] 30 | [rule.redefines-builtin-id] -------------------------------------------------------------------------------- /core/module/charm_select.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package module package 6 | package module 7 | 8 | import ( 9 | "fmt" 10 | "io" 11 | 12 | "github.com/charmbracelet/bubbles/list" 13 | tea "github.com/charmbracelet/bubbletea" 14 | "github.com/charmbracelet/lipgloss" 15 | ) 16 | 17 | // Example 18 | // 19 | // ``` 20 | // import ( 21 | // "github.com/clivern/goenv/core/module" 22 | // ) 23 | // 24 | // 25 | // list := module.NewCharmSelect("Which go version to install?", module.golangReleases) 26 | // 27 | // if err := list.Start(); err != nil { 28 | // fmt.Println("Error running program:", err) 29 | // os.Exit(1) 30 | // } 31 | // 32 | // if module.SelectedValue == "" { 33 | // os.Exit(1) 34 | // } 35 | // ``` 36 | 37 | // NewCharmSelect Creates a new instance of tea.Program 38 | func NewCharmSelect(title string, versions []string) *tea.Program { 39 | items := []list.Item{} 40 | 41 | for i := 0; i < len(versions); i++ { 42 | items = append(items, item(versions[i])) 43 | } 44 | 45 | l := list.New(items, itemDelegate{}, defaultWidth, listHeight) 46 | l.Title = title 47 | l.SetShowStatusBar(true) 48 | l.SetFilteringEnabled(false) 49 | l.Styles.Title = titleStyle 50 | l.Styles.PaginationStyle = paginationStyle 51 | l.Styles.HelpStyle = helpStyle 52 | 53 | m := listModel{list: l} 54 | 55 | return tea.NewProgram(m) 56 | } 57 | 58 | // SelectedValue the list selected value 59 | var SelectedValue string 60 | 61 | // listHeight variable 62 | var listHeight = 14 63 | 64 | // defaultWidth variable 65 | var defaultWidth = 20 66 | 67 | // titleStyle variable 68 | var titleStyle = lipgloss.NewStyle().MarginLeft(2) 69 | 70 | // itemStyle variable 71 | var itemStyle = lipgloss.NewStyle().PaddingLeft(4) 72 | 73 | // selectedItemStyle variable 74 | var selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) 75 | 76 | // paginationStyle variable 77 | var paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4) 78 | 79 | // helpStyle variable 80 | var helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1) 81 | 82 | // quitTextStyle variable 83 | var quitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4) 84 | 85 | // item type 86 | type item string 87 | 88 | // FilterValue method 89 | func (i item) FilterValue() string { 90 | return "" 91 | } 92 | 93 | // itemDelegate type 94 | type itemDelegate struct{} 95 | 96 | // Height method 97 | func (d itemDelegate) Height() int { 98 | return 1 99 | } 100 | 101 | // Spacing method 102 | func (d itemDelegate) Spacing() int { 103 | return 0 104 | } 105 | 106 | // Update method 107 | func (d itemDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { 108 | return nil 109 | } 110 | 111 | // Render method 112 | func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { 113 | i, ok := listItem.(item) 114 | if !ok { 115 | return 116 | } 117 | 118 | str := fmt.Sprintf("%d. %s", index+1, i) 119 | 120 | fn := itemStyle.Render 121 | if index == m.Index() { 122 | fn = func(s string) string { 123 | return selectedItemStyle.Render("> " + s) 124 | } 125 | } 126 | 127 | fmt.Fprintf(w, fn(str)) 128 | } 129 | 130 | // listModel type 131 | type listModel struct { 132 | list list.Model 133 | items []item 134 | choice string 135 | quitting bool 136 | } 137 | 138 | // Init init the list model 139 | func (m listModel) Init() tea.Cmd { 140 | return nil 141 | } 142 | 143 | // Update update the list 144 | func (m listModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 145 | switch msg := msg.(type) { 146 | 147 | case tea.WindowSizeMsg: 148 | m.list.SetWidth(msg.Width) 149 | return m, nil 150 | 151 | case tea.KeyMsg: 152 | switch keypress := msg.String(); keypress { 153 | case "ctrl+c": 154 | m.quitting = true 155 | return m, tea.Quit 156 | 157 | case "enter": 158 | i, ok := m.list.SelectedItem().(item) 159 | if ok { 160 | m.choice = string(i) 161 | } 162 | return m, tea.Quit 163 | } 164 | } 165 | 166 | var cmd tea.Cmd 167 | m.list, cmd = m.list.Update(msg) 168 | return m, cmd 169 | } 170 | 171 | // View show the list 172 | func (m listModel) View() string { 173 | if m.choice != "" { 174 | SelectedValue = m.choice 175 | return "" 176 | } 177 | 178 | if m.quitting { 179 | return quitTextStyle.Render("Action cancelled! That’s cool.") 180 | } 181 | 182 | return "\n" + m.list.View() 183 | } 184 | -------------------------------------------------------------------------------- /core/module/charm_spinner.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package module 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/charmbracelet/bubbles/spinner" 11 | tea "github.com/charmbracelet/bubbletea" 12 | "github.com/charmbracelet/lipgloss" 13 | ) 14 | 15 | // Example 16 | // 17 | // ``` 18 | // import ( 19 | // "github.com/clivern/goenv/core/module" 20 | // ) 21 | // 22 | // 23 | // spinner := module.NewCharmSpinner("Getting Environment Ready!") 24 | // 25 | // go func() { 26 | // // Start a long running job 27 | // spinner.Quit() 28 | // }() 29 | // 30 | // if err := spinner.Start(); err != nil { 31 | // fmt.Println(err) 32 | // os.Exit(1) 33 | // } 34 | // ``` 35 | 36 | // NewCharmSpinner Creates a new instance of tea.Program 37 | func NewCharmSpinner(loadingMsg string) *tea.Program { 38 | return tea.NewProgram(initialSpinnerModel(loadingMsg)) 39 | } 40 | 41 | // errMsg type 42 | type errMsg error 43 | 44 | // spinnerModel spinner type 45 | type spinnerModel struct { 46 | spinner spinner.Model 47 | spin bool 48 | quitting bool 49 | err error 50 | loadingMsg string 51 | } 52 | 53 | // initialSpinnerModel creats a spinner model 54 | func initialSpinnerModel(loadingMsg string) spinnerModel { 55 | s := spinner.New() 56 | 57 | s.Spinner = spinner.Dot 58 | s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) 59 | 60 | return spinnerModel{ 61 | spinner: s, 62 | spin: true, 63 | loadingMsg: loadingMsg, 64 | } 65 | } 66 | 67 | // Init init the spinner model 68 | func (m spinnerModel) Init() tea.Cmd { 69 | return m.spinner.Tick 70 | } 71 | 72 | // Update update the spinner 73 | func (m spinnerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 74 | switch msg := msg.(type) { 75 | 76 | case tea.KeyMsg: 77 | switch msg.String() { 78 | case "q", "esc", "ctrl+c": 79 | m.quitting = true 80 | return m, tea.Quit 81 | default: 82 | return m, nil 83 | } 84 | 85 | case errMsg: 86 | m.err = msg 87 | return m, nil 88 | 89 | case spinner.TickMsg: 90 | var cmd tea.Cmd 91 | // If m.spin is false, don't update on the spinner, effectively stopping it. 92 | if m.spin { 93 | m.spinner, cmd = m.spinner.Update(msg) 94 | } 95 | return m, cmd 96 | 97 | default: 98 | return m, nil 99 | } 100 | } 101 | 102 | // View show the spinner 103 | func (m spinnerModel) View() string { 104 | if m.err != nil { 105 | return m.err.Error() 106 | } 107 | 108 | str := fmt.Sprintf("\n %s %s ...\n", m.spinner.View(), m.loadingMsg) 109 | 110 | if m.quitting { 111 | return str + "\n" 112 | } 113 | 114 | return str 115 | } 116 | -------------------------------------------------------------------------------- /core/module/const.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package module 6 | 7 | import ( 8 | "runtime" 9 | "strings" 10 | ) 11 | 12 | // golangRegistry the registry to download from 13 | var golangRegistry = "https://dl.google.com/go/go${VERSION}.${OS}-${ARCH}.tar.gz" 14 | 15 | // golangRegistryChecksum the registry checksum URL 16 | var golangRegistryChecksum = "https://dl.google.com/go/go${VERSION}.${OS}-${ARCH}.tar.gz.sha256" 17 | 18 | // golangArchive the archive name 19 | var golangArchive = "go${VERSION}.${OS}-${ARCH}.tar.gz" 20 | 21 | // GolangReleases a list of golang releases 22 | var GolangReleases = []string{ 23 | "1.5beta1", 24 | "1.5beta2", 25 | "1.5beta3", 26 | "1.5rc1", 27 | "1.5", 28 | "1.5.1", 29 | "1.5.2", 30 | "1.5.3", 31 | "1.5.4", 32 | "1.6beta1", 33 | "1.6beta2", 34 | "1.6rc1", 35 | "1.6rc2", 36 | "1.6", 37 | "1.6.1", 38 | "1.6.2", 39 | "1.6.3", 40 | "1.6.4", 41 | "1.7beta1", 42 | "1.7beta2", 43 | "1.7rc1", 44 | "1.7rc2", 45 | "1.7rc3", 46 | "1.7rc4", 47 | "1.7rc5", 48 | "1.7rc6", 49 | "1.7", 50 | "1.7.1", 51 | "1.7.2", 52 | "1.7.3", 53 | "1.7.4", 54 | "1.7.5", 55 | "1.7.6", 56 | "1.8beta1", 57 | "1.8beta2", 58 | "1.8rc1", 59 | "1.8rc2", 60 | "1.8rc3", 61 | "1.8", 62 | "1.8.1", 63 | "1.8.2", 64 | "1.8.3", 65 | "1.8.4", 66 | "1.8.5", 67 | "1.8.5rc4", 68 | "1.8.6", 69 | "1.8.7", 70 | "1.9beta1", 71 | "1.9beta2", 72 | "1.9rc1", 73 | "1.9rc2", 74 | "1.9", 75 | "1.9.1", 76 | "1.9.2", 77 | "1.9.3", 78 | "1.9.4", 79 | "1.9.5", 80 | "1.9.6", 81 | "1.9.7", 82 | "1.10beta1", 83 | "1.10beta2", 84 | "1.10rc1", 85 | "1.10rc2", 86 | "1.10", 87 | "1.10.1", 88 | "1.10.2", 89 | "1.10.3", 90 | "1.10.4", 91 | "1.10.5", 92 | "1.10.6", 93 | "1.10.7", 94 | "1.10.8", 95 | "1.11beta1", 96 | "1.11beta2", 97 | "1.11beta3", 98 | "1.11rc1", 99 | "1.11rc2", 100 | "1.11", 101 | "1.11.1", 102 | "1.11.2", 103 | "1.11.3", 104 | "1.11.4", 105 | "1.11.5", 106 | "1.11.6", 107 | "1.11.7", 108 | "1.11.8", 109 | "1.11.9", 110 | "1.11.10", 111 | "1.11.11", 112 | "1.11.12", 113 | "1.11.13", 114 | "1.12beta1", 115 | "1.12beta2", 116 | "1.12rc1", 117 | "1.12", 118 | "1.12.1", 119 | "1.12.2", 120 | "1.12.3", 121 | "1.12.4", 122 | "1.12.5", 123 | "1.12.6", 124 | "1.12.7", 125 | "1.12.8", 126 | "1.12.9", 127 | "1.12.10", 128 | "1.12.11", 129 | "1.12.12", 130 | "1.12.13", 131 | "1.12.14", 132 | "1.12.15", 133 | "1.12.16", 134 | "1.12.17", 135 | "1.13beta1", 136 | "1.13rc1", 137 | "1.13rc2", 138 | "1.13", 139 | "1.13.1", 140 | "1.13.2", 141 | "1.13.3", 142 | "1.13.4", 143 | "1.13.5", 144 | "1.13.6", 145 | "1.13.7", 146 | "1.13.8", 147 | "1.13.9", 148 | "1.13.10", 149 | "1.13.11", 150 | "1.13.12", 151 | "1.13.13", 152 | "1.13.14", 153 | "1.13.15", 154 | "1.14beta1", 155 | "1.14rc1", 156 | "1.14", 157 | "1.14.1", 158 | "1.14.2", 159 | "1.14.3", 160 | "1.14.4", 161 | "1.14.5", 162 | "1.14.6", 163 | "1.14.7", 164 | "1.14.8", 165 | "1.14.9", 166 | "1.14.10", 167 | "1.14.11", 168 | "1.14.12", 169 | "1.14.13", 170 | "1.14.14", 171 | "1.14.15", 172 | "1.15beta1", 173 | "1.15rc1", 174 | "1.15rc2", 175 | "1.15", 176 | "1.15.1", 177 | "1.15.2", 178 | "1.15.3", 179 | "1.15.4", 180 | "1.15.5", 181 | "1.15.6", 182 | "1.15.7", 183 | "1.15.8", 184 | "1.15.9", 185 | "1.15.10", 186 | "1.15.11", 187 | "1.15.12", 188 | "1.15.13", 189 | "1.15.14", 190 | "1.15.15", 191 | "1.16beta1", 192 | "1.16rc1", 193 | "1.16", 194 | "1.16.1", 195 | "1.16.2", 196 | "1.16.3", 197 | "1.16.4", 198 | "1.16.5", 199 | "1.16.6", 200 | "1.16.7", 201 | "1.16.8", 202 | "1.16.9", 203 | "1.16.10", 204 | "1.16.11", 205 | "1.16.12", 206 | "1.16.13", 207 | "1.16.14", 208 | "1.16.15", 209 | "1.17beta1", 210 | "1.17rc1", 211 | "1.17rc2", 212 | "1.17", 213 | "1.17.1", 214 | "1.17.2", 215 | "1.17.3", 216 | "1.17.4", 217 | "1.17.5", 218 | "1.17.6", 219 | "1.17.7", 220 | "1.17.8", 221 | "1.17.9", 222 | "1.17.10", 223 | "1.17.11", 224 | "1.17.12", 225 | "1.17.13", 226 | "1.18beta1", 227 | "1.18beta2", 228 | "1.18rc1", 229 | "1.18", 230 | "1.18.1", 231 | "1.18.2", 232 | "1.18.3", 233 | "1.18.4", 234 | "1.18.5", 235 | "1.18.6", 236 | "1.18.7", 237 | "1.18.8", 238 | "1.18.9", 239 | "1.18.10", 240 | "1.19beta1", 241 | "1.19rc1", 242 | "1.19rc2", 243 | "1.19", 244 | "1.19.1", 245 | "1.19.2", 246 | "1.19.3", 247 | "1.19.4", 248 | "1.19.5", 249 | "1.19.6", 250 | "1.19.7", 251 | "1.19.8", 252 | "1.19.9", 253 | "1.19.10", 254 | "1.19.11", 255 | "1.19.12", 256 | "1.19.13", 257 | "1.20rc1", 258 | "1.20rc2", 259 | "1.20rc3", 260 | "1.20", 261 | "1.20.1", 262 | "1.20.2", 263 | "1.20.3", 264 | "1.20.4", 265 | "1.20.5", 266 | "1.20.6", 267 | "1.20.7", 268 | "1.20.8", 269 | "1.20.9", 270 | "1.20.10", 271 | "1.20.11", 272 | "1.20.12", 273 | "1.20.13", 274 | "1.20.14", 275 | "1.21rc1", 276 | "1.21rc2", 277 | "1.21rc3", 278 | "1.21rc4", 279 | "1.21.0", 280 | "1.21.1", 281 | "1.21.2", 282 | "1.21.3", 283 | "1.21.4", 284 | "1.21.5", 285 | "1.21.6", 286 | "1.21.7", 287 | "1.21.8", 288 | "1.21.9", 289 | "1.21.10", 290 | "1.21.11", 291 | "1.21.12", 292 | "1.21.13", 293 | "1.22rc1", 294 | "1.22rc2", 295 | "1.22.0", 296 | "1.22.1", 297 | "1.22.2", 298 | "1.22.3", 299 | "1.22.4", 300 | "1.22.5", 301 | "1.22.6", 302 | "1.22.7", 303 | "1.22.8", 304 | "1.22.9", 305 | "1.22.10", 306 | "1.22.11", 307 | "1.23rc1", 308 | "1.23rc2", 309 | "1.23.0", 310 | "1.23.1", 311 | "1.23.2", 312 | "1.23.3", 313 | "1.23.4", 314 | "1.23.5", 315 | "1.24rc1", 316 | "1.24rc2", 317 | "1.24.0", 318 | "1.24.1", 319 | "1.24.2", 320 | } 321 | 322 | // goShimContent shim for go binary 323 | var goShimContent = `#!/usr/bin/env bash 324 | 325 | GO_BINARY_NAME=go 326 | 327 | GO_VERSION_PATH=$(goenv exec) 328 | 329 | GOENV_ROOT="$HOME/%s" \ 330 | GOPATH=$GO_VERSION_PATH \ 331 | GOBIN=$GO_VERSION_PATH/bin \ 332 | "$GO_VERSION_PATH/bin/$GO_BINARY_NAME" "$@" 333 | 334 | if [[ "$@" =~ 'get' ]] 335 | then 336 | goenv rehash 337 | fi 338 | 339 | if [[ "$@" =~ 'install' ]] 340 | then 341 | goenv rehash 342 | fi 343 | 344 | ` 345 | 346 | // binaryShimContent shim other go binaries 347 | var binaryShimContent = `#!/usr/bin/env bash 348 | 349 | GO_BINARY_NAME=%s 350 | 351 | GO_VERSION_PATH=$(goenv exec) 352 | 353 | GOENV_ROOT="$HOME/%s" \ 354 | GOPATH=$GO_VERSION_PATH \ 355 | GOBIN=$GO_VERSION_PATH/bin \ 356 | "$GO_VERSION_PATH/bin/$GO_BINARY_NAME" "$@" 357 | 358 | ` 359 | 360 | // getDownloadURL returns the download link 361 | func getDownloadURL(version string) string { 362 | url := strings.Replace(golangRegistry, "${VERSION}", version, -1) 363 | url = strings.Replace(url, "${OS}", runtime.GOOS, -1) 364 | url = strings.Replace(url, "${ARCH}", runtime.GOARCH, -1) 365 | 366 | return url 367 | } 368 | 369 | // getChecksumURL returns the checksum link 370 | func getChecksumURL(version string) string { 371 | url := strings.Replace(golangRegistryChecksum, "${VERSION}", version, -1) 372 | url = strings.Replace(url, "${OS}", runtime.GOOS, -1) 373 | url = strings.Replace(url, "${ARCH}", runtime.GOARCH, -1) 374 | 375 | return url 376 | } 377 | 378 | // getArchiveName returns the archive name 379 | func getArchiveName(version string) string { 380 | name := strings.Replace(golangArchive, "${VERSION}", version, -1) 381 | name = strings.Replace(name, "${OS}", runtime.GOOS, -1) 382 | name = strings.Replace(name, "${ARCH}", runtime.GOARCH, -1) 383 | 384 | return name 385 | } 386 | -------------------------------------------------------------------------------- /core/module/golang.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package module 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | 13 | "github.com/clivern/goenv/core/service" 14 | "github.com/clivern/goenv/core/util" 15 | ) 16 | 17 | // Golang type 18 | type Golang struct { 19 | RootPath string 20 | EnvironmentDir string 21 | VersionsDir string 22 | ShimsDir string 23 | VersionFile string 24 | FileSystem *service.FileSystem 25 | Installer *service.Installer 26 | } 27 | 28 | // NewGolangEnvironment creates a new instance 29 | func NewGolangEnvironment(homePath string) *Golang { 30 | 31 | fs := service.NewFileSystem() 32 | 33 | return &Golang{ 34 | RootPath: fmt.Sprintf("%s/%s", fs.RemoveTrailingSlash(homePath), ".goenv"), 35 | VersionsDir: "versions", 36 | ShimsDir: "shims", 37 | VersionFile: ".go-version", 38 | EnvironmentDir: ".goenv", 39 | FileSystem: fs, 40 | Installer: service.NewInstaller(), 41 | } 42 | } 43 | 44 | // Install installs a golang version 45 | func (g *Golang) Install(version string) error { 46 | 47 | url := getDownloadURL(version) 48 | 49 | versionsDir := fmt.Sprintf("%s/%s", g.RootPath, g.VersionsDir) 50 | 51 | _, err := g.Installer.DownloadFromURL( 52 | versionsDir, 53 | url, 54 | ) 55 | 56 | if err != nil { 57 | return fmt.Errorf( 58 | "Error while downloading go version %s url %s: %s", 59 | version, 60 | url, 61 | err.Error(), 62 | ) 63 | } 64 | 65 | // Local Checksum 66 | checksum, err := util.GetFileSha256Sum(fmt.Sprintf("%s/%s", versionsDir, getArchiveName(version))) 67 | 68 | if err != nil { 69 | return fmt.Errorf( 70 | "Error while generating the checksum from file %s: %s", 71 | fmt.Sprintf("%s/%s", versionsDir, getArchiveName(version)), 72 | err.Error(), 73 | ) 74 | } 75 | 76 | // Remote Checksum 77 | remoteChecksum, err := util.GetSha256Sum(getChecksumURL(version)) 78 | 79 | if err != nil { 80 | return fmt.Errorf( 81 | "Error while fetching the checksum from remote server %s: %s", 82 | getChecksumURL(version), 83 | err.Error(), 84 | ) 85 | } 86 | 87 | if (remoteChecksum != "") && (remoteChecksum != checksum) { 88 | return fmt.Errorf( 89 | "Checksum not matching, try downloading again %s != %s", 90 | remoteChecksum, 91 | checksum, 92 | ) 93 | } 94 | 95 | err = g.Installer.Untar( 96 | versionsDir, 97 | fmt.Sprintf("%s/%s", versionsDir, getArchiveName(version)), 98 | ) 99 | 100 | if err != nil { 101 | return fmt.Errorf( 102 | "Error while uncompressing the go archive version %s url %s: %s", 103 | version, 104 | url, 105 | err.Error(), 106 | ) 107 | } 108 | 109 | err = g.FileSystem.Rename( 110 | fmt.Sprintf("%s/go", versionsDir), 111 | fmt.Sprintf("%s/%s", versionsDir, version), 112 | ) 113 | 114 | if err != nil { 115 | return fmt.Errorf( 116 | "Error while renaming the go directory from %s to %s: %s", 117 | fmt.Sprintf("%s/go", versionsDir), 118 | fmt.Sprintf("%s/%s", versionsDir, version), 119 | err.Error(), 120 | ) 121 | } 122 | 123 | err = g.FileSystem.DeleteFile(fmt.Sprintf( 124 | "%s/%s", 125 | versionsDir, 126 | getArchiveName(version), 127 | )) 128 | 129 | if err != nil { 130 | return fmt.Errorf( 131 | "Error while deleting file %s: %s", 132 | fmt.Sprintf("%s/%s", versionsDir, getArchiveName(version)), 133 | err.Error(), 134 | ) 135 | } 136 | 137 | return nil 138 | } 139 | 140 | // Uninstall removes a golang installed version 141 | func (g *Golang) Uninstall(version string) error { 142 | 143 | path := fmt.Sprintf( 144 | "%s/%s/%s", 145 | g.RootPath, 146 | g.VersionsDir, 147 | version, 148 | ) 149 | 150 | if !g.FileSystem.DirExists(path) { 151 | return fmt.Errorf( 152 | "Unable to locate version %s path %s", 153 | version, 154 | path, 155 | ) 156 | } 157 | 158 | err := g.FileSystem.ClearDir(path) 159 | 160 | if err != nil { 161 | return fmt.Errorf( 162 | "Unable to clear version %s path %s", 163 | version, 164 | path, 165 | ) 166 | } 167 | 168 | err = g.FileSystem.DeleteDir(path) 169 | 170 | if err != nil { 171 | return fmt.Errorf( 172 | "Unable to delete version %s path %s", 173 | version, 174 | path, 175 | ) 176 | } 177 | 178 | return nil 179 | } 180 | 181 | // SetVersion sets the local or global golang version 182 | func (g *Golang) SetVersion(path, version string) error { 183 | 184 | err := g.FileSystem.StoreFile(path, fmt.Sprintf("%s\n", version)) 185 | 186 | if err != nil { 187 | return fmt.Errorf( 188 | "Unable to set go version to %s path %s", 189 | version, 190 | path, 191 | ) 192 | } 193 | 194 | return nil 195 | } 196 | 197 | // SetGlobalVersion sets the global golang version 198 | func (g *Golang) SetGlobalVersion(version string) error { 199 | 200 | path := fmt.Sprintf( 201 | "%s/%s", 202 | g.RootPath, 203 | g.VersionFile, 204 | ) 205 | 206 | return g.SetVersion(path, version) 207 | } 208 | 209 | // SetLocalVersion sets the local golang version 210 | func (g *Golang) SetLocalVersion(version string) error { 211 | 212 | cdir, err := os.Getwd() 213 | 214 | if err != nil { 215 | return err 216 | } 217 | 218 | path := fmt.Sprintf( 219 | "%s/%s", 220 | g.FileSystem.RemoveTrailingSlash(cdir), 221 | g.VersionFile, 222 | ) 223 | 224 | return g.SetVersion(path, version) 225 | } 226 | 227 | // GetLocalVersion returns the local golang version 228 | func (g *Golang) GetLocalVersion(dir string) (string, error) { 229 | 230 | var versionFile string 231 | 232 | baseDir := g.FileSystem.RemoveTrailingSlash(dir) 233 | 234 | for { 235 | if baseDir == "/" { 236 | versionFile = fmt.Sprintf("/%s", g.VersionFile) 237 | } else { 238 | versionFile = fmt.Sprintf("%s/%s", baseDir, g.VersionFile) 239 | } 240 | 241 | if g.FileSystem.FileExists(versionFile) { 242 | break 243 | } 244 | 245 | if baseDir == "/" { 246 | return "", fmt.Errorf("Unable to locate local version file") 247 | } 248 | 249 | baseDir = filepath.Dir(baseDir) 250 | } 251 | 252 | content, err := g.FileSystem.ReadFile(versionFile) 253 | 254 | if err != nil { 255 | return "", fmt.Errorf( 256 | "Unable to read local version file, path %s and error %s", 257 | versionFile, 258 | err.Error(), 259 | ) 260 | } 261 | 262 | return strings.TrimSuffix(content, "\n"), nil 263 | } 264 | 265 | // GetGlobalVersion returns the global golang version 266 | func (g *Golang) GetGlobalVersion() (string, error) { 267 | 268 | file := fmt.Sprintf( 269 | "%s/%s", 270 | g.RootPath, 271 | g.VersionFile, 272 | ) 273 | 274 | if !g.FileSystem.FileExists(file) { 275 | return "", fmt.Errorf( 276 | "Global go version is not set yet, path %s", 277 | file, 278 | ) 279 | } 280 | 281 | content, err := g.FileSystem.ReadFile(file) 282 | 283 | if err != nil { 284 | return "", fmt.Errorf( 285 | "Unable to read global version file, path %s and error %s", 286 | file, 287 | err.Error(), 288 | ) 289 | } 290 | 291 | return strings.TrimSuffix(content, "\n"), nil 292 | } 293 | 294 | // Configure configures the environment 295 | func (g *Golang) Configure() error { 296 | 297 | var err error 298 | 299 | if !g.FileSystem.DirExists(g.RootPath) { 300 | err = g.FileSystem.EnsureDir(g.RootPath, 0775) 301 | } 302 | 303 | if err != nil { 304 | return fmt.Errorf("Unable to configure environment: %s", err.Error()) 305 | } 306 | 307 | if !g.FileSystem.DirExists(fmt.Sprintf("%s/%s", g.RootPath, g.VersionsDir)) { 308 | err = g.FileSystem.EnsureDir(fmt.Sprintf("%s/%s", g.RootPath, g.VersionsDir), 0775) 309 | } 310 | 311 | if err != nil { 312 | return fmt.Errorf("Unable to configure environment: %s", err.Error()) 313 | } 314 | 315 | if !g.FileSystem.DirExists(fmt.Sprintf("%s/%s", g.RootPath, g.ShimsDir)) { 316 | err = g.FileSystem.EnsureDir(fmt.Sprintf("%s/%s", g.RootPath, g.ShimsDir), 0775) 317 | } 318 | 319 | if err != nil { 320 | return fmt.Errorf("Unable to configure environment: %s", err.Error()) 321 | } 322 | 323 | return nil 324 | } 325 | 326 | // GetVersions returns a list of all available versions 327 | func (g *Golang) GetVersions() []string { 328 | return GolangReleases 329 | } 330 | 331 | // GetInstalledVersions returns a list of installed versions 332 | func (g *Golang) GetInstalledVersions() ([]string, error) { 333 | 334 | path := fmt.Sprintf("%s/%s", g.RootPath, g.VersionsDir) 335 | 336 | result, err := g.FileSystem.GetSubDirectoriesNames(path) 337 | 338 | versions := []string{} 339 | 340 | if err != nil { 341 | return versions, fmt.Errorf( 342 | "Unable to list directory %s: %s", 343 | path, 344 | err.Error(), 345 | ) 346 | } 347 | 348 | for i := 0; i < len(result); i++ { 349 | if !util.InArray(result[i], g.GetVersions()) { 350 | continue 351 | } 352 | 353 | versions = append(versions, result[i]) 354 | } 355 | 356 | return versions, nil 357 | } 358 | 359 | // ValidateVersion validates if a version is valid 360 | func (g *Golang) ValidateVersion(version string) bool { 361 | return util.InArray(version, g.GetVersions()) 362 | } 363 | 364 | // ValidateInstalledVersion validates if an installed version is valid 365 | func (g *Golang) ValidateInstalledVersion(version string) (bool, error) { 366 | 367 | versions, err := g.GetInstalledVersions() 368 | 369 | if err != nil { 370 | return false, err 371 | } 372 | 373 | return util.InArray(version, versions), nil 374 | } 375 | 376 | // Rehash gets a list of binaries under a certain 377 | // go bin directory and create shim for them 378 | func (g *Golang) Rehash() error { 379 | var err error 380 | var content string 381 | 382 | // create third party binaries 383 | vers, err := g.GetInstalledVersions() 384 | 385 | if err != nil { 386 | return err 387 | } 388 | 389 | for i := 0; i < len(vers); i++ { 390 | bins, err := g.FileSystem.GetDirectoryFileNames(fmt.Sprintf( 391 | "%s/%s/%s/bin", 392 | g.RootPath, 393 | g.VersionsDir, 394 | vers[i], 395 | )) 396 | 397 | if err != nil { 398 | return err 399 | } 400 | 401 | for i := 0; i < len(bins); i++ { 402 | bin := fmt.Sprintf("%s/%s/%s", g.RootPath, g.ShimsDir, bins[i]) 403 | 404 | if !g.FileSystem.FileExists(bin) { 405 | // If go, create a go shim 406 | if bins[i] == "go" { 407 | content = fmt.Sprintf(goShimContent, g.EnvironmentDir) 408 | } else { 409 | // If not go, it might be gofmt or other installed binaries 410 | content = fmt.Sprintf(binaryShimContent, bins[i], g.EnvironmentDir) 411 | } 412 | 413 | err = g.FileSystem.StoreFile(bin, content) 414 | 415 | if err != nil { 416 | return err 417 | } 418 | 419 | g.FileSystem.ChangePermission(bin, 0775) 420 | } 421 | } 422 | } 423 | 424 | return nil 425 | } 426 | -------------------------------------------------------------------------------- /core/module/golang_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package module 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | 11 | "github.com/clivern/goenv/pkg" 12 | 13 | "github.com/franela/goblin" 14 | ) 15 | 16 | // TestUnitGolang 17 | func TestUnitGolang(t *testing.T) { 18 | g := goblin.Goblin(t) 19 | 20 | baseDir := fmt.Sprintf("%s/cache", pkg.GetBaseDir("cache")) 21 | goenv := NewGolangEnvironment(baseDir) 22 | 23 | g.Describe("#Golang", func() { 24 | g.It("It should satisfy test cases", func() { 25 | // Test GetVersions 26 | g.Assert(len(goenv.GetVersions())).Equal(297) 27 | 28 | // Test SetVersion 29 | versionFile := fmt.Sprintf("%s/.goenv/%s", baseDir, ".go-version") 30 | g.Assert(goenv.SetVersion(versionFile, "1.18")).Equal(nil) 31 | 32 | // Test Configure 33 | g.Assert(goenv.Configure()).Equal(nil) 34 | 35 | // Test GetLocalVersion 36 | out, err := goenv.GetLocalVersion(fmt.Sprintf("%s/.goenv", baseDir)) 37 | g.Assert(out).Equal("1.18") 38 | g.Assert(err).Equal(nil) 39 | 40 | // Test GetGlobalVersion 41 | out, err = goenv.GetGlobalVersion() 42 | g.Assert(out).Equal("1.18") 43 | g.Assert(err).Equal(nil) 44 | 45 | // Cleanup 46 | goenv.FileSystem.ClearDir(fmt.Sprintf("%s/.goenv", baseDir)) 47 | goenv.FileSystem.DeleteDir(fmt.Sprintf("%s/.goenv", baseDir)) 48 | }) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /core/service/file_system.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package service package 6 | package service 7 | 8 | import ( 9 | "fmt" 10 | "io/ioutil" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | ) 15 | 16 | // FileSystem struct 17 | type FileSystem struct { 18 | } 19 | 20 | // NewFileSystem creates a new instance 21 | func NewFileSystem() *FileSystem { 22 | return &FileSystem{} 23 | } 24 | 25 | // FileExists reports whether the named file exists 26 | func (fs *FileSystem) FileExists(path string) bool { 27 | if fi, err := os.Stat(path); err == nil { 28 | if fi.Mode().IsRegular() { 29 | return true 30 | } 31 | } 32 | 33 | return false 34 | } 35 | 36 | // DirExists reports whether the dir exists 37 | func (fs *FileSystem) DirExists(path string) bool { 38 | if fi, err := os.Stat(path); err == nil { 39 | if fi.Mode().IsDir() { 40 | return true 41 | } 42 | } 43 | 44 | return false 45 | } 46 | 47 | // EnsureDir ensures that directory exists 48 | func (fs *FileSystem) EnsureDir(dirName string, mode int) error { 49 | err := os.MkdirAll(dirName, os.FileMode(mode)) 50 | 51 | if err == nil || os.IsExist(err) { 52 | return nil 53 | } 54 | 55 | return err 56 | } 57 | 58 | // DeleteDir deletes a dir 59 | func (fs *FileSystem) DeleteDir(dir string) error { 60 | err := os.RemoveAll(dir) 61 | 62 | if err != nil { 63 | return err 64 | } 65 | 66 | return nil 67 | } 68 | 69 | // EnsureTrailingSlash ensure there is a trailing slash 70 | func (fs *FileSystem) EnsureTrailingSlash(dir string) string { 71 | return fmt.Sprintf( 72 | "%s%s", 73 | strings.TrimRight(dir, string(os.PathSeparator)), 74 | string(os.PathSeparator), 75 | ) 76 | } 77 | 78 | // RemoveTrailingSlash removes any trailing slash 79 | func (fs *FileSystem) RemoveTrailingSlash(dir string) string { 80 | return strings.TrimRight(dir, string(os.PathSeparator)) 81 | } 82 | 83 | // RemoveStartingSlash removes any starting slash 84 | func (fs *FileSystem) RemoveStartingSlash(dir string) string { 85 | return strings.TrimLeft(dir, string(os.PathSeparator)) 86 | } 87 | 88 | // ClearDir removes all files and sub dirs 89 | func (fs *FileSystem) ClearDir(dir string) error { 90 | files, err := filepath.Glob(filepath.Join(dir, "*")) 91 | if err != nil { 92 | return err 93 | } 94 | for _, file := range files { 95 | err = os.RemoveAll(file) 96 | if err != nil { 97 | return err 98 | } 99 | } 100 | return nil 101 | } 102 | 103 | // StoreFile stores a file content 104 | func (fs *FileSystem) StoreFile(path, content string) error { 105 | dir := filepath.Dir(path) 106 | 107 | err := os.MkdirAll(dir, 0775) 108 | 109 | if err != nil { 110 | return err 111 | } 112 | 113 | f, err := os.Create(path) 114 | 115 | if err != nil { 116 | return err 117 | } 118 | 119 | defer f.Close() 120 | 121 | _, err = f.WriteString(content) 122 | 123 | return err 124 | } 125 | 126 | // PathExists reports whether the path exists 127 | func (fs *FileSystem) PathExists(path string) bool { 128 | if _, err := os.Stat(path); os.IsNotExist(err) { 129 | return false 130 | } 131 | return true 132 | } 133 | 134 | // DeleteFile deletes a file 135 | func (fs *FileSystem) DeleteFile(path string) error { 136 | return os.Remove(path) 137 | } 138 | 139 | // ReadFile get the file content 140 | func (fs *FileSystem) ReadFile(path string) (string, error) { 141 | data, err := ioutil.ReadFile(path) 142 | 143 | if err != nil { 144 | return "", err 145 | } 146 | return string(data), nil 147 | } 148 | 149 | // Rename rename a dir or a file 150 | func (fs *FileSystem) Rename(old, new string) error { 151 | return os.Rename(old, new) 152 | } 153 | 154 | // GetSubDirectoriesNames gets a list of sub directories 155 | func (fs *FileSystem) GetSubDirectoriesNames(path string) ([]string, error) { 156 | result := []string{} 157 | 158 | files, err := ioutil.ReadDir(path) 159 | 160 | if err != nil { 161 | return result, err 162 | } 163 | 164 | for _, fileInfo := range files { 165 | if fileInfo.IsDir() { 166 | result = append(result, fileInfo.Name()) 167 | } 168 | } 169 | 170 | return result, nil 171 | } 172 | 173 | // GetDirectoryFileNames get a list of files inside a directory 174 | func (fs *FileSystem) GetDirectoryFileNames(path string) ([]string, error) { 175 | result := []string{} 176 | 177 | files, err := ioutil.ReadDir(path) 178 | 179 | if err != nil { 180 | return result, err 181 | } 182 | 183 | for _, fileInfo := range files { 184 | if !fileInfo.IsDir() { 185 | result = append(result, fileInfo.Name()) 186 | } 187 | } 188 | 189 | return result, nil 190 | } 191 | 192 | // ChangePermission changes the file permission 193 | func (fs *FileSystem) ChangePermission(path string, permisson os.FileMode) { 194 | os.Chmod(path, permisson) 195 | } 196 | -------------------------------------------------------------------------------- /core/service/file_system_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package service 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | 11 | "github.com/clivern/goenv/pkg" 12 | 13 | "github.com/franela/goblin" 14 | ) 15 | 16 | // TestUnitFileSystem 17 | func TestUnitFileSystem(t *testing.T) { 18 | g := goblin.Goblin(t) 19 | 20 | fs := NewFileSystem() 21 | 22 | g.Describe("#FileSystem", func() { 23 | g.It("It should satisfy test cases", func() { 24 | g.Assert(fs.FileExists(fmt.Sprintf("%s/.gitignore", pkg.GetBaseDir("cache")))).Equal(true) 25 | g.Assert(fs.FileExists(fmt.Sprintf("%s/not_found.md", pkg.GetBaseDir("cache")))).Equal(false) 26 | g.Assert(fs.DirExists(pkg.GetBaseDir("cache"))).Equal(true) 27 | g.Assert(fs.DirExists(fmt.Sprintf("%s/not_found", pkg.GetBaseDir("cache")))).Equal(false) 28 | g.Assert(fs.EnsureDir(fmt.Sprintf("%s/new", pkg.GetBaseDir("cache")), 775)).Equal(nil) 29 | g.Assert(fs.DirExists(fmt.Sprintf("%s/new", pkg.GetBaseDir("cache")))).Equal(true) 30 | g.Assert(fs.DeleteDir(fmt.Sprintf("%s/new", pkg.GetBaseDir("cache")))).Equal(nil) 31 | }) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /core/service/installer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package service 6 | 7 | import ( 8 | "archive/tar" 9 | "compress/gzip" 10 | "fmt" 11 | "io" 12 | "net/http" 13 | "os" 14 | "path/filepath" 15 | "strings" 16 | ) 17 | 18 | // Installer struct 19 | type Installer struct { 20 | } 21 | 22 | // NewInstaller creates a new instance 23 | func NewInstaller() *Installer { 24 | return &Installer{} 25 | } 26 | 27 | // DownloadFromURL downloads from a URL 28 | func (i *Installer) DownloadFromURL(dir string, url string) (string, error) { 29 | 30 | tokens := strings.Split(url, "/") 31 | 32 | fileName := tokens[len(tokens)-1] 33 | 34 | response, err := http.Get(url) 35 | 36 | if err != nil { 37 | return "", err 38 | } 39 | 40 | defer response.Body.Close() 41 | 42 | if response.StatusCode != 200 { 43 | return "", fmt.Errorf( 44 | "Unable to download from %s", 45 | url, 46 | ) 47 | } 48 | 49 | tarFile := filepath.Join(dir, fileName) 50 | 51 | output, err := os.Create(tarFile) 52 | 53 | if err != nil { 54 | return "", err 55 | } 56 | 57 | defer output.Close() 58 | 59 | _, err = io.Copy(output, response.Body) 60 | 61 | if err != nil { 62 | return "", err 63 | } 64 | 65 | return tarFile, nil 66 | } 67 | 68 | // Untar uncompress a .tar.gz file 69 | func (i *Installer) Untar(extractPath, sourcefilePath string) error { 70 | 71 | file, err := os.Open(sourcefilePath) 72 | 73 | if err != nil { 74 | return err 75 | } 76 | 77 | defer file.Close() 78 | 79 | var fileReader io.ReadCloser = file 80 | 81 | if strings.HasSuffix(sourcefilePath, ".gz") { 82 | 83 | if fileReader, err = gzip.NewReader(file); err != nil { 84 | return err 85 | } 86 | 87 | defer fileReader.Close() 88 | } 89 | 90 | tarBallReader := tar.NewReader(fileReader) 91 | 92 | for { 93 | header, err := tarBallReader.Next() 94 | 95 | if err != nil { 96 | if err == io.EOF { 97 | break 98 | } 99 | 100 | return err 101 | } 102 | 103 | filename := filepath.Join(extractPath, filepath.FromSlash(header.Name)) 104 | 105 | switch header.Typeflag { 106 | 107 | case tar.TypeDir: 108 | err = os.MkdirAll(filename, os.FileMode(header.Mode)) 109 | 110 | if err != nil { 111 | return err 112 | } 113 | 114 | case tar.TypeReg: 115 | writer, err := os.Create(filename) 116 | 117 | if err != nil { 118 | return err 119 | } 120 | 121 | io.Copy(writer, tarBallReader) 122 | 123 | err = os.Chmod(filename, os.FileMode(header.Mode)) 124 | 125 | if err != nil { 126 | return err 127 | } 128 | 129 | writer.Close() 130 | default: 131 | return fmt.Errorf("Unable to untar type: %c in file %s", header.Typeflag, filename) 132 | } 133 | } 134 | 135 | return nil 136 | } 137 | -------------------------------------------------------------------------------- /core/service/installer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | package service 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | 11 | "github.com/clivern/goenv/pkg" 12 | 13 | "github.com/franela/goblin" 14 | ) 15 | 16 | // TestUnitInstaller 17 | func TestUnitInstaller(t *testing.T) { 18 | g := goblin.Goblin(t) 19 | 20 | installer := NewInstaller() 21 | fs := NewFileSystem() 22 | 23 | g.Describe("#Installer", func() { 24 | g.It("It should satisfy test cases", func() { 25 | out, err := installer.DownloadFromURL( 26 | fmt.Sprintf("%s/cache/", pkg.GetBaseDir("cache")), 27 | "https://github.com/Clivern/Rhino/releases/download/1.6.1/Rhino_1.6.1_Darwin_x86_64.tar.gz", 28 | ) 29 | 30 | g.Assert(err).Equal(nil) 31 | g.Assert(out != "").Equal(true) 32 | 33 | // Create a dir for output 34 | fs.EnsureDir(fmt.Sprintf("%s/cache/rhino", pkg.GetBaseDir("cache")), 0755) 35 | 36 | err = installer.Untar( 37 | fmt.Sprintf("%s/cache/rhino", pkg.GetBaseDir("cache")), 38 | fmt.Sprintf("%s/cache/Rhino_1.6.1_Darwin_x86_64.tar.gz", pkg.GetBaseDir("cache")), 39 | ) 40 | 41 | g.Assert(err).Equal(nil) 42 | 43 | g.Assert(fs.FileExists(fmt.Sprintf("%s/cache/rhino/README.md", pkg.GetBaseDir("cache")))).Equal(true) 44 | 45 | // Cleanup 46 | fs.ClearDir(fmt.Sprintf("%s/cache/rhino", pkg.GetBaseDir("cache"))) 47 | fs.DeleteDir(fmt.Sprintf("%s/cache/rhino", pkg.GetBaseDir("cache"))) 48 | g.Assert(fs.DeleteFile(fmt.Sprintf("%s/cache/Rhino_1.6.1_Darwin_x86_64.tar.gz", pkg.GetBaseDir("cache")))).Equal(nil) 49 | }) 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /core/util/helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package util package 6 | package util 7 | 8 | import ( 9 | "crypto/sha256" 10 | "fmt" 11 | "io" 12 | "io/ioutil" 13 | "net/http" 14 | "os" 15 | "reflect" 16 | "strings" 17 | ) 18 | 19 | // InArray check if value is on array 20 | func InArray(val interface{}, array interface{}) bool { 21 | switch reflect.TypeOf(array).Kind() { 22 | case reflect.Slice: 23 | s := reflect.ValueOf(array) 24 | 25 | for i := 0; i < s.Len(); i++ { 26 | if reflect.DeepEqual(val, s.Index(i).Interface()) { 27 | return true 28 | } 29 | } 30 | } 31 | 32 | return false 33 | } 34 | 35 | // GetFileSha256Sum gets the file sha256 sum 36 | func GetFileSha256Sum(path string) (string, error) { 37 | f, err := os.Open(path) 38 | 39 | if err != nil { 40 | return "", err 41 | } 42 | 43 | defer f.Close() 44 | 45 | h := sha256.New() 46 | 47 | if _, err := io.Copy(h, f); err != nil { 48 | return "", err 49 | } 50 | 51 | return fmt.Sprintf("%x", h.Sum(nil)), nil 52 | } 53 | 54 | // GetSha256Sum gets the sha256 sum from a URL 55 | func GetSha256Sum(url string) (string, error) { 56 | resp, err := http.Get(url) 57 | 58 | if err != nil { 59 | return "", err 60 | } 61 | 62 | body, err := ioutil.ReadAll(resp.Body) 63 | 64 | if err != nil { 65 | return "", err 66 | } 67 | 68 | if strings.Contains(string(body), "Not Found") { 69 | return "", nil 70 | } 71 | 72 | return string(body), nil 73 | } 74 | -------------------------------------------------------------------------------- /core/util/helpers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build unit 6 | // +build unit 7 | 8 | package util 9 | 10 | import ( 11 | "fmt" 12 | "testing" 13 | 14 | "github.com/franela/goblin" 15 | ) 16 | 17 | // TesUnitHelpers 18 | func TesUnitHelpers(t *testing.T) { 19 | g := goblin.Goblin(t) 20 | 21 | g.Describe("#TestInArray", func() { 22 | g.It("It should satisfy test cases", func() { 23 | g.Assert(InArray("A", []string{"A", "B", "C", "D"})).Equal(true) 24 | g.Assert(InArray("B", []string{"A", "B", "C", "D"})).Equal(true) 25 | g.Assert(InArray("H", []string{"A", "B", "C", "D"})).Equal(false) 26 | g.Assert(InArray(1, []int{2, 3, 1})).Equal(true) 27 | g.Assert(InArray(9, []int{2, 3, 1})).Equal(false) 28 | }) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/clivern/goenv 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/charmbracelet/bubbles v0.16.0 7 | github.com/charmbracelet/bubbletea v0.27.0 8 | github.com/charmbracelet/lipgloss v0.6.0 9 | github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf 10 | github.com/spf13/cobra v1.9.1 11 | ) 12 | 13 | require ( 14 | github.com/atotto/clipboard v0.1.4 // indirect 15 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 16 | github.com/charmbracelet/x/ansi v0.1.4 // indirect 17 | github.com/charmbracelet/x/input v0.1.0 // indirect 18 | github.com/charmbracelet/x/term v0.1.1 // indirect 19 | github.com/charmbracelet/x/windows v0.1.0 // indirect 20 | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect 21 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 22 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 23 | github.com/mattn/go-isatty v0.0.18 // indirect 24 | github.com/mattn/go-localereader v0.0.1 // indirect 25 | github.com/mattn/go-runewidth v0.0.15 // indirect 26 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect 27 | github.com/muesli/cancelreader v0.2.2 // indirect 28 | github.com/muesli/reflow v0.3.0 // indirect 29 | github.com/muesli/termenv v0.15.2 // indirect 30 | github.com/rivo/uniseg v0.4.7 // indirect 31 | github.com/sahilm/fuzzy v0.1.0 // indirect 32 | github.com/spf13/pflag v1.0.6 // indirect 33 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect 34 | golang.org/x/sync v0.8.0 // indirect 35 | golang.org/x/sys v0.24.0 // indirect 36 | golang.org/x/text v0.3.8 // indirect 37 | ) 38 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= 2 | github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= 3 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 4 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 5 | github.com/charmbracelet/bubbles v0.16.0 h1:jlJxnPj7T0G4rVQhh3gmG9tZu1Xkn+slKax0itFX6fQ= 6 | github.com/charmbracelet/bubbles v0.16.0/go.mod h1:D4UDwNOTGBU2NtVEWtwfhStJO75L8OsqcO7csWH6RrA= 7 | github.com/charmbracelet/bubbletea v0.27.0 h1:Mznj+vvYuYagD9Pn2mY7fuelGvP0HAXtZYGgRBCbHvU= 8 | github.com/charmbracelet/bubbletea v0.27.0/go.mod h1:5MdP9XH6MbQkgGhnlxUqCNmBXf9I74KRQ8HIidRxV1Y= 9 | github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= 10 | github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= 11 | github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= 12 | github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= 13 | github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ= 14 | github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28= 15 | github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= 16 | github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= 17 | github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4= 18 | github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= 19 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 20 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 21 | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= 22 | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= 23 | github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf h1:NrF81UtW8gG2LBGkXFQFqlfNnvMt9WdB46sfdJY4oqc= 24 | github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= 25 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 26 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 27 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 28 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 29 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 30 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 31 | github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= 32 | github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 33 | github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= 34 | github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= 35 | github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 36 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 37 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 38 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= 39 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 40 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= 41 | github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= 42 | github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= 43 | github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= 44 | github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= 45 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= 46 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= 47 | github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= 48 | github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= 49 | github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= 50 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 51 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 52 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 53 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 54 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 55 | github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= 56 | github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= 57 | github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= 58 | github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 59 | github.com/spf13/cobra v1.9.0 h1:Py5fIuq/lJsRYxcxfOtsJqpmwJWCMOUy2tMJYV8TNHE= 60 | github.com/spf13/cobra v1.9.0/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 61 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 62 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 63 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 64 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 65 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 66 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 67 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= 68 | github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= 69 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= 70 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 71 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 72 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 73 | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 74 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 75 | golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= 76 | golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 77 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= 78 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 79 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 80 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 81 | -------------------------------------------------------------------------------- /goenv.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | // main package 6 | package main 7 | 8 | import ( 9 | "os" 10 | "strings" 11 | 12 | "github.com/clivern/goenv/cmd" 13 | ) 14 | 15 | var ( 16 | version = "dev" 17 | commit = "none" 18 | date = "unknown" 19 | builtBy = "unknown" 20 | ) 21 | 22 | func main() { 23 | cmd.Version = version 24 | cmd.Commit = commit 25 | cmd.Date = date 26 | cmd.BuiltBy = builtBy 27 | cmd.HOME = strings.TrimSpace(os.Getenv("HOME")) 28 | 29 | cmd.Execute() 30 | } 31 | -------------------------------------------------------------------------------- /pkg/loader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Clivern. All rights reserved. 2 | // Use of this source code is governed by the MIT 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package pkg package 6 | package pkg 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | "path/filepath" 12 | ) 13 | 14 | // GetBaseDir returns the project base dir 15 | // Base dir identified if dirName found 16 | // This function for testing purposes only 17 | func GetBaseDir(dirName string) string { 18 | baseDir, _ := os.Getwd() 19 | cacheDir := fmt.Sprintf("%s/%s", baseDir, dirName) 20 | 21 | for { 22 | if fi, err := os.Stat(cacheDir); err == nil { 23 | if fi.Mode().IsDir() { 24 | return baseDir 25 | } 26 | } 27 | baseDir = filepath.Dir(baseDir) 28 | cacheDir = fmt.Sprintf("%s/%s", baseDir, dirName) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"] 3 | } 4 | -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Goenv/e97086d1f9163d416dad1839621c1c6ef8e36937/static/logo.png -------------------------------------------------------------------------------- /static/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Goenv/e97086d1f9163d416dad1839621c1c6ef8e36937/static/logo1.png -------------------------------------------------------------------------------- /static/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clivern/Goenv/e97086d1f9163d416dad1839621c1c6ef8e36937/static/screenshot-1.png -------------------------------------------------------------------------------- /techstack.md: -------------------------------------------------------------------------------- 1 | 26 |
27 | 28 | # Tech Stack File 29 | ![](https://img.stackshare.io/repo.svg "repo") [Norwik/Goenv](https://github.com/Norwik/Goenv)![](https://img.stackshare.io/public_badge.svg "public") 30 |

31 | |14
Tools used|12/21/23
Report generated| 32 | |------|------| 33 |
34 | 35 | ## Languages (1) 36 | 37 | 44 | 45 | 46 |
38 | Golang 39 |
40 | Golang 41 |
42 | 43 |
47 | 48 | ## DevOps (2) 49 | 50 | 57 | 58 | 65 | 66 | 67 |
51 | Git 52 |
53 | Git 54 |
55 | 56 |
59 | GitHub Actions 60 |
61 | GitHub Actions 62 |
63 | 64 |
68 | 69 | ## Other (1) 70 | 71 | 78 | 79 | 80 |
72 | Shell 73 |
74 | Shell 75 |
76 | 77 |
81 | 82 | 83 | ## Open source packages (10) 84 | 85 | ## Go Packages (10) 86 | 87 | |NAME|VERSION|LAST UPDATED|LAST UPDATED BY|LICENSE|VULNERABILITIES| 88 | |:------|:------|:------|:------|:------|:------| 89 | |[clipboard](https://pkg.go.dev/github.com/atotto/clipboard)|v0.1.4|02/27/21|Clivern |BSD-3-Clause|N/A| 90 | |[cobra](https://pkg.go.dev/github.com/spf13/cobra)|v1.8.0|11/07/23|dependabot[bot] |Apache-2.0|N/A| 91 | |[go-colorful](https://pkg.go.dev/github.com/lucasb-eyer/go-colorful)|v1.2.0|02/27/21|Clivern |MIT|N/A| 92 | |[go-isatty](https://pkg.go.dev/github.com/mattn/go-isatty)|v0.0.14|05/10/23|dependabot[bot] |MIT|N/A| 93 | |[go-runewidth](https://pkg.go.dev/github.com/mattn/go-runewidth)|v0.0.10|11/09/22|renovate[bot] |MIT|N/A| 94 | |[goblin](https://pkg.go.dev/github.com/franela/goblin)|v0.0.0|02/24/21|Clivern |MIT|N/A| 95 | |[mousetrap](https://pkg.go.dev/github.com/inconshreveable/mousetrap)|v1.1.0|04/06/23|dependabot[bot] |Apache-2.0|N/A| 96 | |[pflag](https://pkg.go.dev/github.com/spf13/pflag)|v1.0.5|02/27/21|Clivern |BSD-3-Clause|N/A| 97 | |[sys](https://pkg.go.dev/golang.org/x/sys)|v0.0.0|02/24/21|Clivern |BSD-3-Clause|N/A| 98 | |[text](https://pkg.go.dev/golang.org/x/text)|v0.3.8|05/10/23|dependabot[bot] |BSD-3-Clause|N/A| 99 | 100 |
101 |
102 | 103 | Generated via [Stack File](https://github.com/marketplace/stack-file) 104 | -------------------------------------------------------------------------------- /techstack.yml: -------------------------------------------------------------------------------- 1 | repo_name: Norwik/Goenv 2 | report_id: aff69af49b0c8c04026a6ecfb45542b6 3 | version: 0.1 4 | repo_type: Public 5 | timestamp: '2023-12-21T15:24:03+00:00' 6 | requested_by: renovate[bot] 7 | provider: github 8 | branch: main 9 | detected_tools_count: 14 10 | tools: 11 | - name: Golang 12 | description: An open source programming language that makes it easy to build simple, 13 | reliable, and efficient software 14 | website_url: http://golang.org/ 15 | license: BSD-3-Clause 16 | open_source: true 17 | hosted_saas: false 18 | category: Languages & Frameworks 19 | sub_category: Languages 20 | image_url: https://img.stackshare.io/service/1005/O6AczwfV_400x400.png 21 | detection_source: Repo Metadata 22 | - name: Git 23 | description: Fast, scalable, distributed revision control system 24 | website_url: http://git-scm.com/ 25 | open_source: true 26 | hosted_saas: false 27 | category: Build, Test, Deploy 28 | sub_category: Version Control System 29 | image_url: https://img.stackshare.io/service/1046/git.png 30 | detection_source: Repo Metadata 31 | - name: GitHub Actions 32 | description: Automate your workflow from idea to production 33 | website_url: https://github.com/features/actions 34 | open_source: false 35 | hosted_saas: true 36 | category: Build, Test, Deploy 37 | sub_category: Continuous Integration 38 | image_url: https://img.stackshare.io/service/11563/actions.png 39 | detection_source: ".github/workflows/build.yml" 40 | last_updated_by: renovate[bot] 41 | last_updated_on: 2023-12-12 21:15:10.000000000 Z 42 | - name: Shell 43 | description: A shell is a text-based terminal, used for manipulating programs and 44 | files. Shell scripts typically manage program execution. 45 | website_url: https://en.wikipedia.org/wiki/Shell_script 46 | open_source: false 47 | hosted_saas: false 48 | category: Languages & Frameworks 49 | sub_category: Languages 50 | image_url: https://img.stackshare.io/service/4631/default_c2062d40130562bdc836c13dbca02d318205a962.png 51 | detection_source: Repo Metadata 52 | - name: clipboard 53 | description: Clipboard for golang 54 | package_url: https://pkg.go.dev/github.com/atotto/clipboard 55 | version: 0.1.4 56 | license: BSD-3-Clause 57 | open_source: true 58 | hosted_saas: false 59 | category: Libraries 60 | sub_category: Go Modules Packages 61 | image_url: https://img.stackshare.io/package/go-packages/image.png 62 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 63 | detection_source: go.mod 64 | last_updated_by: Clivern 65 | last_updated_on: 2021-02-27 16:34:25.000000000 Z 66 | - name: cobra 67 | description: A Commander for modern Go CLI interactions 68 | package_url: https://pkg.go.dev/github.com/spf13/cobra 69 | version: 1.8.0 70 | license: Apache-2.0 71 | open_source: true 72 | hosted_saas: false 73 | category: Libraries 74 | sub_category: Go Modules Packages 75 | image_url: https://img.stackshare.io/package/go-packages/image.png 76 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 77 | detection_source: go.mod 78 | last_updated_by: dependabot[bot] 79 | last_updated_on: 2023-11-07 15:18:01.000000000 Z 80 | - name: go-colorful 81 | description: A library for playing with colors in go 82 | package_url: https://pkg.go.dev/github.com/lucasb-eyer/go-colorful 83 | version: 1.2.0 84 | license: MIT 85 | open_source: true 86 | hosted_saas: false 87 | category: Libraries 88 | sub_category: Go Modules Packages 89 | image_url: https://img.stackshare.io/package/go-packages/image.png 90 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 91 | detection_source: go.mod 92 | last_updated_by: Clivern 93 | last_updated_on: 2021-02-27 16:34:25.000000000 Z 94 | - name: go-isatty 95 | description: Package isatty implements interface to isatty 96 | package_url: https://pkg.go.dev/github.com/mattn/go-isatty 97 | version: 0.0.14 98 | license: MIT 99 | open_source: true 100 | hosted_saas: false 101 | category: Libraries 102 | sub_category: Go Modules Packages 103 | image_url: https://img.stackshare.io/package/go-packages/image.png 104 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 105 | detection_source: go.mod 106 | last_updated_by: dependabot[bot] 107 | last_updated_on: 2023-05-10 15:11:59.000000000 Z 108 | - name: go-runewidth 109 | description: Go-runewidth Provides functions to get fixed width of the character 110 | or string 111 | package_url: https://pkg.go.dev/github.com/mattn/go-runewidth 112 | version: 0.0.10 113 | license: MIT 114 | open_source: true 115 | hosted_saas: false 116 | category: Libraries 117 | sub_category: Go Modules Packages 118 | image_url: https://img.stackshare.io/package/go-packages/image.png 119 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 120 | detection_source: go.mod 121 | last_updated_by: renovate[bot] 122 | last_updated_on: 2022-11-09 21:04:51.000000000 Z 123 | - name: goblin 124 | description: Minimal and Beautiful Go testing framework 125 | package_url: https://pkg.go.dev/github.com/franela/goblin 126 | version: 0.0.0 127 | license: MIT 128 | open_source: true 129 | hosted_saas: false 130 | category: Libraries 131 | sub_category: Go Modules Packages 132 | image_url: https://img.stackshare.io/package/go-packages/image.png 133 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 134 | detection_source: go.mod 135 | last_updated_by: Clivern 136 | last_updated_on: 2021-02-24 20:50:47.000000000 Z 137 | - name: mousetrap 138 | description: Detect starting from Windows explorer 139 | package_url: https://pkg.go.dev/github.com/inconshreveable/mousetrap 140 | version: 1.1.0 141 | license: Apache-2.0 142 | open_source: true 143 | hosted_saas: false 144 | category: Libraries 145 | sub_category: Go Modules Packages 146 | image_url: https://img.stackshare.io/package/go-packages/image.png 147 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 148 | detection_source: go.mod 149 | last_updated_by: dependabot[bot] 150 | last_updated_on: 2023-04-06 00:53:47.000000000 Z 151 | - name: pflag 152 | description: Drop-in replacement for Go's flag package 153 | package_url: https://pkg.go.dev/github.com/spf13/pflag 154 | version: 1.0.5 155 | license: BSD-3-Clause 156 | open_source: true 157 | hosted_saas: false 158 | category: Libraries 159 | sub_category: Go Modules Packages 160 | image_url: https://img.stackshare.io/package/go-packages/image.png 161 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 162 | detection_source: go.mod 163 | last_updated_by: Clivern 164 | last_updated_on: 2021-02-27 16:34:25.000000000 Z 165 | - name: sys 166 | description: Go packages for low-level interaction with the operating system 167 | package_url: https://pkg.go.dev/golang.org/x/sys 168 | version: 0.0.0 169 | license: BSD-3-Clause 170 | open_source: true 171 | hosted_saas: false 172 | category: Libraries 173 | sub_category: Go Modules Packages 174 | image_url: https://img.stackshare.io/package/go-packages/image.png 175 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 176 | detection_source: go.mod 177 | last_updated_by: Clivern 178 | last_updated_on: 2021-02-24 20:47:01.000000000 Z 179 | - name: text 180 | description: Go text processing support 181 | package_url: https://pkg.go.dev/golang.org/x/text 182 | version: 0.3.8 183 | license: BSD-3-Clause 184 | open_source: true 185 | hosted_saas: false 186 | category: Libraries 187 | sub_category: Go Modules Packages 188 | image_url: https://img.stackshare.io/package/go-packages/image.png 189 | detection_source_url: https://github.com/Norwik/Goenv/blob/main/go.sum 190 | detection_source: go.mod 191 | last_updated_by: dependabot[bot] 192 | last_updated_on: 2023-05-10 15:11:59.000000000 Z 193 | --------------------------------------------------------------------------------