├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── c3pm.yml │ └── release.yml ├── .gitignore ├── .goreleaser.yml ├── LICENSE.txt ├── README.md ├── adapter ├── adapter.go ├── cmake_adapter │ └── cmake_adapter.go ├── defaultadapter │ ├── cmakegen.go │ ├── cmakegen_test.go │ ├── config.go │ ├── defaultadapter.go │ ├── defaultadapter_suite_test.go │ ├── exec.go │ ├── lib.go │ ├── linux.go │ └── platformconfig.go ├── irrlichtadapter │ ├── cmakeConfig.go │ └── irrlichtadapter.go └── ncurses_adapter │ └── ncurses_adapter.go ├── adapter_interface └── interface.go ├── api ├── api.go ├── api_suite_test.go ├── auth.go ├── example_test.go ├── metrics.go ├── test │ └── mock.go ├── upload.go └── upload_test.go ├── cmake ├── cmake.go └── cmake_test.go ├── cmd ├── add.go ├── build.go ├── init.go ├── input │ ├── init.go │ ├── input.go │ └── login.go ├── list.go ├── login.go ├── logout.go ├── publish.go ├── remove.go ├── root.go └── test.go ├── config ├── auth.go ├── auth_test.go ├── config.go ├── config_suite_test.go ├── config_test.go └── manifest │ ├── c3pmversion.go │ ├── c3pmversion_test.go │ ├── customcmake.go │ ├── dependencies.go │ ├── dependencies_test.go │ ├── manifest.go │ ├── manifest_suite_test.go │ ├── manifest_test.go │ ├── type.go │ └── version.go ├── ctpm ├── add.go ├── build.go ├── build_test.go ├── ctpm_suite_test.go ├── doc.go ├── init.go ├── init_test.go ├── install.go ├── list.go ├── login.go ├── login_test.go ├── logout.go ├── logout_test.go ├── publish.go ├── publish_test.go ├── remove.go ├── remove_test.go ├── test.go └── test_test.go ├── dependencies ├── dependencies_suite_test.go ├── depgraph.go ├── depgraph_test.go └── fetcher.go ├── env ├── dev.go ├── doc.go └── prod.go ├── go.mod ├── go.sum ├── main.go ├── package-lock.json ├── registry ├── registry.go ├── registry_suite_test.go └── registry_test.go └── test_helpers ├── main.cpp ├── projects ├── cmakeProject │ ├── CMakeLists.txt │ └── main.cpp ├── genProject │ ├── lib │ │ └── hello.cpp │ └── main.cpp └── publishProjects │ ├── basic │ ├── README.md │ ├── c3pm.yml │ ├── src │ │ └── main.cpp │ └── toto.txt │ ├── excludeManifest │ ├── README.md │ ├── c3pm.yml │ ├── src │ │ └── main.cpp │ └── toto.txt │ ├── includeAndExcludeFile │ ├── README.md │ ├── c3pm.yml │ ├── src │ │ └── main.cpp │ └── toto.txt │ └── includeExclude │ ├── README.md │ ├── c3pm.yml │ ├── src │ └── main.cpp │ ├── tmp.tar │ └── toto.txt ├── tars └── single.tar └── yamls └── c3pm.yml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A description of what you expected to happen. 18 | 19 | **Please tell us about your environment:** 20 | - Version: XX 21 | - OS: [all | Linux distribution XX | Windows XX | MacOS XX] 22 | 23 | **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc) 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/workflows/c3pm.yml: -------------------------------------------------------------------------------- 1 | name: c3pm CI 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Setup Go 12 | uses: actions/setup-go@v2.0.3 13 | - name: Build project 14 | working-directory: ./ 15 | run: go build 16 | test: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Setup Go 21 | uses: actions/setup-go@v2.0.3 22 | - name: Install ginkgo 23 | run: go get github.com/onsi/ginkgo/ginkgo 24 | - name: Test project 25 | working-directory: ./ 26 | run: ~/go/bin/ginkgo -v -r 27 | lint: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v2 31 | - name: Lint project 32 | uses: golangci/golangci-lint-action@v2 33 | with: 34 | version: v1.28 35 | args: -E bodyclose -E gofmt 36 | working-directory: ./ 37 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' 5 | 6 | name: Create Release 7 | 8 | jobs: 9 | build: 10 | name: Create Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | - name: Set up Go 18 | uses: actions/setup-go@v2 19 | with: 20 | go-version: 1.14 21 | - name: Run GoReleaser 22 | uses: goreleaser/goreleaser-action@v2 23 | with: 24 | version: latest 25 | args: release --rm-dist 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.coverprofile 2 | .idea/ 3 | .coverage 4 | .vscode -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod download 4 | builds: 5 | - env: 6 | - CGO_ENABLED=0 7 | binary: ctpm 8 | goos: 9 | - darwin 10 | - linux 11 | - windows 12 | goarch: 13 | - amd64 14 | - 386 15 | archives: 16 | - replacements: 17 | darwin: Darwin 18 | linux: Linux 19 | windows: Windows 20 | 386: i386 21 | amd64: x86_64 22 | brews: 23 | - name: c3pm 24 | tap: 25 | owner: c3pm-labs 26 | name: homebrew-c3pm 27 | install: | 28 | bin.install "ctpm" 29 | nfpms: 30 | - homepage: c3pm.io 31 | maintainer: Codelax 32 | formats: 33 | - deb 34 | - rpm 35 | bindir: /usr/local/bin 36 | dependencies: 37 | - cmake 38 | description: c3pm is a C++ package manager that abstracts your build system and eases the management of your dependencies. 39 | vendor: c3pm-labs 40 | checksum: 41 | name_template: "checksums.txt" 42 | snapshot: 43 | name_template: "{{ .Tag }}-SNAPSHOT" 44 | changelog: 45 | sort: asc 46 | filters: 47 | exclude: 48 | - "^docs:" 49 | - "^test:" 50 | - "^meta:" 51 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 C3PM team 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | c3pm 4 | 5 |

6 | 7 |

8 | Your toolkit to dive into C++ easily 9 |

