From f461d701925c6cb5331a50dc6a7fefc8985160b4 Mon Sep 17 00:00:00 2001 From: Shulhan Date: Sun, 6 Jul 2025 16:24:17 +0700 Subject: [wip] all: implement function to install Go tools --- .gitignore | 1 - CHANGELOG.adoc | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 194 --------------------------------------------- README | 203 +++++++++++++++++++++++++++++++++++++++++++++++ README.adoc | 1 + README.md | 190 -------------------------------------------- _doc/README.adoc | 1 + _doc/index.adoc | 21 +++++ beku.go | 151 +++++++++++++++++++++++++++++++++++ 9 files changed, 611 insertions(+), 385 deletions(-) create mode 100644 CHANGELOG.adoc delete mode 100644 CHANGELOG.md create mode 100644 README create mode 120000 README.adoc delete mode 100644 README.md create mode 120000 _doc/README.adoc create mode 100644 _doc/index.adoc diff --git a/.gitignore b/.gitignore index fd7e024..68026fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ *.html /beku /beku.test -/cover.html /cover.out /cpu.prof /mem.prof diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc new file mode 100644 index 0000000..938f921 --- /dev/null +++ b/CHANGELOG.adoc @@ -0,0 +1,234 @@ += Change log for beku +:toc: +:sectanchors: +:sectlinks: + +[#v0_7_1] +== beku v0.7.1 (2022-01-30) + +This is the last release for v0 of beku. +The next release will pivot the beku program to maintain Go versions. + +[#v0_7_1_chores] +=== Chores + +go.mod: update all dependencies:: +This changes affect code that use lib/test.Assert(). + +all: simplify running linter:: +Instead of enabling all linters, run the default linter provided by +the latest golangci-lint. + +all: replace beku_test in testdata with gitsubmodule:: ++ +-- +Previously, the beku_test is bare clone of git repository stored as is. +This cause an initial clone of the beku repository itself does not +recognize it as git repository, which make the test fail: + +---- +go test -v -coverprofile=cover.out ./... || rm -f cover.out +fatal: '/home/ms/go/src/git.sr.ht/~shulhan/beku/testdata/beku_test.git' does not appear to be a git repository +fatal: Could not read from remote repository. + +Please make sure you have the correct access rights +and the repository exists. +2022/01/30 10:56:19 gitInstall: Clone: exit status 128 +FAIL github.com/shuLhan/beku 0.008s +---- + +This changes make the testdata/beku_test.git as git submodule, to make +the test run well. +-- + +[#v0_7_0] +== Beku v0.7.0 (2019-06-28) + +[#v0_7_0_enhancements] +=== Enhancements + +Do not auto cleanup unused repositories:: ++ +-- +Previously, we remove all unused repositories from $GOPATH, which caused +some important repositories that we forgot to track get removed during +freeze operation. + +This commit remove the auto cleanup and only print the unused repositories +to the screen. +-- + +Add $GOCACHE and $HOME to environments on GoInstall:: +The latest Go release will require $GOCACHE or $HOME environment variable +upon running "go install" command. + + +[#v0_6_0] +== Beku v0.6.0 (2019-03-30) + +[#v0_6_0_new_features] +=== New Features + +Make Go command works with Go v1.12 and later:: +This means turning off GO111MODULE when running Go command. + +Install package if missing when issuing SyncAll:: + +[#v0_6_0_enhancements] +=== Enhancement + +Use copy of "golang.org/x/tools/go/vcs":: ++ +-- +The indirect dependencies of package "golang.org/x/tools/go/vcs" is +overwhelming. Using "go get" on this package alone will pull up all +dependency of "golang.org/x/tools". + +To minimize unneeded download of unneeded packages we copy the package +vcs to our own repository including their license file. +-- + +[#v0_5_2] +== Beku v0.5.2 (2018-12-14) + +[#v0_5_2_enhancements] +=== Enhancement + +No need to reinstall all packages after freezing:: + +[#v0_5_2_enhancements] +=== Bug Fix + +git: set package remote URL according to value in database:: + + +[#v0_5_1] +== Beku v0.5.1 (2018-11-02) + +[#v0_5_1_bug_fix] +=== Bug Fix + +Fix sync all that cause version set to true:: + + +[#v0_5_0] +== Beku v0.5.0 (2018-11-01) + +[#v0_5_0_enhancements] +=== Enhancements + +Refactoring test to clone from local directory:: + +Get and save package remote branch in database:: +Some package does not have "master" branch. This will minimize parsing +and filter operation to get default branch before checking out revision. + +Scan package only if its not exist on local system:: +This will minimize freeze operations, removing unneeded fetching revision +(tag/commit) and parsing remote URL. + +Move all commons functions to shared package:: +"github.com/shuLhan/share/lib/{git,io}" + +[#v0_5_0_bug_fixes] +=== Bug Fixes + +cmd/beku: fix parsing multiple subcommand on Sync:: +Sync operation should accept both update and no dependency options in one +line as in "-Sud". + +Scan: Update package version only if current and new package both are tag:: + +env: fix get package from database that return first match by prefix:: +In case two packages have the same prefix, for example "a" and "a-a", +the GetPackageFromDB will always return "a" when the parameter importPath +is "a-a". + +Fix sync "--into" command:: + +[#v0_4_0] +== Beku v0.4.0 (2018-09-04) + +[#v0_4_0_breaking_changes] +=== Breaking Changes + +Remove vendor tools: gdm and govendor:: ++ +-- +govendor [1], cannot handle transitive dependencies (error when building +Consul) + +Turn out gdm [2] is not vendor tool, its use GOPATH the same as beku. Using +`gdm` will result in inconsistent build if two or more package depends on the +same dependency. For example, package A and B depends on X, package A +depends on X v0.4.0 and package B depends on X v0.5.0, while our repository +is depends on X v0.6.0. Running beku with the following order: `beku` on X, +`gdm` on A, and then `gdm` on B, will result in package X will be set to +v0.5.0, not v0.6.0. +-- + +Do not use "git stash" in pre and post version checking:: +Using "git stash" introduce many problems when rebuilding package after +update. + +[1] https://github.com/kardianos/govendor/issues/348 +[2] https://github.com/sparrc/gdm + +[#v0_4_0_enhancements] +=== Enhancements + +Add newline on each freeze commands and on each package when doing reinstall +all:: + +Add option "--version", to display current command version:: + + +[#v0_3_0] +== Beku v0.3.0 (2018-06-06) + +[#v0_3_0_new_features] +=== New Features + +* Use vendor tools to install dependencies. The following vendor tools is + known by beku: gdm, govendor, dep. +* Add option "-d" or "--nodeps" to disable installing dependencies +* Add common option "-V, --vendor" to work with vendor directory +* Able to install missing dependencies +* Handle custom import URL + +[#v0_3_0_bug_fixes] +=== Bug Fixes + +* Fix panic if package not found in database +* Clean non-empty directory on installation, after confirmed by user +* Save database on first time sync + + +[#v0_2_0] +== Beku v0.2.0 (2018-05-31) + +[#v0_2_0_new_features] +=== New Features + +* Add operation to exclude package from database +* Add option "--noconfirm" to by pass confirmation + +[#v0_2_0_bug_fixes] +=== Bug Fixes + +* Fetch new package commits before updating version +* Fix scan on non-exist "$GOPATH/src" directory +* package: GoInstall: set default PATH if it's empty + + +[#v0_1_0] +== Beku v0.1.0 (2018-05-27) + +In this version, beku can handle the following operations, + +* Scanning and saving all dependencies in GOPATH (-S) +* Installing a package (-S ) +* Updating a package (-S ) +* Removing a package with or without dependencies (-R[-s] ) +* Updating all packages in database (-Su) +* Freezing all packages (-B) diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 0c83c10..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,194 +0,0 @@ -# Beku v0.7.1 (2022-01-30) - -This is the last release for v0 of beku. -The next release will pivot the beku program to maintain Go versions. - -## Chores - -* go.mod: update all dependencies - - This changes affect code that use lib/test.Assert(). - -* all: simplify running linter - - Instead of enabling all linters, run the default linter provided by - the latest golangci-lint. - -* all: replace beku_test in testdata with gitsubmodule - - Previously, the beku_test is bare clone of git repository stored as is. - This cause an initial clone of the beku repository itself does not - recognize it as git repository, which make the test fail: - - go test -v -coverprofile=cover.out ./... || rm -f cover.out - fatal: '/home/ms/go/src/git.sr.ht/~shulhan/beku/testdata/beku_test.git' does not appear to be a git repository - fatal: Could not read from remote repository. - - Please make sure you have the correct access rights - and the repository exists. - 2022/01/30 10:56:19 gitInstall: Clone: exit status 128 - FAIL github.com/shuLhan/beku 0.008s - - This changes make the testdata/beku_test.git as git submodule, to make - the test run well. - - -# Beku v0.7.0 (2019-06-28) - -## Enhancements - -- Do not auto cleanup unused repositories. - - Previously, we remove all unused repositories from $GOPATH, which caused - some important repositories that we forgot to track get removed during - freeze operation. - - This commit remove the auto cleanup and only print the unused repositories - to the screen. - -- Add $GOCACHE and $HOME to environments on GoInstall - - The latest Go release will require $GOCACHE or $HOME environment variable - upon running "go install" command. - - -# Beku v0.6.0 (2019-03-30) - -## New Features - -- Make Go command works with Go v1.12 and later. - This means turning off GO111MODULE when running Go command. - -- Install package if missing when issuing SyncAll - -## Enhancement - -- Use copy of "golang.org/x/tools/go/vcs". - - The indirect dependencies of package "golang.org/x/tools/go/vcs" is - overwhelming. Using "go get" on this package alone will pull up all - dependency of "golang.org/x/tools". - - To minimize unneeded download of unneeded packages we copy the package - vcs to our own repository including their license file. - - -# Beku v0.5.2 (2018-12-14) - -## Enhancement - -- No need to reinstall all packages after freezing - -## Bug Fix - -- git: set package remote URL according to value in database - - -# Beku v0.5.1 (2018-11-02) - -## Bug Fix - -- Fix sync all that cause version set to true - - -# Beku v0.5.0 (2018-11-01) - -## Enhancements - -- Refactoring test to clone from local directory - -- Get and save package remote branch in database - Some package does not have "master" branch. This will minimize parsing - and filter operation to get default branch before checking out revision. - -- Scan package only if its not exist on local system. - This will minimize freeze operations, removing unneeded fetching revision - (tag/commit) and parsing remote URL. - -- Move all commons functions to shared package - "github.com/shuLhan/share/lib/{git,io}" - -## Bug Fixes - -- cmd/beku: fix parsing multiple subcommand on Sync - Sync operation should accept both update and no dependency options in one - line as in "-Sud". - -- Scan: Update package version only if current and new package both are tag - -- env: fix get package from database that return first match by prefix - In case two packages have the same prefix, for example "a" and "a-a", - the GetPackageFromDB will always return "a" when the parameter importPath - is "a-a". - -- Fix sync "--into" command - -# Beku v0.4.0 (2018-09-04) - -## Breaking Changes - -- Remove vendor tools: gdm and govendor - -govendor [1], cannot handle transitive dependencies (error when building -Consul) - -Turn out gdm [2] is not vendor tool, its use GOPATH the same as beku. Using -`gdm` will result in inconsistent build if two or more package depends on the -same dependency. For example, package A and B depends on X, package A -depends on X v0.4.0 and package B depends on X v0.5.0, while our repository -is depends on X v0.6.0. Running beku with the following order: `beku` on X, -`gdm` on A, and then `gdm` on B, will result in package X will be set to -v0.5.0, not v0.6.0. - -- Do not use "git stash" in pre and post version checking. Using "git stash" - introduce many problems when rebuilding package after update. - -[1] https://github.com/kardianos/govendor/issues/348 -[2] https://github.com/sparrc/gdm - -## Enhancements - -- Add newline on each freeze commands and on each package when doing reinstall - all. -- Add option "--version", to display current command version. - -# Beku v0.3.0 (2018-06-06) - -## New Features - -- Use vendor tools to install dependencies. The following vendor tools is - known by beku: gdm, govendor, dep. -- Add option "-d" or "--nodeps" to disable installing dependencies -- Add common option "-V, --vendor" to work with vendor directory -- Able to install missing dependencies -- Handle custom import URL - -## Bug Fixes - -- Fix panic if package not found in database -- Clean non-empty directory on installation, after confirmed by user -- Save database on first time sync - -# Beku v0.2.0 (2018-05-31) - -## New Features - -- Add operation to exclude package from database -- Add option "--noconfirm" to by pass confirmation - -## Bug Fixes - -- Fetch new package commits before updating version -- Fix scan on non-exist "$GOPATH/src" directory -- package: GoInstall: set default PATH if it's empty - -# Beku v0.1.0 (2018-05-27) - -In this version, beku can handle the following operations, - -- Scanning and saving all dependencies in GOPATH (-S) -- Installing a package (-S ) -- Updating a package (-S ) -- Removing a package with or without dependencies (-R[-s] ) -- Updating all packages in database (-Su) -- Freezing all packages (-B) diff --git a/README b/README new file mode 100644 index 0000000..ff934d9 --- /dev/null +++ b/README @@ -0,0 +1,203 @@ += Beku +:toc: +:sectanchors: +:sectlinks: + +Beku is a library and program to manage Go version and installed module, +program, or source codes in user's GOPATH environment. + + +== Issues + +This section describe list of issues trying to solve by this program. + +Manage installation of Go tools binary. + +Manage installation of Go module, including its source code and installed +binaries. + + +== Workspace structure + +`${GOPATH}`:: This is the root of beku working directory. + +`${GOPATH}/beku/cache`:: A directory that store temporary files, for example +downloaded Go tools binary in compressed format. + +`${GOPATH}/beku/share`:: Contains the extracted Go tools binary, using the +following directory name format `goX.Y.Z`. + +`${GOPATH}/beku/go`:: Symlink to one of Go version in the share directory, +indicated the active Go version. +The path will be set as the value of ${GOROOT}. + +`${GOPATH}/beku/go_bootstrap`:: Symlink to one of Go version in the share +directory, indicated the active Go version used for bootstrapping (building +the Go from source). +The path will be set as the value of ${GOROOT_BOOTSTRAP}. + +`${GOPATH}/beku/var/beku.db`:: Beku database file. +Beku read and write the package database into a file named "beku.db". ++ +At first execution, beku will try to open the database file. +If the file does not exist, beku will scan entire "${GOPATH}/src" and record +it into the database file. + + +== Global options + + --noconfirm + +No confirmation will be asked on any operation. +Useful when running beku inside a script. + + -d, --nodeps + +Do not install any missing dependencies. This options can be used on freeze +or sync operations. + + +== Install operation + + install <"go"|module>[@version] + +Install a Go tools or module. + +Given a parameter "go", it will download the Go tools from "https://go.dev/dl" +using the current operating system and architecture (or values of $GOOS and +$GOARCH) with version defined in parameter "version". +If no version is given, it will clone the Go source code and build it. + +Given a module import path, beku will try to clone the module source code into +source directory and checkout the tag defined in the "version". +If no tag found, it will use the latest commit on master branch. + +If package already exist, it will reset the HEAD to the version that is set +on database file. + +=== Options + + [--into ] + +This option will install the package import path into custom directory. +It is useful if you have the fork of the main package but want to install +it to the legacy directory. + +=== Examples + + $ beku install go@1.17.6 + +Download the Go tools version 1.17.6 into `${GOPATH}/beku/cache/`, and extract +it into `${GOPATH}/beku/share/go1.17.6`. + + $ beku install golang.org/x/text + +Download package `golang.org/x/text` into `${GOPATH}/src/golang.org/x/text`, +and set their version to the latest commit on branch master. + + $ beku -S github.com/golang/text --into golang.org/x/text + +Download package `github.com/golang/text` into +`${GOPATH}/src/golang.org/x/text`, and set their version to the latest commit +on branch master. + + $ beku -S golang.org/x/text@v0.3.0 + +Download package `golang.org/x/text` into `${GOPATH}/src/golang.org/x/text` +and checkout the tag `v0.3.0` as the working version. + + $ beku -S golang.org/x/text@5c1cf69 + +Download package `golang.org/x/text` into `${GOPATH}/src/golang.org/x/text` +and checkout the commit `5c1cf69` as the working version. + + +== Freeze Operation + + -B, --freeze + +Operate on the package database and user's environment. This operation will +ensure that all packages listed on database file is installed with their +specific version. Also, all packages that are not registered will +be removed from "src" and "pkg" directories. + +== Database Operation + + -D, --database + +Modify the package database. This operation required one of the options +below. + +=== Options + + -e, --exclude + +Remove list of package by import path from database and add mark it as +excluded package. Excluded package will be ignored on future operations. + +=== Examples + + $ beku -De github.com/shuLhan/beku + +Exclude package "github.com/shuLhan/beku" from future scanning, +installation, or removal operations. + + +== Query Operation + + -Q, --query [pkg ...] + +Query the package database. + + +== Remove Operation + + -R, --remove [pkg] + +Remove package from environment, including source and installed binaries and +archives. + +=== Options + + [-s,--recursive] + +Also remove all target dependencies, as long as is not required by other +packages. + +=== Examples + + $ beku -R github.com/shuLhan/beku + +Remove package "github.com/shuLhan/beku" source in "{prefix}/src", +their installed binaries in "{prefix}/bin", and their installed archives on +"{prefix}/pkg/{GOOS}\_{GOARCH}". + + $ beku -R github.com/shuLhan/beku --recursive + $ beku -Rs github.com/shuLhan/beku + +Remove package "github.com/shuLhan/beku" source in "{prefix}/src", +their installed binaries in "{prefix}/bin", their installed archives on +"{prefix}/pkg/{GOOS}\_{GOARCH}", and all their dependencies. + +== Development + +This repository use git submodules to test functionalities that use `git +clone`. +Command for cloning git repository for new development: + + $ git clone --recurse-submodules + +or for existing clone, + + $ git submodule update --init --recursive + +Then run `make` to make sure all test run well, + + $ make + + +== Known Limitations + +- Only work with package hosted with Git on HTTPS or SSH. + +- Tested only on Git v2.17 or greater diff --git a/README.adoc b/README.adoc new file mode 120000 index 0000000..100b938 --- /dev/null +++ b/README.adoc @@ -0,0 +1 @@ +README \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index c719057..0000000 --- a/README.md +++ /dev/null @@ -1,190 +0,0 @@ -# Beku - -Beku is a library and program to manage packages in user's environment (GOPATH -directory). - -For beku as library see the following -[![GoDoc](https://godoc.org/github.com/shuLhan/beku?status.svg)](https://godoc.org/github.com/shuLhan/beku). - -For beku as program see the below documentation or at -[![GoDoc](https://godoc.org/github.com/shuLhan/beku/cmd/beku?status.svg)](https://godoc.org/github.com/shuLhan/beku/cmd/beku). - -## Beku program - -Beku is command line program to manage packages in user's environment (GOPATH -directory). Beku provide syntax like `pacman`. - -Beku read and write the package database into a file named "beku.db". - -At first execution, beku will try to open the package database in current -directory. If no file found, it will try to open -"{prefix}/var/beku/beku.db". When both locations does not provide -package database, beku will scan entire "{prefix}/src" and write the -package database into "{prefix}/var/beku/beku.db". - -## Global Options - - --noconfirm - -No confirmation will be asked on any operation. Useful when running beku -inside a script. - - -d, --nodeps - -Do not install any missing dependencies. This options can be used on freeze -or sync operations. - - -## Freeze Operation - - -B, --freeze - -Operate on the package database and user's environment. This operation will -ensure that all packages listed on database file is installed with their -specific version. Also, all packages that are not registered will -be removed from "src" and "pkg" directories. - -## Database Operation - - -D, --database - -Modify the package database. This operation required one of the options -below. - -### Options - - -e, --exclude - -Remove list of package by import path from database and add mark it as -excluded package. Excluded package will be ignored on future operations. - -### Examples - - $ beku -De github.com/shuLhan/beku - -Exclude package "github.com/shuLhan/beku" from future scanning, -installation, or removal operations. - -## Query Operation - - -Q, --query [pkg ...] - -Query the package database. - -## Remove Operation - - -R, --remove [pkg] - -Remove package from environment, including source and installed binaries and -archives. - -### Options - - [-s,--recursive] - -Also remove all target dependencies, as long as is not required by other -packages. - -### Examples - - $ beku -R github.com/shuLhan/beku - -Remove package "github.com/shuLhan/beku" source in "{prefix}/src", -their installed binaries in "{prefix}/bin", and their installed archives on -"{prefix}/pkg/{GOOS}\_{GOARCH}". - - $ beku -R github.com/shuLhan/beku --recursive - $ beku -Rs github.com/shuLhan/beku - -Remove package "github.com/shuLhan/beku" source in "{prefix}/src", -their installed binaries in "{prefix}/bin", their installed archives on -"{prefix}/pkg/{GOOS}\_{GOARCH}", and all their dependencies. - -## Sync Operation - - -S, --sync - -Synchronizes package. Given a package import path, beku will try to clone -the package into source directory and set the package version to -latest the tag. If no tag found, it will use the latest commit on master -branch. A specific version can be set using "@version" suffix. - -If package already exist, it will reset the HEAD to the version that is set -on database file. - -If no parameter is given, beku will do a rescan, checking for new packages. - -Beku will install the package dependencies manually. - -### Options - - [--into ] - -This option will install the package import path into custom directory. -It is useful if you have the fork of the main package but want to install -it to the legacy directory. - - [-u,--update] - -Fetch new tag or commit from remote repository. User will be asked for -confirmation before upgrade. - -### Examples - - $ beku -S golang.org/x/text - -Download package `golang.org/x/text` into `{prefix}/src/golang.org/x/text`, -and set their version to the latest commit on branch master. - - $ beku -S github.com/golang/text --into golang.org/x/text - -Download package `github.com/golang/text` into -`{prefix}/src/golang.org/x/text`, and set their version to the latest commit -on branch master. - - $ beku -S golang.org/x/text@v0.3.0 - -Download package `golang.org/x/text` into `{prefix}/src/golang.org/x/text` -and checkout the tag `v0.3.0` as the working version. - - $ beku -S golang.org/x/text@5c1cf69 - -Download package `golang.org/x/text` into `{prefix}/src/golang.org/x/text` -and checkout the commit `5c1cf69` as the working version. - - $ beku -Su - -Update all packages in database to new tag or commits with approval from -user. - - -## Development - -This repository use git submodules to test functionalities that use `git -clone`. -Command for cloning git repository for new development: - - $ git clone --recurse-submodules - -or for existing clone, - - $ git submodule update --init --recursive - -Then run `make` to make sure all test run well, - - $ make - -## Known Limitations - -- Only work with package hosted with Git on HTTPS or SSH. - -- Tested only on Git v2.17 or greater - - -## References - -[1] https://www.archlinux.org/pacman/ - -[2] https://github.com/kardianos/govendor/issues/348 - -[3] https://github.com/sparrc/gdm diff --git a/_doc/README.adoc b/_doc/README.adoc new file mode 120000 index 0000000..a7ab0b1 --- /dev/null +++ b/_doc/README.adoc @@ -0,0 +1 @@ +../README.adoc \ No newline at end of file diff --git a/_doc/index.adoc b/_doc/index.adoc new file mode 100644 index 0000000..0b3162e --- /dev/null +++ b/_doc/index.adoc @@ -0,0 +1,21 @@ += beku +:toc: +:sectanchors: +:sectlinks: + +== Documentation + +link:CHANGELOG.html[CHANGELOG]:: History of release. + +link:README.html[README]:: Manual page for beku. + +== Development + +https://git.sr.ht/~shulhan/beku[Repository^]:: +Link to the source code. + +https://lists.sr.ht/~shulhan/beku[Mailing list^]:: +Place for discussion and sending patches. + +https://todo.sr.ht/~shulhan/beku[Issues^]:: +Link to open an issue or request for new feature. diff --git a/beku.go b/beku.go index 9ca8500..fe1796d 100644 --- a/beku.go +++ b/beku.go @@ -6,7 +6,13 @@ package beku import ( "errors" + "fmt" + "go/build" "os" + "path/filepath" + "strings" + + libhttp "git.sr.ht/~shulhan/pakakeh.go/lib/http" ) const ( @@ -87,3 +93,148 @@ var ( defStdout = os.Stdout //nolint: gochecknoglobals defStderr = os.Stderr //nolint: gochecknoglobals ) + +type Beku struct { + env *Env + httpc *libhttp.Client +} + +func NewBeku(env *Env) (beku *Beku) { + beku = &Beku{ + env: env, + } + return beku +} + +// Install the Go binary or a module. +func (beku *Beku) Install(module string) (err error) { + module = strings.ToLower(module) + + var ( + nameVersion = strings.Split(module, "@") + modName = nameVersion[0] + modVersion string + ) + + if len(nameVersion) > 1 { + modVersion = nameVersion[1] + } + + if modName == "go" { + if len(modVersion) == 0 { + return beku.installGoFromSource() + } + return beku.installGo(modVersion) + } + + return nil +} + +// checkInstalledGo check if specific Go tools version already installed. +// It will return nil if it not exist. +func (beku *Beku) checkInstalledGo(version string) (err error) { + pathGo := filepath.Join(beku.env.prefix, "beku", "share", "go"+version) + _, err = os.Stat(pathGo) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + return fmt.Errorf("%s already installed", pathGo) +} + +func (beku *Beku) downloadGo(version string) (err error) { + var ( + logp = "downloadGo" + compression = "tar.gz" + ) + + if build.Default.GOOS == "windows" { + compression = "zip" + } + + goDownload := fmt.Sprintf("go%s.%s-%s.%s", version, + build.Default.GOOS, build.Default.GOARCH, + compression) + + pathCache := filepath.Join(beku.env.prefix, "beku", "cache", goDownload) + + fi, err := os.Stat(pathCache) + if err != nil { + if !os.IsNotExist(err) { + return fmt.Errorf("%s: %w", logp, err) + } + err = nil + } + if fi != nil { + // The downloaded file already exist on cache directory. + return nil + } + + fout, err := os.Create(pathCache) + if err != nil { + return fmt.Errorf("%s: %w", logp, err) + } + + beku.initHttpClient() + + downloadReq := libhttp.DownloadRequest{ + ClientRequest: libhttp.ClientRequest{ + Path: "/dl/" + goDownload, + }, + Output: fout, + } + + _, err = beku.httpc.Download(downloadReq) + if err != nil { + errClose := fout.Close() + if errClose != nil { + err = fmt.Errorf("%s: %w: %s", logp, err, errClose) + } else { + err = fmt.Errorf("%s: %w", logp, err) + } + return err + } + + err = fout.Close() + if err != nil { + return fmt.Errorf("%s: %w", logp, err) + } + + return nil +} + +func (beku *Beku) initHttpClient() { + if beku.httpc != nil { + return + } + opts := libhttp.ClientOptions{ + ServerURL: "https://go.dev", + } + beku.httpc = libhttp.NewClient(opts) +} + +// installGo install specific Go tools version by downloading the binary from +// official server and extract it into $GOPATH/beku/share/go{version}. +func (beku *Beku) installGo(version string) (err error) { + var ( + logp = "installGo " + version + ) + + err = beku.checkInstalledGo(version) + if err != nil { + return fmt.Errorf("%s: %w", logp, err) + } + + err = beku.downloadGo(version) + if err != nil { + return fmt.Errorf("%s: %w", logp, err) + } + + return nil +} + +func (beku *Beku) installGoFromSource() (err error) { + return nil +} -- cgit v1.3