Repository: azukaar/GuPM Branch: master Commit: 53371f326bf9 Files: 52 Total size: 92.8 KB Directory structure: gitextract_o4jwiwqj/ ├── .circleci/ │ └── config.yml ├── .gitignore ├── .gupm_rc.gs ├── LICENCE ├── build.gs ├── ci/ │ └── publish.gs ├── docs/ │ └── install.sh ├── gupm.json ├── plugins/ │ ├── provider-git/ │ │ ├── gupm.json │ │ ├── postGetDependency.gs │ │ └── resolveDependencyLocation.gs │ ├── provider-http/ │ │ ├── gupm.json │ │ └── resolveDependencyLocation.gs │ └── provider-https/ │ ├── gupm.json │ └── resolveDependencyLocation.gs ├── readme.md ├── src/ │ ├── addDependency.go │ ├── bootstrap.go │ ├── cache.go │ ├── cli.go │ ├── cli_test.go │ ├── defaultProvider/ │ │ ├── defaultProvider.go │ │ └── publish.go │ ├── distribution_gupm.json │ ├── gitHooks.go │ ├── global.go │ ├── index.go │ ├── installProject.go │ ├── jsVm/ │ │ └── jsVm.go │ ├── plugins.go │ ├── provider/ │ │ ├── bootstrap.go │ │ ├── dependencyTree.go │ │ ├── install.go │ │ ├── provider.go │ │ └── publish.go │ ├── publish.go │ ├── removeDependency.go │ ├── self.go │ ├── test.go │ ├── ui/ │ │ └── log.go │ ├── utils/ │ │ ├── files.go │ │ ├── json.go │ │ ├── repo.go │ │ ├── types.go │ │ ├── untar.go │ │ └── utils.go │ └── windows/ │ └── windows_install.go └── tests/ ├── install/ │ └── normal.test.gs ├── make/ │ ├── nodeps.test.gs │ └── normal.test.gs ├── plugins/ │ └── normal.test.gs └── scripts/ └── normal.test.gs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ version: 2 jobs: build: docker: - image: golang:1.12.5-stretch steps: - checkout - run: name: Install GuPM command: curl -fsSL https://azukaar.github.io/GuPM/install.sh | bash - run: name: Install provider command: g plugin install https://azukaar.github.io/GuPM-official/repo:provider-go - run: name: Make command: g make - run: name: Build command: g build - run: name: Cleanup command: g pl delete provider-go - run: name: Run tests command: g test - run: name: Run unit tests command: g gotest - run: name: Install provider command: g plugin install https://azukaar.github.io/GuPM-official/repo:provider-go - run: name: Build Linux command: g ci/publish - run: name: Build Mac command: g ci/publish mac - run: name: Build Windows command: g ci/publish windows - run: git config user.email "nobody@circleci.com" - run: git config user.name "Circle Ci" - run: git add docs - run: git commit -m "[skip ci] Release" - run: git push origin master workflows: version: 2 build-all: jobs: - build: filters: branches: only: - master ================================================ FILE: .gitignore ================================================ .DS_Store .bin build .vscode node_modules temp cache sampleproject test.gs package.json go_modules gupm_modules ================================================ FILE: .gupm_rc.gs ================================================ env("GOPATH", run("go", ["env", "GOROOT"]) + ":" + pwd() + "/go_modules") ================================================ FILE: LICENCE ================================================ Copyright (c) Yann Stepienik (yann.stepienik@gmail.com) Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ================================================ FILE: build.gs ================================================ removeFiles(["build/dg", "build/plugins", "build/gupm.json"]) var goArgs = ["build", "-o", "build/dg"] goArgs = goArgs.concat(dir("src/*.go")) exec("go", goArgs) copyFiles("plugins", "build/plugins") copyFiles("src/distribution_gupm.json", "build/gupm.json") console.log("\nBuild done! 💖") ================================================ FILE: ci/publish.gs ================================================ removeFiles("gupm") var goArgs = ["build", "-o"] if(typeof $1 != "undefined" && $1 == "windows") { goArgs.push("gupm/g.exe") } else { goArgs.push("gupm/g") } goArgs = goArgs.concat(dir("src/*.go")) copyFiles("plugins", "gupm/plugins") copyFiles("src/distribution_gupm.json", "gupm/gupm.json") if(typeof $1 != "undefined" && $1 == "mac") { env("GOOS", "darwin") env("go version", "amd64") exec("go", goArgs) var arch = tar("gupm") removeFiles("docs/gupm_mac.tar.gz") saveFileAt(arch, "docs/gupm_mac.tar.gz") } if(typeof $1 != "undefined" && $1 == "windows") { env("GOOS", "windows") env("GOARCH", "amd64") exec("go", goArgs) var arch = tar("gupm") removeFiles("docs/gupm_windows.tar.gz") saveFileAt(arch, "docs/gupm_windows.tar.gz") } else { env("GOOS", "linux") env("GOARCH", "amd64") exec("go", goArgs) var arch = tar("gupm") removeFiles("docs/gupm.tar.gz") saveFileAt(arch, "docs/gupm.tar.gz") } removeFiles("gupm") ================================================ FILE: docs/install.sh ================================================ #!/bin/sh if [ "$(uname)" = "Darwin" ]; then curl --output gupm.tar.gz https://azukaar.github.io/GuPM/gupm_mac.tar.gz elif [ "$(uname)" = "Linux" ]; then curl --output gupm.tar.gz https://azukaar.github.io/GuPM/gupm.tar.gz fi mkdir ~/.gupm tar -C ~/.gupm -zxvf gupm.tar.gz chmod -R 755 ~/.gupm/gupm/ rm gupm.tar.gz if [ -d "/usr/local/bin" ] then if [ -f "/usr/local/bin/g" ] then rm /usr/local/bin/g fi ln -s ~/.gupm/gupm/g /usr/local/bin/g else if [ -f "/bin/g" ] then rm /bin/g fi ln -s ~/.gupm/gupm/g /bin/g fi if [ "$(uname)" = "Darwin" ]; then read -r -p "Do you want to make Homebrew your default provider? (Recommended) [y/N] " response if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]] then g plugin install https://azukaar.github.io/GuPM-official/repo:provider-brew sed -ie 's/"defaultProvider": "gupm"/"defaultProvider": "os"/' ~/.gupm/gupm/gupm.json fi fi echo "------" echo "Installaton complete" echo "------" ================================================ FILE: gupm.json ================================================ { "cli": { "aliases": { "gotest": "go test ./src ./src/utils", "start": "./build/dg" } }, "dependencies": { "default": { "go://github.com/Masterminds/semver": "master", "go://github.com/bmatcuk/doublestar": "master", "go://github.com/fatih/color": "master", "go://github.com/gosuri/uilive": "master", "go://github.com/mattn/go-isatty": "master", "go://github.com/mitchellh/go-homedir": "master", "go://github.com/otiai10/copy": "master", "go://github.com/robertkrimen/otto": "master", "go://github.com/stretchr/testify": "master", "go://gopkg.in/sourcemap.v1": "v1.0.5" }, "defaultProvider": "go" }, "git": { "hooks": { "precommit": "gofmt -w -s src/index.go $StagedFiles(**/*.go)", "prepush": [ "g test", "g gotest" ] } }, "name": "gupm", "version": "1.2.1" } ================================================ FILE: plugins/provider-git/gupm.json ================================================ { "name": "provider-git", "version": "0.0.1", "config": { "default": { "entrypoint": "gupm.json", "installPath": "gupm_modules" } } } ================================================ FILE: plugins/provider-git/postGetDependency.gs ================================================ // Provider : name of provider (npm) // Name : name of downloaded package // Version : version of downloaded package // Url : URL of downloaded package // Path : Future path of downloaded package // Result : binary downloaded var folder = unzip(Result); var firstChildrenName = Object.keys(folder.Children)[0]; var firstChildren = folder.Children[firstChildrenName]; saveFileAt(firstChildren, Path); saveLockDep(Path); Path; ================================================ FILE: plugins/provider-git/resolveDependencyLocation.gs ================================================ var name = Dependency.name; var version = Dependency.version; Dependency.url = 'https://' + name + '/archive/master.zip' if(Dependency.version === "*.*.*") { Dependency.version = "master" } // https://github.com/src-d/go-git/archive/6e931e4fdefa202c76242109453447182ae16444.zip Dependency; ================================================ FILE: plugins/provider-http/gupm.json ================================================ { "name": "provider-http", "version": "0.0.1", "config": { "default": { "entrypoint": "gupm.json", "installPath": "gupm_modules" } } } ================================================ FILE: plugins/provider-http/resolveDependencyLocation.gs ================================================ var name = Dependency.name; if(name.split(':').length > 1) { var version = Dependency.version var names = name.split(':') // exact version if (version.match(/^\d+\.\d+\.\d+/) && !version.match(/\sx/)) { } // test ranges else { var repoList = 'http://' + names[0] + '/gupm_repo.json' var payload = httpGetJson(repoList); if(payload.packages[names[1]] && payload.packages[names[1]].length) { var versionList = payload.packages[names[1]]; version = semverLatestInRange(version, versionList); } else { console.error("Package "+names[1]+" not found in " + names[0]) exit(1) } } var realName = names[1] var namespace = '' if( realName.split('/').length > 1) { namespace = (realName.split('/')[0] + '/').replace("OS", _OSNAME) realName = realName.split('/')[1] } Dependency.url = 'http://' + names[0] + '/' + namespace + realName + '/' + version + '/' + realName + '-' + version + '.tgz' Dependency.version = version } else { Dependency.url = 'http://' + name } Dependency; ================================================ FILE: plugins/provider-https/gupm.json ================================================ { "name": "provider-https", "version": "0.0.1", "config": { "default": { "entrypoint": "gupm.json", "installPath": "gupm_modules" } } } ================================================ FILE: plugins/provider-https/resolveDependencyLocation.gs ================================================ var name = Dependency.name; if(name.split(':').length > 1) { var version = Dependency.version var names = name.split(':') // exact version if (version.match(/^\d+\.\d+\.\d+/) && !version.match(/\sx/)) { } // test ranges else { var repoList = 'https://' + names[0] + '/gupm_repo.json' var payload = httpGetJson(repoList); if(payload.packages[names[1]] && payload.packages[names[1]].length) { var versionList = payload.packages[names[1]]; version = semverLatestInRange(version, versionList); } else { console.error("Package "+names[1]+" not found in " + names[0]) exit(1) } } var realName = names[1] var namespace = '' if( realName.split('/').length > 1) { namespace = (realName.split('/')[0] + '/').replace("OS", _OSNAME) realName = realName.split('/')[1] } Dependency.url = 'https://' + names[0] + '/' + namespace + realName + '/' + version + '/' + realName + '-' + version + '.tgz' Dependency.version = version } else { Dependency.url = 'https://' + name } Dependency; ================================================ FILE: readme.md ================================================ ![dog](./docs/banner.png) ---




Global Universal Project Manager -- Package manager, CLI tool, and scripts for all your projects and your system. Whether you are a developer managing dependencies, or a sysadmin looking for your new toolbelt (bye bash!) you are among friends. Check the Wiki for documentation.