10 | 11 | --- 12 | 13 | 14 | 15 | **Table of Contents** 16 | 17 | - [What is c3pm?](#what-is-c3pm) 18 | - [Installing c3pm](#installing-c3pm) 19 | - [Usage](#usage) 20 | - [Start your project](#start-your-project) 21 | - [Add a package](#add-a-package) 22 | - [Building your project](#building-your-project) 23 | - [Publishing your project](#publishing-your-project) 24 | - [Releases](#releases) 25 | - [Contributing](#contributing) 26 | - [Support](#support) 27 | - [Ask a question](#ask-a-question) 28 | - [Create a bug report](#create-a-bug-report) 29 | - [Submit a feature request](#submit-a-feature-request) 30 | 31 | 32 | 33 | ## What is c3pm? 34 | 35 | **c3pm** stands for C++ package manager. 36 | 37 | c3pm abstracts your build system and eases the management of your dependencies. 38 | 39 | It manages your CMake and compiles your project with **minimal configuration**. 40 | 41 | Feel free to explore the available packages on our [platform](https://c3pm.io). 42 | 43 | ## Installing c3pm 44 | 45 | You can check [our documentation](https://docs.c3pm.io/docs/getting_started/install) to see how to install c3pm 46 | 47 | ## Usage 48 | 49 | Once you installed c3pm, you can start using it. 50 | Here are some basic commands you will need. 51 | 52 | ### Start your project 53 | ```shell 54 | $ ctpm init 55 | ``` 56 | 57 | ### Add a package 58 | ```shell 59 | $ ctpm add 60 | ``` 61 | 62 | 63 | ### Building your project 64 | ```shell 65 | $ ctpm build 66 | ``` 67 | 68 | ### Publishing your project 69 | 70 | ```shell 71 | $ ctpm publish 72 | ``` 73 | 74 |
75 | 76 | You can find a more complete list of the available commands [here](https://github.com/gabrielcolson/c3pm/tree/master/specs/cli) ! 77 | 78 | ## Contributing 79 | 80 | Contributions are welcomed! 81 | 82 | Refer to our [contribution guidelines](https://github.com/gabrielcolson/c3pm/blob/master/CONTRIBUTING.md). 83 | 84 | You also may need the documentation of the different stacks: 85 | [api](https://github.com/gabrielcolson/c3pm/blob/master/api/README.md) - 86 | [web](https://github.com/gabrielcolson/c3pm/blob/master/web/README.md) - 87 | [cli](https://github.com/gabrielcolson/c3pm/blob/master/cli/README.md) 88 | 89 | ## Support 90 | 91 | ### Ask a question 92 | 93 | You can email us at: contact@c3pm.io or join our [discord](https://discord.gg/CU8Qbex3wP) 94 | -------------------------------------------------------------------------------- /adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "errors" 5 | "github.com/c3pm-labs/c3pm/adapter/cmake_adapter" 6 | "github.com/c3pm-labs/c3pm/adapter/defaultadapter" 7 | "github.com/c3pm-labs/c3pm/adapter/irrlichtadapter" 8 | "github.com/c3pm-labs/c3pm/adapter/ncurses_adapter" 9 | "github.com/c3pm-labs/c3pm/adapter_interface" 10 | "github.com/c3pm-labs/c3pm/config/manifest" 11 | ) 12 | 13 | type AdapterGetterImp struct{} 14 | 15 | func (AdapterGetterImp) FromPC(adp *manifest.AdapterConfig) (adapter_interface.Adapter, error) { 16 | switch { 17 | case adp.Name == "c3pm" && adp.Version.String() == "0.0.1": 18 | return defaultadapter.New(AdapterGetterImp{}), nil 19 | case adp.Name == "irrlicht" && adp.Version.String() == "0.0.1": 20 | return irrlichtadapter.New(), nil 21 | case adp.Name == "cmake" && adp.Version.String() == "0.0.1": 22 | return cmake_adapter.New(), nil 23 | case adp.Name == "ncurses" && adp.Version.String() == "0.0.1": 24 | return ncurses_adapter.New(), nil 25 | default: 26 | return nil, errors.New("only default adapter is supported") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /adapter/cmake_adapter/cmake_adapter.go: -------------------------------------------------------------------------------- 1 | package cmake_adapter 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/adapter_interface" 6 | "github.com/c3pm-labs/c3pm/cmake" 7 | "github.com/c3pm-labs/c3pm/config" 8 | "gopkg.in/yaml.v2" 9 | "os" 10 | "path/filepath" 11 | ) 12 | 13 | type Adapter struct{} 14 | 15 | func New() *Adapter { 16 | return &Adapter{} 17 | } 18 | 19 | var _ adapter_interface.Adapter = (*Adapter)(nil) 20 | 21 | type Config struct { 22 | Targets []string `yaml:"targets"` 23 | Variables map[string]string `yaml:"variables"` 24 | } 25 | 26 | func (a Adapter) Build(pc *config.ProjectConfig) error { 27 | cfg, err := parseConfig(pc.Manifest.Build.Config) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | buildDir := filepath.Join(pc.LocalC3PMDirPath(), "build") 33 | 34 | err = cmake.GenerateBuildFiles(pc.ProjectRoot, buildDir, cfg.Variables) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | for _, target := range cfg.Targets { 40 | err = cmake.Build(buildDir, target) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | err := os.Rename(filepath.Join(buildDir, target), filepath.Join(pc.ProjectRoot, target)) 46 | if err != nil { 47 | return fmt.Errorf("failed to move target %s to project directory: %v", target, err) 48 | } 49 | } 50 | 51 | return nil 52 | } 53 | 54 | func (a Adapter) Targets(pc *config.ProjectConfig) ([]string, error) { 55 | cfg, err := parseConfig(pc.Manifest.Build.Config) 56 | if err != nil { 57 | return nil, err 58 | } 59 | 60 | return cfg.Targets, nil 61 | } 62 | 63 | func (a Adapter) CmakeConfig(*config.ProjectConfig) (string, error) { 64 | return "", nil 65 | } 66 | 67 | func parseConfig(c interface{}) (*Config, error) { 68 | out, err := yaml.Marshal(c) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | cfg := &Config{} 74 | err = yaml.Unmarshal(out, cfg) 75 | if err != nil { 76 | return nil, err 77 | } 78 | return cfg, nil 79 | } 80 | -------------------------------------------------------------------------------- /adapter/defaultadapter/cmakegen.go: -------------------------------------------------------------------------------- 1 | // Package cmakegen handles the templating and generation of CMake configuration files. 2 | package defaultadapter 3 | 4 | import ( 5 | "fmt" 6 | "github.com/bmatcuk/doublestar" 7 | "github.com/c3pm-labs/c3pm/adapter_interface" 8 | "github.com/c3pm-labs/c3pm/config" 9 | "github.com/c3pm-labs/c3pm/config/manifest" 10 | "io/ioutil" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | ) 15 | 16 | //dependency is holds metadata about a dependency of a project. 17 | type dependency struct { 18 | // Name is the package name of the dependency 19 | Name string 20 | // Version is the version of the dependency to depend on 21 | Version string 22 | // Targets is the list of the libraries contained by the dependencies. 23 | // In most cases this will only contain one entry, but there are cases of packages containing several libraries, for 24 | // separation of concerns reasons. 25 | Targets []string 26 | // IncludeDirs is the list of the directories in which header files for the library can be found. 27 | IncludeDirs []string 28 | } 29 | 30 | //cmakeVars is the structure passed to the templates used for generating CMake config files. 31 | type cmakeVars struct { 32 | //ProjectName is the name of the current project 33 | ProjectName string 34 | //ProjectVersion is the current version of the project 35 | ProjectVersion string 36 | //Sources is a string containing the list of all of the project's sources, space-separated. 37 | Sources string 38 | //Headers is string containing the list of all of the project's header files, space-separated. 39 | Headers string 40 | //IncludeDirs is a string containing the list of all of the project's additional header directories, space-separated. 41 | IncludeDirs string 42 | //ExportedDir is the path to the directory containing export headers for the project. 43 | ExportedDir string 44 | //C3PMGlobalDir is the path to the current $HOME user directory. 45 | C3PMGlobalDir string 46 | //Dependencies is a list of all the data for each dependency of the project 47 | Dependencies []dependency 48 | //TODO: Unused 49 | PublicIncludeDir string 50 | //LinuxConfig holds linux-specific configuration information 51 | LinuxConfig *LinuxConfig 52 | //LanguageStandard is the C++ language standard version to use. 53 | LanguageStandard string 54 | //DependenciesConfig is a string containing all the cmake command needed by dependencies 55 | DependenciesConfig string 56 | } 57 | 58 | func dependenciesToCMake(pc *config.ProjectConfig, adapterGetter adapter_interface.AdapterGetter) ([]dependency, string, error) { 59 | deps := make([]dependency, len(pc.Manifest.Dependencies)) 60 | var depsConfig = "" 61 | i := 0 62 | for n, v := range pc.Manifest.Dependencies { 63 | m, err := manifest.Load(filepath.Join(config.LibCachePath(n, v), "c3pm.yml")) 64 | if err != nil { 65 | return nil, "", err 66 | } 67 | deps[i] = dependency{ 68 | Name: n, 69 | Version: v, 70 | Targets: m.Targets(), 71 | IncludeDirs: m.Publish.IncludeDirs, 72 | } 73 | adp, err := adapterGetter.FromPC(m.Build.Adapter) 74 | if err != nil { 75 | return nil, "", err 76 | } 77 | dependencyConfig, err := adp.CmakeConfig(pc) 78 | if err != nil { 79 | return nil, "", err 80 | } 81 | depsConfig = depsConfig + dependencyConfig 82 | i++ 83 | } 84 | return deps, depsConfig, nil 85 | } 86 | 87 | func globbingExprToFiles(globStr string) ([]string, error) { 88 | return doublestar.Glob(globStr) 89 | } 90 | 91 | func filterInternalSources(files []string, projectRoot string) []string { 92 | var newFiles []string 93 | for _, file := range files { 94 | if !strings.HasPrefix(file, filepath.Join(projectRoot, ".c3pm")) { 95 | newFiles = append(newFiles, file) 96 | } 97 | } 98 | return newFiles 99 | } 100 | 101 | func globbingExprsToCMakeVar(globs []string, projectRoot string) (string, error) { 102 | var files []string 103 | for _, glob := range globs { 104 | globFiles, err := globbingExprToFiles(filepath.Join(projectRoot, glob)) 105 | if err != nil { 106 | return "", fmt.Errorf("could not get files from globbing expression: %w", err) 107 | } 108 | files = append(files, globFiles...) 109 | } 110 | files = filterInternalSources(files, projectRoot) 111 | return strings.Join(files, " "), nil 112 | } 113 | 114 | func pathListToCmakeVar(paths []string, projectRoot string) string { 115 | res := "" 116 | for _, path := range paths { 117 | res += " " 118 | res += filepath.ToSlash(filepath.Join(projectRoot, path)) 119 | } 120 | return res 121 | } 122 | 123 | func varsFromProjectConfig(pc *config.ProjectConfig, adapterGetter adapter_interface.AdapterGetter) (cmakeVars, error) { 124 | dependencies, dependenciesConfig, err := dependenciesToCMake(pc, adapterGetter) 125 | if err != nil { 126 | return cmakeVars{}, err 127 | } 128 | 129 | adapterCfg, err := Parse(pc.Manifest.Build.Config) 130 | if err != nil { 131 | return cmakeVars{}, err 132 | } 133 | 134 | sources, err := globbingExprsToCMakeVar(adapterCfg.Sources, pc.ProjectRoot) 135 | if err != nil { 136 | return cmakeVars{}, fmt.Errorf("could not parse Sources: %w", err) 137 | } 138 | headers, err := globbingExprsToCMakeVar(adapterCfg.Headers, pc.ProjectRoot) 139 | if err != nil { 140 | return cmakeVars{}, fmt.Errorf("could not parse Includes: %w", err) 141 | } 142 | 143 | vars := cmakeVars{ 144 | ProjectName: pc.Manifest.Name, 145 | ProjectVersion: pc.Manifest.Version.String(), 146 | Sources: filepath.ToSlash(sources), 147 | Headers: filepath.ToSlash(headers), 148 | IncludeDirs: pathListToCmakeVar(adapterCfg.IncludeDirs, pc.ProjectRoot), 149 | C3PMGlobalDir: filepath.ToSlash(config.GlobalC3PMDirPath()), 150 | Dependencies: dependencies, 151 | LinuxConfig: adapterCfg.LinuxConfig, 152 | LanguageStandard: pc.Manifest.Standard, 153 | DependenciesConfig: dependenciesConfig, 154 | } 155 | 156 | return vars, nil 157 | } 158 | 159 | func fromProjectConfig(pc *config.ProjectConfig, adapterGetter adapter_interface.AdapterGetter) (string, error) { 160 | var cmake string 161 | var vars cmakeVars 162 | 163 | vars, err := varsFromProjectConfig(pc, adapterGetter) 164 | if err != nil { 165 | return "", fmt.Errorf("failed to generate cmake variables: %w", err) 166 | } 167 | switch pc.Manifest.Type { 168 | case manifest.Executable: 169 | cmake, err = (func() (string, error) { return executable(vars) })() 170 | case manifest.Library: 171 | cmake, err = (func() (string, error) { return library(vars) })() 172 | } 173 | if err != nil { 174 | return "", fmt.Errorf("failed to generate cmake: %w", err) 175 | } 176 | return cmake, nil 177 | } 178 | 179 | //generateCMakeScripts takes a config.ProjectConfig and creates CMake configuration files based on the project config. 180 | func generateCMakeScripts(targetDir string, pc *config.ProjectConfig, adapterGetter adapter_interface.AdapterGetter) error { 181 | cmakeContent, err := fromProjectConfig(pc, adapterGetter) 182 | if err != nil { 183 | return fmt.Errorf("failed to generate cmake scripts: %w", err) 184 | } 185 | err = os.MkdirAll(targetDir, os.ModePerm) 186 | if err != nil { 187 | return fmt.Errorf("failed to create c3pm cmake directory: %w", err) 188 | } 189 | err = ioutil.WriteFile(filepath.Join(targetDir, "CMakeLists.txt"), []byte(cmakeContent), 0644) 190 | if err != nil { 191 | return fmt.Errorf("failed to create CMakeLists.txt: %w", err) 192 | } 193 | return nil 194 | } 195 | -------------------------------------------------------------------------------- /adapter/defaultadapter/cmakegen_test.go: -------------------------------------------------------------------------------- 1 | package defaultadapter 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Masterminds/semver/v3" 6 | "github.com/c3pm-labs/c3pm/config" 7 | "github.com/c3pm-labs/c3pm/config/manifest" 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | "io/ioutil" 11 | "os" 12 | "path/filepath" 13 | ) 14 | 15 | var _ = Describe("Gen Test", func() { 16 | path, _ := filepath.Abs("../../test_helpers/projects/genProject") 17 | var ( 18 | simpleProject = &config.ProjectConfig{ 19 | Manifest: manifest.Manifest{ 20 | C3PMVersion: manifest.C3PMVersion1, 21 | Type: manifest.Executable, 22 | Name: "hello-bin", 23 | Description: "Demo binary", 24 | Version: manifest.Version{ 25 | Version: semver.MustParse("1.1.5"), 26 | }, 27 | Standard: "20", 28 | License: "ISC", 29 | Build: &manifest.BuildConfig{ 30 | Adapter: &manifest.AdapterConfig{ 31 | Name: "c3pm", 32 | Version: CurrentVersion, 33 | }, 34 | Config: &Config{ 35 | Sources: []string{"**/*.cpp"}, 36 | Headers: []string{"**/*.hpp"}, 37 | IncludeDirs: []string{"include"}, 38 | }, 39 | }, 40 | Dependencies: manifest.Dependencies{}, 41 | }, 42 | ProjectRoot: path, 43 | } 44 | projectWithDependencies = &config.ProjectConfig{ 45 | Manifest: manifest.Manifest{ 46 | C3PMVersion: manifest.C3PMVersion1, 47 | Type: manifest.Executable, 48 | Name: "hello-bin", 49 | Description: "Demo binary", 50 | Version: manifest.Version{ 51 | Version: semver.MustParse("1.1.5"), 52 | }, 53 | Standard: "20", 54 | License: "ISC", 55 | Build: &manifest.BuildConfig{ 56 | Adapter: &manifest.AdapterConfig{ 57 | Name: "c3pm", 58 | Version: CurrentVersion, 59 | }, 60 | Config: &Config{ 61 | Sources: []string{"**/*.cpp"}, 62 | Headers: []string{"**/*.hpp"}, 63 | IncludeDirs: []string{"include"}, 64 | }, 65 | }, 66 | Dependencies: manifest.Dependencies{ 67 | "hello": "1.0.3", 68 | "m": "2.0.0", 69 | }, 70 | }, 71 | ProjectRoot: path, 72 | } 73 | ) 74 | 75 | BeforeEach(func() { 76 | }) 77 | AfterEach(func() { 78 | err := os.RemoveAll(cmakeDirFromPc(simpleProject)) 79 | Ω(err).ShouldNot(HaveOccurred()) 80 | }) 81 | Context("generates a cmake file without dependencies", func() { 82 | err := generateCMakeScripts(cmakeDirFromPc(simpleProject), simpleProject, nil) 83 | fmt.Println(err) 84 | Ω(err).ShouldNot(HaveOccurred()) 85 | data, err := ioutil.ReadFile(filepath.Join(cmakeDirFromPc(simpleProject), "CMakeLists.txt")) 86 | Ω(err).ShouldNot(HaveOccurred()) 87 | content := string(data) 88 | 89 | It("contains the correct source files", func() { 90 | mainPath, err := filepath.Abs(filepath.Join(path, "main.cpp")) 91 | Ω(err).ShouldNot(HaveOccurred()) 92 | libPath, err := filepath.Abs(filepath.Join(path, "lib", "hello.cpp")) 93 | Ω(err).ShouldNot(HaveOccurred()) 94 | Ω(content).Should(ContainSubstring(mainPath)) 95 | Ω(content).Should(ContainSubstring(libPath)) 96 | }) 97 | It("doesn't contain dependencies", func() { 98 | Ω(content).ShouldNot(ContainSubstring("-l")) 99 | Ω(content).ShouldNot(ContainSubstring("-L")) 100 | }) 101 | }) 102 | Context("generates a cmake file with dependencies", func() { 103 | _ = projectWithDependencies 104 | //TODO: dependencies tests 105 | //err := cmakegen.generateCMakeScripts(projectWithDependencies) 106 | //Ω(err).ShouldNot(HaveOccurred()) 107 | //data, err := ioutil.ReadFile(filepath.Join(projectWithDependencies.CMakeDir(), "CMakeLists.txt")) 108 | //Ω(err).ShouldNot(HaveOccurred()) 109 | //content := string(data) 110 | //fmt.Println(content) 111 | //It("contains the correct source files", func() { 112 | // mainPath, err := filepath.Abs(filepath.Join(path, "main.cpp")) 113 | // Ω(err).ShouldNot(HaveOccurred()) 114 | // libPath, err := filepath.Abs(filepath.Join(path, "lib", "hello.cpp")) 115 | // Ω(err).ShouldNot(HaveOccurred()) 116 | // Ω(content).Should(ContainSubstring(mainPath)) 117 | // Ω(content).Should(ContainSubstring(libPath)) 118 | //}) 119 | //It("contains links to the dependencies", func() { 120 | // Ω(content).Should(ContainSubstring("-lhello")) 121 | // Ω(content).Should(ContainSubstring("-lm")) 122 | // Ω(content).Should(ContainSubstring("-L")) 123 | //}) 124 | }) 125 | }) 126 | -------------------------------------------------------------------------------- /adapter/defaultadapter/config.go: -------------------------------------------------------------------------------- 1 | package defaultadapter 2 | 3 | import ( 4 | "gopkg.in/yaml.v2" 5 | ) 6 | 7 | // Config holds the config for the builtin adapter 8 | type Config struct { 9 | // Sources lists the project's source files. 10 | Sources []string `yaml:"sources"` 11 | // Headers lists the project's header files. 12 | Headers []string `yaml:"headers"` 13 | // TestSources lists the project's tests source files 14 | TestSources []string `yaml:"test_sources"` 15 | // TestHeaders list the tests' header files 16 | TestHeaders []string `yaml:"test_headers"` 17 | // IncludeDirs lists the projects additional header directories. 18 | IncludeDirs []string `yaml:"include_dirs"` 19 | // LinuxConfig holds Linux specific configuration 20 | LinuxConfig *LinuxConfig `yaml:"linux,omitempty"` 21 | } 22 | 23 | // LinuxConfig holds specific configuration on Linux operating systems. 24 | type LinuxConfig struct { 25 | UsePthread bool `yaml:"use_pthread"` 26 | } 27 | 28 | // Parse parses a Config 29 | func Parse(c interface{}) (*Config, error) { 30 | // TODO: is there a better way to do that? 31 | out, err := yaml.Marshal(c) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | cfg := &Config{} 37 | err = yaml.Unmarshal(out, cfg) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return cfg, nil 42 | } 43 | -------------------------------------------------------------------------------- /adapter/defaultadapter/defaultadapter.go: -------------------------------------------------------------------------------- 1 | package defaultadapter 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bmatcuk/doublestar" 6 | "github.com/c3pm-labs/c3pm/adapter_interface" 7 | "github.com/c3pm-labs/c3pm/cmake" 8 | "github.com/c3pm-labs/c3pm/config" 9 | "github.com/c3pm-labs/c3pm/config/manifest" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | ) 14 | 15 | // DefaultAdapter is the builtin adapter used by default in c3pm 16 | type DefaultAdapter struct { 17 | adapterGetter adapter_interface.AdapterGetter 18 | } 19 | 20 | // New creates a new builtin DefaultAdapter 21 | func New(adapterGetter adapter_interface.AdapterGetter) *DefaultAdapter { 22 | return &DefaultAdapter{adapterGetter} 23 | } 24 | 25 | var CurrentVersion, _ = manifest.VersionFromString("0.0.1") 26 | 27 | func isInCache(pc *config.ProjectConfig) bool { 28 | root, _ := filepath.Abs(pc.ProjectRoot) 29 | c3pmDir, _ := filepath.Abs(config.GlobalC3PMDirPath()) 30 | if !strings.Contains(root, filepath.Join(c3pmDir, "cache")) { 31 | // Project dir not in cache directory, skipping 32 | return false 33 | } 34 | if _, err := os.Stat(filepath.Join(root, fmt.Sprintf("lib%s.a", pc.Manifest.Name))); err != nil { 35 | // Lib file does not exist yet 36 | return false 37 | } 38 | return true 39 | } 40 | 41 | func (a *DefaultAdapter) Build(pc *config.ProjectConfig) error { 42 | cmakeVariables := map[string]string{ 43 | "CMAKE_LIBRARY_OUTPUT_DIRECTORY": pc.ProjectRoot, 44 | "CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE": pc.ProjectRoot, 45 | "CMAKE_ARCHIVE_OUTPUT_DIRECTORY": pc.ProjectRoot, 46 | "CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE": pc.ProjectRoot, 47 | "CMAKE_RUNTIME_OUTPUT_DIRECTORY": pc.ProjectRoot, 48 | "CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE": pc.ProjectRoot, 49 | "CMAKE_INSTALL_PREFIX": filepath.ToSlash(filepath.Join(config.GlobalC3PMDirPath(), "cache", pc.Manifest.Name, pc.Manifest.Version.String())), 50 | "CMAKE_BUILD_TYPE": "Release", 51 | // Useful for Windows build 52 | //"MSVC_TOOLSET_VERSION": "141", 53 | //"MSVC_VERSION": "1916", 54 | } 55 | 56 | headerOnly, err := isHeaderOnly(pc) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | // don't build if the lib is header only 62 | if headerOnly && pc.Manifest.Type == manifest.Library { 63 | // TODO: generate cmake files so we can have IDE integration 64 | return nil 65 | } 66 | 67 | // dont rebuild if already in cache 68 | if pc.Manifest.Type == manifest.Library && isInCache(pc) { 69 | return nil 70 | } 71 | 72 | err = generateCMakeScripts(cmakeDirFromPc(pc), pc, a.adapterGetter) 73 | if err != nil { 74 | return fmt.Errorf("error generating config files: %w", err) 75 | } 76 | 77 | err = cmake.GenerateBuildFiles(cmakeDirFromPc(pc), buildDirFromPc(pc), cmakeVariables) 78 | if err != nil { 79 | return fmt.Errorf("cmake build failed: %w", err) 80 | } 81 | 82 | err = cmake.Build(buildDirFromPc(pc), pc.Manifest.Name) 83 | if err != nil { 84 | return fmt.Errorf("build failed: %w", err) 85 | } 86 | return nil 87 | } 88 | 89 | func isHeaderOnly(pc *config.ProjectConfig) (bool, error) { 90 | cfg, err := Parse(pc.Manifest.Build.Config) 91 | if err != nil { 92 | return false, err 93 | } 94 | 95 | hasSources, err := hasFileMatchingRule(cfg.Sources, pc.ProjectRoot) 96 | return !hasSources, err 97 | } 98 | 99 | func hasFileMatchingRule(rules []string, projectRoot string) (bool, error) { 100 | for _, rule := range rules { 101 | files, err := doublestar.Glob(filepath.Join(projectRoot, rule)) 102 | if err != nil { 103 | return false, err 104 | } 105 | if len(files) > 0 { 106 | return true, nil 107 | } 108 | } 109 | return false, nil 110 | } 111 | 112 | func (a *DefaultAdapter) Targets(_ *config.ProjectConfig) ([]string, error) { 113 | return nil, nil 114 | } 115 | 116 | func (a *DefaultAdapter) CmakeConfig(_ *config.ProjectConfig) (string, error) { 117 | return "", nil 118 | } 119 | 120 | func cmakeDirFromPc(pc *config.ProjectConfig) string { 121 | return filepath.Join(pc.LocalC3PMDirPath(), "cmake") 122 | } 123 | 124 | func buildDirFromPc(pc *config.ProjectConfig) string { 125 | return filepath.Join(pc.LocalC3PMDirPath(), "build") 126 | } 127 | 128 | func (a *DefaultAdapter) Test(pc *config.ProjectConfig) error { 129 | adapterCfg, err := Parse(pc.Manifest.Build.Config) 130 | if err != nil { 131 | return err 132 | } 133 | sources := adapterCfg.Sources 134 | headers := adapterCfg.Headers 135 | name := pc.Manifest.Name 136 | adapterCfg.Sources = adapterCfg.TestSources 137 | adapterCfg.Headers = adapterCfg.TestHeaders 138 | pc.Manifest.Name = pc.Manifest.Name + "_test" 139 | pc.Manifest.Build.Config = adapterCfg 140 | err = a.Build(pc) 141 | adapterCfg.Sources = sources 142 | adapterCfg.Headers = headers 143 | pc.Manifest.Name = name 144 | pc.Manifest.Build.Config = adapterCfg 145 | return err 146 | } 147 | -------------------------------------------------------------------------------- /adapter/defaultadapter/defaultadapter_suite_test.go: -------------------------------------------------------------------------------- 1 | package defaultadapter_test 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | func TestCMakeGen(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | path, err := filepath.Abs("testsArtifacts") 15 | if err != nil { 16 | t.Fatal("Failed to get testsArtifacts absolute path") 17 | } 18 | RunSpecs(t, "CMakeGen Suite") 19 | err = os.RemoveAll(path) 20 | if err != nil { 21 | t.Fatal("Failed to clean test artifacts\n") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /adapter/defaultadapter/exec.go: -------------------------------------------------------------------------------- 1 | package defaultadapter 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | "text/template" 8 | ) 9 | 10 | var executableTemplate = `cmake_minimum_required(VERSION 3.16) 11 | project({{.ProjectName}} VERSION {{.ProjectVersion}}) 12 | 13 | set(CMAKE_CXX_STANDARD {{.LanguageStandard}}) 14 | set(C3PM_PROJECT_NAME {{.ProjectName}}) 15 | set(C3PM_GLOBAL_DIR {{.C3PMGlobalDir}}) 16 | 17 | add_executable({{.ProjectName}}) 18 | 19 | target_sources({{.ProjectName}} PRIVATE 20 | {{.Sources}} 21 | {{.Headers}} 22 | ) 23 | {{$c3pmGlobalDir:=.C3PMGlobalDir}} 24 | 25 | target_include_directories( 26 | {{- .ProjectName}} PRIVATE {{.IncludeDirs}} 27 | {{- range .Dependencies }} 28 | {{- $name:=.Name }} 29 | {{- $version:=.Version}} 30 | {{- range .IncludeDirs }} 31 | {{ $c3pmGlobalDir }}/cache/{{$name}}/{{$version}}/{{.}} 32 | {{- end }} 33 | {{- end }} 34 | ) 35 | {{range .Dependencies}} 36 | find_library({{ .Name | ToUpper}} {{.Name}} "{{$c3pmGlobalDir}}/cache/{{.Name}}/{{.Version}}/") 37 | {{end}} 38 | 39 | {{.DependenciesConfig}} 40 | 41 | target_link_libraries( 42 | {{.ProjectName}} 43 | PUBLIC 44 | {{range .Dependencies}} 45 | $<$>:{{"${"}}{{.Name|ToUpper}}{{"}"}}> 46 | {{- end}} 47 | ) 48 | ` 49 | 50 | func executable(v cmakeVars) (string, error) { 51 | funcMap := template.FuncMap{ 52 | "ToUpper": strings.ToUpper, 53 | } 54 | cmake := bytes.NewBuffer([]byte{}) 55 | tmpl, err := template.New("cmakeExecutable").Funcs(funcMap).Parse(addPlatformSpecificCMake(executableTemplate, v)) 56 | if err != nil { 57 | return "", fmt.Errorf("could not parse cmake template: %w", err) 58 | } 59 | if err := tmpl.Execute(cmake, v); err != nil { 60 | return "", fmt.Errorf("could not template cmake: %w", err) 61 | } 62 | return cmake.String(), nil 63 | } 64 | -------------------------------------------------------------------------------- /adapter/defaultadapter/lib.go: -------------------------------------------------------------------------------- 1 | package defaultadapter 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "strings" 8 | "text/template" 9 | ) 10 | 11 | var libraryTemplate = `cmake_minimum_required(VERSION 3.16) 12 | project({{.ProjectName}} VERSION {{.ProjectVersion}}) 13 | 14 | set(CMAKE_CXX_STANDARD {{.LanguageStandard}}) 15 | 16 | add_library({{.ProjectName}} STATIC) 17 | 18 | {{- $c3pmGlobalDir:=.C3PMGlobalDir}} 19 | 20 | target_sources({{.ProjectName}} PRIVATE {{.Sources}} {{.Headers}}) 21 | target_include_directories( 22 | {{- .ProjectName}} PRIVATE {{.IncludeDirs}} 23 | {{- range .Dependencies }} 24 | {{- $name:=.Name }} 25 | {{- $version:=.Version}} 26 | {{- range .IncludeDirs }} 27 | {{ $c3pmGlobalDir }}/cache/{{$name}}/{{$version}}/{{.}} 28 | {{- end }} 29 | {{- end }} 30 | ) 31 | ` 32 | 33 | func removeCommand(cmake string, command string) string { 34 | var cmakeClean string 35 | scanner := bufio.NewScanner(strings.NewReader(cmake)) 36 | for scanner.Scan() { 37 | if !strings.HasPrefix(scanner.Text(), command) { 38 | cmakeClean += scanner.Text() + "\n" 39 | } 40 | } 41 | return cmakeClean 42 | } 43 | 44 | func library(v cmakeVars) (string, error) { 45 | funcMap := template.FuncMap{ 46 | "AddTrailingSlash": func(text string) string { 47 | if !strings.HasSuffix(text, "/") { 48 | return text + "/" 49 | } 50 | return text 51 | }, 52 | } 53 | cmake := bytes.NewBuffer([]byte{}) 54 | tmpl, err := template.New("cmakeLibrary").Funcs(funcMap).Parse(addPlatformSpecificCMake(libraryTemplate, v)) 55 | if err != nil { 56 | return "", fmt.Errorf("could not parse cmake template: %w", err) 57 | } 58 | if err := tmpl.Execute(cmake, v); err != nil { 59 | return "", fmt.Errorf("could not template cmake: %w", err) 60 | } 61 | cmakeClean := cmake.String() 62 | if len(v.ExportedDir) == 0 { 63 | cmakeClean = removeCommand(cmakeClean, "install(DIRECTORY") 64 | } 65 | return cmakeClean, nil 66 | } 67 | -------------------------------------------------------------------------------- /adapter/defaultadapter/linux.go: -------------------------------------------------------------------------------- 1 | package defaultadapter 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | var pthreadTemplate = ` 8 | set(THREADS_PREFER_PTHREAD_FLAG ON) 9 | find_package(Threads REQUIRED) 10 | target_link_libraries({{.ProjectName}} PUBLIC Threads::Threads) 11 | ` 12 | 13 | func addLinuxData(sb *strings.Builder, v cmakeVars) { 14 | if v.LinuxConfig.UsePthread { 15 | sb.WriteString(pthreadTemplate) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /adapter/defaultadapter/platformconfig.go: -------------------------------------------------------------------------------- 1 | package defaultadapter 2 | 3 | import ( 4 | "runtime" 5 | "strings" 6 | ) 7 | 8 | func addPlatformSpecificCMake(base string, v cmakeVars) string { 9 | var tmpl strings.Builder 10 | tmpl.WriteString(base) 11 | if runtime.GOOS == "linux" && v.LinuxConfig != nil { 12 | addLinuxData(&tmpl, v) 13 | } 14 | return tmpl.String() 15 | } 16 | -------------------------------------------------------------------------------- /adapter/irrlichtadapter/cmakeConfig.go: -------------------------------------------------------------------------------- 1 | package irrlichtadapter 2 | 3 | var CmakeConfig = ` 4 | 5 | if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -lXxf86vm -lXext -lX11 -lGL -lGLU") 7 | endif ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") 8 | find_package(OpenGL REQUIRED) 9 | find_package(X11 REQUIRED) 10 | find_package(GLUT REQUIRED) 11 | find_package(ZLIB REQUIRED) 12 | set(LIBRARIES ${IRRLICHT_LIBRARY} ${OPENGL_LIBRARIES} ${X11_X11_LIB} ${GLUT_LIBRARY} ${ZLIB_LIBRARIES}) 13 | 14 | if (APPLE) 15 | find_library(CARBON_LIBRARY Carbon) 16 | find_library(COCOA_LIBRARY Cocoa) 17 | find_library(IOKIT_LIBRARY IOKit) 18 | set(OSX_LIBRARIES ${CARBON_LIBRARY} ${COCOA_LIBRARY} ${IOKIT_LIBRARY}) 19 | endif (APPLE) 20 | 21 | target_include_directories(${C3PM_PROJECT_NAME} PRIVATE src ${IRRLICHT_INCLUDE_DIR}) 22 | target_link_libraries(${C3PM_PROJECT_NAME} PUBLIC ${OSX_LIBRARIES} ${LIBRARIES}) 23 | ` 24 | -------------------------------------------------------------------------------- /adapter/irrlichtadapter/irrlichtadapter.go: -------------------------------------------------------------------------------- 1 | package irrlichtadapter 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/config" 6 | "github.com/c3pm-labs/c3pm/config/manifest" 7 | "io/ioutil" 8 | "os" 9 | "os/exec" 10 | "runtime" 11 | "strings" 12 | ) 13 | 14 | type IrrlichtAdapter struct { 15 | } 16 | 17 | // New creates a new builtin MakefileAdapter 18 | func New() *IrrlichtAdapter { 19 | return &IrrlichtAdapter{} 20 | } 21 | 22 | var CurrentVersion, _ = manifest.VersionFromString("0.0.1") 23 | 24 | func visit(path string, old string, new string) error { 25 | read, err := ioutil.ReadFile(path) 26 | if err != nil { 27 | return err 28 | } 29 | newContents := strings.Replace(string(read), old, new, -1) 30 | err = ioutil.WriteFile(path, []byte(newContents), 0) 31 | if err != nil { 32 | return err 33 | } 34 | return nil 35 | } 36 | 37 | func executeCli(command string, args ...string) error { 38 | cmd := exec.Command(command, args...) 39 | cmd.Stdout = os.Stdout 40 | cmd.Stderr = os.Stderr 41 | err := cmd.Start() 42 | if err != nil { 43 | return fmt.Errorf("failed to start %s: %w", command, err) 44 | } 45 | if err = cmd.Wait(); err != nil { 46 | return fmt.Errorf("%s process failed: %w", command, err) 47 | } 48 | return nil 49 | } 50 | 51 | func buildOnMacOS(pc *config.ProjectConfig) error { 52 | err := visit(pc.ProjectRoot+"/src/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm", 53 | "[NSApp setDelegate:(id)", 54 | "[NSApp setDelegate:(id)", 55 | ) 56 | if err != nil { 57 | return err 58 | } 59 | err = visit(pc.ProjectRoot+"/src/Irrlicht/libpng/pngpriv.h", 60 | "# error ZLIB_VERNUM != PNG_ZLIB_VERNUM \\", 61 | "# warning ZLIB_VERNUM != PNG_ZLIB_VERNUM \\", 62 | ) 63 | if err != nil { 64 | return err 65 | } 66 | var path = pc.ProjectRoot + "/src/Irrlicht/MacOSX/MacOSX.xcodeproj" 67 | return executeCli("xcodebuild", "-project", path, "-target", "libIrrlicht.a", "SYSMROOT=build") 68 | } 69 | 70 | func buildOnLinux(pc *config.ProjectConfig) error { 71 | return executeCli("make", "-C", pc.ProjectRoot+"/src/Irrlicht") 72 | } 73 | 74 | func (a *IrrlichtAdapter) Build(pc *config.ProjectConfig) error { 75 | switch runtime.GOOS { 76 | case "darwin": 77 | err := buildOnMacOS(pc) 78 | if err != nil { 79 | return err 80 | } 81 | oldLocation := pc.ProjectRoot + "/src/Irrlicht/MacOSX/build/Release/libIrrlicht.a" 82 | err = os.Rename(oldLocation, pc.ProjectRoot+"/libIrrlicht.a") 83 | if err != nil { 84 | return err 85 | } 86 | case "linux": 87 | err := buildOnLinux(pc) 88 | if err != nil { 89 | return err 90 | } 91 | oldLocation := pc.ProjectRoot + "/lib/Linux/libIrrlicht.a" 92 | err = os.Rename(oldLocation, pc.ProjectRoot+"/libIrrlicht.a") 93 | if err != nil { 94 | return err 95 | } 96 | case "windows": 97 | return nil 98 | } 99 | return nil 100 | } 101 | 102 | func (a *IrrlichtAdapter) CmakeConfig(pc *config.ProjectConfig) (string, error) { 103 | return CmakeConfig, nil 104 | } 105 | 106 | func (a *IrrlichtAdapter) Targets(_ *config.ProjectConfig) ([]string, error) { 107 | return nil, nil 108 | } 109 | -------------------------------------------------------------------------------- /adapter/ncurses_adapter/ncurses_adapter.go: -------------------------------------------------------------------------------- 1 | package ncurses_adapter 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/config" 6 | "gopkg.in/yaml.v2" 7 | "os" 8 | "os/exec" 9 | ) 10 | 11 | type Adapter struct { 12 | } 13 | 14 | type Config struct { 15 | Targets []string `yaml:"targets"` 16 | Flags map[string]string `yaml:"variables"` 17 | } 18 | 19 | func New() *Adapter { 20 | return &Adapter{} 21 | } 22 | 23 | func executeCli(command string, dir string, args ...string) error { 24 | cmd := exec.Command(command, args...) 25 | cmd.Stdout = os.Stdout 26 | cmd.Stderr = os.Stderr 27 | cmd.Dir = dir 28 | err := cmd.Start() 29 | if err != nil { 30 | return fmt.Errorf("failed to start %s: %w", command, err) 31 | } 32 | if err = cmd.Wait(); err != nil { 33 | return fmt.Errorf("%s process failed: %w", command, err) 34 | } 35 | return nil 36 | } 37 | 38 | func (a *Adapter) Build(pc *config.ProjectConfig) error { 39 | err := executeCli("chmod", pc.ProjectRoot, "+x", pc.ProjectRoot+"/configure") 40 | if err != nil { 41 | return err 42 | } 43 | err = executeCli( 44 | pc.ProjectRoot+"/configure", 45 | pc.ProjectRoot, 46 | "--prefix", pc.ProjectRoot+"/library", 47 | "--datadir", pc.ProjectRoot+"/library", 48 | ) 49 | if err != nil { 50 | return err 51 | } 52 | err = executeCli("make", pc.ProjectRoot, "install") 53 | if err != nil { 54 | return err 55 | } 56 | err = os.Rename(pc.ProjectRoot+"/library/lib/libncurses.a", pc.ProjectRoot+"/libncurses.a") 57 | if err != nil { 58 | return err 59 | } 60 | return nil 61 | } 62 | 63 | func parseConfig(c interface{}) (*Config, error) { 64 | out, err := yaml.Marshal(c) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | cfg := &Config{} 70 | err = yaml.Unmarshal(out, cfg) 71 | if err != nil { 72 | return nil, err 73 | } 74 | return cfg, nil 75 | } 76 | 77 | func (a *Adapter) Targets(pc *config.ProjectConfig) (targets []string, err error) { 78 | cfg, err := parseConfig(pc.Manifest.Build.Config) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | return cfg.Targets, nil 84 | } 85 | 86 | func (a *Adapter) CmakeConfig(pc *config.ProjectConfig) (string, error) { 87 | return "", nil 88 | } 89 | -------------------------------------------------------------------------------- /adapter_interface/interface.go: -------------------------------------------------------------------------------- 1 | package adapter_interface 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/config" 5 | "github.com/c3pm-labs/c3pm/config/manifest" 6 | ) 7 | 8 | type Adapter interface { 9 | // Build builds the targets 10 | Build(pc *config.ProjectConfig) error 11 | // Targets return the paths of the targets built by the Build function 12 | Targets(pc *config.ProjectConfig) (targets []string, err error) 13 | //CmakeConfig return a string to add in the cmake of the project who use the library 14 | CmakeConfig(pc *config.ProjectConfig) (string, error) 15 | } 16 | 17 | type AdapterTestable interface { 18 | // Test run tests 19 | Test(pc *config.ProjectConfig) error 20 | } 21 | 22 | type AdapterGetter interface { 23 | FromPC(adp *manifest.AdapterConfig) (Adapter, error) 24 | } 25 | -------------------------------------------------------------------------------- /api/api.go: -------------------------------------------------------------------------------- 1 | // Package api handles interaction between the C3PM Command Line Interface (CLI), and C3PM's API. 2 | // The package's role is to abstract all needed low level interaction, such as HTTP or filesystem calls 3 | // exposing an interface simple, clear and easy to understand from the rest of the CLI. 4 | package api 5 | 6 | import ( 7 | "bytes" 8 | "encoding/json" 9 | "fmt" 10 | "github.com/c3pm-labs/c3pm/env" 11 | "github.com/schollz/progressbar/v3" 12 | "io" 13 | "io/ioutil" 14 | "mime/multipart" 15 | "net/http" 16 | "os" 17 | ) 18 | 19 | // API represents a connection to c3pm's backend, used for authentication and publishing purposes 20 | type API struct { 21 | Client *http.Client 22 | Token string 23 | } 24 | 25 | // New builds a new API object 26 | func New(c *http.Client, t string) API { 27 | return API{ 28 | Client: c, 29 | Token: t, 30 | } 31 | } 32 | 33 | func (c API) newRequest(method string, path string, body io.Reader) (*http.Request, error) { 34 | var host string 35 | if customEndpoint := os.Getenv("C3PM_API_ENDPOINT"); customEndpoint == "" { 36 | host = env.API_ENDPOINT 37 | } else { 38 | host = customEndpoint 39 | } 40 | url := host + path 41 | 42 | req, err := http.NewRequest(method, url, body) 43 | if err != nil { 44 | return nil, err 45 | } 46 | if c.Token != "" { 47 | req.Header.Set("Authorization", c.Token) 48 | } 49 | return req, err 50 | } 51 | 52 | func (c API) fetch(method string, path string, body io.Reader, data interface{}) error { 53 | req, err := c.newRequest(method, path, body) 54 | if err != nil { 55 | return fmt.Errorf("failed to create request: %w", err) 56 | } 57 | req.Header.Set("Content-Type", "application/json; charset=utf-8") 58 | 59 | resp, err := c.Client.Do(req) 60 | if err != nil { 61 | return err 62 | } 63 | defer resp.Body.Close() 64 | 65 | success := resp.StatusCode >= 200 && resp.StatusCode < 300 66 | if !success { 67 | return handleHttpError(resp) 68 | } 69 | 70 | b, err := ioutil.ReadAll(resp.Body) 71 | if err != nil { 72 | return fmt.Errorf("failed to read response body: %w", err) 73 | } 74 | 75 | if len(b) > 0 { 76 | err = json.Unmarshal(b, &data) 77 | if err != nil { 78 | return fmt.Errorf("failed to unmarshal response body: %w", err) 79 | } 80 | } 81 | 82 | return nil 83 | } 84 | 85 | func (c API) send(method string, path string, buf io.Reader) error { 86 | body := new(bytes.Buffer) 87 | w := multipart.NewWriter(body) 88 | part, err := w.CreateFormFile("file", "package.tar") 89 | if err != nil { 90 | return err 91 | } 92 | contents, err := ioutil.ReadAll(buf) 93 | if err != nil { 94 | return err 95 | } 96 | _, err = part.Write(contents) 97 | if err != nil { 98 | return err 99 | } 100 | bar := progressbar.DefaultBytes( 101 | int64(body.Len()), 102 | "Uploading package", 103 | ) 104 | barReader := progressbar.NewReader(body, bar) 105 | w.Close() 106 | req, err := c.newRequest(method, path, &barReader) 107 | if err != nil { 108 | return fmt.Errorf("failed to create request: %w", err) 109 | } 110 | req.Header.Add("Content-Type", w.FormDataContentType()) 111 | 112 | resp, err := c.Client.Do(req) 113 | if err != nil { 114 | return err 115 | } 116 | defer resp.Body.Close() 117 | 118 | success := resp.StatusCode >= 200 && resp.StatusCode < 300 119 | if !success { 120 | return handleHttpError(resp) 121 | } 122 | return nil 123 | } 124 | 125 | func handleHttpError(resp *http.Response) error { 126 | body, err := ioutil.ReadAll(resp.Body) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | var message string 132 | var parsedBody struct { 133 | Error string `json:"error"` 134 | } 135 | err = json.Unmarshal(body, &parsedBody) 136 | if err != nil { 137 | message = string(body) 138 | } else { 139 | message = parsedBody.Error 140 | } 141 | 142 | return fmt.Errorf("Client error: '%s' failed (%d): '%s'", resp.Request.URL, resp.StatusCode, message) 143 | } 144 | -------------------------------------------------------------------------------- /api/api_suite_test.go: -------------------------------------------------------------------------------- 1 | package api_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestCtpm(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "API Suite") 13 | } 14 | -------------------------------------------------------------------------------- /api/auth.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "net/http" 7 | ) 8 | 9 | // Login handles the login logic with the API. 10 | // It takes the user's email and password as strings, 11 | // sends them to the API, and returns the ApiKey object 12 | // to be used in further calls to the API. 13 | func (c *API) Login(login, password string) (string, error) { 14 | body, err := json.Marshal(struct { 15 | Login string `json:"login"` 16 | Password string `json:"password"` 17 | }{ 18 | Login: login, 19 | Password: password, 20 | }) 21 | if err != nil { 22 | return "", err 23 | } 24 | 25 | var data = struct { 26 | ApiKey string `json:"apiKey"` 27 | }{} 28 | err = c.fetch(http.MethodPost, "/auth/login", bytes.NewReader(body), &data) 29 | return data.ApiKey, err 30 | } 31 | -------------------------------------------------------------------------------- /api/example_test.go: -------------------------------------------------------------------------------- 1 | package api_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/api" 6 | "net/http" 7 | ) 8 | 9 | func ExampleAPI_Login() { 10 | c := api.New(&http.Client{}, "xxxx") 11 | key, err := c.Login("login", "password") 12 | fmt.Println(key, err) 13 | } 14 | 15 | func ExampleAPI_Upload() { 16 | c := api.New(&http.Client{}, "xxxx") 17 | err := c.Upload([]string{"../../testhelpers/tars/single.tar"}) 18 | fmt.Println(err) 19 | } 20 | -------------------------------------------------------------------------------- /api/metrics.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | ) 9 | 10 | func (c *API) CountDownload(packageName string) error { 11 | body, err := json.Marshal(struct { 12 | PackageName string `json:"packageName"` 13 | }{ 14 | PackageName: packageName, 15 | }) 16 | if err != nil { 17 | return err 18 | } 19 | err = c.fetch(http.MethodPost, "/packages/countDownloads", bytes.NewReader(body), nil) 20 | if err != nil { 21 | return fmt.Errorf("failed to count download: %w", err) 22 | } 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /api/test/mock.go: -------------------------------------------------------------------------------- 1 | // Package test provides simple mock servers to be used when testing interaction with C3PM's API. 2 | package apitest 3 | 4 | import "net/http" 5 | 6 | func createServer(code int) http.Handler { 7 | sm := http.NewServeMux() 8 | sm.HandleFunc("/packages/publish", func(w http.ResponseWriter, r *http.Request) { 9 | w.WriteHeader(code) 10 | }) 11 | sm.HandleFunc("/auth/login", func(w http.ResponseWriter, r *http.Request) { 12 | w.WriteHeader(code) 13 | }) 14 | return sm 15 | } 16 | 17 | //MockServer returns an HTTP server always responding with HTTP 200 response codes 18 | func MockServer() http.Handler { 19 | return createServer(http.StatusOK) 20 | } 21 | 22 | //ErrorServer returns an HTTP server always responding with HTTP 507 response codes 23 | func ErrorServer() http.Handler { 24 | return createServer(http.StatusInsufficientStorage) 25 | } 26 | -------------------------------------------------------------------------------- /api/upload.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "archive/tar" 5 | "bytes" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | ) 10 | 11 | func buildTarFile(files []string) bytes.Buffer { 12 | var buf bytes.Buffer 13 | tw := tar.NewWriter(&buf) 14 | for _, fn := range files { 15 | st, err := os.Lstat(fn) 16 | if err != nil { 17 | fmt.Printf("error handling %s: %s. ignoring...\n", fn, err.Error()) 18 | continue 19 | } 20 | if st.Mode()&os.ModeSymlink != 0 { 21 | target, err := os.Readlink(fn) 22 | if err != nil { 23 | fmt.Printf("error handling %s: %s. ignoring...\n", fn, err.Error()) 24 | continue 25 | } 26 | hdr := &tar.Header{ 27 | Name: fn, 28 | Linkname: target, 29 | Typeflag: tar.TypeSymlink, 30 | } 31 | if err := tw.WriteHeader(hdr); err != nil { 32 | fmt.Printf("error writing %s: %s. ignoring...", fn, err.Error()) 33 | continue 34 | } 35 | } else { 36 | contents, err := ioutil.ReadFile(fn) 37 | if err != nil { 38 | fmt.Printf("error reading %s: %s. ignoring...\n", fn, err.Error()) 39 | continue 40 | } 41 | hdr := &tar.Header{ 42 | Name: fn, 43 | Mode: 0600, 44 | Size: st.Size(), 45 | } 46 | if err := tw.WriteHeader(hdr); err != nil { 47 | fmt.Printf("error writing %s: %s. ignoring...", fn, err.Error()) 48 | continue 49 | } 50 | if _, err := tw.Write(contents); err != nil { 51 | fmt.Printf("error writing %s: %s. ignoring...", fn, err.Error()) 52 | continue 53 | } 54 | } 55 | } 56 | tw.Close() 57 | return buf 58 | } 59 | 60 | // Upload is a wrapper function around C3PM's publish endpoint. 61 | // It takes an array of file paths, creates a tar file containing 62 | // them, then uploads it to the API. 63 | func (c API) Upload(files []string) error { 64 | buf := buildTarFile(files) 65 | return c.send("POST", "/packages/publish", &buf) 66 | } 67 | -------------------------------------------------------------------------------- /api/upload_test.go: -------------------------------------------------------------------------------- 1 | package api_test 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/api" 5 | apitest "github.com/c3pm-labs/c3pm/api/test" 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | "net/http/httptest" 9 | "os" 10 | ) 11 | 12 | var _ = Describe("Upload", func() { 13 | var client api.API 14 | 15 | Context("server returning an error", func() { 16 | var srv *httptest.Server 17 | 18 | BeforeEach(func() { 19 | srv = httptest.NewServer(apitest.ErrorServer()) 20 | client = api.New(srv.Client(), "xxx") 21 | }) 22 | AfterEach(func() { 23 | srv.Close() 24 | }) 25 | 26 | It("doesn't have enough storage available", func() { 27 | err := client.Upload([]string{"../test_helpers/main.cpp"}) 28 | Ω(err).Should(HaveOccurred()) 29 | }) 30 | }) 31 | Context("server working correctly", func() { 32 | var srv *httptest.Server 33 | 34 | BeforeEach(func() { 35 | srv = httptest.NewServer(apitest.MockServer()) 36 | client = api.New(srv.Client(), "xxx") 37 | err := os.Setenv("C3PM_API_ENDPOINT", srv.URL) 38 | Ω(err).ShouldNot(HaveOccurred()) 39 | }) 40 | AfterEach(func() { 41 | srv.Close() 42 | err := os.Unsetenv("C3PM_API_ENDPOINT") 43 | Ω(err).ShouldNot(HaveOccurred()) 44 | }) 45 | 46 | It("uploads the file correctly", func() { 47 | err := client.Upload([]string{"../test_helpers/main.cpp"}) 48 | Ω(err).ShouldNot(HaveOccurred()) 49 | }) 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /cmake/cmake.go: -------------------------------------------------------------------------------- 1 | // Package cmake handles interaction with the CMake Command Line Interface. 2 | // CMake is used internally by C3PM to manage the build and installation phases of using a C3PM project. 3 | // 4 | // More information about what the CMake CLI does can be found on CMake's website: https://cmake.org/cmake/help/latest/manual/cmake.1.html 5 | package cmake 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "os/exec" 11 | ) 12 | 13 | func executeCMakeCLI(args ...string) error { 14 | cmd := exec.Command("cmake", args...) 15 | cmd.Stdout = os.Stdout 16 | cmd.Stderr = os.Stderr 17 | err := cmd.Start() 18 | if err != nil { 19 | return fmt.Errorf("failed to start cmake: %w", err) 20 | } 21 | if err = cmd.Wait(); err != nil { 22 | return fmt.Errorf("cmake process failed: %w", err) 23 | } 24 | return nil 25 | } 26 | 27 | //cmakeGenerateBuildFiles runs the cmake CLI to generate CMake build files. 28 | //C3PM uses CMake's -S option for setting the source directory, the -B option for the build directory, and the -D option for setting build variables. 29 | // 30 | //See CMake's documentation for more information: https://cmake.org/cmake/help/latest/manual/cmake.1.html#generate-a-project-buildsystem 31 | func GenerateBuildFiles(sourceDir, buildDir string, variables map[string]string) error { 32 | args := []string{ 33 | "-S", sourceDir, 34 | "-B", buildDir, 35 | } 36 | for key, value := range variables { 37 | args = append(args, fmt.Sprintf("-D%s=%s", key, value)) 38 | } 39 | return executeCMakeCLI(args...) 40 | } 41 | 42 | //cmakeBuild runs the CMake CLI to build a C3PM project 43 | // 44 | //See CMake's documentation for more information: https://cmake.org/cmake/help/latest/manual/cmake.1.html#build-a-project 45 | func Build(buildDir string, target string) error { 46 | return executeCMakeCLI("--build", buildDir, "--config", "Release") 47 | } 48 | -------------------------------------------------------------------------------- /cmake/cmake_test.go: -------------------------------------------------------------------------------- 1 | package cmake 2 | 3 | import ( 4 | "bufio" 5 | . "github.com/onsi/ginkgo" 6 | . "github.com/onsi/gomega" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | var _ = Describe("CMake interaction", func() { 13 | Describe("CMake build file generation", func() { 14 | const ( 15 | BUILD_DIR = "/tmp/c3pm_cmake_test1" 16 | ) 17 | 18 | AfterEach(func() { 19 | _ = os.RemoveAll(BUILD_DIR) 20 | }) 21 | It("does generate the build directory", func() { 22 | err := GenerateBuildFiles("../../test_helpers/projects/cmakeProject", BUILD_DIR, map[string]string{}) 23 | Ω(err).ShouldNot(HaveOccurred()) 24 | _, err = os.Stat(BUILD_DIR) 25 | Ω(err).ShouldNot(HaveOccurred()) 26 | }) 27 | It("uses the variables added", func() { 28 | err := GenerateBuildFiles("../../test_helpers/projects/cmakeProject", BUILD_DIR, map[string]string{"CMAKE_AR:FILEPATH": "/bin/ls"}) 29 | Ω(err).ShouldNot(HaveOccurred()) 30 | _, err = os.Stat(BUILD_DIR) 31 | Ω(err).ShouldNot(HaveOccurred()) 32 | // To verify that the variable has been applied, check that it is contained in the CMakeCache.txt file in the build directory. 33 | // The file contains a line per variable, with the format NAME:TYPE=VALUE 34 | f, err := os.Open(filepath.Join(BUILD_DIR, "CMakeCache.txt")) 35 | Ω(err).ShouldNot(HaveOccurred()) 36 | rd := bufio.NewReader(f) 37 | found := false 38 | var s string 39 | for s, err = "", nil; err == nil; s, err = rd.ReadString('\n') { 40 | if strings.Contains(s, "CMAKE_AR") && strings.Contains(s, "/bin/ls") { 41 | found = true 42 | } 43 | } 44 | Ω(found).Should(BeTrue()) 45 | }) 46 | }) 47 | 48 | Describe("CMake cmakeBuild", func() { 49 | const ( 50 | BUILD_DIR = "/tmp/c3pm_cmake_test2" 51 | ) 52 | 53 | AfterEach(func() { 54 | _ = os.RemoveAll(BUILD_DIR) 55 | }) 56 | It("builds the project", func() { 57 | // Generate files 58 | err := GenerateBuildFiles("../../test_helpers/projects/cmakeProject", BUILD_DIR, map[string]string{}) 59 | Ω(err).ShouldNot(HaveOccurred()) 60 | _, err = os.Stat(BUILD_DIR) 61 | Ω(err).ShouldNot(HaveOccurred()) 62 | 63 | // Build the project 64 | err = Build(BUILD_DIR, "test1") 65 | Ω(err).ShouldNot(HaveOccurred()) 66 | _, err = os.Stat(filepath.Join(BUILD_DIR, "test1")) 67 | Ω(err).ShouldNot(HaveOccurred()) 68 | }) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /cmd/add.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/config" 6 | "github.com/c3pm-labs/c3pm/ctpm" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var addCmdFlags = ctpm.AddOptions{} 11 | 12 | var addCmd = &cobra.Command{ 13 | Use: "add [dependencies...]", 14 | Short: "Add one or more new dependency", 15 | Args: cobra.MinimumNArgs(1), 16 | RunE: func(cmd *cobra.Command, args []string) error { 17 | pc, err := config.Load(".") 18 | if err != nil { 19 | return fmt.Errorf("failed to read c3pm.yml: %w", err) 20 | } 21 | addCmdFlags.Dependencies = args 22 | err = ctpm.Add(pc, addCmdFlags) 23 | if err != nil { 24 | return fmt.Errorf("failed to add dependencies: %w", err) 25 | } 26 | return nil 27 | }, 28 | } 29 | 30 | func init() { 31 | addCmd.Flags().BoolVarP(&addCmdFlags.Force, "force", "f", false, "Ignore cache.") 32 | addCmd.Flags().StringVarP(&addCmdFlags.RegistryURL, "registry-url", "r", "", "Select specific registry to use.") 33 | } 34 | -------------------------------------------------------------------------------- /cmd/build.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/config" 6 | "github.com/c3pm-labs/c3pm/ctpm" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var buildCmd = &cobra.Command{ 11 | Use: "build", 12 | Short: "Build a c3pm project", 13 | Args: cobra.NoArgs, 14 | RunE: func(cmd *cobra.Command, args []string) error { 15 | pc, err := config.Load(".") 16 | if err != nil { 17 | return fmt.Errorf("failed to read c3pm.yml: %w", err) 18 | } 19 | return ctpm.AddDependenciesAndBuild(pc) 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /cmd/init.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/cmd/input" 6 | "github.com/c3pm-labs/c3pm/config" 7 | "github.com/c3pm-labs/c3pm/config/manifest" 8 | "github.com/c3pm-labs/c3pm/ctpm" 9 | "github.com/spf13/cobra" 10 | "path/filepath" 11 | ) 12 | 13 | type InitCmdFlags struct { 14 | ctpm.InitOptions 15 | input.InitValues 16 | } 17 | 18 | var initCmdFlags = InitCmdFlags{} 19 | 20 | var initCmd = &cobra.Command{ 21 | Use: "init [path]", 22 | Short: "Init a c3pm project", 23 | Long: "Init a c3pm project\n\n" + 24 | "Project path defaults to working directory", 25 | Args: func(cmd *cobra.Command, args []string) error { 26 | if len(args) > 1 { 27 | return fmt.Errorf("requires one or no arguments") 28 | } 29 | return nil 30 | }, 31 | RunE: func(cmd *cobra.Command, args []string) error { 32 | var path string 33 | if len(args) == 1 { 34 | path = args[0] 35 | } else { 36 | path = "." 37 | } 38 | 39 | var man manifest.Manifest 40 | var err error 41 | if len(initCmdFlags.Name) == 0 { 42 | man, err = input.Init() 43 | } else { 44 | man, err = input.InitNonInteractive(initCmdFlags.InitValues) 45 | } 46 | 47 | if err != nil { 48 | return fmt.Errorf("failed to init project config: %w", err) 49 | } 50 | projectRoot, err := filepath.Abs(path) 51 | if err != nil { 52 | return err 53 | } 54 | pc := &config.ProjectConfig{Manifest: man, ProjectRoot: projectRoot} 55 | err = ctpm.Init(pc, initCmdFlags.InitOptions) 56 | if err != nil { 57 | return fmt.Errorf("failed to init project: %w", err) 58 | } 59 | return nil 60 | }, 61 | } 62 | 63 | func init() { 64 | initCmd.Flags().BoolVar(&initCmdFlags.NoTemplate, "no-template", ctpm.InitDefaultOptions.NoTemplate, "Prevents the creation of CMake files") 65 | 66 | // Non Interactive Mode 67 | initCmd.Flags().StringVar(&initCmdFlags.InitValues.Name, "name", "", "Give project name to skip interactive entry and enter non-interactive mode") 68 | initCmd.Flags().StringVar(&initCmdFlags.InitValues.Type, "type", "", "Project's type when using non-interactive mode") 69 | initCmd.Flags().StringVar(&initCmdFlags.InitValues.Description, "desc", "", "Project description when using non-interactive mode") 70 | initCmd.Flags().StringVar(&initCmdFlags.InitValues.Version, "version", "1.0.0", "Project version when using non-interactive mode") 71 | initCmd.Flags().StringVar(&initCmdFlags.InitValues.License, "license", "UNLICENSED", "Project license when using non-interactive mode") 72 | } 73 | -------------------------------------------------------------------------------- /cmd/input/init.go: -------------------------------------------------------------------------------- 1 | package input 2 | 3 | import ( 4 | "fmt" 5 | "github.com/AlecAivazis/survey/v2" 6 | "github.com/Masterminds/semver/v3" 7 | "github.com/c3pm-labs/c3pm/config/manifest" 8 | ) 9 | 10 | //InitSurvey is the definition of the user interaction happening in the init command to choose project parameters. 11 | var InitSurvey = []*survey.Question{ 12 | { 13 | Name: "Name", 14 | Prompt: &survey.Input{Message: "Project name"}, 15 | Validate: survey.Required, 16 | }, 17 | { 18 | Name: "Type", 19 | Prompt: &survey.Select{Message: "Project type", 20 | Options: []string{ 21 | manifest.Executable.String(), 22 | manifest.Library.String(), 23 | }, 24 | }, 25 | }, 26 | { 27 | Name: "Description", 28 | Prompt: &survey.Input{Message: "Project description"}, 29 | }, 30 | { 31 | Name: "Version", 32 | Prompt: &survey.Input{ 33 | Message: "Project version", 34 | Default: "1.0.0", 35 | }, 36 | Validate: func(ans interface{}) error { 37 | _, err := semver.NewVersion(ans.(string)) 38 | return err 39 | }, 40 | Transform: func(ans interface{}) (newAns interface{}) { 41 | v, _ := semver.NewVersion(ans.(string)) 42 | return v.String() 43 | }, 44 | }, 45 | { 46 | Name: "License", 47 | Prompt: &survey.Input{ 48 | Message: "Project license", 49 | Default: "UNLICENSED", 50 | Help: "You can read about code licenses on https://choosealicense.com/", 51 | }, 52 | }, 53 | } 54 | 55 | type InitValues = struct { 56 | Name string 57 | Type string 58 | Description string 59 | Version string 60 | License string 61 | } 62 | 63 | //Init handles the user interaction happening during the init command 64 | func Init() (manifest.Manifest, error) { 65 | man := manifest.New() 66 | err := survey.Ask(InitSurvey, &man, SurveyOptions...) 67 | return man, err 68 | } 69 | 70 | func InitNonInteractive(val InitValues) (manifest.Manifest, error) { 71 | var err error 72 | man := manifest.New() 73 | man.Name = val.Name 74 | man.Type, err = manifest.TypeFromString(val.Type) 75 | if err != nil { 76 | return manifest.Manifest{}, fmt.Errorf("failed to parse project type: %w", err) 77 | } 78 | man.Description = val.Description 79 | v, err := manifest.VersionFromString(val.Version) 80 | if err != nil { 81 | return man, fmt.Errorf("invalid version given: %w", err) 82 | } 83 | man.Version = v 84 | man.License = val.License 85 | return man, nil 86 | } 87 | -------------------------------------------------------------------------------- /cmd/input/input.go: -------------------------------------------------------------------------------- 1 | // Package input manages interaction with the user via input/output. 2 | package input 3 | 4 | import "github.com/AlecAivazis/survey/v2" 5 | 6 | //SurveyOptions is the options passed to the survey library 7 | var SurveyOptions = make([]survey.AskOpt, 0) 8 | -------------------------------------------------------------------------------- /cmd/input/login.go: -------------------------------------------------------------------------------- 1 | package input 2 | 3 | import "github.com/AlecAivazis/survey/v2" 4 | 5 | var loginSurvey = []*survey.Question{ 6 | { 7 | Name: "login", 8 | Prompt: &survey.Input{Message: "Email or username"}, 9 | Validate: survey.Required, 10 | }, 11 | { 12 | Name: "password", 13 | Prompt: &survey.Password{Message: "Password"}, 14 | Validate: survey.Required, 15 | }, 16 | } 17 | 18 | //LoginPayload is the data retrieved from the user to log them in. 19 | type LoginPayload struct { 20 | Login string 21 | Password string 22 | } 23 | 24 | //Login handles the user interaction during the login command. 25 | func Login() (*LoginPayload, error) { 26 | payload := &LoginPayload{} 27 | err := survey.Ask(loginSurvey, payload, SurveyOptions...) 28 | return payload, err 29 | } 30 | -------------------------------------------------------------------------------- /cmd/list.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/config" 6 | "github.com/c3pm-labs/c3pm/ctpm" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | type ListCmdFlags struct { 11 | ctpm.ListOptions 12 | } 13 | 14 | var listCmdFlags = ListCmdFlags{} 15 | 16 | var listCmd = &cobra.Command{ 17 | Use: "list", 18 | Short: "list all project dependencies", 19 | Args: cobra.MinimumNArgs(0), 20 | RunE: func(cmd *cobra.Command, args []string) error { 21 | pc, err := config.Load(".") 22 | if err != nil { 23 | return fmt.Errorf("failed to read c3pm.yml: %w", err) 24 | } 25 | err = ctpm.List(pc, listCmdFlags.ListOptions) 26 | if err != nil { 27 | return fmt.Errorf("failed to add dependencies: %w", err) 28 | } 29 | return nil 30 | }, 31 | } 32 | 33 | func init() { 34 | listCmd.Flags().BoolVar(&listCmdFlags.Tree, "tree", ctpm.ListDefaultOptions.Tree, "List dependencies in indented tree form") 35 | } 36 | -------------------------------------------------------------------------------- /cmd/login.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/api" 5 | "github.com/c3pm-labs/c3pm/cmd/input" 6 | "github.com/c3pm-labs/c3pm/ctpm" 7 | "github.com/spf13/cobra" 8 | "net/http" 9 | ) 10 | 11 | var loginCmd = &cobra.Command{ 12 | Use: "login", 13 | Short: "Login to the api", 14 | Args: cobra.NoArgs, 15 | RunE: func(cmd *cobra.Command, args []string) error { 16 | payload, err := input.Login() 17 | if err != nil { 18 | return err 19 | } 20 | client := api.New(&http.Client{}, "") 21 | return ctpm.Login(client, payload.Login, payload.Password) 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /cmd/logout.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/ctpm" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var logoutCmd = &cobra.Command{ 9 | Use: "logout", 10 | Short: "Logout from the api", 11 | Args: cobra.NoArgs, 12 | RunE: func(cmd *cobra.Command, args []string) error { 13 | return ctpm.Logout() 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /cmd/publish.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/api" 6 | "github.com/c3pm-labs/c3pm/config" 7 | "github.com/c3pm-labs/c3pm/ctpm" 8 | "github.com/spf13/cobra" 9 | "net/http" 10 | ) 11 | 12 | var publishCmd = &cobra.Command{ 13 | Use: "publish", 14 | Short: "Publish a c3pm project", 15 | RunE: func(cmd *cobra.Command, args []string) error { 16 | token, err := config.TokenStrict() 17 | if err != nil { 18 | return fmt.Errorf("not logged in: %w", err) 19 | } 20 | client := api.New(&http.Client{}, token) 21 | pc, err := config.Load(".") 22 | if err != nil { 23 | return fmt.Errorf("failed to read c3pm.yml: %w", err) 24 | } 25 | return ctpm.Publish(pc, client) 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /cmd/remove.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/config" 6 | "github.com/c3pm-labs/c3pm/ctpm" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var removeCmd = &cobra.Command{ 11 | Use: "remove [dependencies]", 12 | Short: "Remove one or more dependency", 13 | Args: cobra.MinimumNArgs(1), 14 | RunE: func(cmd *cobra.Command, args []string) error { 15 | dependencies := args 16 | pc, err := config.Load(".") 17 | if err != nil { 18 | return fmt.Errorf("failed to read c3pm.yml: %w", err) 19 | } 20 | err = ctpm.Remove(pc, ctpm.RemoveOptions{Dependencies: dependencies}) 21 | if err != nil { 22 | return fmt.Errorf("failed to remove dependencies: %w", err) 23 | } 24 | return nil 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | // Package cmd hosts the configuration and handling of the command line interface of C3PM. 2 | package cmd 3 | 4 | import ( 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var RootCmd = &cobra.Command{ 9 | Use: "ctpm", 10 | Short: "c3pm abstracts your build system and eases the management of your dependencies.", 11 | Long: "C3PM is a next-generation package manager for C++.\nYou can use C3PM to share and use packages with other developers around the world.", 12 | } 13 | 14 | func init() { 15 | RootCmd.AddCommand(addCmd) 16 | RootCmd.AddCommand(buildCmd) 17 | RootCmd.AddCommand(initCmd) 18 | RootCmd.AddCommand(logoutCmd) 19 | RootCmd.AddCommand(loginCmd) 20 | RootCmd.AddCommand(publishCmd) 21 | RootCmd.AddCommand(removeCmd) 22 | RootCmd.AddCommand(listCmd) 23 | RootCmd.AddCommand(testCmd) 24 | } 25 | -------------------------------------------------------------------------------- /cmd/test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/config" 6 | "github.com/c3pm-labs/c3pm/ctpm" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var testCmd = &cobra.Command{ 11 | Use: "test", 12 | Short: "Test a c3pm project", 13 | Args: cobra.NoArgs, 14 | RunE: func(cmd *cobra.Command, args []string) error { 15 | pc, err := config.Load(".") 16 | if err != nil { 17 | return fmt.Errorf("failed to read c3pm.yml: %w", err) 18 | } 19 | return ctpm.AddDependenciesAndTest(pc) 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /config/auth.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "path" 7 | ) 8 | 9 | // TokenStrict gets the authentication token from the auth file. 10 | func TokenStrict() (string, error) { 11 | content, err := ioutil.ReadFile(AuthFilePath()) 12 | if err != nil { 13 | return "", fmt.Errorf("failed to read auth file: %w", err) 14 | } 15 | return string(content), nil 16 | } 17 | 18 | // Token returns an empty string if it fails to read token 19 | func Token() string { 20 | token, _ := TokenStrict() 21 | return token 22 | } 23 | 24 | //AuthFilePath returns the path to the global auth file. 25 | func AuthFilePath() string { 26 | return path.Join(GlobalC3PMDirPath(), "auth.cfg") 27 | } 28 | -------------------------------------------------------------------------------- /config/auth_test.go: -------------------------------------------------------------------------------- 1 | package config_test 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/config" 5 | . "github.com/onsi/ginkgo" 6 | . "github.com/onsi/gomega" 7 | "io/ioutil" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | var _ = Describe("Authentication configuration", func() { 13 | var ( 14 | TargetDir string 15 | ) 16 | 17 | BeforeEach(func() { 18 | dir, err := ioutil.TempDir("", "c3pm_test_*") 19 | Ω(err).ShouldNot(HaveOccurred()) 20 | TargetDir = dir 21 | err = ioutil.WriteFile(filepath.Join(TargetDir, "auth.cfg"), []byte("testtoken"), os.ModePerm) 22 | Ω(err).ShouldNot(HaveOccurred()) 23 | }) 24 | 25 | AfterEach(func() { 26 | _ = os.RemoveAll(TargetDir) 27 | err := os.Unsetenv("C3PM_USER_DIR") 28 | Ω(err).ShouldNot(HaveOccurred()) 29 | }) 30 | Context("TokenStrict", func() { 31 | It("gets the correct token", func() { 32 | err := os.Setenv("C3PM_USER_DIR", TargetDir) 33 | Ω(err).ShouldNot(HaveOccurred()) 34 | token, err := config.TokenStrict() 35 | Ω(err).ShouldNot(HaveOccurred()) 36 | Ω(token).Should(Equal("testtoken")) 37 | }) 38 | }) 39 | Context("Token", func() { 40 | It("gets the correct token", func() { 41 | err := os.Setenv("C3PM_USER_DIR", TargetDir) 42 | Ω(err).ShouldNot(HaveOccurred()) 43 | token := config.Token() 44 | Ω(token).Should(Equal("testtoken")) 45 | }) 46 | It("returns an empty string in case of error", func() { 47 | err := os.Setenv("C3PM_USER_DIR", "/tmp/fakedir") 48 | Ω(err).ShouldNot(HaveOccurred()) 49 | token := config.Token() 50 | Ω(token).Should(BeEmpty()) 51 | }) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | // Package config handles the interactions with C3PM's various configuration files. 2 | // It handles interaction with both the c3pm.yml file (see package manifest), and 3 | // the storage of authentication tokens in the global C3PM directory as found by GlobalC3PMDirPath. 4 | package config 5 | 6 | import ( 7 | "github.com/c3pm-labs/c3pm/config/manifest" 8 | "os" 9 | "path" 10 | "path/filepath" 11 | "runtime" 12 | ) 13 | 14 | //ProjectConfig represents the configuration of a C3PM project. 15 | type ProjectConfig struct { 16 | //Manifest is the representation of the contents of the c3pm.yml file. 17 | Manifest manifest.Manifest 18 | //ProjectRoot stores the absolute path to the C3PM project. 19 | ProjectRoot string 20 | } 21 | 22 | //Load takes the path of a project and creates the ProjectConfig object that represents the configuration of this project. 23 | func Load(projectPath string) (*ProjectConfig, error) { 24 | absolutePath, err := filepath.Abs(projectPath) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | m, err := manifest.Load(filepath.Join(absolutePath, "c3pm.yml")) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | return &ProjectConfig{ 35 | Manifest: m, 36 | ProjectRoot: absolutePath, 37 | }, nil 38 | } 39 | 40 | //Save writes the current configuration and writes it to the project directory. 41 | func (pc *ProjectConfig) Save() error { 42 | return pc.Manifest.Save(filepath.Join(pc.ProjectRoot, "c3pm.yml")) 43 | } 44 | 45 | // LocalC3PMDirPath returns the path to the local C3PM directory. 46 | func (pc *ProjectConfig) LocalC3PMDirPath() string { 47 | return filepath.Join(pc.ProjectRoot, ".c3pm") 48 | } 49 | 50 | //GlobalC3PMDirPath finds the path to the global C3PM directory. 51 | func GlobalC3PMDirPath() string { 52 | if dir := os.Getenv("C3PM_USER_DIR"); dir != "" { 53 | return dir 54 | } 55 | var homeDir string 56 | if runtime.GOOS == "windows" { 57 | homeDir = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 58 | } else { 59 | homeDir = os.Getenv("HOME") 60 | } 61 | return path.Join(homeDir, ".c3pm") 62 | } 63 | 64 | //LibCachePath returns the path to the global C3PM cache 65 | func LibCachePath(name, version string) string { 66 | return filepath.Join(GlobalC3PMDirPath(), "cache", name, version) 67 | } 68 | -------------------------------------------------------------------------------- /config/config_suite_test.go: -------------------------------------------------------------------------------- 1 | package config_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestCtpm(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Config Suite") 13 | } 14 | -------------------------------------------------------------------------------- /config/config_test.go: -------------------------------------------------------------------------------- 1 | package config_test 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/Masterminds/semver/v3" 9 | "github.com/c3pm-labs/c3pm/adapter/defaultadapter" 10 | "github.com/c3pm-labs/c3pm/config" 11 | "github.com/c3pm-labs/c3pm/config/manifest" 12 | "github.com/mohae/deepcopy" 13 | . "github.com/onsi/ginkgo" 14 | . "github.com/onsi/gomega" 15 | ) 16 | 17 | const ( 18 | OriginalDir = "../test_helpers/yamls" 19 | ) 20 | 21 | var ( 22 | OriginalDirAbs, _ = filepath.Abs(OriginalDir) 23 | TestConfig = &config.ProjectConfig{ 24 | Manifest: manifest.Manifest{ 25 | C3PMVersion: manifest.C3PMVersion1, 26 | Type: manifest.Executable, 27 | Name: "hello-bin", 28 | Description: "Demo binary", 29 | Version: manifest.Version{ 30 | Version: semver.MustParse("1.1.5"), 31 | }, 32 | Standard: "20", 33 | License: "ISC", 34 | Publish: &manifest.PublishConfig{ 35 | Include: []string{"test-include"}, 36 | Exclude: []string{"test-exclude"}, 37 | }, 38 | Build: &manifest.BuildConfig{ 39 | Adapter: &manifest.AdapterConfig{ 40 | Name: "c3pm", 41 | Version: defaultadapter.CurrentVersion, 42 | }, 43 | Config: nil, 44 | }, 45 | Documentation: "", 46 | Website: "", 47 | Repository: "", 48 | Contributors: "", 49 | Dependencies: manifest.Dependencies{ 50 | "hello": "1.0.5", 51 | }, 52 | TestDependencies: manifest.Dependencies{}, 53 | }, 54 | ProjectRoot: OriginalDirAbs, 55 | } 56 | ) 57 | 58 | var _ = Describe("Config loading and writing", func() { 59 | 60 | Context("file reads", func() { 61 | It("loads the file properly", func() { 62 | p, err := config.Load(OriginalDir) 63 | Ω(err).ShouldNot(HaveOccurred()) 64 | p.Manifest.Build.Config = nil // we ignore the Build.Config field 65 | Ω(p).Should(Equal(TestConfig)) 66 | }) 67 | }) 68 | 69 | Context("file writes", func() { 70 | var ( 71 | TargetDir string 72 | ) 73 | 74 | BeforeEach(func() { 75 | dir, err := ioutil.TempDir("", "c3pm_test_*") 76 | Ω(err).ShouldNot(HaveOccurred()) 77 | TargetDir = dir 78 | }) 79 | 80 | AfterEach(func() { 81 | _ = os.RemoveAll(TargetDir) 82 | }) 83 | 84 | It("Updates the file correctly", func() { 85 | p := deepcopy.Copy(TestConfig).(*config.ProjectConfig) 86 | p.ProjectRoot = TargetDir 87 | p.Manifest.Version = TestConfig.Manifest.Version // Private values are not copied by deep copy, so let's just add the value ourselves 88 | p.Manifest.Build.Adapter.Version = TestConfig.Manifest.Build.Adapter.Version // Private values are not copied by deep copy, so let's just add the value ourselves 89 | p.Manifest.Description = "Different Description" 90 | p.Manifest.Build.Config = nil // we ignore the Build.Config field 91 | err := p.Save() 92 | Ω(err).ShouldNot(HaveOccurred()) 93 | p2, err := config.Load(TargetDir) 94 | Ω(err).ShouldNot(HaveOccurred()) 95 | p2.Manifest.Build.Config = nil // we ignore the Build.Config field 96 | Ω(p2).ShouldNot(Equal(TestConfig), "Test against the original config") 97 | Ω(p2).Should(Equal(p)) 98 | }) 99 | }) 100 | }) 101 | 102 | var _ = Describe("Config utils", func() { 103 | Context("Global directory", func() { 104 | var ( 105 | OriginalHomeDir = os.Getenv("HOME") 106 | ) 107 | AfterEach(func() { 108 | err := os.Setenv("HOME", OriginalHomeDir) 109 | Ω(err).ShouldNot(HaveOccurred()) 110 | err = os.Unsetenv("C3PM_USER_DIR") 111 | Ω(err).ShouldNot(HaveOccurred()) 112 | }) 113 | 114 | Describe("Root global directory", func() { 115 | It("gets from HOME", func() { 116 | err := os.Setenv("HOME", ".") 117 | Ω(err).ShouldNot(HaveOccurred()) 118 | path := config.GlobalC3PMDirPath() 119 | Ω(path).Should(Equal(".c3pm")) 120 | }) 121 | It("ets from C3PM_USER_DIR", func() { 122 | err := os.Setenv("C3PM_USER_DIR", "../.c3pm") 123 | Ω(err).ShouldNot(HaveOccurred()) 124 | path := config.GlobalC3PMDirPath() 125 | Ω(path).Should(Equal("../.c3pm")) 126 | }) 127 | It("has priority from C3PM_USER_DIR over HOME", func() { 128 | err := os.Setenv("C3PM_USER_DIR", "../.c3pm") 129 | Ω(err).ShouldNot(HaveOccurred()) 130 | err = os.Setenv("HOME", "..") 131 | Ω(err).ShouldNot(HaveOccurred()) 132 | path := config.GlobalC3PMDirPath() 133 | Ω(path).Should(Equal("../.c3pm")) 134 | }) 135 | }) 136 | Describe("Cache directory", func() { 137 | It("gets the cache directory", func() { 138 | err := os.Setenv("C3PM_USER_DIR", ".c3pm") 139 | Ω(err).ShouldNot(HaveOccurred()) 140 | path := config.LibCachePath("a", "b") 141 | Ω(path).Should(Equal(".c3pm/cache/a/b")) 142 | }) 143 | }) 144 | }) 145 | }) 146 | -------------------------------------------------------------------------------- /config/manifest/c3pmversion.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | //C3PMVersion holds the version of the c3pm manifest to use. 10 | type C3PMVersion uint32 11 | 12 | const ( 13 | // C3PMVersion1 is the most recent manifest version. 14 | C3PMVersion1 = C3PMVersion(1) 15 | ) 16 | 17 | // ErrParseC3PMVersion is returned when not able to parse the C3PM version to use. 18 | var ErrParseC3PMVersion = errors.New("Could not parse C3PM version") 19 | 20 | // C3PMVersionFromString returns the C3PMVersion corresponding to a given string. 21 | func C3PMVersionFromString(version string) (C3PMVersion, error) { 22 | if !strings.HasPrefix(version, "v") { 23 | return C3PMVersion(0), ErrParseC3PMVersion 24 | } 25 | version = strings.TrimLeft(version, "v") 26 | val, err := strconv.ParseUint(version, 10, 32) 27 | if err != nil { 28 | return C3PMVersion(0), ErrParseC3PMVersion 29 | } 30 | return C3PMVersion(val), nil 31 | } 32 | 33 | // UnmarshalYAML is used to read the version from the manifest YAML file. 34 | func (v *C3PMVersion) UnmarshalYAML(unmarshal func(interface{}) error) error { 35 | var version string 36 | err := unmarshal(&version) 37 | if err != nil { 38 | return err 39 | } 40 | *v, err = C3PMVersionFromString(version) 41 | if err != nil { 42 | return err 43 | } 44 | return nil 45 | } 46 | 47 | // String is used to return the stringified representation of the version. 48 | func (v C3PMVersion) String() string { 49 | return "v" + strconv.FormatUint(uint64(v), 10) 50 | } 51 | 52 | // MarshalYAML is used to write the version as YAML. 53 | func (v C3PMVersion) MarshalYAML() (interface{}, error) { 54 | return v.String(), nil 55 | } 56 | -------------------------------------------------------------------------------- /config/manifest/c3pmversion_test.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("c3pm version Test", func() { 9 | It("Check C3PM version from string", func() { 10 | v, err := C3PMVersionFromString("v1") 11 | Ω(err).ShouldNot(HaveOccurred()) 12 | 13 | Ω(v).To(Equal(C3PMVersion1)) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /config/manifest/customcmake.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | // CustomCMake is used in cases where a project needs a complex custom CMake system. 4 | type CustomCMake struct { 5 | Path string `yaml:"path"` 6 | Variables map[string]string `yaml:"variables"` 7 | Targets []string `yaml:"targets"` 8 | } 9 | -------------------------------------------------------------------------------- /config/manifest/dependencies.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // Dependencies holds the list of the dependencies of a project. 10 | type Dependencies map[string]string 11 | 12 | // ErrParseDependencies is returned when an error occured while reading dependencies. 13 | var ErrParseDependencies = errors.New("Could not parse dependencies") 14 | 15 | // TODO: Unused 16 | func DependenciesFromMap(dependencies map[string]string) (Dependencies, error) { 17 | if dependencies == nil { 18 | return Dependencies(nil), ErrParseDependencies 19 | } 20 | return Dependencies(dependencies), nil 21 | } 22 | 23 | // MarshalYAML is used to write dependencies as YAML. 24 | func (d Dependencies) MarshalYAML() (interface{}, error) { 25 | return d, nil 26 | } 27 | 28 | func (d Dependencies) String() string { 29 | sb := strings.Builder{} 30 | i := 0 31 | for n, v := range d { 32 | if i != 0 { 33 | sb.WriteString(",") 34 | i = 1 35 | } 36 | sb.WriteString(fmt.Sprintf("[%s/%s]", n, v)) 37 | } 38 | return sb.String() 39 | } 40 | 41 | func (d Dependencies) Append(d2 Dependencies) Dependencies { 42 | if d == nil { 43 | d = Dependencies{} 44 | } 45 | for n, v := range d2 { 46 | d[n] = v 47 | } 48 | return d 49 | } 50 | -------------------------------------------------------------------------------- /config/manifest/dependencies_test.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("dependencies", func() { 9 | It("test dependencies map", func() { 10 | var m = make(map[string]string) 11 | m["boost"] = "1.2.3" 12 | d, err := DependenciesFromMap(m) 13 | Ω(err).ShouldNot(HaveOccurred()) 14 | 15 | Ω(d).To(Equal(Dependencies(m))) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /config/manifest/manifest.go: -------------------------------------------------------------------------------- 1 | //Package manifest handles loading and writing of the C3PM manifest file (usually c3pm.yml). 2 | //It also stores the various types supported by the manifest, as well as utility functions to use them. 3 | package manifest 4 | 5 | import ( 6 | "errors" 7 | "gopkg.in/yaml.v2" 8 | "io/ioutil" 9 | ) 10 | 11 | // Manifest is the main configuration structure for C3PM. 12 | type Manifest struct { 13 | C3PMVersion C3PMVersion `yaml:"c3pm_version"` 14 | Type Type `yaml:"type"` 15 | Name string `yaml:"name"` 16 | Description string `yaml:"description"` 17 | Version Version `yaml:"version"` 18 | Publish *PublishConfig `yaml:"publish,omitempty"` 19 | Build *BuildConfig `yaml:"build,omitempty"` 20 | Documentation string `yaml:"documentation"` 21 | Website string `yaml:"website"` 22 | Repository string `yaml:"repository"` 23 | Contributors string `yaml:"contributors"` 24 | Standard string `yaml:"standard"` 25 | License string `yaml:"license"` 26 | Dependencies Dependencies `yaml:"dependencies"` 27 | TestDependencies Dependencies `yaml:"test_dependencies"` 28 | Tags []string `yaml:"tags,omitempty"` 29 | } 30 | 31 | type BuildConfig struct { 32 | Adapter *AdapterConfig `yaml:"adapter,omitempty"` 33 | Config interface{} `yaml:"config,omitempty"` 34 | } 35 | 36 | type AdapterConfig struct { 37 | Name string `yaml:"name"` 38 | Version Version `yaml:"version,omitempty"` 39 | } 40 | 41 | type PublishConfig struct { 42 | IncludeDirs []string `yaml:"include_dirs,omitempty"` 43 | Include []string `yaml:"include,omitempty"` 44 | Exclude []string `yaml:"exclude,omitempty"` 45 | } 46 | 47 | // New returns the default manifest values. 48 | func New() Manifest { 49 | c3pmAdapterVersion, _ := VersionFromString("0.0.1") 50 | defaultManifest := Manifest{ 51 | C3PMVersion: C3PMVersion1, 52 | Dependencies: make(map[string]string), 53 | TestDependencies: make(map[string]string), 54 | Standard: "20", 55 | Publish: &PublishConfig{ 56 | Exclude: []string{}, 57 | Include: []string{"**/**"}, 58 | }, 59 | Build: &BuildConfig{ 60 | Adapter: &AdapterConfig{ 61 | Name: "c3pm", 62 | Version: c3pmAdapterVersion, 63 | }, 64 | Config: &struct { 65 | Sources []string 66 | Headers []string 67 | IncludeDirs []string `yaml:"include_dirs"` 68 | }{ 69 | Sources: []string{"**/*.cpp"}, 70 | Headers: []string{"**/*.hpp"}, 71 | IncludeDirs: []string{"include"}, 72 | }, 73 | }, 74 | } 75 | return defaultManifest 76 | } 77 | 78 | func deserialize(config []byte) (Manifest, error) { 79 | man := New() 80 | err := yaml.Unmarshal(config, &man) 81 | if err != nil { 82 | return man, err 83 | } 84 | return man, nil 85 | } 86 | 87 | func checkManifest(man Manifest) error { 88 | if man.Name == "" { 89 | return errors.New("Field \"name\" is missing") 90 | } 91 | if man.Type != "executable" && man.Type != "library" { 92 | return errors.New("Field \"type\" is missing or incorrect [executable | library]") 93 | } 94 | if man.Version.Version == nil { 95 | return errors.New("Field \"version\" is missing") 96 | } 97 | return nil 98 | } 99 | 100 | // Load reads the file located at the given path, and stores its contents in a new Manifest struct. 101 | func Load(path string) (Manifest, error) { 102 | data, err := ioutil.ReadFile(path) 103 | if err != nil { 104 | return Manifest{}, err 105 | } 106 | man, err := deserialize(data) 107 | if err != nil { 108 | return man, err 109 | } 110 | if err = checkManifest(man); err != nil { 111 | return man, err 112 | } 113 | return man, err 114 | } 115 | 116 | func (m *Manifest) serialize() ([]byte, error) { 117 | return yaml.Marshal(&m) 118 | } 119 | 120 | // Save writes the current Manifest struct into the destination path. 121 | func (m *Manifest) Save(destination string) error { 122 | data, err := m.serialize() 123 | if err != nil { 124 | return err 125 | } 126 | return ioutil.WriteFile(destination, data, 0644) 127 | } 128 | 129 | // Targets returns the CMake targets to use. 130 | // TODO: delete this 131 | func (m *Manifest) Targets() []string { 132 | return []string{m.Name} 133 | } 134 | -------------------------------------------------------------------------------- /config/manifest/manifest_suite_test.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | "os" 5 | 6 | "path/filepath" 7 | "testing" 8 | 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | func TestCMakeGen(t *testing.T) { 14 | RegisterFailHandler(Fail) 15 | path, err := filepath.Abs("testsArtifacts") 16 | if err != nil { 17 | t.Fatal("Failed to get testsArtifacts absolute path") 18 | } 19 | RunSpecs(t, "CMakeGenSuite Suite") 20 | err = os.RemoveAll(path) 21 | if err != nil { 22 | t.Fatal("Failed to clean test artifacts\n") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /config/manifest/manifest_test.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var exampleConfig = ` 9 | c3pm_version: v1 10 | type: library 11 | name: c3pm 12 | description: This is the package c3pm 13 | version: 1.0.0 14 | Documentation: "http://docs.c3pm.io/" 15 | Website: "https://c3pm.io/" 16 | Repository: "https://github.com/c3pm-labs" 17 | Contributors: "Alex Hugh - Ramy J." 18 | license: ISC 19 | publish: 20 | include_dirs: 21 | - 'include/public' 22 | build: 23 | adapter: 24 | name: c3pm 25 | version: 0.0.1 26 | config: 27 | sources: 28 | - 'src/**/*.cpp' 29 | headers: 30 | - include/private/header.h 31 | - include/private/private.h 32 | include_dirs: 33 | - include/private 34 | dependencies: 35 | future: 12.2.3 36 | past: 2.0.0 37 | test_dependencies: 38 | catch: 2.0.0 39 | ` 40 | 41 | var _ = Describe("manifest", func() { 42 | It("test load of the manifest", func() { 43 | pc, err := deserialize([]byte(exampleConfig)) 44 | Ω(err).ShouldNot(HaveOccurred()) 45 | 46 | Ω(pc.C3PMVersion).To(Equal(C3PMVersion1)) 47 | Ω(pc.Type).To(Equal(Library)) 48 | Ω(pc.Name).To(Equal("c3pm")) 49 | Ω(pc.Description).To(Equal("This is the package c3pm")) 50 | v, err := VersionFromString("1.0.0") 51 | Ω(err).ShouldNot(HaveOccurred()) 52 | 53 | Ω(pc.Version).To(Equal(v)) 54 | Ω(pc.License).To(Equal("ISC")) 55 | var m = make(map[string]string) 56 | m["future"] = "12.2.3" 57 | m["past"] = "2.0.0" 58 | Ω(pc.Dependencies["future"]).To(Equal(m["future"])) 59 | Ω(pc.Dependencies["past"]).To(Equal(m["past"])) 60 | }) 61 | 62 | It("Test save manifest", func() { 63 | pc, err := deserialize([]byte(exampleConfig)) 64 | Ω(err).ShouldNot(HaveOccurred()) 65 | 66 | data, err := pc.serialize() 67 | Ω(err).ShouldNot(HaveOccurred()) 68 | 69 | newPc, err := deserialize(data) 70 | Ω(err).To(BeNil(), "saved config was: %s", string(data)) 71 | Ω(newPc).To(Equal(pc)) 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /config/manifest/type.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | "fmt" 5 | "github.com/AlecAivazis/survey/v2" 6 | "reflect" 7 | ) 8 | 9 | // Type holds the kind of project to manage. 10 | type Type string 11 | 12 | const ( 13 | // Executable is the Type of a project yielding a binary executable. 14 | Executable = Type("executable") 15 | // Library is the Type of a project yielding a package to be included by others. 16 | Library = Type("library") 17 | ) 18 | 19 | // String returns the string representation of a Type 20 | func (t Type) String() string { 21 | return string(t) 22 | } 23 | 24 | // TypeFromString creates a Type instance from a corresponding string. 25 | func TypeFromString(t string) (Type, error) { 26 | switch t { 27 | case Executable.String(): 28 | return Executable, nil 29 | case Library.String(): 30 | return Library, nil 31 | default: 32 | return Type(""), fmt.Errorf("invalid project type") 33 | } 34 | } 35 | 36 | // WriteAnswer allows type to be used in survey questions 37 | func (t *Type) WriteAnswer(name string, value interface{}) (err error) { 38 | var ts string 39 | //nolint:gosimple 40 | switch value.(type) { 41 | case string: 42 | ts = value.(string) 43 | case survey.OptionAnswer: 44 | ts = value.(survey.OptionAnswer).Value 45 | default: 46 | return fmt.Errorf("Type is not a string (" + reflect.TypeOf(value).String() + ")") 47 | } 48 | *t, err = TypeFromString(ts) 49 | return err 50 | } 51 | -------------------------------------------------------------------------------- /config/manifest/version.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Masterminds/semver/v3" 6 | "reflect" 7 | ) 8 | 9 | // Version holds the Semantic Versioning-compatible version of a package. 10 | type Version struct { 11 | *semver.Version 12 | } 13 | 14 | // VersionFromString converts a string to a Version instance. 15 | func VersionFromString(version string) (v Version, err error) { 16 | v.Version, err = semver.NewVersion(version) 17 | if err != nil { 18 | return v, err 19 | } 20 | return v, nil 21 | } 22 | 23 | // UnmarshalYAML is used to read a Version from a YAML file. 24 | func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error { 25 | var version string 26 | err := unmarshal(&version) 27 | if err != nil { 28 | return err 29 | } 30 | v.Version, err = semver.NewVersion(version) 31 | if err != nil { 32 | return err 33 | } 34 | return nil 35 | } 36 | 37 | // MarshalYAML is used to write a Version to a YAML file. 38 | func (v Version) MarshalYAML() (interface{}, error) { 39 | return v.String(), nil 40 | } 41 | 42 | // WriteAnswer allows type to be used in survey questions 43 | func (v *Version) WriteAnswer(name string, value interface{}) (err error) { 44 | vs, ok := value.(string) 45 | if !ok { 46 | return fmt.Errorf("Version is not a string (" + reflect.TypeOf(vs).String() + ")") 47 | } 48 | v.Version, err = semver.NewVersion(vs) 49 | return err 50 | } 51 | -------------------------------------------------------------------------------- /ctpm/add.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Masterminds/semver/v3" 6 | "github.com/c3pm-labs/c3pm/config" 7 | "github.com/c3pm-labs/c3pm/config/manifest" 8 | "github.com/c3pm-labs/c3pm/dependencies" 9 | "github.com/c3pm-labs/c3pm/env" 10 | "github.com/c3pm-labs/c3pm/registry" 11 | "regexp" 12 | "strings" 13 | ) 14 | 15 | func Add(pc *config.ProjectConfig, opts AddOptions) error { 16 | options := buildOptions(opts) 17 | for _, dep := range opts.Dependencies { 18 | name, version, err := getRequiredVersion(dep, options) 19 | if err != nil { 20 | return fmt.Errorf("error getting dependencies: %w", err) 21 | } 22 | _, err = dependencies.Install(dependencies.PackageRequest{Name: name, Version: version.String()}, InstallHandler{}) 23 | if err != nil { 24 | return fmt.Errorf("error adding %s: %w", dep, err) 25 | } 26 | libPath := config.LibCachePath(name, version.String()) 27 | libPc, err := config.Load(libPath) 28 | if err != nil { 29 | return fmt.Errorf("failed to read c3pm.yml: %w", err) 30 | } 31 | err = Build(libPc) 32 | if err != nil { 33 | return err 34 | } 35 | if pc.Manifest.Dependencies == nil { 36 | pc.Manifest.Dependencies = make(manifest.Dependencies) 37 | } 38 | pc.Manifest.Dependencies[name] = version.String() 39 | } 40 | if err := pc.Save(); err != nil { 41 | return fmt.Errorf("error saving project config: %w", err) 42 | } 43 | return nil 44 | } 45 | 46 | type AddOptions struct { 47 | Force bool 48 | RegistryURL string 49 | 50 | Dependencies []string 51 | } 52 | 53 | func buildOptions(opts AddOptions) AddOptions { 54 | if opts.RegistryURL == "" { 55 | opts.RegistryURL = env.REGISTRY_ENDPOINT 56 | } 57 | return opts 58 | } 59 | 60 | const depRegexString = `^[\-a-zA-Z0-9_\/]*(@.*)?$` 61 | 62 | var depRegex *regexp.Regexp 63 | 64 | func getDepRegexp() (regex *regexp.Regexp, err error) { 65 | if depRegex == nil { 66 | depRegex, err = regexp.Compile(depRegexString) 67 | } 68 | regex = depRegex 69 | return 70 | } 71 | 72 | func validateDependency(dep string) error { 73 | regex, err := getDepRegexp() 74 | if err != nil { 75 | return fmt.Errorf("error compiling validation regexp: %w", err) 76 | } 77 | if regex.MatchString(dep) { 78 | return nil 79 | } 80 | return fmt.Errorf("%s is not a valid dependency string", dep) 81 | } 82 | 83 | func getRequiredVersion(dep string, options AddOptions) (name string, version *semver.Version, err error) { 84 | if err := validateDependency(dep); err != nil { 85 | return "", nil, err 86 | } 87 | dependency := strings.Split(dep, "@") 88 | if len(dependency) == 1 { 89 | version, err = registry.GetLastVersion(dep, registry.Options{ 90 | RegistryURL: options.RegistryURL, 91 | }) 92 | name = dep 93 | return 94 | } 95 | name = dependency[0] 96 | version, err = semver.NewVersion(dependency[1]) 97 | if err != nil { 98 | return "", nil, fmt.Errorf("invalid semver string: %w", err) 99 | } 100 | return 101 | } 102 | -------------------------------------------------------------------------------- /ctpm/build.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Masterminds/semver/v3" 6 | "github.com/c3pm-labs/c3pm/adapter" 7 | "github.com/c3pm-labs/c3pm/adapter_interface" 8 | "github.com/c3pm-labs/c3pm/config" 9 | "github.com/c3pm-labs/c3pm/config/manifest" 10 | "github.com/c3pm-labs/c3pm/dependencies" 11 | ) 12 | 13 | type DependencyBuilder struct { 14 | Done manifest.Dependencies 15 | } 16 | 17 | func (d DependencyBuilder) FetchDeps(request dependencies.PackageRequest) (dependencies.Dependencies, error) { 18 | if _, ok := d.Done[request.Name]; ok { 19 | return dependencies.Dependencies{}, nil 20 | } 21 | libPath := config.LibCachePath(request.Name, request.Version) 22 | pc, err := config.Load(libPath) 23 | if err != nil { 24 | return nil, fmt.Errorf("failed to read c3pm.yml: %w", err) 25 | } 26 | ret := make(dependencies.Dependencies) 27 | for k, v := range pc.Manifest.Dependencies { 28 | ret[k] = v 29 | } 30 | return ret, nil 31 | } 32 | 33 | func (d DependencyBuilder) PreAct(_ dependencies.PackageRequest) error { return nil } 34 | func (d DependencyBuilder) PostAct(request dependencies.PackageRequest) error { 35 | fmt.Printf("Building %s:%s\n", request.Name, request.Version) 36 | 37 | libPath := config.LibCachePath(request.Name, request.Version) 38 | pc, err := config.Load(libPath) 39 | if err != nil { 40 | return fmt.Errorf("failed to read c3pm.yml: %w", err) 41 | } 42 | getter := adapter.AdapterGetterImp{} 43 | var adp adapter_interface.Adapter 44 | adp, err = getter.FromPC(pc.Manifest.Build.Adapter) 45 | if err != nil { 46 | return err 47 | } 48 | err = adp.Build(pc) 49 | if err != nil { 50 | return fmt.Errorf("error building: %w", err) 51 | } 52 | d.Done[fmt.Sprintf(request.Name)] = request.Version 53 | return nil 54 | } 55 | 56 | func getAllDependencies(pc *config.ProjectConfig) error { 57 | allDeps := make(manifest.Dependencies) 58 | allDeps[pc.Manifest.Name] = pc.Manifest.Version.String() 59 | for name, version := range pc.Manifest.Dependencies { 60 | _, err := dependencies.Install(dependencies.PackageRequest{Name: name, Version: version}, DependencyBuilder{Done: allDeps}) 61 | if err != nil { 62 | return err 63 | } 64 | } 65 | delete(allDeps, pc.Manifest.Name) 66 | pc.Manifest.Dependencies = allDeps 67 | return nil 68 | } 69 | 70 | func Build(pc *config.ProjectConfig) error { 71 | getter := adapter.AdapterGetterImp{} 72 | adp, err := getter.FromPC(pc.Manifest.Build.Adapter) 73 | if err != nil { 74 | return err 75 | } 76 | err = getAllDependencies(pc) 77 | if err != nil { 78 | return err 79 | } 80 | return adp.Build(pc) 81 | } 82 | 83 | func addAllDependencies(pc *config.ProjectConfig) error { 84 | for dep, version := range pc.Manifest.Dependencies { 85 | semverVersion, err := semver.NewVersion(version) 86 | if err != nil { 87 | return fmt.Errorf("error getting dependencies: %w", err) 88 | } 89 | if err := Install(dep, semverVersion.String()); err != nil { 90 | return fmt.Errorf("error adding %s: %w", dep, err) 91 | } 92 | } 93 | return nil 94 | } 95 | 96 | func AddDependenciesAndBuild(pc *config.ProjectConfig) error { 97 | err := addAllDependencies(pc) 98 | if err != nil { 99 | return fmt.Errorf("error installing dependencies: %w", err) 100 | } 101 | err = Build(pc) 102 | if err != nil { 103 | return fmt.Errorf("build failed: %w", err) 104 | } 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /ctpm/build_test.go: -------------------------------------------------------------------------------- 1 | package ctpm_test 2 | 3 | import ( 4 | "bytes" 5 | "github.com/c3pm-labs/c3pm/adapter/defaultadapter" 6 | "io/ioutil" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | 11 | "github.com/c3pm-labs/c3pm/config" 12 | "github.com/c3pm-labs/c3pm/config/manifest" 13 | "github.com/c3pm-labs/c3pm/ctpm" 14 | . "github.com/onsi/ginkgo" 15 | . "github.com/onsi/gomega" 16 | ) 17 | 18 | var execSrc = `#include 19 | int main() { 20 | std::cout << "Build test" << std::endl; 21 | } 22 | ` 23 | 24 | var _ = Describe("Build", func() { 25 | Describe("Executable build", func() { 26 | projectFolder := getTestFolder("BuildTestFolder") 27 | projectRoot, _ := filepath.Abs(projectFolder) 28 | m := manifest.New() 29 | pc := &config.ProjectConfig{Manifest: m, ProjectRoot: projectRoot} 30 | var wd string 31 | 32 | BeforeEach(func() { 33 | var err error 34 | err = os.MkdirAll(projectFolder, os.ModePerm) 35 | Ω(err).ShouldNot(HaveOccurred()) 36 | wd, err = os.Getwd() 37 | Ω(err).ShouldNot(HaveOccurred()) 38 | err = os.Chdir(projectFolder) 39 | Ω(err).ShouldNot(HaveOccurred()) 40 | }) 41 | AfterEach(func() { 42 | err := os.Chdir(wd) 43 | Ω(err).ShouldNot(HaveOccurred()) 44 | }) 45 | It("Run build", func() { 46 | pc.Manifest.Name = "buildTest" 47 | pc.Manifest.Type = manifest.Executable 48 | pc.Manifest.Build = &manifest.BuildConfig{ 49 | Adapter: &manifest.AdapterConfig{ 50 | Name: "c3pm", 51 | Version: defaultadapter.CurrentVersion, 52 | }, 53 | Config: &defaultadapter.Config{ 54 | Sources: []string{"main.cpp"}, 55 | }, 56 | } 57 | pc.Manifest.Version, _ = manifest.VersionFromString("1.0.0") 58 | 59 | err := ioutil.WriteFile("main.cpp", []byte(execSrc), 0644) 60 | Ω(err).ShouldNot(HaveOccurred()) 61 | 62 | err = ctpm.Build(pc) 63 | Ω(err).ShouldNot(HaveOccurred()) 64 | }) 65 | 66 | It("Generate cmake scripts", func() { 67 | fi, err := os.Stat(".c3pm/cmake/CMakeLists.txt") 68 | Ω(err).ShouldNot(HaveOccurred()) 69 | 70 | Ω(fi.Size()).To(BeNumerically(">", 0)) 71 | }) 72 | It("Generate makefiles", func() { 73 | fi, err := os.Stat(".c3pm/build/Makefile") 74 | Ω(err).ShouldNot(HaveOccurred()) 75 | 76 | Ω(fi.Size()).To(BeNumerically(">", 0)) 77 | }) 78 | It("build m", func() { 79 | fi, err := os.Stat("buildTest") 80 | Ω(err).ShouldNot(HaveOccurred()) 81 | 82 | Ω(fi.Size()).To(BeNumerically(">", 0)) 83 | buf := bytes.NewBuffer([]byte{}) 84 | cmd := exec.Command("./buildTest") 85 | cmd.Stdout = buf 86 | err = cmd.Start() 87 | Ω(err).ShouldNot(HaveOccurred()) 88 | 89 | err = cmd.Wait() 90 | Ω(err).ShouldNot(HaveOccurred()) 91 | 92 | Ω(buf.String()).To(Equal("Build test\n")) 93 | }) 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /ctpm/ctpm_suite_test.go: -------------------------------------------------------------------------------- 1 | package ctpm_test 2 | 3 | import ( 4 | "os" 5 | 6 | //"os" 7 | "path/filepath" 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo" 11 | . "github.com/onsi/gomega" 12 | ) 13 | 14 | func TestCtpm(t *testing.T) { 15 | RegisterFailHandler(Fail) 16 | path, err := filepath.Abs("testsArtifacts") 17 | if err != nil { 18 | t.Fatal("Failed to get testsArtifacts absolute path") 19 | } 20 | RunSpecs(t, "Ctpm Suite") 21 | err = os.RemoveAll(path) 22 | if err != nil { 23 | t.Fatal("Failed to clean test artifacts\n") 24 | } 25 | } 26 | 27 | func getTestFolder(path string) string { 28 | return filepath.Join("testsArtifacts/", path) 29 | } 30 | -------------------------------------------------------------------------------- /ctpm/doc.go: -------------------------------------------------------------------------------- 1 | // Package ctpm manages the action of the various CLI commands. 2 | package ctpm 3 | -------------------------------------------------------------------------------- /ctpm/init.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "text/template" 11 | 12 | "github.com/mitchellh/go-spdx" 13 | 14 | "github.com/c3pm-labs/c3pm/config" 15 | ) 16 | 17 | type InitOptions struct { 18 | NoTemplate bool 19 | } 20 | 21 | var InitDefaultOptions = InitOptions{ 22 | NoTemplate: false, 23 | } 24 | 25 | func Init(pc *config.ProjectConfig, opt InitOptions) error { 26 | err := os.MkdirAll(pc.ProjectRoot, os.ModePerm) // umask will take care of permissions 27 | if err != nil { 28 | return err 29 | } 30 | if pc.Manifest.License != "UNLICENSED" { 31 | err = generateLicenseFile(pc) 32 | if err != nil { 33 | return err 34 | } 35 | } 36 | if !opt.NoTemplate { 37 | err = generateReadMe(pc) 38 | if err != nil { 39 | return err 40 | } 41 | if pc.Manifest.Type == "executable" { 42 | err := saveExecutableTemplate(pc) 43 | if err != nil { 44 | return err 45 | } 46 | } else { 47 | err := saveLibraryTemplate(pc) 48 | if err != nil { 49 | return err 50 | } 51 | } 52 | } 53 | 54 | return pc.Save() 55 | } 56 | 57 | const execTemplate = `#include 58 | 59 | int main() { 60 | std::cout << "Hello c3pm!" << std::endl; 61 | return 0; 62 | }` 63 | const includeTemplate = `#pragma once 64 | 65 | void hello();` 66 | const libTemplate = `#include 67 | #include "{{.Name}}.hpp" 68 | 69 | void hello() { 70 | std::cout << "Hello c3pm!" << std::endl; 71 | }` 72 | const readMeTemplate = `# {{.Name}} 73 | 74 | A new C++ project. 75 | 76 | ## Getting Started 77 | 78 | This project is a starting point for a C++ project. 79 | 80 | A few helpful commands to get you started if this is your first time using c3pm: 81 | 82 | ### Building your project 83 | ` + "```" + `shell 84 | $ ctpm build 85 | ` + "```" + ` 86 | 87 | ### Add a package 88 | ` + "```" + `shell 89 | $ ctpm add 90 | ` + "```" + ` 91 | 92 | ### Publishing your project 93 | ` + "```" + `shell 94 | $ ctpm publish 95 | ` + "```" + ` 96 | 97 | For help getting started with c3pm, view our 98 | [online documentation](https://docs.c3pm.io/), which offers tutorials, samples and 99 | a list of all available commands. 100 | ` 101 | 102 | func generateReadMe(pc *config.ProjectConfig) error { 103 | t := template.Must(template.New("readMeTemplate").Parse(readMeTemplate)) 104 | f, err := os.Create(filepath.Join(pc.ProjectRoot, "README.md")) 105 | if err != nil { 106 | return err 107 | } 108 | return t.ExecuteTemplate(f, "readMeTemplate", pc.Manifest) 109 | } 110 | 111 | func generateLicenseFile(pc *config.ProjectConfig) error { 112 | lic, err := spdx.License(pc.Manifest.License) 113 | if err != nil { 114 | return err 115 | } 116 | if len(lic.Text) == 0 { 117 | return fmt.Errorf("generate %s", lic.Name) 118 | } 119 | return ioutil.WriteFile(filepath.Join(pc.ProjectRoot, "LICENSE"), []byte(lic.Text), 0644) 120 | } 121 | 122 | func yesOrNo(label string) string { 123 | choices := "y/n" 124 | 125 | r := bufio.NewReader(os.Stdin) 126 | var s string 127 | 128 | _, err := fmt.Fprintf(os.Stderr, "%s (%s) ", label, choices) 129 | if err != nil { 130 | return "" 131 | } 132 | s, _ = r.ReadString('\n') 133 | s = strings.TrimSpace(s) 134 | if s == "" { 135 | return "n" 136 | } 137 | s = strings.ToLower(s) 138 | return s 139 | } 140 | 141 | func overrideDirectory(label string, path string) bool { 142 | answer := "" 143 | if _, err := os.Stat(path); !os.IsNotExist(err) { 144 | answer = yesOrNo(label) 145 | } 146 | if answer == "n" || answer == "no" { 147 | return false 148 | } 149 | if answer == "y" || answer == "yes" { 150 | if err := os.RemoveAll(path); err != nil { 151 | return false 152 | } 153 | } 154 | if err := os.Mkdir(path, os.ModePerm); err != nil { 155 | return false 156 | } 157 | return true 158 | } 159 | 160 | func saveExecutableTemplate(pc *config.ProjectConfig) error { 161 | if status := overrideDirectory( 162 | "You already have a src directory, do you want to override it?", 163 | filepath.Join(pc.ProjectRoot, "src")); status { 164 | return ioutil.WriteFile(filepath.Join(pc.ProjectRoot, "src", "main.cpp"), []byte(execTemplate), 0644) 165 | } 166 | return nil 167 | } 168 | 169 | func saveLibraryTemplate(pc *config.ProjectConfig) error { 170 | t := template.Must(template.New("libTemplate").Parse(libTemplate)) 171 | var f *os.File = nil 172 | var err error = nil 173 | if status := overrideDirectory( 174 | "You already have a src directory, do you want to override it?", 175 | filepath.Join(pc.ProjectRoot, "src")); status { 176 | f, err = os.Create(filepath.Join(pc.ProjectRoot, "src", pc.Manifest.Name+".cpp")) 177 | if err != nil { 178 | return err 179 | } 180 | defer f.Close() 181 | 182 | if err := t.ExecuteTemplate(f, "libTemplate", pc.Manifest); err != nil { 183 | return err 184 | } 185 | } 186 | 187 | if status := overrideDirectory( 188 | "You already have a include directory, do you want to override it?", 189 | filepath.Join(pc.ProjectRoot, "include")); status { 190 | f, err = os.Create(filepath.Join(pc.ProjectRoot, "include", pc.Manifest.Name+".hpp")) 191 | if err != nil { 192 | return err 193 | } 194 | defer f.Close() 195 | 196 | _, err = f.Write([]byte(includeTemplate)) 197 | } 198 | return err 199 | } 200 | -------------------------------------------------------------------------------- /ctpm/init_test.go: -------------------------------------------------------------------------------- 1 | package ctpm_test 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/config" 5 | "github.com/c3pm-labs/c3pm/config/manifest" 6 | "github.com/c3pm-labs/c3pm/ctpm" 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | "os" 10 | "path/filepath" 11 | ) 12 | 13 | var _ = Describe("Init", func() { 14 | Describe("Project creation", func() { 15 | var projectFolder = getTestFolder("InitTestFolder") 16 | projectFolder, err := filepath.Abs(projectFolder) 17 | Ω(err).ShouldNot(HaveOccurred()) 18 | 19 | var projectName = "InitProject" 20 | var projectType = manifest.Library 21 | var projectDesc = "description" 22 | var projectLicense = "MIT" 23 | 24 | When("initializing manifest", func() { 25 | It("Did not fail", func() { 26 | var err error 27 | m := manifest.New() 28 | m.Name = projectName 29 | m.Version, err = manifest.VersionFromString("1.0.0") 30 | Ω(err).ShouldNot(HaveOccurred()) 31 | 32 | m.Type = projectType 33 | m.License = projectLicense 34 | m.Description = projectDesc 35 | pc := &config.ProjectConfig{Manifest: m, ProjectRoot: projectFolder} 36 | err = ctpm.Init(pc, ctpm.InitDefaultOptions) 37 | Ω(err).ShouldNot(HaveOccurred()) 38 | }) 39 | }) 40 | 41 | It("Create c3pm.yml file", func() { 42 | pc, err := manifest.Load(filepath.Join(projectFolder, "c3pm.yml")) 43 | Ω(err).ShouldNot(HaveOccurred()) 44 | 45 | Ω(pc.Name).To(Equal(projectName)) 46 | Ω(pc.License).To(Equal(projectLicense)) 47 | Ω(pc.Type).To(Equal(projectType)) 48 | Ω(pc.Description).To(Equal(projectDesc)) 49 | }) 50 | // PIt("Project template of either library or an executable") 51 | It("Depending on the user's choice, a LICENSE file", func() { 52 | lic, err := os.Stat(filepath.Join(projectFolder, "LICENSE")) 53 | Ω(err).ShouldNot(HaveOccurred()) 54 | 55 | Ω(lic).ShouldNot(BeNil()) 56 | }) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /ctpm/install.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "archive/tar" 5 | "fmt" 6 | "github.com/c3pm-labs/c3pm/config" 7 | "github.com/c3pm-labs/c3pm/dependencies" 8 | "github.com/c3pm-labs/c3pm/env" 9 | "github.com/c3pm-labs/c3pm/registry" 10 | "io" 11 | "os" 12 | "path/filepath" 13 | ) 14 | 15 | type InstallHandler struct{} 16 | 17 | func (i InstallHandler) FetchDeps(request dependencies.PackageRequest) (dependencies.Dependencies, error) { 18 | libPath := config.LibCachePath(request.Name, request.Version) 19 | pc, err := config.Load(libPath) 20 | if err != nil { 21 | return nil, fmt.Errorf("failed to read c3pm.yml: %w", err) 22 | } 23 | ret := make(dependencies.Dependencies) 24 | for k, v := range pc.Manifest.Dependencies { 25 | ret[k] = v 26 | } 27 | return ret, nil 28 | } 29 | 30 | func (i InstallHandler) PreAct(request dependencies.PackageRequest) error { 31 | fmt.Printf("Installing %s:%s\n", request.Name, request.Version) 32 | return Install(request.Name, request.Version) 33 | } 34 | 35 | func (i InstallHandler) PostAct(request dependencies.PackageRequest) error { 36 | // Check that the lib has correctly been installed 37 | libPath := config.LibCachePath(request.Name, request.Version) 38 | _, err := config.Load(libPath) 39 | if err != nil { 40 | return fmt.Errorf("failed to read c3pm.yml: %w", err) 41 | } 42 | return nil 43 | } 44 | 45 | // Install fetches the package, unpacks it in the c3pm cache and builds it. If the lib already is 46 | // in the cache, we don't do anything 47 | func Install(name, version string) error { 48 | libPath := config.LibCachePath(name, version) 49 | // return early if lib is already in cache 50 | if _, err := os.Stat(libPath); !os.IsNotExist(err) { 51 | return nil 52 | } 53 | // fetch the tarball 54 | tarball, err := registry.FetchPackage(name, version, registry.Options{ 55 | RegistryURL: env.REGISTRY_ENDPOINT, 56 | }) 57 | if err != nil { 58 | return fmt.Errorf("error fetching package: %w", err) 59 | } 60 | // unpack the tarball 61 | err = unpackTarball(tarball, libPath) 62 | return err 63 | } 64 | 65 | func unpackTarball(pkg *os.File, dest string) error { 66 | tr := tar.NewReader(pkg) 67 | err := os.MkdirAll(dest, os.ModePerm) 68 | if err != nil { 69 | return fmt.Errorf("error creating package directory: %w", err) 70 | } 71 | for { 72 | header, err := tr.Next() 73 | switch { 74 | case err == io.EOF: 75 | _ = os.Remove(pkg.Name()) 76 | return nil 77 | case err != nil: 78 | return fmt.Errorf("error unpacking tarball: %w", err) 79 | case header == nil: 80 | continue 81 | } 82 | target := filepath.Join(dest, header.Name) 83 | switch header.Typeflag { 84 | case tar.TypeDir: 85 | if _, err := os.Stat(target); err != nil { 86 | if err := os.MkdirAll(target, os.ModePerm); err != nil { 87 | return err 88 | } 89 | } 90 | case tar.TypeReg: 91 | _ = os.MkdirAll(filepath.Dir(target), os.ModePerm) 92 | f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) 93 | if err != nil { 94 | return err 95 | } 96 | if _, err := io.Copy(f, tr); err != nil { 97 | return err 98 | } 99 | _ = f.Close() 100 | case tar.TypeSymlink: 101 | err = os.MkdirAll(filepath.Dir(target), os.ModePerm) 102 | if err != nil { 103 | return err 104 | } 105 | if err = os.Symlink(header.Linkname, target); err != nil { 106 | return err 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /ctpm/list.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/config" 6 | "github.com/c3pm-labs/c3pm/config/manifest" 7 | "github.com/c3pm-labs/c3pm/dependencies" 8 | "strings" 9 | ) 10 | 11 | type ListOptions struct { 12 | Tree bool 13 | } 14 | 15 | var ListDefaultOptions = ListOptions{ 16 | Tree: false, 17 | } 18 | 19 | type DependencyNode struct { 20 | Children []*DependencyNode 21 | Name string 22 | } 23 | 24 | func (d DependencyNode) String() string { 25 | sb := strings.Builder{} 26 | sb.WriteString(d.Name) 27 | sb.WriteString(": ") 28 | sb.WriteString("[") 29 | for i := 0; i < len(d.Children); i++ { 30 | sb.WriteString(d.Children[i].Name) 31 | if i < len(d.Children)-1 { 32 | sb.WriteString(", ") 33 | } 34 | } 35 | sb.WriteString("]") 36 | return sb.String() 37 | } 38 | 39 | type DependencyBranch []*DependencyNode 40 | 41 | func (d DependencyBranch) String() string { 42 | sb := strings.Builder{} 43 | sb.WriteString("[") 44 | for i := 0; i < len(d); i++ { 45 | sb.WriteString(d[i].Name) 46 | if i < len(d)-1 { 47 | sb.WriteString(", ") 48 | } 49 | } 50 | sb.WriteString("]") 51 | return sb.String() 52 | } 53 | 54 | type DependencyFetcher struct { 55 | Done manifest.Dependencies 56 | CurrentNode *DependencyNode 57 | CurrentBranch DependencyBranch 58 | } 59 | 60 | func (d *DependencyFetcher) FetchDeps(request dependencies.PackageRequest) (dependencies.Dependencies, error) { 61 | if _, ok := d.Done[request.Name]; ok { 62 | return dependencies.Dependencies{}, nil 63 | } 64 | // Find new current node 65 | var currentNode *DependencyNode = nil 66 | found := false 67 | for !found { 68 | for _, node := range d.CurrentNode.Children { 69 | if node.Name == fmt.Sprintf("%s@%s", request.Name, request.Version) { 70 | found = true 71 | d.CurrentBranch = append(d.CurrentBranch, node) 72 | currentNode = node 73 | } 74 | } 75 | if !found { 76 | // Move up one level 77 | d.CurrentBranch = d.CurrentBranch[:len(d.CurrentBranch)-1] 78 | d.CurrentNode = d.CurrentBranch[len(d.CurrentBranch)-1] 79 | } 80 | } 81 | d.Done[request.Name] = request.Version 82 | libPath := config.LibCachePath(request.Name, request.Version) 83 | pc, err := config.Load(libPath) 84 | if err != nil { 85 | return nil, fmt.Errorf("failed to read c3pm.yml: %w", err) 86 | } 87 | ret := make(dependencies.Dependencies) 88 | for k, v := range pc.Manifest.Dependencies { 89 | ret[k] = v 90 | currentNode.Children = append(currentNode.Children, &DependencyNode{Name: fmt.Sprintf("%s@%s", k, v), Children: []*DependencyNode{}}) 91 | } 92 | d.CurrentNode = currentNode 93 | return ret, nil 94 | } 95 | 96 | func (d *DependencyFetcher) PreAct(_ dependencies.PackageRequest) error { 97 | return nil 98 | } 99 | 100 | func (d *DependencyFetcher) PostAct(_ dependencies.PackageRequest) error { 101 | return nil 102 | } 103 | 104 | func displayTree(root *DependencyNode, under string) { 105 | fmt.Printf("───%s\n", root.Name) 106 | for i := 0; i < len(root.Children); i++ { 107 | if i < len(root.Children)-1 { 108 | fmt.Printf("%s├", under) 109 | displayTree(root.Children[i], fmt.Sprintf("%s%s", under, "│ ")) 110 | } else { 111 | fmt.Printf("%s└", under) 112 | displayTree(root.Children[i], fmt.Sprintf("%s%s", under, " ")) 113 | } 114 | } 115 | } 116 | 117 | func List(pc *config.ProjectConfig, opt ListOptions) error { 118 | var topLevelDeps []*DependencyNode 119 | for name, version := range pc.Manifest.Dependencies { 120 | topLevelDeps = append(topLevelDeps, &DependencyNode{Name: fmt.Sprintf("%s@%s", name, version), Children: []*DependencyNode{}}) 121 | } 122 | treeRoot := &DependencyNode{Name: fmt.Sprintf("%s@%s", pc.Manifest.Name, pc.Manifest.Version), Children: topLevelDeps} 123 | 124 | allDeps := make(manifest.Dependencies) 125 | for name, version := range pc.Manifest.Dependencies { 126 | _, err := dependencies.Install(dependencies.PackageRequest{Name: name, Version: version}, &DependencyFetcher{Done: allDeps, CurrentNode: treeRoot, CurrentBranch: []*DependencyNode{treeRoot}}) 127 | if err != nil { 128 | return err 129 | } 130 | } 131 | 132 | if !opt.Tree { 133 | for key, val := range allDeps { 134 | fmt.Printf("%s@%s\n", key, val) 135 | } 136 | } else { 137 | displayTree(treeRoot, " ") 138 | } 139 | 140 | return nil 141 | } 142 | -------------------------------------------------------------------------------- /ctpm/login.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/api" 6 | "github.com/c3pm-labs/c3pm/config" 7 | "io/ioutil" 8 | "os" 9 | ) 10 | 11 | func Login(client api.API, login, password string) error { 12 | apiKey, err := client.Login(login, password) 13 | if err != nil { 14 | return err 15 | } 16 | 17 | err = ioutil.WriteFile(config.AuthFilePath(), []byte(apiKey), 0644) 18 | if err == nil { 19 | return nil 20 | } 21 | if !os.IsNotExist(err) { 22 | return fmt.Errorf("failed to write auth file %s: %w", config.AuthFilePath(), err) 23 | } 24 | err = os.Mkdir(config.GlobalC3PMDirPath(), os.ModePerm) 25 | if err != nil { 26 | return fmt.Errorf("failed to create .c3pm at %s: %w", config.GlobalC3PMDirPath(), err) 27 | } 28 | return ioutil.WriteFile(config.AuthFilePath(), []byte(apiKey), 0644) 29 | } 30 | -------------------------------------------------------------------------------- /ctpm/login_test.go: -------------------------------------------------------------------------------- 1 | package ctpm_test 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/c3pm-labs/c3pm/api" 6 | "github.com/c3pm-labs/c3pm/ctpm" 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | "io/ioutil" 10 | "net/http" 11 | "net/http/httptest" 12 | "os" 13 | "path" 14 | ) 15 | 16 | var _ = Describe("Login", func() { 17 | c3pmHomeDir := getTestFolder("LoginUserHome") 18 | testLogin := "demo@demo.com" 19 | testPassword := "demodemo" 20 | testApiKey := "demo" 21 | 22 | It("should login without error", func() { 23 | server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 24 | Ω(req.URL.String()).To(Equal("/auth/login")) 25 | Ω(req.Method).To(Equal(http.MethodPost)) 26 | 27 | type loginPayload struct { 28 | Login string `json:"login"` 29 | Password string `json:"password"` 30 | } 31 | want := loginPayload{testLogin, testPassword} 32 | 33 | var got loginPayload 34 | body, err := ioutil.ReadAll(req.Body) 35 | Ω(err).ShouldNot(HaveOccurred()) 36 | 37 | err = json.Unmarshal(body, &got) 38 | Ω(err).ShouldNot(HaveOccurred()) 39 | 40 | Ω(want).To(Equal(got)) 41 | 42 | res, err := json.Marshal(struct{ ApiKey string }{testApiKey}) 43 | Ω(err).ShouldNot(HaveOccurred()) 44 | 45 | _, err = rw.Write(res) 46 | Ω(err).ShouldNot(HaveOccurred()) 47 | 48 | })) 49 | defer server.Close() 50 | apiClient := api.New(server.Client(), "") 51 | 52 | err := os.MkdirAll(c3pmHomeDir, os.ModePerm) 53 | Ω(err).ShouldNot(HaveOccurred()) 54 | 55 | err = os.Setenv("C3PM_USER_DIR", c3pmHomeDir) 56 | Ω(err).ShouldNot(HaveOccurred()) 57 | 58 | defer os.Unsetenv("C3PM_USER_DIR") 59 | 60 | err = os.Setenv("C3PM_API_ENDPOINT", server.URL) 61 | Ω(err).ShouldNot(HaveOccurred()) 62 | 63 | defer os.Unsetenv("C3PM_API_ENDPOINT") 64 | 65 | err = ctpm.Login(apiClient, testLogin, testPassword) 66 | Ω(err).ShouldNot(HaveOccurred()) 67 | 68 | }) 69 | 70 | It("should create auth file", func() { 71 | got, err := ioutil.ReadFile(path.Join(c3pmHomeDir, "auth.cfg")) 72 | Ω(err).ShouldNot(HaveOccurred()) 73 | 74 | Ω(string(got)).To(Equal(testApiKey)) 75 | }) 76 | }) 77 | -------------------------------------------------------------------------------- /ctpm/logout.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/config" 5 | "os" 6 | ) 7 | 8 | func Logout() error { 9 | return os.Remove(config.AuthFilePath()) 10 | } 11 | -------------------------------------------------------------------------------- /ctpm/logout_test.go: -------------------------------------------------------------------------------- 1 | package ctpm_test 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/ctpm" 5 | . "github.com/onsi/ginkgo" 6 | . "github.com/onsi/gomega" 7 | "os" 8 | "path" 9 | ) 10 | 11 | var _ = Describe("Logout", func() { 12 | c3pmHomeDir := getTestFolder("LogoutUserHome") 13 | 14 | It("should delete auth file", func() { 15 | err := os.MkdirAll(c3pmHomeDir, os.ModePerm) 16 | Ω(err).ShouldNot(HaveOccurred()) 17 | 18 | err = os.Setenv("C3PM_USER_DIR", c3pmHomeDir) 19 | Ω(err).ShouldNot(HaveOccurred()) 20 | 21 | defer os.Unsetenv("C3PM_USER_DIR") 22 | 23 | authFilePath := path.Join(c3pmHomeDir, "auth.cfg") 24 | f, err := os.Create(authFilePath) 25 | Ω(err).ShouldNot(HaveOccurred()) 26 | 27 | err = f.Close() 28 | Ω(err).ShouldNot(HaveOccurred()) 29 | 30 | err = ctpm.Logout() 31 | Ω(err).ShouldNot(HaveOccurred()) 32 | 33 | _, err = os.Stat(authFilePath) 34 | Ω(os.IsNotExist(err)).To(BeTrue()) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /ctpm/publish.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "fmt" 5 | "github.com/bmatcuk/doublestar" 6 | "github.com/c3pm-labs/c3pm/api" 7 | "github.com/c3pm-labs/c3pm/config" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | func isFileInList(path string, rules []string) (bool, error) { 13 | for _, rule := range rules { 14 | ok, err := doublestar.Match(rule, path) 15 | if err != nil { 16 | return false, fmt.Errorf("failed to match [%s] with [%s] regex: %w", path, rule, err) 17 | } 18 | if ok { 19 | return true, nil 20 | } 21 | } 22 | return false, nil 23 | } 24 | 25 | func getFilesFromRules(included []string, excluded []string) ([]string, error) { 26 | var files []string 27 | err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error { 28 | if err != nil { 29 | return fmt.Errorf("publish list files walk: %w", err) 30 | } 31 | if info.IsDir() { 32 | return nil 33 | } 34 | isIncluded, err := isFileInList(path, included) 35 | if err != nil { 36 | return fmt.Errorf("could not find if path [%s] is included: %w", path, err) 37 | } 38 | if isIncluded { 39 | isExcluded, err := isFileInList(path, excluded) 40 | if err != nil { 41 | return fmt.Errorf("could not find if path [%s] is excluded: %w", path, err) 42 | } 43 | if !isExcluded { 44 | files = append(files, path) 45 | } 46 | } 47 | return nil 48 | }) 49 | return files, err 50 | } 51 | 52 | // Publish function makes an array of the files to include in the tarball 53 | // based on the Include and Exclude fields of the c3pm.yaml 54 | // The array is then given to the Upload function in the client 55 | // We enforce the exclusion of the .git and .c3pm directories and we enforce 56 | // the inclusion of the c3pm.yml file 57 | func Publish(pc *config.ProjectConfig, client api.API) error { 58 | included := append(pc.Manifest.Publish.Include, "c3pm.yml") 59 | excluded := append(pc.Manifest.Publish.Exclude, ".git/**", ".c3pm/**") 60 | 61 | files, err := getFilesFromRules(included, excluded) 62 | if err != nil { 63 | return fmt.Errorf("failed to list files to publish: %w", err) 64 | } 65 | return client.Upload(files) 66 | } 67 | -------------------------------------------------------------------------------- /ctpm/publish_test.go: -------------------------------------------------------------------------------- 1 | package ctpm_test 2 | 3 | import ( 4 | "archive/tar" 5 | "github.com/c3pm-labs/c3pm/api" 6 | "github.com/c3pm-labs/c3pm/config" 7 | "github.com/c3pm-labs/c3pm/config/manifest" 8 | "github.com/c3pm-labs/c3pm/ctpm" 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/gomega" 11 | "io" 12 | "net/http" 13 | "net/http/httptest" 14 | "os" 15 | "path/filepath" 16 | ) 17 | 18 | var _ = Describe("Publish", func() { 19 | var wd string 20 | var filesFound []string 21 | server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 22 | Ω(req.URL.String()).To(Equal("/packages/publish")) 23 | Ω(req.Method).To(Equal(http.MethodPost)) 24 | err := req.ParseMultipartForm(32 << 20) 25 | Ω(err).ShouldNot(HaveOccurred()) 26 | file, _, _ := req.FormFile("file") 27 | tr := tar.NewReader(file) 28 | 29 | loop: 30 | for { 31 | header, err := tr.Next() 32 | switch { 33 | case err == io.EOF: 34 | break loop 35 | case err != nil: 36 | Ω(err).ShouldNot(HaveOccurred()) 37 | case header == nil: 38 | continue 39 | } 40 | filesFound = append(filesFound, header.Name) 41 | 42 | } 43 | defer req.Body.Close() 44 | rw.WriteHeader(http.StatusOK) 45 | })) 46 | projectsFolder := "../test_helpers/projects/publishProjects/" 47 | moveToProjectDirectory := func(project string) { 48 | err := os.Chdir(projectsFolder + project) 49 | Ω(err).ShouldNot(HaveOccurred()) 50 | } 51 | getPc := func() *config.ProjectConfig { 52 | projectRoot, err := filepath.Abs(".") 53 | Ω(err).ShouldNot(HaveOccurred()) 54 | manifestPath := "./c3pm.yml" 55 | m, err := manifest.Load(manifestPath) 56 | Ω(err).ShouldNot(HaveOccurred()) 57 | pc := &config.ProjectConfig{Manifest: m, ProjectRoot: projectRoot} 58 | return pc 59 | } 60 | 61 | runPublishWithMockServer := func() { 62 | apiClient := api.New(server.Client(), "") 63 | 64 | err := os.Setenv("C3PM_API_ENDPOINT", server.URL) 65 | Ω(err).ShouldNot(HaveOccurred()) 66 | defer os.Unsetenv("C3PM_API_ENDPOINT") 67 | 68 | err = ctpm.Publish(getPc(), apiClient) 69 | Ω(err).ShouldNot(HaveOccurred()) 70 | } 71 | 72 | BeforeEach(func() { 73 | var err error 74 | wd, err = os.Getwd() 75 | Ω(err).ShouldNot(HaveOccurred()) 76 | filesFound = nil 77 | }) 78 | AfterEach(func() { 79 | err := os.Chdir(wd) 80 | Ω(err).ShouldNot(HaveOccurred()) 81 | }) 82 | 83 | It("should have correct default include and exclude rules", func() { 84 | moveToProjectDirectory("basic") 85 | expectedFiles := []string{"README.md", "c3pm.yml", "src/main.cpp", "toto.txt"} 86 | runPublishWithMockServer() 87 | Ω(filesFound).Should(Equal(expectedFiles)) 88 | }) 89 | 90 | It("should succeed with mixed include and exclude rules", func() { 91 | moveToProjectDirectory("includeExclude") 92 | expectedFiles := []string{"c3pm.yml", "src/main.cpp", "toto.txt"} 93 | runPublishWithMockServer() 94 | Ω(filesFound).Should(Equal(expectedFiles)) 95 | }) 96 | 97 | It("should exclude a file that is both included and excluded", func() { 98 | moveToProjectDirectory("includeAndExcludeFile") 99 | expectedFiles := []string{"c3pm.yml", "toto.txt"} 100 | runPublishWithMockServer() 101 | Ω(filesFound).Should(Equal(expectedFiles)) 102 | }) 103 | 104 | It("overwrite default include: shouldn't have c3pm.yml in tarball", func() { 105 | moveToProjectDirectory("excludeManifest") 106 | expectedFiles := []string{"README.md", "src/main.cpp", "toto.txt"} 107 | runPublishWithMockServer() 108 | Ω(filesFound).Should(Equal(expectedFiles)) 109 | }) 110 | }) 111 | -------------------------------------------------------------------------------- /ctpm/remove.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "fmt" 5 | "github.com/c3pm-labs/c3pm/config" 6 | ) 7 | 8 | type RemoveOptions struct { 9 | Dependencies []string 10 | } 11 | 12 | func Remove(pc *config.ProjectConfig, opts RemoveOptions) error { 13 | if pc.Manifest.Dependencies == nil || len(pc.Manifest.Dependencies) == 0 { 14 | return fmt.Errorf("cannot remove dependency: there is no dependency in the project") 15 | } 16 | for _, dep := range opts.Dependencies { 17 | if _, ok := pc.Manifest.Dependencies[dep]; !ok { 18 | fmt.Printf("cannot remove dependency \"%s\": there is no dependency with this name in the project", dep) 19 | } else { 20 | delete(pc.Manifest.Dependencies, dep) 21 | } 22 | } 23 | if err := pc.Save(); err != nil { 24 | return fmt.Errorf("error saving project config: %w", err) 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /ctpm/remove_test.go: -------------------------------------------------------------------------------- 1 | package ctpm_test 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/config" 5 | "github.com/c3pm-labs/c3pm/config/manifest" 6 | "github.com/c3pm-labs/c3pm/ctpm" 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | "os" 10 | "path/filepath" 11 | ) 12 | 13 | var _ = Describe("Remove", func() { 14 | Describe("Remove dependencies", func() { 15 | var projectFolder = getTestFolder("RemoveTestFolder") 16 | var projectName = "Test-Remove" 17 | var projectType = manifest.Executable 18 | var projectDesc = "description" 19 | var projectLicense = "MIT" 20 | var dependencies = map[string]string{ 21 | "calculator": "1.0.0", 22 | "sort": "1.0.0", 23 | "simple-math": "1.0.0", 24 | "super-math": "1.0.0", 25 | } 26 | var pc *config.ProjectConfig 27 | err := os.MkdirAll(projectFolder, os.ModePerm) 28 | Ω(err).ShouldNot(HaveOccurred()) 29 | 30 | BeforeEach(func() { 31 | m := manifest.New() 32 | m.Name = projectName 33 | m.Version, err = manifest.VersionFromString("1.0.0") 34 | Ω(err).ShouldNot(HaveOccurred()) 35 | m.Type = projectType 36 | m.License = projectLicense 37 | m.Description = projectDesc 38 | m.Dependencies = dependencies 39 | 40 | pc = &config.ProjectConfig{Manifest: m, ProjectRoot: projectFolder} 41 | 42 | _, err = os.Create(filepath.Join(projectFolder, "c3pm.yml")) 43 | Ω(err).ShouldNot(HaveOccurred()) 44 | }) 45 | AfterEach(func() { 46 | os.Remove(filepath.Join(projectFolder, "c3pm.yml")) 47 | }) 48 | 49 | It("remove one dependency", func() { 50 | toRemove := []string{"calculator"} 51 | err := ctpm.Remove(pc, ctpm.RemoveOptions{Dependencies: toRemove}) 52 | Ω(err).ShouldNot(HaveOccurred()) 53 | _, ok := pc.Manifest.Dependencies[toRemove[0]] 54 | Ω(ok).Should(BeFalse()) 55 | 56 | }) 57 | It("remove several dependencies at the same time", func() { 58 | toRemove := []string{"calculator", "sort", "simple-math"} 59 | err := ctpm.Remove(pc, ctpm.RemoveOptions{Dependencies: toRemove}) 60 | Ω(err).ShouldNot(HaveOccurred()) 61 | for _, dep := range toRemove { 62 | _, ok := pc.Manifest.Dependencies[dep] 63 | Ω(ok).Should(BeFalse()) 64 | } 65 | }) 66 | It("remove unexisting dependency", func() { 67 | toRemove := []string{"toto"} 68 | err := ctpm.Remove(pc, ctpm.RemoveOptions{Dependencies: toRemove}) 69 | Ω(err).ShouldNot(HaveOccurred()) 70 | for _, dep := range toRemove { 71 | _, ok := pc.Manifest.Dependencies[dep] 72 | Ω(ok).Should(BeFalse()) 73 | } 74 | }) 75 | It("remove dependency from an empty dependency array", func() { 76 | pc.Manifest.Dependencies = map[string]string{} 77 | toRemove := []string{"toto"} 78 | err := ctpm.Remove(pc, ctpm.RemoveOptions{Dependencies: toRemove}) 79 | Ω(err).Should(HaveOccurred()) 80 | }) 81 | It("remove dependency from an nil dependency array", func() { 82 | pc.Manifest.Dependencies = nil 83 | toRemove := []string{"toto"} 84 | err := ctpm.Remove(pc, ctpm.RemoveOptions{Dependencies: toRemove}) 85 | Ω(err).Should(HaveOccurred()) 86 | }) 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /ctpm/test.go: -------------------------------------------------------------------------------- 1 | package ctpm 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/c3pm-labs/c3pm/adapter" 7 | "github.com/c3pm-labs/c3pm/adapter_interface" 8 | "github.com/c3pm-labs/c3pm/config" 9 | ) 10 | 11 | func Test(pc *config.ProjectConfig) error { 12 | getter := adapter.AdapterGetterImp{} 13 | adp, err := getter.FromPC(pc.Manifest.Build.Adapter) 14 | if err != nil { 15 | return err 16 | } 17 | adpt, ok := adp.(adapter_interface.AdapterTestable) 18 | if !ok { 19 | return errors.New("Adapter does not support testing") 20 | } 21 | return adpt.Test(pc) 22 | } 23 | 24 | func AddDependenciesAndTest(pc *config.ProjectConfig) error { 25 | pc.Manifest.Dependencies = pc.Manifest.Dependencies.Append(pc.Manifest.TestDependencies) 26 | err := addAllDependencies(pc) 27 | if err != nil { 28 | return fmt.Errorf("error installing dependencies: %w", err) 29 | } 30 | err = Test(pc) 31 | if err != nil { 32 | return fmt.Errorf("build failed: %w", err) 33 | } 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /ctpm/test_test.go: -------------------------------------------------------------------------------- 1 | package ctpm_test 2 | 3 | import ( 4 | "bytes" 5 | "github.com/c3pm-labs/c3pm/adapter/defaultadapter" 6 | "github.com/c3pm-labs/c3pm/config" 7 | "github.com/c3pm-labs/c3pm/config/manifest" 8 | "github.com/c3pm-labs/c3pm/ctpm" 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/gomega" 11 | "io/ioutil" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | ) 16 | 17 | var testSrc = `#include 18 | int main() { 19 | std::cout << "Test test" << std::endl; 20 | return 0; 21 | } 22 | ` 23 | 24 | var _ = Describe("Test", func() { 25 | Describe("Test executable build", func() { 26 | projectFolder := getTestFolder("TestTestFolder") 27 | projectRoot, _ := filepath.Abs(projectFolder) 28 | m := manifest.New() 29 | pc := &config.ProjectConfig{Manifest: m, ProjectRoot: projectRoot} 30 | var wd string 31 | 32 | BeforeEach(func() { 33 | var err error 34 | err = os.MkdirAll(projectFolder, os.ModePerm) 35 | Ω(err).ShouldNot(HaveOccurred()) 36 | wd, err = os.Getwd() 37 | Ω(err).ShouldNot(HaveOccurred()) 38 | err = os.Chdir(projectFolder) 39 | Ω(err).ShouldNot(HaveOccurred()) 40 | }) 41 | AfterEach(func() { 42 | err := os.Chdir(wd) 43 | Ω(err).ShouldNot(HaveOccurred()) 44 | }) 45 | It("Run test", func() { 46 | pc.Manifest.Name = "buildTest" 47 | pc.Manifest.Type = manifest.Executable 48 | pc.Manifest.Build = &manifest.BuildConfig{ 49 | Adapter: &manifest.AdapterConfig{ 50 | Name: "c3pm", 51 | Version: defaultadapter.CurrentVersion, 52 | }, 53 | Config: &defaultadapter.Config{ 54 | Sources: []string{"main.cpp"}, 55 | TestSources: []string{"test.cpp"}, 56 | }, 57 | } 58 | pc.Manifest.Version, _ = manifest.VersionFromString("1.0.0") 59 | 60 | err := ioutil.WriteFile("main.cpp", []byte(execSrc), 0644) 61 | Ω(err).ShouldNot(HaveOccurred()) 62 | 63 | err = ioutil.WriteFile("test.cpp", []byte(testSrc), 0644) 64 | Ω(err).ShouldNot(HaveOccurred()) 65 | 66 | err = ctpm.Test(pc) 67 | Ω(err).ShouldNot(HaveOccurred()) 68 | }) 69 | 70 | It("Generate cmake scripts", func() { 71 | fi, err := os.Stat(".c3pm/cmake/CMakeLists.txt") 72 | Ω(err).ShouldNot(HaveOccurred()) 73 | 74 | Ω(fi.Size()).To(BeNumerically(">", 0)) 75 | }) 76 | It("Generate makefiles", func() { 77 | fi, err := os.Stat(".c3pm/build/Makefile") 78 | Ω(err).ShouldNot(HaveOccurred()) 79 | 80 | Ω(fi.Size()).To(BeNumerically(">", 0)) 81 | }) 82 | It("ran tests", func() { 83 | fi, err := os.Stat("buildTest_test") 84 | Ω(err).ShouldNot(HaveOccurred()) 85 | 86 | Ω(fi.Size()).To(BeNumerically(">", 0)) 87 | buf := bytes.NewBuffer([]byte{}) 88 | cmd := exec.Command("./buildTest_test") 89 | cmd.Stdout = buf 90 | err = cmd.Start() 91 | Ω(err).ShouldNot(HaveOccurred()) 92 | 93 | err = cmd.Wait() 94 | Ω(err).ShouldNot(HaveOccurred()) 95 | 96 | Ω(buf.String()).To(Equal("Test test\n")) 97 | }) 98 | }) 99 | }) 100 | -------------------------------------------------------------------------------- /dependencies/dependencies_suite_test.go: -------------------------------------------------------------------------------- 1 | package dependencies_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestDependencies(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Dependencies Suite") 13 | } 14 | -------------------------------------------------------------------------------- /dependencies/depgraph.go: -------------------------------------------------------------------------------- 1 | package dependencies 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Packages is a map from names to the list of versions. 9 | // The versions are represented as a map of string over struct{} to use the speed and uniqueness of map keys. 10 | type Packages map[string]map[string]struct{} 11 | 12 | func (ps Packages) Find(name, version string) bool { 13 | vs, ok := ps[name] 14 | if !ok { 15 | return false 16 | } 17 | _, ok = vs[version] 18 | return ok 19 | } 20 | 21 | func (ps Packages) add(name, version string) { 22 | vs, ok := ps[name] 23 | if !ok { 24 | ps[name] = map[string]struct{}{version: {}} 25 | } else { 26 | vs[version] = struct{}{} 27 | } 28 | } 29 | 30 | func (ps Packages) String() string { 31 | sb := strings.Builder{} 32 | i := 0 33 | for name, versions := range ps { 34 | sb.WriteString(name + "@") 35 | sb.WriteString("[") 36 | j := 0 37 | for version := range versions { 38 | sb.WriteString(version) 39 | if j != len(versions)-1 && len(versions) >= 1 { 40 | sb.WriteString(",") 41 | } 42 | j += 1 43 | } 44 | sb.WriteString("]") 45 | if i != len(ps)-1 && len(ps) >= 1 { 46 | sb.WriteString(", ") 47 | } 48 | i += 1 49 | } 50 | return sb.String() 51 | } 52 | 53 | type Dependencies map[string]string 54 | 55 | type PackageRequest struct { 56 | Name string 57 | Version string 58 | } 59 | 60 | func install(r PackageRequest, depHandler DependencyHandler, installedPackages *Packages) error { 61 | // If package has already been installed, pass 62 | if installedPackages.Find(r.Name, r.Version) { 63 | return nil 64 | } 65 | err := depHandler.PreAct(r) 66 | if err != nil { 67 | return fmt.Errorf("error during prefetch action for %s/%s: %w", r.Name, r.Version, err) 68 | } 69 | installedPackages.add(r.Name, r.Version) 70 | deps, err := depHandler.FetchDeps(r) 71 | if err != nil { 72 | return fmt.Errorf("error fetching dependencies of %s/%s: %w", r.Name, r.Version, err) 73 | } 74 | if deps == nil { 75 | return nil 76 | } 77 | for name, version := range deps { 78 | err := install(PackageRequest{ 79 | Name: name, 80 | Version: version, 81 | }, depHandler, installedPackages) 82 | if err != nil { 83 | return err 84 | } 85 | } 86 | err = depHandler.PostAct(r) 87 | if err != nil { 88 | return fmt.Errorf("error during postfetch action for %s/%s: %w", r.Name, r.Version, err) 89 | } 90 | return nil 91 | } 92 | 93 | func Install(r PackageRequest, depHandler DependencyHandler) (Packages, error) { 94 | installedPackages := Packages{} 95 | err := install(r, depHandler, &installedPackages) 96 | return installedPackages, err 97 | } 98 | -------------------------------------------------------------------------------- /dependencies/depgraph_test.go: -------------------------------------------------------------------------------- 1 | package dependencies_test 2 | 3 | import ( 4 | "errors" 5 | "github.com/c3pm-labs/c3pm/dependencies" 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | type PackageMeta struct { 11 | Version string 12 | Dependencies dependencies.Dependencies 13 | } 14 | 15 | var packages = map[string][]PackageMeta{ 16 | "boost/assert": {{ 17 | Version: "0.0.0", 18 | Dependencies: dependencies.Dependencies{"boost/config": "0.0.0"}, 19 | }, { 20 | Version: "0.0.1", 21 | Dependencies: dependencies.Dependencies{"boost/config": "0.0.0"}, 22 | }}, 23 | "boost/chrono": {{"0.0.0", dependencies.Dependencies{"boost/assert": "0.0.0", "boost/config": "0.0.0", "boost/core": "0.0.0", "boost/mpl": "0.0.0", "boost/static_assert": "0.0.0", "boost/integer": "0.0.0", "boost/typeof": "0.0.0", "boost/type_traits": "0.0.0", "boost/winapi": "0.0.0", "boost/move": "0.0.0", "boost/system": "0.0.0", "boost/throw_exception": "0.0.0", "boost/ratio": "0.0.0"}}}, 24 | "boost/config": {{"0.0.0", nil}}, 25 | "boost/core": {{"0.0.0", dependencies.Dependencies{"boost/config": "0.0.0", "boost/assert": "0.0.0"}}}, 26 | "boost/integer": {{"0.0.0", dependencies.Dependencies{"boost/config": "0.0.0", "boost/static_assert": "0.0.0"}}}, 27 | "boost/move": {{"0.0.0", dependencies.Dependencies{"boost/assert": "0.0.0", "boost/config": "0.0.0", "boost/core": "0.0.0", "boost/static_assert": "0.0.0"}}}, 28 | "boost/mpl": {{"0.0.0", dependencies.Dependencies{"boost/core": "0.0.0", "boost/static_assert": "0.0.0", "boost/predef": "0.0.0", "boost/preprocessor": "0.0.0", "boost/throw_exception": "0.0.0", "boost/type_traits": "0.0.0", "boost/utility": "0.0.0"}}}, 29 | "boost/predef": {{"0.0.0", nil}}, 30 | "boost/preprocessor": {{"0.0.0", nil}}, 31 | "boost/ratio": {{"0.0.0", dependencies.Dependencies{"boost/config": "0.0.0", "boost/core": "0.0.0", "boost/mpl": "0.0.0", "boost/integer": "0.0.0", "boost/type_traits": "0.0.0"}}}, 32 | "boost/static_assert": {{"0.0.0", dependencies.Dependencies{"boost/config": "0.0.0"}}}, 33 | "boost/system": {{"0.0.0", dependencies.Dependencies{"boost/core": "0.0.0", "boost/assert": "0.0.0", "boost/predef": "0.0.0"}}}, 34 | "boost/throw_exception": {{"0.0.0", dependencies.Dependencies{"boost/assert": "0.0.0", "boost/config": "0.0.0"}}}, 35 | "boost/type_traits": {{"0.0.0", dependencies.Dependencies{"boost/core": "0.0.0", "boost/config": "0.0.0", "boost/static_assert": "0.0.0"}}}, 36 | "boost/typeof": {{"0.0.0", dependencies.Dependencies{"boost/config": "0.0.0", "boost/core": "0.0.0", "boost/mpl": "0.0.0", "boost/preprocessor": "0.0.0", "boost/type_traits": "0.0.0"}}}, 37 | "boost/utility": {{"0.0.0", dependencies.Dependencies{"boost/mpl": "0.0.0", "boost/throw_exception": "0.0.0"}}}, 38 | "boost/winapi": {{"0.0.0", dependencies.Dependencies{"boost/config": "0.0.0", "boost/predef": "0.0.0"}}}, 39 | } 40 | 41 | type TestDependencyHandler int 42 | 43 | func (TestDependencyHandler) FetchDeps(request dependencies.PackageRequest) (dependencies.Dependencies, error) { 44 | versions, ok := packages[request.Name] 45 | if !ok { 46 | return nil, errors.New("package not found") 47 | } 48 | for _, p := range versions { 49 | if p.Version == request.Version { 50 | return p.Dependencies, nil 51 | } 52 | } 53 | return nil, errors.New("version not found") 54 | } 55 | 56 | func (TestDependencyHandler) PreAct(request dependencies.PackageRequest) error { 57 | return nil 58 | } 59 | func (TestDependencyHandler) PostAct(request dependencies.PackageRequest) error { 60 | return nil 61 | } 62 | 63 | const fetcher TestDependencyHandler = 0 64 | 65 | var _ = Describe("Dependencies", func() { 66 | It("fetches no dependencies if the package has none", func() { 67 | packages := dependencies.Packages{ 68 | "boost/config": {"0.0.0": struct{}{}}, 69 | } 70 | pkgs, err := dependencies.Install(dependencies.PackageRequest{ 71 | Name: "boost/config", 72 | Version: "0.0.0", 73 | }, fetcher) 74 | Ω(err).ShouldNot(HaveOccurred()) 75 | Ω(pkgs).Should(Equal(packages)) 76 | }) 77 | 78 | It("fetches the correct dependencies with a single dependency", func() { 79 | packages := dependencies.Packages{ 80 | "boost/config": {"0.0.0": struct{}{}}, 81 | "boost/assert": {"0.0.0": struct{}{}}, 82 | } 83 | pkgs, err := dependencies.Install(dependencies.PackageRequest{ 84 | Name: "boost/assert", 85 | Version: "0.0.0", 86 | }, fetcher) 87 | Ω(err).ShouldNot(HaveOccurred()) 88 | Ω(pkgs).Should(Equal(packages)) 89 | }) 90 | 91 | It("fetches the correct dependencies with duplicate dependencies", func() { 92 | packages := dependencies.Packages{ 93 | "boost/assert": {"0.0.0": struct{}{}}, 94 | "boost/config": {"0.0.0": struct{}{}}, 95 | "boost/core": {"0.0.0": struct{}{}}, 96 | } 97 | pkgs, err := dependencies.Install(dependencies.PackageRequest{ 98 | Name: "boost/core", 99 | Version: "0.0.0", 100 | }, fetcher) 101 | Ω(err).ShouldNot(HaveOccurred()) 102 | Ω(pkgs).Should(Equal(packages)) 103 | }) 104 | 105 | It("fetches the correct dependencies with two levels of dependency", func() { 106 | packages := dependencies.Packages{ 107 | "boost/assert": {"0.0.0": struct{}{}}, 108 | "boost/config": {"0.0.0": struct{}{}}, 109 | "boost/core": {"0.0.0": struct{}{}}, 110 | "boost/predef": {"0.0.0": struct{}{}}, 111 | "boost/system": {"0.0.0": struct{}{}}, 112 | } 113 | pkgs, err := dependencies.Install(dependencies.PackageRequest{ 114 | Name: "boost/system", 115 | Version: "0.0.0", 116 | }, fetcher) 117 | Ω(err).ShouldNot(HaveOccurred()) 118 | Ω(pkgs).Should(Equal(packages)) 119 | }) 120 | 121 | It("resolves circular dependencies", func(done Done) { 122 | packages := dependencies.Packages{ 123 | "boost/assert": {"0.0.0": struct{}{}}, 124 | "boost/config": {"0.0.0": struct{}{}}, 125 | "boost/core": {"0.0.0": struct{}{}}, 126 | "boost/mpl": {"0.0.0": struct{}{}}, 127 | "boost/predef": {"0.0.0": struct{}{}}, 128 | "boost/preprocessor": {"0.0.0": struct{}{}}, 129 | "boost/type_traits": {"0.0.0": struct{}{}}, 130 | "boost/throw_exception": {"0.0.0": struct{}{}}, 131 | "boost/static_assert": {"0.0.0": struct{}{}}, 132 | "boost/utility": {"0.0.0": struct{}{}}, 133 | } 134 | pkgs, err := dependencies.Install(dependencies.PackageRequest{ 135 | Name: "boost/mpl", 136 | Version: "0.0.0", 137 | }, fetcher) 138 | Ω(err).ShouldNot(HaveOccurred()) 139 | Ω(pkgs).Should(Equal(packages)) 140 | close(done) 141 | }) 142 | 143 | It("resolves an important (17) number of packages", func(done Done) { 144 | packages := dependencies.Packages{ 145 | "boost/assert": {"0.0.0": struct{}{}}, 146 | "boost/config": {"0.0.0": struct{}{}}, 147 | "boost/core": {"0.0.0": struct{}{}}, 148 | "boost/chrono": {"0.0.0": struct{}{}}, 149 | "boost/integer": {"0.0.0": struct{}{}}, 150 | "boost/move": {"0.0.0": struct{}{}}, 151 | "boost/mpl": {"0.0.0": struct{}{}}, 152 | "boost/predef": {"0.0.0": struct{}{}}, 153 | "boost/preprocessor": {"0.0.0": struct{}{}}, 154 | "boost/ratio": {"0.0.0": struct{}{}}, 155 | "boost/type_traits": {"0.0.0": struct{}{}}, 156 | "boost/typeof": {"0.0.0": struct{}{}}, 157 | "boost/throw_exception": {"0.0.0": struct{}{}}, 158 | "boost/static_assert": {"0.0.0": struct{}{}}, 159 | "boost/system": {"0.0.0": struct{}{}}, 160 | "boost/utility": {"0.0.0": struct{}{}}, 161 | "boost/winapi": {"0.0.0": struct{}{}}, 162 | } 163 | pkgs, err := dependencies.Install(dependencies.PackageRequest{ 164 | Name: "boost/chrono", 165 | Version: "0.0.0", 166 | }, fetcher) 167 | Ω(err).ShouldNot(HaveOccurred()) 168 | Ω(pkgs).Should(Equal(packages)) 169 | close(done) 170 | 171 | }) 172 | }) 173 | -------------------------------------------------------------------------------- /dependencies/fetcher.go: -------------------------------------------------------------------------------- 1 | package dependencies 2 | 3 | type DependencyHandler interface { 4 | FetchDeps(request PackageRequest) (Dependencies, error) 5 | PreAct(request PackageRequest) error 6 | PostAct(request PackageRequest) error 7 | } 8 | -------------------------------------------------------------------------------- /env/dev.go: -------------------------------------------------------------------------------- 1 | //+build dev 2 | 3 | package env 4 | 5 | const ( 6 | API_ENDPOINT = "http://localhost:4000/v1" 7 | REGISTRY_ENDPOINT = "http://localhost:4566" 8 | ) 9 | -------------------------------------------------------------------------------- /env/doc.go: -------------------------------------------------------------------------------- 1 | // Package env declares variable to be defined on specific deployment environment (prod, dev, etc...) 2 | package env 3 | -------------------------------------------------------------------------------- /env/prod.go: -------------------------------------------------------------------------------- 1 | //+build !dev 2 | 3 | package env 4 | 5 | const ( 6 | //API_ENDPOINT is the URL of the C3PM API 7 | API_ENDPOINT = "https://c3pm.herokuapp.com/v1" 8 | //REGISTRY_ENDPOINT is the URL to an S3-compatible bucket that will be used as the package registry. 9 | REGISTRY_ENDPOINT = "https://registry-c3pm-io.s3.fr-par.scw.cloud" 10 | ) 11 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/c3pm-labs/c3pm 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/AlecAivazis/survey/v2 v2.2.12 7 | github.com/Masterminds/semver/v3 v3.1.1 8 | github.com/bmatcuk/doublestar v1.3.4 9 | github.com/mattn/go-colorable v0.1.8 // indirect 10 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 11 | github.com/mitchellh/go-spdx v0.1.0 12 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 13 | github.com/onsi/ginkgo v1.15.2 14 | github.com/onsi/gomega v1.11.0 15 | github.com/schollz/progressbar/v3 v3.8.3 16 | github.com/spf13/cobra v1.1.3 17 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect 18 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect 19 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect 20 | golang.org/x/text v0.3.4 // indirect 21 | gopkg.in/yaml.v2 v2.4.0 22 | ) 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 9 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 10 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 12 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 13 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 14 | github.com/AlecAivazis/survey/v2 v2.2.12 h1:5a07y93zA6SZ09gOa9wLVLznF5zTJMQ+pJ3cZK4IuO8= 15 | github.com/AlecAivazis/survey/v2 v2.2.12/go.mod h1:6d4saEvBsfSHXeN1a5OA5m2+HJ2LuVokllnC77pAIKI= 16 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 17 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 18 | github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= 19 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 20 | github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= 21 | github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= 22 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 23 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 24 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 25 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 26 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 27 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 28 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 29 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 30 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 31 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 32 | github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= 33 | github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= 34 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 35 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 36 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 37 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 38 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 39 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 40 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 41 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 42 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 43 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 44 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 45 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 46 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 47 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 48 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 49 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 50 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 51 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 52 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 53 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 54 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 55 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 56 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 57 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 58 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 59 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 60 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 61 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 62 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 63 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 64 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 65 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 66 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 67 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 68 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 69 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 70 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 71 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 72 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 73 | github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= 74 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 75 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 76 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 77 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 78 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 79 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 80 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 81 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 82 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 83 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 84 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 85 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 86 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 87 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 88 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 89 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 90 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 91 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 92 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 93 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 94 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 95 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 96 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 97 | github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= 98 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 99 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 100 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 101 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 102 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 103 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 104 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 105 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 106 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 107 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 108 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 109 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 110 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 111 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 112 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 113 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 114 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 115 | github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= 116 | github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= 117 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 118 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 119 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 120 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 121 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 122 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 123 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 124 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 125 | github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= 126 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 127 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 128 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 129 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 130 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 131 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 132 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 133 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 134 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 135 | github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ= 136 | github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 137 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 138 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 139 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 140 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 141 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 142 | github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= 143 | github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 144 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 145 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 146 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 147 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 148 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 149 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 150 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 151 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 152 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 153 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= 154 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 155 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 156 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 157 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 158 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 159 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 160 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 161 | github.com/mitchellh/go-spdx v0.1.0 h1:50JnVzkL3kWreQ5Qb4Pi3Qx9e+bbYrt8QglJDpfeBEs= 162 | github.com/mitchellh/go-spdx v0.1.0/go.mod h1:FFi4Cg1fBuN/JCtPtP8PEDmcBjvO3gijQVl28YjIBVQ= 163 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 164 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 165 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 166 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 167 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 168 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 169 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 170 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= 171 | github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= 172 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 173 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 174 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 175 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 176 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 177 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 178 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 179 | github.com/onsi/ginkgo v1.15.2 h1:l77YT15o814C2qVL47NOyjV/6RbaP7kKdrvZnxQ3Org= 180 | github.com/onsi/ginkgo v1.15.2/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o= 181 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 182 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 183 | github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug= 184 | github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= 185 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 186 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 187 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 188 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 189 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 190 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 191 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 192 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 193 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 194 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 195 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 196 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 197 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 198 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 199 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 200 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 201 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 202 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 203 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 204 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 205 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 206 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 207 | github.com/schollz/progressbar/v3 v3.8.3 h1:FnLGl3ewlDUP+YdSwveXBaXs053Mem/du+wr7XSYKl8= 208 | github.com/schollz/progressbar/v3 v3.8.3/go.mod h1:pWnVCjSBZsT2X3nx9HfRdnCDrpbevliMeoEVhStwHko= 209 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 210 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 211 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 212 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 213 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 214 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 215 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 216 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 217 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 218 | github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= 219 | github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= 220 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 221 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 222 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 223 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 224 | github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 225 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 226 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 227 | github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 228 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 229 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 230 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 231 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 232 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 233 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 234 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 235 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 236 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 237 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 238 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 239 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 240 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 241 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 242 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 243 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 244 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 245 | golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 246 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 247 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 248 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 249 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 250 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= 251 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 252 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 253 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 254 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 255 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 256 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 257 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 258 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 259 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 260 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 261 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 262 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 263 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 264 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 265 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 266 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 267 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 268 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 269 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 270 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 271 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 272 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 273 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 274 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 275 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 276 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 277 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 278 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 279 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 280 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 281 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 282 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 283 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 284 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 285 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 286 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 287 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 288 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 289 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= 290 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 291 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 292 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 293 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 294 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 295 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 296 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 297 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 298 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 299 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 300 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 301 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 302 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 303 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 304 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 305 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 306 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 307 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 308 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 309 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 310 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 311 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 312 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 313 | golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 314 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 315 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 316 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 317 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 318 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 319 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 320 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 321 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 322 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 323 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 324 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 325 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 326 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 327 | golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 328 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw= 329 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 330 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 331 | golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 332 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= 333 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 334 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 335 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 336 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 337 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 338 | golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= 339 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 340 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 341 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 342 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 343 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 344 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 345 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 346 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 347 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 348 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 349 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 350 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 351 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 352 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 353 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 354 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 355 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 356 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 357 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 358 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 359 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 360 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 361 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 362 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 363 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 364 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 365 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 366 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 367 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 368 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 369 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 370 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 371 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 372 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 373 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 374 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 375 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 376 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 377 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 378 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 379 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 380 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 381 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 382 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 383 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 384 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 385 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 386 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 387 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 388 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 389 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 390 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 391 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 392 | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= 393 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 394 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 395 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 396 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 397 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 398 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 399 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 400 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 401 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 402 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 403 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 404 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 405 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 406 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 407 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 408 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 409 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 410 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 411 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 412 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 413 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 414 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 415 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/c3pm-labs/c3pm/cmd" 5 | ) 6 | 7 | var version = "1.0.0" 8 | 9 | func main() { 10 | cmd.RootCmd.Version = version 11 | cmd.RootCmd.InitDefaultVersionFlag() 12 | _ = cmd.RootCmd.Execute() 13 | } 14 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1 3 | } 4 | -------------------------------------------------------------------------------- /registry/registry.go: -------------------------------------------------------------------------------- 1 | // Package registry handles interaction with the C3PM registry. 2 | // It handles file downloading and version querying. 3 | package registry 4 | 5 | import ( 6 | "encoding/xml" 7 | "fmt" 8 | "github.com/Masterminds/semver/v3" 9 | "github.com/c3pm-labs/c3pm/api" 10 | "github.com/schollz/progressbar/v3" 11 | "io" 12 | "io/ioutil" 13 | "net/http" 14 | "os" 15 | "path/filepath" 16 | "sort" 17 | ) 18 | 19 | //Options holds the options to pass to every function interacting with the registry 20 | type Options struct { 21 | //RegistryURL is the URL to call to reach the registry. 22 | RegistryURL string 23 | } 24 | 25 | //ListRegistryResponse is the representation of the XML structure returned by the registry. 26 | type ListRegistryResponse struct { 27 | Name string 28 | Contents []struct { 29 | Key string `xml:"Key"` 30 | } `xml:"Contents"` 31 | } 32 | 33 | //GetLastVersion calls the registry to find the latest version published to the API. 34 | //The version found can be different to the version that has been published to the API in case of support of ancient versions. 35 | //For example, if a package is currently at version 3.3.0, but the maintainer last pushed version 2.7.3, a patch for version 2.7. 36 | //The version returned by GetLastVersion will be 3.3.0, because it is the highest SemVer version number. 37 | func GetLastVersion(dependency string, options Options) (*semver.Version, error) { 38 | client := http.Client{} 39 | req, err := http.NewRequest("GET", options.RegistryURL, nil) 40 | if err != nil { 41 | return nil, fmt.Errorf("error creating version query: %w", err) 42 | } 43 | q := req.URL.Query() 44 | q.Add("typeList", "2") 45 | q.Add("prefix", dependency) 46 | req.URL.RawQuery = q.Encode() 47 | resp, err := client.Do(req) 48 | if err != nil { 49 | return nil, fmt.Errorf("error fetching versions: %w", err) 50 | } 51 | defer resp.Body.Close() 52 | var registryResponse ListRegistryResponse 53 | body, err := ioutil.ReadAll(resp.Body) 54 | if err != nil { 55 | return nil, fmt.Errorf("error reading body: %w", err) 56 | } 57 | err = xml.Unmarshal(body, ®istryResponse) 58 | if err != nil { 59 | return nil, fmt.Errorf("error unmarshaling versions: %w", err) 60 | } 61 | if len(registryResponse.Contents) == 0 { 62 | fmt.Printf("%s: package not found\n", dependency) 63 | os.Exit(1) 64 | } 65 | vs := make([]*semver.Version, len(registryResponse.Contents)) 66 | for i, r := range registryResponse.Contents { 67 | version := filepath.Base(r.Key) 68 | v, err := semver.NewVersion(version) 69 | if err != nil { 70 | return nil, fmt.Errorf("error parsing version %s: %w", r, err) 71 | } 72 | vs[i] = v 73 | } 74 | sort.Sort(semver.Collection(vs)) 75 | return vs[len(vs)-1], nil 76 | } 77 | 78 | func SendMetric(dependency string) error { 79 | client := api.New(&http.Client{}, "") 80 | return client.CountDownload(dependency) 81 | } 82 | 83 | //FetchPackage downloads a package given it's name and version number. 84 | func FetchPackage(dependency string, version string, options Options) (*os.File, error) { 85 | client := http.Client{} 86 | resp, err := client.Get(fmt.Sprintf("%s/%s/%s", options.RegistryURL, dependency, version)) 87 | if err != nil { 88 | return nil, fmt.Errorf("error fetching package %s: %w", dependency, err) 89 | } 90 | defer resp.Body.Close() 91 | file, err := ioutil.TempFile("", fmt.Sprintf("%s.%s.*.tar", dependency, version)) 92 | if err != nil { 93 | return nil, fmt.Errorf("error creating temporary package file: %w", err) 94 | } 95 | bar := progressbar.DefaultBytes( 96 | resp.ContentLength, 97 | "Downloading "+fmt.Sprintf("%s:%s", dependency, version), 98 | ) 99 | _, err = io.Copy(io.MultiWriter(file, bar), resp.Body) 100 | if err != nil { 101 | return nil, fmt.Errorf("error downloading package: %w", err) 102 | } 103 | _, err = file.Seek(0, 0) 104 | if err != nil { 105 | return nil, fmt.Errorf("error returning to file beginning: %w", err) 106 | } 107 | _ = SendMetric(dependency) 108 | return file, nil 109 | } 110 | -------------------------------------------------------------------------------- /registry/registry_suite_test.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "os" 5 | 6 | "path/filepath" 7 | "testing" 8 | 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | func TestCMakeGen(t *testing.T) { 14 | RegisterFailHandler(Fail) 15 | path, err := filepath.Abs("testsArtifacts") 16 | if err != nil { 17 | t.Fatal("Failed to get testsArtifacts absolute path") 18 | } 19 | RunSpecs(t, "CMakeGenSuite Suite") 20 | err = os.RemoveAll(path) 21 | if err != nil { 22 | t.Fatal("Failed to clean test artifacts\n") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /registry/registry_test.go: -------------------------------------------------------------------------------- 1 | package registry_test 2 | 3 | import ( 4 | "encoding/xml" 5 | "github.com/Masterminds/semver/v3" 6 | "github.com/c3pm-labs/c3pm/registry" 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | "github.com/onsi/gomega/ghttp" 10 | ) 11 | 12 | var _ = Describe("Add", func() { 13 | Describe("Registry", func() { 14 | var server *ghttp.Server 15 | var options registry.Options 16 | 17 | BeforeEach(func() { 18 | server = ghttp.NewServer() 19 | options.RegistryURL = server.URL() 20 | versions := registry.ListRegistryResponse{ 21 | Name: "versions", 22 | Contents: []struct { 23 | Key string `xml:"Key"` 24 | }{{Key: "0.0.1"}, {Key: "1.0.0"}}, 25 | } 26 | data, err := xml.Marshal(versions) 27 | Ω(err).ShouldNot(HaveOccurred()) 28 | server.AppendHandlers( 29 | ghttp.CombineHandlers( 30 | ghttp.VerifyRequest("GET", "/", "prefix=boost&typeList=2"), 31 | ghttp.RespondWith(200, data), 32 | ), 33 | ) 34 | }) 35 | It("fetches the right version", func() { 36 | version, err := registry.GetLastVersion("boost", options) 37 | Ω(err).ShouldNot(HaveOccurred()) 38 | Ω(server.ReceivedRequests()).Should(HaveLen(1)) 39 | Ω(version).Should(Equal(semver.MustParse("1.0.0"))) 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /test_helpers/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "Hello World!"; 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test_helpers/projects/cmakeProject/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(test1) 4 | 5 | add_executable(test1 main.cpp) -------------------------------------------------------------------------------- /test_helpers/projects/cmakeProject/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "Hello World!"; 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test_helpers/projects/genProject/lib/hello.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | std::string hello() { 4 | return "Hello World!"; 5 | } -------------------------------------------------------------------------------- /test_helpers/projects/genProject/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | std::string hello(); 5 | 6 | int main() { 7 | std::cout << hello(); 8 | return 0; 9 | } -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/basic/README.md: -------------------------------------------------------------------------------- 1 | readme.md 2 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/basic/c3pm.yml: -------------------------------------------------------------------------------- 1 | c3pm_version: v1 2 | type: executable 3 | name: includeAndExcludeFile 4 | description: includeAndExcludeFile for publish tests 5 | version: 1.0.0 6 | standard: "20" 7 | license: UNLICENSED 8 | files: 9 | sources: 10 | - '**/*.cpp' 11 | includes: 12 | - '**/*.hpp' 13 | include_dirs: [] 14 | exported_dir: "" 15 | exported_include_dirs: [] 16 | dependencies: {} 17 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/basic/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "Hello c3pm!" << std::endl; 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/basic/toto.txt: -------------------------------------------------------------------------------- 1 | this is a super file 2 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/excludeManifest/README.md: -------------------------------------------------------------------------------- 1 | readme.md 2 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/excludeManifest/c3pm.yml: -------------------------------------------------------------------------------- 1 | c3pm_version: v1 2 | type: executable 3 | name: includeAndExcludeFile 4 | description: includeAndExcludeFile for publish tests 5 | version: 1.0.0 6 | standard: "20" 7 | license: UNLICENSED 8 | files: 9 | sources: 10 | - '**/*.cpp' 11 | includes: 12 | - '**/*.hpp' 13 | include_dirs: [] 14 | exported_dir: "" 15 | exported_include_dirs: [] 16 | publish: 17 | exclude: 18 | - 'c3pm.yml' 19 | dependencies: {} 20 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/excludeManifest/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "Hello c3pm!" << std::endl; 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/excludeManifest/toto.txt: -------------------------------------------------------------------------------- 1 | this is a super file 2 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/includeAndExcludeFile/README.md: -------------------------------------------------------------------------------- 1 | readme.md 2 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/includeAndExcludeFile/c3pm.yml: -------------------------------------------------------------------------------- 1 | c3pm_version: v1 2 | type: executable 3 | name: includeAndExcludeFile 4 | description: includeAndExcludeFile for publish tests 5 | version: 1.0.0 6 | standard: "20" 7 | license: UNLICENSED 8 | publish: 9 | exclude: 10 | - 'README.md' 11 | include: 12 | - 'README.md' 13 | - 'toto.txt' 14 | dependencies: {} 15 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/includeAndExcludeFile/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "Hello c3pm!" << std::endl; 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/includeAndExcludeFile/toto.txt: -------------------------------------------------------------------------------- 1 | this is a super file 2 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/includeExclude/README.md: -------------------------------------------------------------------------------- 1 | readme.md 2 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/includeExclude/c3pm.yml: -------------------------------------------------------------------------------- 1 | c3pm_version: v1 2 | type: executable 3 | name: includeAndExcludeFile 4 | description: includeAndExcludeFile for publish tests 5 | version: 1.0.0 6 | standard: "20" 7 | license: UNLICENSED 8 | files: 9 | sources: 10 | - '**/*.cpp' 11 | includes: 12 | - '**/*.hpp' 13 | include_dirs: [] 14 | exported_dir: "" 15 | exported_include_dirs: [] 16 | publish: 17 | include: 18 | - '**/*.cpp' 19 | - '**/*.hpp' 20 | - 'toto.txt' 21 | exclude: 22 | - 'README.md' 23 | dependencies: {} 24 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/includeExclude/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "Hello c3pm!" << std::endl; 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/includeExclude/tmp.tar: -------------------------------------------------------------------------------- 1 | --9bb064c66f5b7cffdbfff061b40154f1d00e97a410e6d2b9bfb8e71e888f 2 | Content-Disposition: form-data; name="file"; filename="package.tar" 3 | Content-Type: application/octet-stream 4 | 5 | README.md0000600000000000000000000000001200000000000010445 0ustar0000000000000000readme.md 6 | c3pm.yml0000600000000000000000000000057000000000000010564 0ustar0000000000000000c3pm_version: v1 7 | type: executable 8 | name: publishProject 9 | description: publishProject for publish tests 10 | version: 1.0.0 11 | standard: "20" 12 | license: UNLICENSED 13 | files: 14 | sources: 15 | - '**/*.cpp' 16 | includes: 17 | - '**/*.hpp' 18 | include_dirs: [] 19 | exported_dir: "" 20 | exported_include_dirs: [] 21 | include: 22 | - '**/*.cpp' 23 | - '**/*.hpp' 24 | - 'toto.txt' 25 | - 'README.md' 26 | exclude: [] 27 | dependencies: {} 28 | src/main.cpp0000600000000000000000000000013000000000000011406 0ustar0000000000000000#include 29 | 30 | int main() { 31 | std::cout << "Hello c3pm!" << std::endl; 32 | return 0; 33 | }toto.txt0000600000000000000000000000002500000000000010720 0ustar0000000000000000this is a super file 34 | 35 | --9bb064c66f5b7cffdbfff061b40154f1d00e97a410e6d2b9bfb8e71e888f-- 36 | -------------------------------------------------------------------------------- /test_helpers/projects/publishProjects/includeExclude/toto.txt: -------------------------------------------------------------------------------- 1 | this is a super file 2 | -------------------------------------------------------------------------------- /test_helpers/tars/single.tar: -------------------------------------------------------------------------------- 1 | main.cpp0000664000175000017500000000012213765747765012156 0ustar oursinoursin#include 2 | 3 | int main() { 4 | std::cout << "Hello World!"; 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test_helpers/yamls/c3pm.yml: -------------------------------------------------------------------------------- 1 | c3pm_version: v1 2 | type: executable 3 | name: hello-bin 4 | description: Demo binary 5 | version: 1.1.5 6 | documentation: "" 7 | website: "" 8 | repository: "" 9 | contributors: "" 10 | standard: "20" 11 | license: ISC 12 | publish: 13 | include: 14 | - test-include 15 | exclude: 16 | - test-exclude 17 | build: 18 | adapter: 19 | name: c3pm 20 | version: 0.0.1 21 | dependencies: 22 | hello: 1.0.5 23 | --------------------------------------------------------------------------------