* ⏱ **Fast**. Written in native code, with real multi-threading. * 👓 **Smart**. Memory efficient solution using hard-link, which do not duplicate dependencies across project. * 🌍 **Global**. Windows, Mac and Linux compatibility. * 🌈 **Universal**. Usable in any kind of project (Ruby, JS, Go, C, Python, etc...) * 👗 **Customizable**. Flexible plugin system: make GuPM your own. * 👝 **Future Proof**. Let's make this the last PM you will ever need. * 🌳 **Decentralized**. You keep control of the sources you tap into. * 🐳 **No dependencies**. You don't need anythind else (you don't need NPM to use NPM's repository with GuPM) This idea is born from the frustration of having to give up my habits whenever I would switch off Javascript and lose NPM (Whether it would be in Ruby, Go, or even situations outside of coding). GuPM is claiming to take inspiration from the best things in Brew, NPM, Gem, etc... And compile them in a single tool, usable in any project.
* 📦 **Packages Manager**. Install packages from any repository and manage dependencies in a seamless way. * 🖥 **CLI Manager**. Install and use CLI tools in a flexible way without conflicts. * 🚏 **Scripting**. GuPM is bundled with GuScript, allowing you to build cross platform scripts for your project. * 🐙 **Packed with features**. Manage configs, git hooks, parallel executions, environment variables, CI, and more. * 🔥 **Even more to come!** See : [Next](https://github.com/azukaar/GuPM/projects/1#column-5571474) for the roadmap of feature. You are welcomed to contribute!
--- # Getting started : ## Quick links * [Wiki](https://github.com/azukaar/GuPM/wiki) * [Quick Start](https://github.com/azukaar/GuPM/wiki/quick-start) * [Getting started with Node](https://medium.com/@azukaar/gupm-to-manage-your-node-js-project-b7664503f3de?sk=f901b86d888b44dcdb78c644bd5df002) * [Getting started with Go](https://medium.com/@azukaar/gupm-to-manage-your-go-project-5d19c341403c) * [Create your own repository](https://github.com/azukaar/GuPM/wiki/repositories) * [Official GuPM repository](https://github.com/azukaar/GuPM-official) ## Installation ### Linux and Mac OS : ⌨️ `$ curl -fsSL https://azukaar.github.io/GuPM/install.sh | bash` ### Windows 💾 [Windows_install.exe](https://azukaar.github.io/GuPM/windows_install.exe) ## JS/NPM example This example is setting up a project using the [NPM plugin](https://github.com/azukaar/GuPM-official). More details on how to use GuPM with node [here]().

## Go example This example is setting up a project using the [Go plugin](https://github.com/azukaar/GuPM-official). More details on how to use GuPM with Go [here]().

# Dependency Manager ## New projects In order to simply bootstrap a new project you can run `g bootstrap` you can also use `b` and add a provider `g b -p npm` ## Make This command will set up your project by getting dependencies. Adding a -p or --provider argument allows you to specify what provider to use initially. Please note you do NOT need to install npm / gem / whatever to use their corresponding provider, GuPM implement everything itself. ```bash # reads gupm.json g make # reads package.json g make -p npm ``` ## Install ```bash # use default repo [Config in gupm.json] g install mysql g i mysql # use brew g install brew://mysql g install -p brew mysql # use NPM g install npm://react@1 # will save in gupm.json g install -p npm react@1 # will save in package.json ``` More commands [in the wiki](https://github.com/azukaar/GuPM/wiki/cli-references) ## GuPM management ### Plugins GuPM needs plugins to work with various repos : ```bash # Install provider-go from the official repo g plugin install https://azukaar.github.io/GuPM-official/repo:provider-go ``` See https://github.com/azukaar/gupm-official for a list of officially suported plugins. See https://github.com/azukaar/GuPM/wiki/how-to-create-a-provider to create your own. ### updates GuPM can be managed using : ```bash g self upgrade ``` More commands [in the wiki](https://github.com/azukaar/GuPM/wiki/cli-references) ## Write GuPM scripts You can use GuScript to write bash-like files, used for setting up your project, use it, or anything literally. Think of GuScript as a replacement for your bash scripts. ``` // name_setup.gs var name = input('What is your name') echo('Welcome' + name) saveName(name) ``` GuScript is based on javascript, and therefore allow advanced object/arrays manipulations, function definitions, etc... Find more details about the available APIs in the [wiki](https://github.com/azukaar/GuPM/wiki) 1 ## VS Code Add this to your `settings.json` to treat .gs file as javascript (temporary fix to plain text) ``` "files.associations": { "*.gs": "javascript" } ``` # Thanks! Package Icon made by [smashicons](https://www.smashicons.com/) Dog Icon made by [Freepik](https://www.freepik.com/) ================================================ FILE: src/addDependency.go ================================================ package main import ( "./provider" "./ui" "./utils" ) func AddDependency(path string, rls []string) error { var err error var packageConfig utils.Json var depList []map[string]interface{} var depProvider = Provider ui.Title("Add dependency...") if !ProviderWasForced && utils.FileExists(path+utils.Path("/gupm.json")) { config, _ := utils.ReadGupmJson(path + utils.Path("/gupm.json")) if config.Dependencies.DefaultProvider != "" { depProvider = config.Dependencies.DefaultProvider } } err = provider.InitProvider(Provider) if err != nil { return err } providerConfig, err = provider.GetProviderConfig(Provider) if err != nil { return err } packageConfig, err = provider.GetPackageConfig(path) if err != nil { return err } packageConfig, err = provider.PostGetPackageConfig(packageConfig) if err != nil { return err } depList, err = provider.GetDependencyList(packageConfig) if err != nil { return err } ui.Title("Adding to dependency list...") for _, str := range rls { dep := utils.BuildDependencyFromString(depProvider, str) resolved, err := provider.ResolveDependencyLocation(dep) if err != nil || resolved["url"].(string) == "" { ui.Error("Can't resolve", str) return err } dep["version"] = resolved["version"] depList = append(depList, dep) } if packageConfig != nil { err = provider.SaveDependencyList(path, depList) if err != nil { return err } } return nil } ================================================ FILE: src/bootstrap.go ================================================ package main import ( "./provider" "./ui" // "fmt" ) func Bootstrap(path string) error { err := provider.InitProvider(Provider) if err != nil { return err } ui.Title("Bootstrap project") errBoot := provider.Bootstrap(path) if errBoot != nil { return errBoot } else { ui.Title("Bootstrap done! ❤️") } return nil } ================================================ FILE: src/cache.go ================================================ package main import ( "./ui" "./utils" "github.com/mitchellh/go-homedir" ) func CacheClear() { hdir, errH := homedir.Dir() if errH != nil { ui.Error(errH) hdir = "." } folder := utils.Path(hdir + "/.gupm/cache/") utils.RemoveFiles([]string{folder}) } ================================================ FILE: src/cli.go ================================================ package main import ( "fmt" "regexp" "strconv" "strings" "./ui" "./utils" ) type json = utils.Json var ProviderWasForced = false type Arguments map[string]string func (a *Arguments) AsJson() json { res := utils.Json{} for i, v := range *a { res[i] = v } return res } func (a *Arguments) Join() string { res := "" for i, v := range *a { if v == "true" || v == "false" { res += "--" + strings.ToLower(i) + " " } else if ok, _ := regexp.MatchString(`^\$\d+`, i); ok { res += v + " " } else { res += "--" + strings.ToLower(i) + " " + v + " " } } return strings.TrimSpace(res) } func (a *Arguments) AsList() []string { res := []string{} i := 1 for (*a)["$"+strconv.Itoa(i)] != "" { res = append(res, (*a)["$"+strconv.Itoa(i)]) i++ } return res } func (a *Arguments) Shift() { i := 2 for (*a)["$"+strconv.Itoa(i)] != "" { (*a)["$"+strconv.Itoa(i-1)] = (*a)["$"+strconv.Itoa(i)] i++ } (*a)["$"+strconv.Itoa(i-1)] = "" } func GetArgs(args []string) (string, Arguments) { arguments := make(Arguments) next := "" dolsI := 1 if len(args) == 0 { arguments["$0"] = "" return "", arguments } command := args[0] if len(args) < 2 { arguments["$0"] = command return command, arguments } argsToParse := args[1:] for _, value := range argsToParse { nameCheck := regexp.MustCompile(`^--?(\w+)`) tryname := nameCheck.FindString(value) if tryname != "" { long, _ := regexp.MatchString(`^--`, tryname) if long { tryname = tryname[1:] } if next != "" { arguments[next] = "true" next = "" } next = strings.ToUpper(tryname[1:2]) + tryname[2:] } else { if next != "" { arguments[next] = value next = "" } else { arguments["$"+strconv.FormatInt(int64(dolsI), 10)] = value dolsI++ } } } if next != "" { arguments[next] = "true" } arguments["$0"] = command + " " + arguments.Join() return command, arguments } func getProvider(c string, args Arguments) string { gupmConfig := utils.GupmConfig() defaultProvider := "gupm" if c == "install" || c == "global" { defaultProvider = gupmConfig.DefaultProvider } if defaultProvider == "os" { osName := utils.OSNAME() if gupmConfig.OsProviders[osName] != "" { defaultProvider = gupmConfig.OsProviders[osName] } else { ui.Error("No provider set for", osName) return utils.DIRNAME() } } if utils.FileExists("gupm.json") { config, err := utils.ReadGupmJson("gupm.json") if err != nil { ui.Error(err) } else { if config.Cli.DefaultProviders[c] != "" { defaultProvider = config.Cli.DefaultProviders[c] } } } if args["Provider"] != "" { ProviderWasForced = true return args["Provider"] } else if args["P"] != "" { ProviderWasForced = true return args["P"] } else { return defaultProvider } } func ExecCli(c string, args Arguments) (bool, error) { var err error notFound := "Cannot find commmand" shorthands := map[string]string{ "h": "help", "m": "make", "i": "install", "d": "delete", "p": "publish", "b": "bootstrap", "c": "cache", "s": "self", "t": "test", "pl": "plugin", "g": "global", } if shorthands[c] != "" { c = shorthands[c] } if provider := getProvider(c, args); provider != "" { Provider = provider } if c == "help" { fmt.Println("make / m :", "[--provider=]", "Install projects depdencies based on info in the entry point (depends on provider)") fmt.Println("install / i :", "[--provider=]", "Install package") fmt.Println("remove / r :", "[--provider=]", "remove package from module config") fmt.Println("publish / p :", "[--provider=]", "publish a project based on the model of your specific provider") fmt.Println("bootstrap / b :", "[--provider=]", "bootstrap a new project based on the model of your specific provider") fmt.Println("test / t :", "[--provider=] Run project's tests in tests folder.") fmt.Println("global / g :", "install or delete global packages") fmt.Println("cache / c :", "clear or check the cache with \"cache clear\" or \"cache check\"") fmt.Println("self / s :", "self manage gupm. Try g \"self upgrade\" or \"g self uninstall\"") fmt.Println("plugin / pl :", "To install a plugin \"g pl install\". Then use \"g pl create\" to create a new one and \"g pl link\" to test your plugin") } else if c == "make" { BuildGitHooks(".") err = InstallProject(".") } else if c == "install" { err = AddDependency(".", args.AsList()) if err == nil { err = InstallProject(".") } } else if c == "publish" { err = Publish(".", args["$1"]) } else if c == "delete" { err = RemoveDependency(".", args.AsList()) } else if c == "global" { if args["$1"] == "install" || args["$1"] == "i" { args.Shift() GlobalAdd(args.AsList()) } else if args["$1"] == "delete" || args["$1"] == "d" { args.Shift() GlobalDelete(args.AsList()) } else { ui.Error(notFound, args["$1"], "\n", "try install or delete") } } else if c == "plugin" { if args["$1"] == "create" { PluginCreate(".") } else if args["$1"] == "link" { PluginLink(".") } else if args["$1"] == "install" { err = PluginInstall(".", args.AsList()[1:]) } else if args["$1"] == "delete" { PluginDelete(".", args.AsList()[1:]) } else { ui.Error(notFound, args["$1"], "\n", "try cache clear or cache check") } } else if c == "cache" { if args["$1"] == "clear" { CacheClear() } else if args["$1"] == "check" { ui.Error("Not implemented yet.") } else { ui.Error(notFound, args["$1"], "\n", "try cache clear or cache check") } } else if c == "self" { if args["$1"] == "upgrade" { SelfUpgrade() } else if args["$1"] == "uninstall" { SelfUninstall() } else { ui.Error(notFound, args["$1"]) } } else if c == "bootstrap" { err = Bootstrap(".") } else if c == "test" { RunTest("tests") } else if c == "hook" { RunHook(".", args["$1"]) } else { return false, nil } return true, err } ================================================ FILE: src/cli_test.go ================================================ package main import ( "testing" // "reflect" _ "fmt" "github.com/stretchr/testify/require" ) func TestArgs(t *testing.T) { require := require.New(t) // Basic command, args := GetArgs([]string{"make", "-p", "npm"}) require.Equal("make", command) require.Equal(Arguments{"$0": "make --p npm", "P": "npm"}, args) // Nothing command, args = GetArgs([]string{}) require.Equal("", command) require.Equal(Arguments{"$0": ""}, args) // No args command, args = GetArgs([]string{"install"}) require.Equal("install", command) require.Equal(Arguments{"$0": "install"}, args) // bools and long mix command, args = GetArgs([]string{"install", "--dev", "--provider", "npm"}) require.Equal("install", command) require.Equal(Arguments{"$0": "install --dev --provider npm", "Dev": "true", "Provider": "npm"}, args) command, args = GetArgs([]string{"install", "--provider", "npm", "--dev"}) require.Equal("install", command) require.Equal(Arguments{"$0": "install --provider npm --dev", "Dev": "true", "Provider": "npm"}, args) // bools at the end command, args = GetArgs([]string{"install", "--dev"}) require.Equal("install", command) require.Equal(Arguments{"$0": "install --dev", "Dev": "true"}, args) // long args command, args = GetArgs([]string{"install", "--provider", "npm"}) require.Equal("install", command) require.Equal(Arguments{"$0": "install --provider npm", "Provider": "npm"}, args) // anonymous command, args = GetArgs([]string{"install", "npm"}) require.Equal("install", command) require.Equal(Arguments{"$0": "install npm", "$1": "npm"}, args) // asList command, args = GetArgs([]string{"install", "go", "npm"}) require.Equal([]string{"go", "npm"}, args.AsList()) } ================================================ FILE: src/defaultProvider/defaultProvider.go ================================================ package defaultProvider import ( "encoding/json" "io/ioutil" "os" "reflect" "regexp" "../ui" "../utils" ) func Bootstrap(path string) { if utils.FileExists(utils.Path(path + "/gupm.json")) { ui.Error("A project already exists in this folder. Aborting bootstrap.") return } name := ui.WaitForInput("Please enter the name of the project: ") description := ui.WaitForInput("Enter a description: ") author := ui.WaitForInput("Enter the author: ") licence := ui.WaitForInput("Enter the licence (ISC): ") if name == "" { ui.Error("Name cannot be empty. Try again") return } else { if licence == "" { licence = "ISC" } fileContent := `{ "name": "` + name + `", "version": "0.0.1", "description": "` + description + `", "author": "` + author + `", "licence": "` + licence + `" }` ioutil.WriteFile(utils.Path(path+"/gupm.json"), []byte(fileContent), os.ModePerm) } } func GetPackageConfig(entryPoint string) map[string]interface{} { var packageConfig map[string]interface{} b, err := ioutil.ReadFile(entryPoint) if err != nil { ui.Error(err.Error() + " : " + entryPoint) } json.Unmarshal([]byte(string(b)), &packageConfig) return packageConfig } func GetDependency(provider string, name string, version string, url string, path string) (string, error) { return string(utils.HttpGet(url)), nil } func PostGetDependency(provider string, name string, version string, url string, path string, result string) (string, error) { os.MkdirAll(path, os.ModePerm) tarCheck := regexp.MustCompile(`\.tgz$`) tryTar := tarCheck.FindString(url) gzCheck := regexp.MustCompile(`\.gz$`) trygz := gzCheck.FindString(url) zipCheck := regexp.MustCompile(`\.zip$`) tryZip := zipCheck.FindString(url) if tryTar != "" { resultFiles, err := utils.Untar(result) if err != nil { return path, err } resultFiles.SaveAt(path) } else if trygz != "" { resultFiles, err := utils.Ungz(result) if err != nil { return path, err } resultFiles.SaveAt(path) } else if tryZip != "" { resultFiles, err := utils.Unzip(result) if err != nil { return path, err } resultFiles.SaveAt(path) } utils.SaveLockDep(path) return path, nil } func GetDependencyList(config map[string]interface{}) []map[string]interface{} { if config == nil { ui.Error("no config found. Please bootstrap the project with `g bootstrap`") return nil } depEnv, ok := config["dependencies"].(map[string]interface{}) if !ok { ui.Log("no dependencies") return nil } depList, hasDefault := depEnv["default"].(map[string]interface{}) if !hasDefault { ui.Log("no dependencies") return nil } result := make([]map[string]interface{}, 0) for name, value := range depList { dep := utils.BuildDependencyFromString("gupm", name) if reflect.TypeOf(value).String() == "string" { dep["version"] = value } else { valueObject := value.(map[string]interface{}) if valueObject["provider"].(string) != "" { dep["provider"] = valueObject["provider"] } if valueObject["version"].(string) != "" { dep["version"] = valueObject["version"] } } result = append(result, dep) } return result } func ExpandDependency(dependency map[string]interface{}) (map[string]interface{}, error) { configFilePath := utils.Path(dependency["path"].(string) + "/gupm.json") if utils.FileExists(configFilePath) { config := GetPackageConfig(configFilePath) dependency["dependencies"] = make(map[string]interface{}) if config["dependencies"] != nil { dependency["dependencies"] = GetDependencyList(config) } } return dependency, nil } func BinaryInstall(dest string, packagePath string) error { packages, _ := utils.ReadDir(packagePath) for _, dep := range packages { configFilePath := utils.Path(packagePath + "/" + dep.Name() + "/gupm.json") if utils.FileExists(configFilePath) { config := GetPackageConfig(configFilePath) if config["binaries"] != nil { bins := config["binaries"].(map[string]string) for name, relPath := range bins { os.Symlink(utils.Path("/../gupm_modules/"+"/"+dep.Name()+relPath), utils.Path(dest+"/"+name)) } } } } return nil } func SaveDependencyList(path string, depList []map[string]interface{}) error { config := GetPackageConfig(utils.Path(path + "/gupm.json")) if config["dependencies"] == nil { config["dependencies"] = make(map[string]interface{}) } config["dependencies"].(map[string]interface{})["default"] = make(map[string]interface{}) for _, dep := range depList { key := utils.BuildStringFromDependency(map[string]interface{}{ "provider": dep["provider"].(string), "name": dep["name"].(string), }) config["dependencies"].(map[string]interface{})["default"].(map[string]interface{})[key] = dep["version"].(string) } utils.WriteJsonFile(utils.Path(path+"/"+"gupm.json"), config) return nil } ================================================ FILE: src/defaultProvider/publish.go ================================================ package defaultProvider import ( "os" "../ui" "../utils" ) func Publish(path string, namespace string) error { configPath := utils.Path(path + "/gupm.json") if utils.FileExists(configPath) { packageConfig := new(utils.GupmEntryPoint) errConfig := utils.ReadJSON(configPath, &packageConfig) if errConfig != nil { ui.Error("Can't read provider configuration") return errConfig } pname := packageConfig.Name if namespace != "" { pname = namespace + "/" + pname } ppath := utils.Path(path + "/" + packageConfig.Publish.Dest) repoConfig := utils.GetOrCreateRepo(ppath) packageList := repoConfig["packages"].(map[string]interface{}) if packageList[pname] != nil { if utils.Contains(packageList[pname], packageConfig.Version) { ui.Error("Package " + pname + "@" + packageConfig.Version + " already published. Please bump the version number.") return nil } else { packageList[pname] = append(utils.ArrString(packageList[pname]), packageConfig.Version) } } else { packageList[pname] = make([]string, 0) packageList[pname] = append(utils.ArrString(packageList[pname]), packageConfig.Version) } installPath := ppath + utils.Path("/"+pname+"/"+packageConfig.Version) os.MkdirAll(installPath, os.ModePerm) sourcePaths := make([]string, 0) for _, src := range packageConfig.Publish.Source { sourcePaths = append(sourcePaths, utils.Path(path+"/"+src)) } arch, _ := utils.Tar(sourcePaths) arch.SaveAt(installPath + utils.Path("/"+packageConfig.Name+"-"+packageConfig.Version+".tgz")) repoConfig["packages"] = packageList utils.SaveRepo(ppath, repoConfig) } else { ui.Error("Can't find provider configuration") } return nil } ================================================ FILE: src/distribution_gupm.json ================================================ { "name": "gupm", "config": { "default": { "entrypoint": "gupm.json", "installPath": "gupm_modules", "defaultProvider": "gupm", "osProviders": { "linux": "linux", "mac": "brew", "windows": "chocolatey" } } } } ================================================ FILE: src/gitHooks.go ================================================ package main import ( "fmt" "os" "regexp" "strings" "./utils" "github.com/bmatcuk/doublestar" ) func BuildGitHooks(path string) { if utils.FileExists(".git") { if !utils.FileExists(".git/hooks/pre-commit") { utils.WriteFile(".git/hooks/pre-commit", "g hook precommit") } if !utils.FileExists(".git/hooks/pre-push") { utils.WriteFile(".git/hooks/pre-push", "g hook prepush") } } } func runhook(hook string) { commandList := strings.Split(hook, " ") stagedFiles, _ := utils.RunCommand("git", []string{"diff", "--cached", "--name-only"}) unPushedFiles := "" fyg, _ := utils.RunCommand("git", []string{"log", "--branches", "--not", "--remotes", "--name-status", "--oneline"}) unPushedFilesRaw := strings.Split(fyg, "\n") extractFileName := regexp.MustCompile(`^[AM]\b(.*)`) for _, v := range unPushedFilesRaw { if extracted := extractFileName.FindStringSubmatch(v); len(extracted) > 0 { unPushedFiles += strings.Trim(extracted[1], " ") + "\n" } } unPushedFiles = strings.Trim(unPushedFiles, " ") for i, v := range commandList { stagedFilesCheck := regexp.MustCompile(`^\$StagedFiles(\(([\w\/\.\*\-\_]+)?\)?)?`) tryStagedFiles := stagedFilesCheck.FindStringSubmatch(v) unpushedFilesCheck := regexp.MustCompile(`^\$UnpushedFiles(\(([\w\/\.\*\-\_]+)?\)?)?`) tryUnpushedFiles := unpushedFilesCheck.FindStringSubmatch(v) if len(tryStagedFiles) == 3 { if tryStagedFiles[2] == "" { commandList[i] = strings.ReplaceAll(stagedFiles, " ", "\\ ") commandList[i] = strings.ReplaceAll(commandList[i], "\n", " ") } else { commandList[i] = "" stagedFilesList := strings.Split(stagedFiles, "\n") for _, s := range stagedFilesList { isIn, _ := doublestar.Match(tryStagedFiles[2], s) if isIn { commandList[i] += strings.ReplaceAll(s, " ", "\\ ") + " " } } commandList[i] = strings.Trim(commandList[i], " ") } } if len(tryUnpushedFiles) == 3 { if tryUnpushedFiles[2] == "" { commandList[i] = strings.ReplaceAll(unPushedFiles, " ", "\\ ") commandList[i] = strings.ReplaceAll(commandList[i], "\n", " ") } else { commandList[i] = "" unpushedFilesList := strings.Split(unPushedFiles, "\n") for _, s := range unpushedFilesList { isIn, _ := doublestar.Match(tryUnpushedFiles[2], s) if isIn { commandList[i] += strings.ReplaceAll(s, " ", "\\ ") + " " } } commandList[i] = strings.Trim(commandList[i], " ") } } } commandListConsolidated := strings.Trim(strings.Join(commandList, " "), " ") commandList = strings.Split(commandListConsolidated, " ") err := utils.ExecCommand(commandList[0], append(commandList[1:])) if err != nil { fmt.Println(err) os.Exit(1) } } func runhooklist(hooklist string) { hooks := strings.Split(hooklist, ";") for _, hook := range hooks { runhook(strings.Trim(hook, " ")) } } func runHooks(hooks interface{}) { ch := make(chan int) listHooks, isArray := hooks.([]interface{}) if isArray { for _, hook := range listHooks { go func(hook string) { runhooklist(hook) ch <- 0 }(hook.(string)) } for range listHooks { <-ch } } else { runhooklist(hooks.(string)) } } func RunHook(path string, hook string) { config, _ := utils.ReadGupmJson("gupm.json") if hook == "precommit" && config.Git.Hooks.Precommit != nil { runHooks(config.Git.Hooks.Precommit) } if hook == "prepush" && config.Git.Hooks.Prepush != nil { runHooks(config.Git.Hooks.Prepush) } } ================================================ FILE: src/global.go ================================================ package main import ( "os" "path/filepath" "./ui" "./utils" ) func installBins(path string) { var GLOBAL = utils.Path(utils.HOMEDIR(".") + "/.gupm/global") bins := utils.RecursiveFileWalkDir(GLOBAL + "/.bin") for _, bin := range bins { name := filepath.Base(bin) if !utils.FileExists(path + "/" + name) { os.Symlink(bin, path+"/"+name) } } } func GlobalAdd(rls []string) { ui.Title("Installing global dependency...") var GLOBAL = utils.Path(utils.HOMEDIR(".") + "/.gupm/global") if !utils.FileExists(GLOBAL + utils.Path("/gupm.json")) { os.MkdirAll(GLOBAL, os.ModePerm) utils.WriteFile(GLOBAL+utils.Path("/gupm.json"), "{}") } ui.Log("Installing...") AddDependency(GLOBAL, rls) InstallProject(GLOBAL) ui.Log("Add binaries...") if utils.OSNAME() != "windows" { if utils.FileExists("/usr/local/bin/") { installBins("/usr/local/bin/") } else { installBins("/usr/bin/") } } else { ui.Error("Global Installation not supported on Windows yet. Please add .gupm/global/.bin to your PATH") } } func GlobalDelete(rls []string) { var GLOBAL = utils.Path(utils.HOMEDIR(".") + "/.gupm/global") if !utils.FileExists(GLOBAL + utils.Path("/gupm.json")) { os.MkdirAll(GLOBAL, os.ModePerm) utils.WriteFile(GLOBAL+utils.Path("/gupm.json"), "{}") } RemoveDependency(GLOBAL, rls) } ================================================ FILE: src/index.go ================================================ package main import ( "fmt" "os" "path/filepath" "regexp" "strings" "./jsVm" "./ui" "./utils" ) var Provider string func ScriptExists(path string) string { if utils.FileExists(path + ".gs") { return path + ".gs" } else if utils.FileExists(path) && !utils.IsDirectory(path) { return path } else { return "" } } func executeFile(path string, args Arguments) { _, err := jsVm.Run(path, args.AsJson()) if err != nil { ui.Error("File execution failed") ui.Error(err) Exit(1) } } func binFile(name string, args []string) { path := utils.Path("./.bin/" + name) realPath, _ := filepath.EvalSymlinks(path) err := utils.ExecCommand(realPath, args) if err != nil { ui.Error(err) Exit(1) } } func Exit(code int) { ui.Stop() os.Exit(code) } func runAlias(alias string) { commands := strings.Split(alias, ";") for _, command := range commands { commandList := strings.Split(command, " ") err := utils.ExecCommand(commandList[0], append(commandList[1:], os.Args[2:]...)) if err != nil { ui.Error(err) } } } func runAliasList(aliasList string) { aliases := strings.Split(aliasList, ";") for _, alias := range aliases { runhook(strings.Trim(alias, " ")) } } func main() { binFolder := make(map[string]bool) if utils.FileExists(".bin") { files, _ := utils.ReadDir(".bin") for _, file := range files { binFolder[file.Name()] = true } } c, args := GetArgs(os.Args[1:]) if utils.FileExists(".gupm_rc.gs") { executeFile(".gupm_rc.gs", args) } aliases := map[string]interface{}{} if utils.FileExists("gupm.json") { packageConfig, errConfig := utils.ReadGupmJson("gupm.json") if errConfig != nil { ui.Error(errConfig) } else { aliases = packageConfig.Cli.Aliases } } script := ScriptExists(c) if didExec, err := ExecCli(c, args); didExec { if err != nil { ui.Error(err) Exit(1) } if script != "" { executeFile(script, args) } } else if c == "env" || c == "e" { toProcess := os.Args[2:] re := regexp.MustCompile(`([\w\-\_]+)=([\w\-\_]+)`) isEnv := re.FindAllStringSubmatch(toProcess[0], -1) for isEnv != nil { name := isEnv[0][1] value := isEnv[0][2] os.Setenv(name, value) toProcess = toProcess[1:] isEnv = re.FindAllStringSubmatch(toProcess[0], -1) } utils.ExecCommand(toProcess[0], toProcess[1:]) } else if aliases[c] != nil { ch := make(chan int) listAlias, isArray := aliases[c].([]interface{}) if isArray { for _, aliasLine := range listAlias { go func(aliasLine string) { runAliasList(aliasLine) ch <- 0 }(aliasLine.(string)) } for range listAlias { <-ch } } else { runAliasList(aliases[c].(string)) } } else if binFolder[c] == true { binFile(c, os.Args[2:]) } else if script != "" { executeFile(script, args) } else if c == "" { fmt.Println("Welcome to GuPM version 1.0.0 \ntry 'g help' for a list of commands. Try 'g filename' to execute a file.") } else { fmt.Println("Command not found. Try 'g help' or check filename.") Exit(1) } ui.Stop() } ================================================ FILE: src/installProject.go ================================================ package main import ( "errors" "fmt" "sync" "time" "./provider" "./ui" "./utils" "github.com/mitchellh/go-homedir" ) var cacheExpanded = make(map[string]map[string]interface{}) var lock = sync.RWMutex{} var lockList = sync.RWMutex{} func expandDepList(depList []map[string]interface{}) []map[string]interface{} { channel := make(chan int) for index, dep := range depList { go (func(channel chan int, index int, dep map[string]interface{}) { if dep["expanded"] != true { newDep := make(map[string]interface{}) for key, value := range dep { newDep[key] = value } newDep, errExpand := provider.ResolveDependencyLocation(newDep) if newDep == nil { ui.Error("Provider " + dep["provider"].(string) + " didnt resolve " + dep["name"].(string) + "@" + dep["version"].(string)) ui.Error(errExpand) channel <- 0 return } hdir, errH := homedir.Dir() if errH != nil { ui.Error(errH) hdir = "." } _, ok := newDep["url"].(string) if !ok || newDep["url"].(string) == "" { ui.Error("Cannot resolve : " + newDep["name"].(string)) channel <- 1 return } newDep["path"] = hdir + "/.gupm/cache/" + newDep["provider"].(string) + "/" + newDep["name"].(string) + "/" + newDep["version"].(string) if !utils.FileExists(newDep["path"].(string)) || !utils.FileExists(newDep["path"].(string)+"/.gupm_locked") { getRes, errorGD := provider.GetDependency( newDep["provider"].(string), newDep["name"].(string), newDep["version"].(string), newDep["url"].(string), newDep["path"].(string), ) if errorGD != nil { ui.Error(errorGD) } newPath, errorPGD := provider.PostGetDependency( newDep["provider"].(string), newDep["name"].(string), newDep["version"].(string), newDep["url"].(string), newDep["path"].(string), getRes, ) newDep["path"] = newPath if errorPGD != nil { ui.Error(errorPGD) } } lock.Lock() if newDep["expanded"] != true { if cacheExpanded[newDep["url"].(string)]["expanded"] != true { newDep, errExpand = provider.ExpandDependency(newDep) if errExpand != nil || newDep == nil || len(newDep) == 0 { ui.Error("Provider " + dep["provider"].(string) + " didnt expand " + dep["name"].(string) + "@" + dep["version"].(string)) ui.Error(errExpand) channel <- 0 return } newDep["expanded"] = true cacheExpanded[newDep["url"].(string)] = newDep } else { newDep = cacheExpanded[newDep["url"].(string)] } } ui.Log("Get dependency " + newDep["name"].(string)) lock.Unlock() lockList.Lock() depList[index] = newDep nextDepList, ok := depList[index]["dependencies"].([]map[string]interface{}) lockList.Unlock() if ok { res := expandDepList(nextDepList) lockList.Lock() depList[index]["dependencies"] = res lockList.Unlock() } } channel <- 1 })(channel, index, dep) } for range depList { i := <-channel if i == 0 { return nil } } return depList } func installDep(path string, depList []map[string]interface{}) map[string]string { installPaths := make(map[string]string) installPathsLock := sync.RWMutex{} var channel = make(chan int) for index, dep := range depList { go (func(channel chan int, index int, dep map[string]interface{}) { depProviderConfig, err := provider.GetProviderConfig(dep["provider"].(string)) destination := utils.Path(path + "/" + depProviderConfig.Config.Default.InstallPath) if dep["path"] != nil { packageConfig, errC := utils.ReadGupmJson(utils.Path(dep["path"].(string) + "/gupm.json")) if errC == nil && packageConfig != nil && packageConfig.WrapInstallFolder != "" { destination += utils.Path("/" + packageConfig.WrapInstallFolder) } } ui.Error(err) ui.Log("Installing " + path) provider.InstallDependency(destination, dep) // if path == "." { installPathsLock.Lock() installPaths[dep["provider"].(string)] = utils.Path(path + "/" + depProviderConfig.Config.Default.InstallPath) installPathsLock.Unlock() // } nextDepList, ok := depList[index]["dependencies"].([]map[string]interface{}) if ok { installDep(utils.Path(destination+"/"+depList[index]["name"].(string)), nextDepList) } channel <- 1 })(channel, index, dep) } for range depList { <-channel } return installPaths } var providerConfig *utils.GupmEntryPoint func InstallProject(path string) error { start := time.Now() ui.Title("Installing project...") var err error var packageConfig utils.Json var depList []map[string]interface{} err = provider.InitProvider(Provider) if err != nil { return err } providerConfig, err = provider.GetProviderConfig(Provider) ui.Error(err) packageConfig, _ = provider.GetPackageConfig(path) packageConfig, _ = provider.PostGetPackageConfig(packageConfig) depList, err = provider.GetDependencyList(packageConfig) if err != nil { return err } if depList == nil { ui.Log("No dependencies found.") return nil } ui.Title("Expand dependency list...") depList = expandDepList(depList) if depList == nil { return errors.New("Failed to expand dependancy list") } ui.Title("Build dependency list...") depList = provider.BuildDependencyTree(depList) if depList == nil { return errors.New("Failed to build dependancy list") } ui.Title("Install dependencies...") installPaths := installDep(path, depList) ui.Title("Install Binaries...") err = provider.BinaryInstall(path, installPaths) if err != nil { return err } ui.Title("Installation done ❤️") timeElapsed := fmt.Sprintf("%f", time.Since(start).Seconds()) ui.Log(timeElapsed + "s elapsed\n") return nil } ================================================ FILE: src/jsVm/jsVm.go ================================================ package jsVm import ( "encoding/json" "errors" "io/ioutil" "os" "path/filepath" "sync" "time" "../ui" "../utils" "github.com/Masterminds/semver" "github.com/robertkrimen/otto" ) var lock = sync.RWMutex{} var scriptCache = make(map[string]string) func Run(path string, input utils.Json) (otto.Value, error) { var err error var ret otto.Value lock.Lock() if scriptCache[path] == "" { file, err := ioutil.ReadFile(path) if err != nil { return otto.UndefinedValue(), err } scriptCache[path] = string(file) } script := scriptCache[path] lock.Unlock() vm := otto.New() vm.Interrupt = make(chan func(), 1) Setup(vm) vm.Set("_DIRNAME", filepath.Dir(path)) for varName, varValue := range input /*.AsObject()*/ { vm.Set(varName, varValue) } ret, err = vm.Run(script) if err != nil { ui.Error(err) return otto.UndefinedValue(), errors.New("Error occured while executing the GS code") } return ret, nil } func Setup(vm *otto.Otto) { vm.Set("httpGetJson", func(call otto.FunctionCall) otto.Value { url, _ := call.Argument(0).ToString() res := utils.HttpGet(url) result, _ := vm.ToValue(utils.StringToJSON(string(res))) return result }) vm.Set("httpGet", func(call otto.FunctionCall) otto.Value { url, _ := call.Argument(0).ToString() res := utils.HttpGet(url) result, _ := vm.ToValue(string(res)) return result }) vm.Set("dir", func(call otto.FunctionCall) otto.Value { glob, _ := call.Argument(0).ToString() res, _ := utils.Dir(glob) result, _ := vm.ToValue(res) return result }) vm.Set("osSleep", func(call otto.FunctionCall) otto.Value { timeMs, _ := call.Argument(0).ToInteger() time.Sleep(time.Duration(timeMs) * time.Millisecond) result, _ := vm.ToValue(true) return result }) vm.Set("readJsonFile", func(call otto.FunctionCall) otto.Value { path, _ := call.Argument(0).ToString() path = utils.Path(path) b, err := ioutil.ReadFile(path) if err != nil { ui.Error(err) } result, _ := vm.ToValue(utils.StringToJSON(string(b))) return result }) vm.Set("readFile", func(call otto.FunctionCall) otto.Value { path, _ := call.Argument(0).ToString() path = utils.Path(path) b, err := ioutil.ReadFile(path) if err != nil { ui.Error(err) } result, _ := vm.ToValue(string(b)) return result }) vm.Set("removeFiles", func(call otto.FunctionCall) otto.Value { files, _ := call.Argument(0).Export() _, isString := files.(string) if isString { files = []string{files.(string)} } utils.RemoveFiles(files.([]string)) result, _ := vm.ToValue(true) return result }) vm.Set("copyFiles", func(call otto.FunctionCall) otto.Value { files, _ := call.Argument(0).Export() _, isString := files.(string) if isString { files = []string{files.(string)} } path, _ := call.Argument(1).ToString() path = utils.Path(path) utils.CopyFiles(files.([]string), path) result, _ := vm.ToValue(true) return result }) vm.Set("pwd", func(call otto.FunctionCall) otto.Value { dir, _ := os.Getwd() result, _ := vm.ToValue(dir) return result }) vm.Set("env", func(call otto.FunctionCall) otto.Value { name, _ := call.Argument(0).ToString() value, _ := call.Argument(1).ToString() if value == "undefined" { result, _ := vm.ToValue(os.Getenv(name)) return result } else { os.Setenv(name, value) res, _ := vm.ToValue(true) return res } }) vm.Set("exec", func(call otto.FunctionCall) otto.Value { exec, _ := call.Argument(0).ToString() args, _ := call.Argument(1).Export() _, ok := args.([]string) if !ok { args = make([]string, 0) } err := utils.ExecCommand(exec, args.([]string)) result, _ := vm.ToValue(err) return result }) vm.Set("run", func(call otto.FunctionCall) otto.Value { exec, _ := call.Argument(0).ToString() args, _ := call.Argument(1).Export() _, ok := args.([]string) if !ok { args = make([]string, 0) } res, err := utils.RunCommand(exec, args.([]string)) if err != nil { ui.Error(err) result, _ := vm.ToValue(false) return result } if res != "" { res = res[:len(res)-1] } result, _ := vm.ToValue(res) return result }) vm.Set("exit", func(call otto.FunctionCall) otto.Value { code, _ := call.Argument(0).ToInteger() os.Exit(int(code)) result, _ := vm.ToValue(true) return result }) vm.Set("writeJsonFile", func(call otto.FunctionCall) otto.Value { path, _ := call.Argument(0).ToString() path = utils.Path(path) toExport, _ := call.Argument(1).Export() file := JsonExport(toExport).(map[string]interface{}) utils.WriteJsonFile(path, file) result, _ := vm.ToValue(true) return result }) vm.Set("writeFile", func(call otto.FunctionCall) otto.Value { path, _ := call.Argument(0).ToString() path = utils.Path(path) toExport, _ := call.Argument(1).ToString() err := ioutil.WriteFile(path, []byte(toExport), os.ModePerm) if err != nil { ui.Error(err) } result, _ := vm.ToValue(true) return result }) vm.Set("_OSNAME", utils.OSNAME()) vm.Set("mkdir", func(call otto.FunctionCall) otto.Value { path, _ := call.Argument(0).ToString() path = utils.Path(path) os.MkdirAll(path, os.ModePerm) result, _ := vm.ToValue(true) return result }) vm.Set("saveLockDep", func(call otto.FunctionCall) otto.Value { path, _ := call.Argument(0).ToString() path = utils.Path(path) utils.SaveLockDep(path) result, _ := vm.ToValue(true) return result }) vm.Set("fileExists", func(call otto.FunctionCall) otto.Value { path, _ := call.Argument(0).ToString() path = utils.Path(path) res := utils.FileExists(path) result, _ := vm.ToValue(res) return result }) vm.Set("waitForInput", func(call otto.FunctionCall) otto.Value { msg, _ := call.Argument(0).ToString() res := ui.WaitForInput(msg) result, _ := vm.ToValue(res) return result }) vm.Set("waitForMenu", func(call otto.FunctionCall) otto.Value { msg, _ := call.Argument(0).Export() res := ui.WaitForMenu(msg.([]string)) result, _ := vm.ToValue(res) return result }) vm.Set("waitForKey", func(call otto.FunctionCall) otto.Value { ui.WaitForKey() result, _ := vm.ToValue(true) return result }) vm.Set("tar", func(call otto.FunctionCall) otto.Value { files, _ := call.Argument(0).Export() _, isString := files.(string) if isString { files = []string{files.(string)} } res, err := utils.Tar(files.([]string)) if err != nil { ui.Error(err) } b, _ := json.Marshal(res) result, _ := vm.ToValue(utils.StringToJSON(string(b))) return result }) vm.Set("readDir", func(call otto.FunctionCall) otto.Value { path, _ := call.Argument(0).ToString() path = utils.Path(path) var filenames = make([]string, 0) files, _ := utils.ReadDir(path) for _, file := range files { filenames = append(filenames, file.Name()) } result, _ := vm.ToValue(filenames) return result }) vm.Set("createSymLink", func(call otto.FunctionCall) otto.Value { from, _ := call.Argument(0).ToString() from = utils.Path(from) to, _ := call.Argument(1).ToString() to = utils.Path(to) err := os.Symlink(from, to) if err != nil { ui.Error(err) } result, _ := vm.ToValue(true) return result }) vm.Set("untar", func(call otto.FunctionCall) otto.Value { var res utils.FileStructure file, _ := call.Argument(0).ToString() res, _ = utils.Untar(file) b, _ := json.Marshal(res) result, _ := vm.ToValue(utils.StringToJSON(string(b))) return result }) vm.Set("unzip", func(call otto.FunctionCall) otto.Value { var res utils.FileStructure file, _ := call.Argument(0).ToString() res, _ = utils.Unzip(file) b, _ := json.Marshal(res) result, _ := vm.ToValue(utils.StringToJSON(string(b))) return result }) vm.Set("saveFileAt", func(call otto.FunctionCall) otto.Value { var fs utils.FileStructure file, _ := call.Argument(0).Export() path, _ := call.Argument(1).ToString() path = utils.Path(path) bytes, _ := json.Marshal(file) json.Unmarshal(bytes, &fs) fs.SaveAt(path) result, _ := vm.ToValue(path) return result }) vm.Set("semverInRange", func(call otto.FunctionCall) otto.Value { rangeStr, _ := call.Argument(0).ToString() version, _ := call.Argument(1).ToString() rangeVer, _ := semver.NewConstraint(rangeStr) sver, _ := semver.NewVersion(version) value := rangeVer.Check(sver) result, _ := vm.ToValue(value) return result }) vm.Set("semverLatestInRange", func(call otto.FunctionCall) otto.Value { rangeStr, _ := call.Argument(0).ToString() versionListUntyped, _ := call.Argument(1).Export() versionList := utils.ArrString(versionListUntyped) var version string var versionSem *semver.Version rangeVer, _ := semver.NewConstraint(rangeStr) for _, verCandUnk := range versionList { verCand := verCandUnk sver, err := semver.NewVersion(verCand) if err != nil { ui.Error(err) } if rangeVer.Check(sver) && (versionSem == nil || sver.GreaterThan(versionSem)) { version = verCand versionSem = sver } } if version != "" { result, _ := vm.ToValue(version) return result } else { return otto.UndefinedValue() } }) } func JsonExport(input interface{}) interface{} { asMap, isMap := input.(map[string]interface{}) asSlice, isSlice := input.([]interface{}) if isMap { for index, value := range asMap { asValue, ok := value.(otto.Value) if ok { exported, _ := asValue.Export() asMap[index] = JsonExport(exported) } } return asMap } else if isSlice { for index, value := range asSlice { asValue, ok := value.(otto.Value) if ok { exported, _ := asValue.Export() asSlice[index] = JsonExport(exported) } } return asSlice } else { return input } } ================================================ FILE: src/plugins.go ================================================ package main import ( "fmt" "os" "path/filepath" "strings" "./provider" "./ui" "./utils" ) func PluginLink(path string) { configPath := utils.Path(path + "/gupm.json") if utils.FileExists(configPath) { packageConfig := new(utils.GupmEntryPoint) errConfig := utils.ReadJSON(configPath, &packageConfig) if errConfig != nil { ui.Error("Can't read provider configuration") ui.Error(errConfig) return } pluginFolder := utils.HOMEDIR(".") + utils.Path("/.gupm/plugins/") os.MkdirAll(pluginFolder, os.ModePerm) err := os.Symlink(utils.AbsPath(path), pluginFolder+packageConfig.Name) if err != nil { ui.Error(err) } } else { ui.Error("Can't find provider configuration") } } func PluginInstall(path string, plugins []string) error { ui.Title("Install plugin...") pluginFolder := utils.HOMEDIR(".") + utils.Path("/.gupm/plugins/") for _, rls := range plugins { ui.Log(rls) newDep, err := provider.ResolveDependencyLocation(utils.BuildDependencyFromString("https", rls)) if err != nil { return err } pluginName := filepath.Base(newDep["name"].(string)) if len(strings.Split(pluginName, ":")) > 1 { pluginName = strings.Split(pluginName, ":")[1] } if len(strings.Split(pluginName, "/")) > 1 { pluginName = strings.Split(pluginName, "/")[1] } newDep["path"] = pluginFolder + utils.Path(pluginName) getRes, errorGD := provider.GetDependency( newDep["provider"].(string), newDep["name"].(string), newDep["version"].(string), newDep["url"].(string), newDep["path"].(string), ) if errorGD != nil { return errorGD } _, errorPGD := provider.PostGetDependency( newDep["provider"].(string), newDep["name"].(string), newDep["version"].(string), newDep["url"].(string), newDep["path"].(string), getRes, ) if errorPGD != nil { return errorPGD } InstallProject(newDep["path"].(string)) ui.Title("Installation done ❤️") } return nil } func PluginDelete(path string, plugins []string) { folders := make([]string, 0) pluginFolder := utils.HOMEDIR(".") + utils.Path("/.gupm/plugins/") for _, str := range plugins { folders = append(folders, pluginFolder+str) } utils.RemoveFiles(folders) fmt.Println("Done deleting.") } func PluginCreate(path string) { fmt.Println("Welcome to the plugin creation assistant") name := "provider-" + ui.WaitForInput("What is the name of the plugin? provider-") description := ui.WaitForInput("Enter a description: ") author := ui.WaitForInput("Enter the author: ") licence := ui.WaitForInput("Enter the licence (ISC): ") ppath := utils.Path(path + "/" + name) os.MkdirAll(ppath, os.ModePerm) os.MkdirAll(ppath+utils.Path("/docs/repo"), os.ModePerm) utils.WriteFile(ppath+utils.Path("/gupm.json"), `{ "name": "`+name+`", "version": "0.0.1", "description": "`+description+`", "author": "`+author+`", "licence": "`+licence+`", "publish": { "source": ["."], "dest": "../docs/repo" }, "config": { "default": { "entrypoint": "gupm.json", "installPath": "gupm_modules" } } }`) fmt.Println("creation done.") } ================================================ FILE: src/provider/bootstrap.go ================================================ package provider import ( "../defaultProvider" "../jsVm" "../utils" "errors" // "fmt" ) func Bootstrap(path string) error { if Provider != "gupm" { var file = utils.FileExists(utils.Path(ProviderPath + "/bootstrap.gs")) if file { input := make(map[string]interface{}) input["Path"] = path _, err := jsVm.Run(utils.Path(ProviderPath+"/bootstrap.gs"), input) if err != nil { return err } } else { return errors.New("Provider doesn't have any bootstrap function. Please use 'g bootstrap' to use the default bootstrap.") } } else { defaultProvider.Bootstrap(path) } return nil } ================================================ FILE: src/provider/dependencyTree.go ================================================ package provider func eliminateRedundancy(tree []map[string]interface{}, path map[string]bool) []map[string]interface{} { var cleanTree = make([]map[string]interface{}, 0) for index, dep := range tree { if dep["name"] != nil { _ = index depKey := dep["name"].(string) + "@" + dep["version"].(string) if path[depKey] != true { cleanTree = append(cleanTree, dep) } } } for index, dep := range cleanTree { if dep["name"] != nil { nextDepList, ok := dep["dependencies"].([]map[string]interface{}) if ok { depKey := dep["name"].(string) + "@" + dep["version"].(string) newPath := make(map[string]bool) for key, value := range path { newPath[key] = value } newPath[depKey] = true newSubTree := eliminateRedundancy(nextDepList, newPath) cleanTree[index]["dependencies"] = newSubTree } } } return cleanTree } func flattenDependencyTree(tree []map[string]interface{}, subTree []map[string]interface{}) ([]map[string]interface{}, []map[string]interface{}) { var cleanTree = make([]map[string]interface{}, 0) for index, dep := range subTree { var rootDeps = make(map[string]string) for _, dep := range tree { rootDeps[dep["name"].(string)] = dep["version"].(string) } if rootDeps[dep["name"].(string)] == "" { tree = append(tree, dep) nextDepList, ok := dep["dependencies"].([]map[string]interface{}) if ok { newTree, newSubTree := flattenDependencyTree(tree, nextDepList) tree = newTree subTree[index]["dependencies"] = newSubTree } } else if rootDeps[dep["name"].(string)] != dep["version"].(string) { nextDepList, ok := dep["dependencies"].([]map[string]interface{}) if ok { newTree, newSubTree := flattenDependencyTree(tree, nextDepList) tree = newTree subTree[index]["dependencies"] = newSubTree } cleanTree = append(cleanTree, subTree[index]) } } return tree, cleanTree } func BuildDependencyTree(tree []map[string]interface{}) []map[string]interface{} { cleanTree := eliminateRedundancy(tree, make(map[string]bool)) for index, dep := range cleanTree { nextDepList, ok := dep["dependencies"].([]map[string]interface{}) if ok { newCleanTree, newDepList := flattenDependencyTree(cleanTree, nextDepList) cleanTree = newCleanTree cleanTree[index]["dependencies"] = newDepList } } return cleanTree } ================================================ FILE: src/provider/install.go ================================================ package provider import ( "io/ioutil" "os" "regexp" "../defaultProvider" "../jsVm" "../ui" "../utils" ) func BinaryInstall(path string, paths map[string]string) error { dest := utils.Path(path + "/.bin") os.RemoveAll(dest) os.MkdirAll(dest, os.ModePerm) for pr, prdir := range paths { depProviderPath := GetProviderPath(pr) var file = utils.FileExists(depProviderPath + utils.Path("/binaryInstall.gs")) if pr != "gupm" && file { input := make(map[string]interface{}) input["Destination"] = dest input["Source"] = prdir res, err := jsVm.Run(depProviderPath+utils.Path("/binaryInstall.gs"), input) if err != nil { return err } _, err1 := res.ToString() return err1 } else { return defaultProvider.BinaryInstall(dest, prdir) } } return nil } func installDependencySubFolders(path string, depPath string) { files, _ := utils.ReadDir(path) for _, file := range files { if file.IsDir() { folderPath := utils.Path(depPath + "/" + file.Name()) os.MkdirAll(folderPath, os.ModePerm) installDependencySubFolders(utils.Path(path+"/"+file.Name()), folderPath) } else { isFileExists := false err := os.Link(utils.Path(path+"/"+file.Name()), utils.Path(depPath+"/"+file.Name())) if err != nil { isFileExists, _ = regexp.MatchString(`file exists$`, err.Error()) } if err != nil && !isFileExists { if !linkHasErrored { ui.Error(err) ui.Error("Error, cannot use hard link on your system. Falling back to copying file (Will be slower!)") linkHasErrored = true } input, err := ioutil.ReadFile(utils.Path(path + "/" + file.Name())) if err != nil { ui.Error(err) return } err = ioutil.WriteFile(utils.Path(depPath+"/"+file.Name()), input, 0644) if err != nil { ui.Error(err) return } } } } } func InstallDependency(path string, dep map[string]interface{}) { depPath := utils.Path(path + "/" + dep["name"].(string)) // if(utils.FileExists(depPath)) { // // TODO: check version // } else { // } _, ok := dep["path"].(string) if ok { os.MkdirAll(utils.Path(depPath), os.ModePerm) installDependencySubFolders(utils.Path(dep["path"].(string)), depPath) } else { ui.Error(dep["name"].(string) + " Cannot be installed.") } } ================================================ FILE: src/provider/provider.go ================================================ package provider import ( "fmt" "os" "path/filepath" "sync" "../defaultProvider" "../jsVm" "../ui" "../utils" ) var Provider string var ProviderPath string var providerConfigCache = make(map[string]*utils.GupmEntryPoint) var linkHasErrored = false var pConfigLock = sync.RWMutex{} func GetProviderPath(name string) string { gupmConfig := utils.GupmConfig() if name == "" { name = gupmConfig.DefaultProvider } if name == "os" { osName := utils.OSNAME() if gupmConfig.OsProviders[osName] != "" { name = gupmConfig.OsProviders[osName] } else { ui.Error("No provider set for", osName) return utils.DIRNAME() } } if name == "gupm" { return utils.DIRNAME() } else { homePlugin := utils.HOMEDIR(".") + utils.Path("/.gupm/plugins/provider-"+name) localPlugin := utils.DIRNAME() + utils.Path("/plugins/provider-"+name) if utils.FileExists(homePlugin) { pluginPath, err := filepath.EvalSymlinks(homePlugin) if err != nil { ui.Error(err) return "" } return pluginPath } else if utils.FileExists(localPlugin) { return localPlugin } else { fmt.Println("Provider cannot be found: " + name + ". Please install it before using it.") os.Exit(1) return "" } } } func InitProvider(provider string) error { Provider = provider ProviderPath = GetProviderPath(provider) if Provider != "" { providerConfig, err := GetProviderConfig(Provider) if err != nil { return err } ui.Log("Initialisation OK for " + providerConfig.Name) } else { providerConfig, err := GetProviderConfig("gupm") if err != nil { return err } ui.Log("Initialisation OK for " + providerConfig.Name) } return nil } func GetProviderConfig(providerName string) (*utils.GupmEntryPoint, error) { providerConfigPath := GetProviderPath(providerName) + utils.Path("/gupm.json") pConfigLock.Lock() if providerConfigCache[providerName] == nil { config, err := utils.ReadGupmJson(providerConfigPath) if err != nil { return nil, err } providerConfigCache[providerName] = config pConfigLock.Unlock() return config, nil } else { config := providerConfigCache[providerName] pConfigLock.Unlock() return config, nil } } func GetPackageConfig(path string) (utils.Json, error) { var file = utils.FileExists(ProviderPath + utils.Path("/getPackageConfig.gs")) if file { input := make(map[string]interface{}) input["Path"] = path res, err := jsVm.Run(ProviderPath+utils.Path("/getPackageConfig.gs"), input) if err != nil { return nil, err } resObj, err1 := res.Export() return resObj.(utils.Json), err1 } else { pc, err := GetProviderConfig(Provider) if err != nil { return nil, err } return defaultProvider.GetPackageConfig(utils.Path(path + "/" + pc.Config.Default.Entrypoint)), nil } } func PostGetPackageConfig(config utils.Json) (utils.Json, error) { var file = utils.FileExists(ProviderPath + utils.Path("/postGetPackageConfig.gs")) if file { input := make(map[string]interface{}) input["PackageConfig"] = config res, err := jsVm.Run(ProviderPath+utils.Path("/postGetPackageConfig.gs"), input) if err != nil { return nil, err } resObj, err1 := res.Export() return resObj.(utils.Json), err1 } else { return config, nil } } func SaveDependencyList(path string, depList []map[string]interface{}) error { var file = utils.FileExists(ProviderPath + utils.Path("/saveDependencyList.gs")) if file { input := make(map[string]interface{}) input["Dependencies"] = depList input["Path"] = path _, err := jsVm.Run(ProviderPath+utils.Path("/saveDependencyList.gs"), input) if err != nil { return err } return nil } else { return defaultProvider.SaveDependencyList(path, depList) } } func GetDependencyList(config utils.Json) ([]map[string]interface{}, error) { var file = utils.FileExists(ProviderPath + utils.Path("/getDependencyList.gs")) if file { input := make(map[string]interface{}) input["PackageConfig"] = config res, err := jsVm.Run(ProviderPath+utils.Path("/getDependencyList.gs"), input) if err != nil { return nil, err } resObj, err1 := res.Export() resMap, ok := resObj.([]map[string]interface{}) if ok { return resMap, err1 } else { return make([]map[string]interface{}, 0), err1 } } else { return defaultProvider.GetDependencyList(config), nil } } func ResolveDependencyLocation(dependency map[string]interface{}) (map[string]interface{}, error) { depProviderPath := GetProviderPath(dependency["provider"].(string)) var file = utils.FileExists(depProviderPath + utils.Path("/resolveDependencyLocation.gs")) if dependency["provider"].(string) != "gupm" && file { input := make(map[string]interface{}) input["Dependency"] = dependency res, err := jsVm.Run(depProviderPath+utils.Path("/resolveDependencyLocation.gs"), input) if err != nil { return nil, err } resObj, err1 := res.Export() if resObj == nil { ui.Error("ERROR Failed to resolve" + dependency["name"].(string) + "Trying again.") return ResolveDependencyLocation(dependency) } return resObj.(map[string]interface{}), err1 } else { dependency["url"] = dependency["name"].(string) return dependency, nil } } func ExpandDependency(dependency map[string]interface{}) (map[string]interface{}, error) { depProviderPath := GetProviderPath(dependency["provider"].(string)) var file = utils.FileExists(depProviderPath + utils.Path("/expandDependency.gs")) if dependency["provider"].(string) != "gupm" && file { input := make(map[string]interface{}) input["Dependency"] = dependency res, err := jsVm.Run(depProviderPath+utils.Path("/expandDependency.gs"), input) if err != nil { return nil, err } toExport, _ := res.Export() resObj := jsVm.JsonExport(toExport).(map[string]interface{}) if resObj == nil { ui.Error("ERROR Failed to resolve" + dependency["name"].(string) + ". Trying again.") return ExpandDependency(dependency) } return resObj, nil } else { return defaultProvider.ExpandDependency(dependency) } } func GetDependency(provider string, name string, version string, url string, path string) (string, error) { depProviderPath := GetProviderPath(provider) var file = utils.FileExists(depProviderPath + utils.Path("/getDependency.gs")) if provider != "gupm" && file { input := make(map[string]interface{}) input["Provider"] = provider input["Name"] = name input["Version"] = version input["Url"] = url input["Path"] = path res, err := jsVm.Run(depProviderPath+utils.Path("/getDependency.gs"), input) if err != nil { return "", err } resStr, err1 := res.ToString() return resStr, err1 } else { return defaultProvider.GetDependency(provider, name, version, url, path) } } func PostGetDependency(provider string, name string, version string, url string, path string, result string) (string, error) { depProviderPath := GetProviderPath(provider) var file = utils.FileExists(depProviderPath + utils.Path("/postGetDependency.gs")) if provider != "gupm" && file { input := make(map[string]interface{}) input["Provider"] = provider input["Name"] = name input["Version"] = version input["Url"] = url input["Path"] = path input["Result"] = result res, err := jsVm.Run(depProviderPath+utils.Path("/postGetDependency.gs"), input) if err != nil { return "", err } resStr, err1 := res.ToString() return resStr, err1 } else { return defaultProvider.PostGetDependency(provider, name, version, url, path, result) } } ================================================ FILE: src/provider/publish.go ================================================ package provider import ( "errors" "../defaultProvider" "../jsVm" "../utils" // "fmt" ) func Publish(path string, namespace string) error { if Provider != "gupm" { var file = utils.FileExists(utils.Path(ProviderPath + "/publish.gs")) if file { input := make(map[string]interface{}) input["Path"] = path _, err := jsVm.Run(utils.Path(ProviderPath+"/publish.gs"), input) return err } else { return errors.New("Provider doesn't have any publish function. Please use 'g publish' to use the default publish.") } } else { return defaultProvider.Publish(path, namespace) } } ================================================ FILE: src/publish.go ================================================ package main import ( "./provider" "./ui" ) func Publish(path string, namespace string) error { err := provider.InitProvider(Provider) if err != nil { return err } ui.Title("Publishing package...") errPub := provider.Publish(path, namespace) if errPub != nil { return errPub } else { ui.Title("Publish done! ❤️") } return nil } ================================================ FILE: src/removeDependency.go ================================================ package main import ( "./provider" "./ui" "./utils" ) func remove(slice []map[string]interface{}, s int) []map[string]interface{} { return append(slice[:s], slice[s+1:]...) } func RemoveDependency(path string, rls []string) error { var err error var packageConfig utils.Json var depList []map[string]interface{} ui.Title("Add dependency...") err = provider.InitProvider(Provider) if err != nil { return err } providerConfig, err = provider.GetProviderConfig(Provider) ui.Error(err) packageConfig, _ = provider.GetPackageConfig(path) packageConfig, _ = provider.PostGetPackageConfig(packageConfig) depList, err = provider.GetDependencyList(packageConfig) if err != nil { return err } ui.Title("Removing from dependency list...") for _, str := range rls { for index, dep := range depList { if dep["name"].(string) == str { depList = remove(depList, index) } } } err = provider.SaveDependencyList(path, depList) if err != nil { return err } // TODO: Remove from module folder return nil } ================================================ FILE: src/self.go ================================================ package main import ( "./ui" "./utils" "io/ioutil" "os" "os/exec" "runtime" ) // TODO: implemement script-free upgrade for all OSes func SelfUpgrade() { SelfUninstall() if runtime.GOOS != "windows" { script := utils.HttpGet("https://azukaar.github.io/GuPM/install.sh") ioutil.WriteFile("TEMP_install.sh", []byte(script), os.ModePerm) cmd := exec.Command("/bin/sh", "TEMP_install.sh") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin cmd.Run() utils.RemoveFiles([]string{"TEMP_install.sh"}) } else { ui.Error("Upgrade not fully implememnted on windows yet. Please execute windows_installer.exe again") } } func SelfUninstall() { utils.RemoveFiles([]string{utils.DIRNAME()}) if runtime.GOOS != "windows" { utils.RemoveFiles([]string{"/usr/local/bin/g", "/bin/g"}) } } ================================================ FILE: src/test.go ================================================ package main import ( "os" "regexp" "strconv" "./jsVm" "./ui" "./utils" ) func RunTest(path string) { _ = jsVm.Run files := utils.RecursiveFileWalkDir(path) i := 0 for _, file := range files { isTest, _ := regexp.MatchString(`\.test\.gs$`, file) if isTest { os.MkdirAll(".tmp_test_gupm", os.ModePerm) utils.CopyFiles([]string{file}, ".tmp_test_gupm/"+strconv.Itoa(i)+".gs") errch := os.Chdir(".tmp_test_gupm") if errch != nil { ui.Error("Could'n execute", file) Exit(1) } _, err := jsVm.Run(strconv.Itoa(i)+".gs", make(map[string]interface{})) os.Chdir("..") utils.RemoveFiles([]string{".tmp_test_gupm"}) if err != nil { ui.Error("Test execution failed:", file) Exit(1) } i++ } } ui.Title("Test passed! ❤️") } ================================================ FILE: src/ui/log.go ================================================ package ui import ( "bufio" "errors" "fmt" "github.com/fatih/color" "github.com/gosuri/uilive" "os" "strconv" "sync" "time" ) var errorList = make([]string, 0) var debugList = make([]string, 0) var currentLog string var currentTitle string var progress int var screenWidth int var positionToDrawAt int var logBox = uilive.New() var lock = sync.RWMutex{} var errorLock = sync.RWMutex{} var redrawNeeded = false var running = true var isWaitingForInput = false func Title(log string) { _ = color.Green currentTitle = log currentLog = "" redrawNeeded = true } func Log(log string) { currentLog = log redrawNeeded = true } func Error(errs ...interface{}) error { res := "" for _, err := range errs { errErr, isErr := err.(error) errStr, isStr := err.(string) if isErr && errErr != nil { res += " " + errErr.Error() } else if isStr { res += " " + errStr } } if res != "" { errorLock.Lock() errorList = append(errorList, res) errorLock.Unlock() if len(errorList) <= 10 { redrawNeeded = true } return errors.New(res) } else { return nil } } func Debug(err string) { debugList = append(debugList, err) if len(debugList) <= 10 { Draw() } } func Progress(p int) { progress = p redrawNeeded = true } // https://github.com/ahmetb/go-cursor/blob/master/cursor.go var Esc = "\x1b" func escape(format string, args ...interface{}) string { return fmt.Sprintf("%s%s", Esc, fmt.Sprintf(format, args...)) } func moveCursor(x int, y int) { escape("[%d;%dH", x, y) } func init() { positionToDrawAt = 0 logBox.Start() go (func() { for range time.Tick(200 * time.Millisecond) { if running { Draw() } } })() } func drawTitle() string { if currentTitle != "" { title := color.New(color.FgBlue, color.Bold) return title.Sprintln("🐶 " + currentTitle) } else { return "" } } func drawLog() string { if currentLog != "" { log := color.New(color.FgGreen) return log.Sprintln("✓ " + currentLog) } else { return "" } } func Stop() { redrawNeeded = true running = false Draw() } func WaitForKey() { isWaitingForInput = true logBox.Flush() reader := bufio.NewReader(os.Stdin) reader.ReadRune() isWaitingForInput = false } func WaitForInput(msg string) string { isWaitingForInput = true logBox.Flush() fmt.Printf(msg) scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { var result = scanner.Text() isWaitingForInput = false return result } if scanner.Err() != nil { } return "" } func WaitForMenu(msgs []string) int { lgth := len(msgs) i := 1 res := 0 for _, msg := range msgs { fmt.Println(strconv.Itoa(i) + " : " + msg) i++ } for res <= 0 || res > lgth { resString := WaitForInput("Please input choice 1 - " + strconv.Itoa(lgth) + ": ") res, _ = strconv.Atoi(resString) } return res } func Draw() { if !redrawNeeded || isWaitingForInput { return } result := "" result += drawTitle() if progress > 0 { fmt.Print("📦📦") for i := 0; i < 20; i++ { if i == progress/5 { fmt.Print("🐕") } else { fmt.Print("-") } } fmt.Println("🏠") } result += drawLog() errorColor := color.New(color.FgRed) limit := 0 errorLock.RLock() for _, v := range errorList { _ = v if limit == 10 { result += errorColor.Sprintln("❌❌❌ Too many errors to display...") limit++ } else if limit < 10 { result += errorColor.Sprintln("❌ " + v) limit++ } } errorLock.RUnlock() limit = 0 for _, v := range debugList { _ = v if limit == 10 { result += "Too many debugs..." limit++ } else if limit < 10 { result += v limit++ } } lock.Lock() if running { fmt.Fprintf(logBox, result) } else { fmt.Fprintf(logBox, "\n") logBox.Stop() fmt.Println(result) } redrawNeeded = false lock.Unlock() } ================================================ FILE: src/utils/files.go ================================================ package utils import ( "os" "strings" // "fmt" "github.com/bmatcuk/doublestar" "github.com/otiai10/copy" ) var EmptyFileStructure = FileStructure{} type FileStructure struct { Children map[string]FileStructure Name string Content []byte Filetype int } func Dir(path string) (matches []string, err error) { return doublestar.Glob(path) } func RemoveFiles(files []string) error { for _, file := range files { return os.RemoveAll(file) } return nil } func CopyFiles(files []string, destination string) error { for _, file := range files { return copy.Copy(file, destination) } return nil } func (g *FileStructure) getOrCreate(path string, options FileStructure) FileStructure { var folders = strings.Split(path, "/") var folder = folders[0] var child, _ = g.Children[folder] if child.Name == "" { if len(folders) > 1 { g.Children[folder] = FileStructure{ Children: make(map[string]FileStructure), Name: folder, Filetype: 0, } } else { g.Children[folder] = FileStructure{ Children: make(map[string]FileStructure), Name: folder, Filetype: options.Filetype, Content: options.Content, } } child, _ = g.Children[folder] } if len(folders) > 1 { next := folders[1:] return child.getOrCreate(strings.Join(next[:], "/"), options) } else { return child } } func (g *FileStructure) SaveSelfAt(path string) error { if g.Filetype == 0 { newPath := Path(path + "/" + g.Name) os.MkdirAll(newPath, os.ModePerm) for _, child := range g.Children { child.SaveSelfAt(newPath) } } else { filePath := path if g.Name != "" { filePath = Path(filePath + "/" + g.Name) } f, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR, os.FileMode(0777)) if err != nil { f.Close() return err } if _, err := f.Write(g.Content); err != nil { f.Close() return err } f.Close() } return nil } func (g *FileStructure) SaveAt(path string) error { if g.Filetype == 0 { os.MkdirAll(Path(path), os.ModePerm) for _, child := range g.Children { child.SaveSelfAt(Path(path)) } } if g.Filetype == 1 { g.SaveSelfAt(Path(path)) } return nil } ================================================ FILE: src/utils/json.go ================================================ package utils // import ( // "reflect" // "fmt" // ) // type Json map[interface{}]interface {} type Json map[string]interface{} // func (j *Json) AsObject() map[string]interface {} { // res := map[string]interface{}{} // for i, v := range *j { // res[i.(string)] = v // } // return res // } func (j *Json) Contains(test interface{}) bool { for i := range *j { if i == test { return true } } return false } func (j *Json) get(index interface{}) interface{} { return (*j)[index.(string)] } // func (j *Json) indexOf(test interface{}) interface{} { // for i, _ := range *j { // if(i == test) { // return true // } // } // return false // } ================================================ FILE: src/utils/repo.go ================================================ package utils import ( "io/ioutil" ) func GetOrCreateRepo(path string) map[string]interface{} { configPath := path + "/gupm_repo.json" if !FileExists(configPath) { baseConfig := `{ "packages": {} }` WriteFile(configPath, baseConfig) return StringToJSON(baseConfig) } file, _ := ioutil.ReadFile(configPath) return StringToJSON(string(file)) } func SaveRepo(path string, file map[string]interface{}) { WriteJsonFile(path+"/gupm_repo.json", file) } ================================================ FILE: src/utils/types.go ================================================ package utils type GupmEntryPoint struct { Name string Version string WrapInstallFolder string Git gupmEntryPointGit Publish gupmEntryPointPublish Cli gupmEntryPointCliList Config gupmEntryPointConfigList Dependencies gupmEntryPointDependenciesList } type gupmEntryPointCliList struct { DefaultProviders map[string]string Aliases map[string]interface{} } type gupmEntryPointGit struct { Hooks gupmEntryPointGitHooks } type gupmEntryPointGitHooks struct { Precommit interface{} Prepush interface{} } type gupmEntryPointDependenciesList struct { DefaultProvider string Default map[string]string } type gupmEntryPointConfigList struct { Default gupmEntryPointConfig } type gupmEntryPointConfig struct { Entrypoint string InstallPath string DefaultProvider string OsProviders map[string]string } type gupmEntryPointPublish struct { Source []string Dest string } ================================================ FILE: src/utils/untar.go ================================================ package utils import "archive/tar" import "archive/zip" import "compress/gzip" import ( "bytes" "io" "io/ioutil" "strings" ) func Ungz(r string) (FileStructure, error) { tr, err := gzip.NewReader(strings.NewReader(r)) if err != nil { return EmptyFileStructure, err } defer tr.Close() buf := new(bytes.Buffer) buf.ReadFrom(tr) root := FileStructure{ Children: make(map[string]FileStructure), Name: tr.Header.Name, Content: buf.Bytes(), Filetype: 1, } return root, nil } func Tar(files []string) (FileStructure, error) { var buf bytes.Buffer gzw := gzip.NewWriter(&buf) tw := tar.NewWriter(gzw) finalList := make([]string, 0) for _, file := range files { if IsDirectory(file) { finalList = append(finalList, RecursiveFileWalkDir(file)...) } else { finalList = append(finalList, file) } } for _, file := range finalList { content, err := ioutil.ReadFile(file) if err != nil { return EmptyFileStructure, err } hdr := &tar.Header{ Name: file, Mode: 0740, Size: int64(len(content)), } if err := tw.WriteHeader(hdr); err != nil { return EmptyFileStructure, err } if _, err := tw.Write([]byte(content)); err != nil { return EmptyFileStructure, err } } if err := tw.Close(); err != nil { return EmptyFileStructure, err } if err := gzw.Close(); err != nil { return EmptyFileStructure, err } root := FileStructure{ Content: buf.Bytes(), Filetype: 1, } return root, nil } func Untar(r string) (FileStructure, error) { gzr, err := gzip.NewReader(strings.NewReader(r)) root := FileStructure{ Children: make(map[string]FileStructure), Name: "/", Filetype: 0, } if err != nil { return EmptyFileStructure, err } defer gzr.Close() tr := tar.NewReader(gzr) for { header, err := tr.Next() switch { case err == io.EOF: return root, nil case err != nil: return root, err case header == nil: continue } switch header.Typeflag { // if its a dir and it doesn't exist create it case tar.TypeDir: { root.getOrCreate(header.Name, FileStructure{ Filetype: 0, }) } // if it's a file create it case tar.TypeReg: buf := new(bytes.Buffer) buf.ReadFrom(tr) root.getOrCreate(header.Name, FileStructure{ Filetype: 1, Content: buf.Bytes(), }) } } return root, nil } func Unzip(r string) (FileStructure, error) { root := FileStructure{ Children: make(map[string]FileStructure), Name: "/", Filetype: 0, } tr, _ := zip.NewReader(strings.NewReader(r), int64(len(r))) for _, f := range tr.File { // if its a dir and it doesn't exist create it if f.FileInfo().IsDir() { root.getOrCreate(f.Name, FileStructure{ Filetype: 0, }) } else { rc, _ := f.Open() buf := new(bytes.Buffer) buf.ReadFrom(rc) root.getOrCreate(f.Name, FileStructure{ Filetype: 1, Content: buf.Bytes(), }) } } return root, nil } ================================================ FILE: src/utils/utils.go ================================================ package utils import ( "encoding/json" "io/ioutil" "net/http" "os" "os/exec" "path/filepath" "reflect" "regexp" "runtime" "sync" "time" "../ui" "github.com/mitchellh/go-homedir" ) type Dependency struct { Name string Provider string Version string } func buildCmd(toRun string, args []string) *exec.Cmd { isNode := regexp.MustCompile(`.js$`) var cmd *exec.Cmd bashargs := []string{} // temporary hack to make windows execute js file with node if isNode.FindString(toRun) != "" { bashargs = append(bashargs, toRun) bashargs = append(bashargs, args...) cmd = exec.Command("node", bashargs...) } else { bashargs = append(bashargs, args...) cmd = exec.Command(toRun, bashargs...) } return cmd } func ExecCommand(toRun string, args []string) error { cmd := buildCmd(toRun, args) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin err := cmd.Run() return err } func ReadGupmJson(path string) (*GupmEntryPoint, error) { if !FileExists(path) { return nil, nil } config := new(GupmEntryPoint) errRead := ReadJSON(path, config) if errRead != nil { ui.Error("Can't read", path, "check your file") return nil, errRead } return config, nil } func RunCommand(toRun string, args []string) (string, error) { cmd := buildCmd(toRun, args) res, err := cmd.Output() if err != nil { return "", err } return string(res), nil } func BuildStringFromDependency(dep map[string]interface{}) string { rep := dep["name"].(string) if dep["version"] != nil && dep["version"].(string) != "" { rep += "@" + dep["version"].(string) } if dep["provider"] != nil && dep["provider"].(string) != "" { rep = dep["provider"].(string) + "://" + rep } return rep } func BuildDependencyFromString(defaultProvider string, dep string) map[string]interface{} { result := make(map[string]interface{}) step := dep versionCheck := regexp.MustCompile(`@[\w\.\-\_\^\~]+$`) tryversion := versionCheck.FindString(step) if tryversion != "" { result["version"] = tryversion[1:] step = versionCheck.ReplaceAllString(step, "") } else { result["version"] = "*.*.*" } providerCheck := regexp.MustCompile(`^[\w\-\_]+\:\/\/`) tryprovider := providerCheck.FindString(step) if tryprovider != "" { result["provider"] = tryprovider[:len(tryprovider)-3] step = providerCheck.ReplaceAllString(step, "") } else { result["provider"] = defaultProvider } result["name"] = step return result } func StringToJSON(b string) map[string]interface{} { var jsonString map[string]interface{} json.Unmarshal([]byte(string(b)), &jsonString) return jsonString } func ReadJSON(path string, target interface{}) error { b, err := os.Open(path) // just pass the file name if err != nil { b.Close() return err } defer b.Close() return json.NewDecoder(b).Decode(target) } var numberConnectionOpened = 0 func HttpGet(url string) []byte { if numberConnectionOpened > 50 { time.Sleep(1000 * time.Millisecond) return HttpGet(url) } numberConnectionOpened++ resp, httperr := http.Get(url) if httperr != nil { numberConnectionOpened-- isRateLimit, _ := regexp.MatchString(`unexpected EOF$`, httperr.Error()) if !isRateLimit { ui.Error("Error accessing", url, "trying again.", httperr) } time.Sleep(1000 * time.Millisecond) return HttpGet(url) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { numberConnectionOpened-- ui.Error("Error reading HTTP response ", err) return HttpGet(url) } numberConnectionOpened-- return body } func FileExists(path string) bool { _, err := os.Stat(path) if err == nil { return true } if os.IsNotExist(err) { return false } return true } func StringInSlice(a string, list []string) bool { for _, b := range list { if b == a { return true } } return false } func RemoveIndex(s []map[string]interface{}, index int) []map[string]interface{} { return append(s[:index], s[index+1:]...) } func RecursiveFileWalkDir(source string) []string { result := make([]string, 0) err := filepath.Walk(source, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { result = append(result, path) } return nil }) if err != nil { ui.Error(err) } return result } var lock = sync.RWMutex{} func ReadDir(path string) ([]os.FileInfo, error) { lock.Lock() files, err := ioutil.ReadDir(path) lock.Unlock() if err != nil { ui.Error(err) return files, err } return files, nil } func IsDirectory(path string) bool { fileInfo, err := os.Stat(path) if err != nil { return false } return fileInfo.IsDir() } func HOMEDIR(fallback string) string { hdir, errH := homedir.Dir() if errH != nil { ui.Error(errH) hdir = fallback } return hdir } func DIRNAME() string { ex, err := os.Executable() exr, err := filepath.EvalSymlinks(ex) if err != nil { panic(err) } dir := filepath.Dir(exr) return dir } func WriteFile(path string, file string) error { return ioutil.WriteFile(Path(path), []byte(file), os.ModePerm) } func WriteJsonFile(path string, file map[string]interface{}) { bytes, _ := json.MarshalIndent(file, "", " ") err := ioutil.WriteFile(path, bytes, os.ModePerm) if err != nil { ui.Error(err) } } // TODO: https://blog.golang.org/pipelines // add proper checksum check func SaveLockDep(path string) { ioutil.WriteFile(path+"/.gupm_locked", []byte("1"), os.ModePerm) } func AbsPath(rel string) string { abs, _ := filepath.Abs(rel) return abs } func Path(path string) string { if runtime.GOOS == "windows" { return filepath.FromSlash(path) } else { return filepath.ToSlash(path) } } func Contains(s interface{}, elem interface{}) bool { arrV := reflect.ValueOf(s) if arrV.Kind() == reflect.Slice { for i := 0; i < arrV.Len(); i++ { // XXX - panics if slice element points to an unexported struct field // see https://golang.org/pkg/reflect/#Value.Interface if arrV.Index(i).Interface() == elem { return true } } } return false } func ArrString(something interface{}) []string { _, ok := something.([]string) if !ok { res := make([]string, 0) for _, v := range something.([]interface{}) { res = append(res, v.(string)) } return res } else { return something.([]string) } } func GupmConfig() gupmEntryPointConfig { gupmjson, _ := ReadGupmJson(Path(DIRNAME() + "/gupm.json")) return gupmjson.Config.Default } func OSNAME() string { osName := runtime.GOOS if osName == "darwin" { return "mac" } return osName } ================================================ FILE: src/windows/windows_install.go ================================================ package main import ( "../ui" "../utils" "fmt" ) func main() { arch := utils.HttpGet("https://azukaar.github.io/GuPM/gupm_windows.tar.gz") files, _ := utils.Untar(string(arch)) path := ui.WaitForInput("Where do you want to save GuPM? (default C:\\)") if path == "" { path = "C:\\" } files.SaveAt(path) fmt.Println("GuPM saved in " + path + ". dont forget to add it to your PATH") } ================================================ FILE: tests/install/normal.test.gs ================================================ writeJsonFile("gupm.json", { "dependencies": { "default": { } } }) var err = exec("../build/dg", ["i", "git://github.com/Masterminds/semver"]) if(err) { throw new Error(JSON.stringify(err)) } if (!fileExists('gupm_modules/github.com/Masterminds/semver')) { throw new Error(1) } ================================================ FILE: tests/make/nodeps.test.gs ================================================ writeJsonFile("gupm.json", { "dependencies": { "default": { } } }) var err = exec("../build/dg", ["make"]) if(err) { throw new Error(JSON.stringify(err)) } ================================================ FILE: tests/make/normal.test.gs ================================================ writeJsonFile("gupm.json", { "dependencies": { "default": { "git://github.com/Masterminds/semver": "master" } } }) var err = exec("../build/dg", ["make"]) if(err) { throw new Error(JSON.stringify(err)) } if (!fileExists('gupm_modules/github.com/Masterminds/semver')) { throw new Error(1) } ================================================ FILE: tests/plugins/normal.test.gs ================================================ writeJsonFile("gupm.json", { "dependencies": { "default": { "npm://webpack": "latest" } } }) var err = exec("../build/dg", ["pl", "install", "https://azukaar.github.io/GuPM-official/repo:provider-npm"]) var err2 = exec("../build/dg", ["make"]) if(err || err2) { throw new Error(JSON.stringify(err)) } if (!fileExists('node_modules/webpack')) { throw new Error(1) } ================================================ FILE: tests/scripts/normal.test.gs ================================================ writeFile("testscript.gs","writeFile('test','it works');") var err = exec("../build/dg", ["testscript"]) if(err) { throw new Error(JSON.stringify(err)) } if (!fileExists('test')) { throw new Error(1) }