From e09d4888a07c567244c0405a034e342265629ffa Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Sun, 26 Aug 2018 17:17:47 -0700 Subject: [PATCH 01/27] MAJOR change. Adding ability to customize package manager. --- example.capsule.yml | 28 ++++-- pkg/config/config.go | 2 +- pkg/engine/engine_chef.go | 124 +++--------------------- pkg/engine/engine_chef_test.go | 2 +- pkg/engine/engine_generic.go | 27 +++--- pkg/engine/engine_golang.go | 45 +++------ pkg/engine/engine_golang_test.go | 2 +- pkg/engine/engine_node.go | 69 +++----------- pkg/engine/engine_node_test.go | 2 +- pkg/engine/engine_python.go | 82 +++------------- pkg/engine/engine_ruby.go | 91 +++--------------- pkg/engine/engine_ruby_test.go | 2 +- pkg/engine/factory.go | 43 +++------ pkg/engine/interface.go | 26 +----- pkg/engine/mock/mock_engine.go | 100 ++++++++++---------- pkg/errors/errors.go | 19 ++-- pkg/errors/errors_test.go | 4 +- pkg/metadata/chef_metadata.go | 6 ++ pkg/metadata/generic_metadata.go | 5 + pkg/metadata/golang_metadata.go | 5 + pkg/metadata/node_metadata.go | 6 ++ pkg/metadata/python_metadata.go | 5 + pkg/metadata/ruby_metadata.go | 6 ++ pkg/mgr/factory.go | 108 +++++++++++++++++++++ pkg/mgr/interface.go | 54 +++++++++++ pkg/mgr/mgr_chef_berkshelf.go | 156 +++++++++++++++++++++++++++++++ pkg/mgr/mgr_generic.go | 52 +++++++++++ pkg/mgr/mgr_golang_dep.go | 75 +++++++++++++++ pkg/mgr/mgr_golang_glide.go | 74 +++++++++++++++ pkg/mgr/mgr_node_npm.go | 104 +++++++++++++++++++++ pkg/mgr/mgr_node_yarn.go | 102 ++++++++++++++++++++ pkg/mgr/mgr_python_pip.go | 116 +++++++++++++++++++++++ pkg/mgr/mgr_ruby_bundler.go | 122 ++++++++++++++++++++++++ pkg/mgr/mock/mock_mgr.go | 108 +++++++++++++++++++++ pkg/pipeline.go | 86 ++++++++++++----- pkg/scm/factory.go | 18 ++-- 36 files changed, 1360 insertions(+), 516 deletions(-) create mode 100644 pkg/metadata/chef_metadata.go create mode 100644 pkg/metadata/generic_metadata.go create mode 100644 pkg/metadata/golang_metadata.go create mode 100644 pkg/metadata/node_metadata.go create mode 100644 pkg/metadata/python_metadata.go create mode 100644 pkg/metadata/ruby_metadata.go create mode 100644 pkg/mgr/factory.go create mode 100644 pkg/mgr/interface.go create mode 100644 pkg/mgr/mgr_chef_berkshelf.go create mode 100644 pkg/mgr/mgr_generic.go create mode 100644 pkg/mgr/mgr_golang_dep.go create mode 100644 pkg/mgr/mgr_golang_glide.go create mode 100644 pkg/mgr/mgr_node_npm.go create mode 100644 pkg/mgr/mgr_node_yarn.go create mode 100644 pkg/mgr/mgr_python_pip.go create mode 100644 pkg/mgr/mgr_ruby_bundler.go create mode 100644 pkg/mgr/mock/mock_mgr.go diff --git a/example.capsule.yml b/example.capsule.yml index 2a5b55c..df50dd2 100644 --- a/example.capsule.yml +++ b/example.capsule.yml @@ -69,17 +69,12 @@ engine_enable_code_mutation: false engine_cmd_security_check: '' engine_disable_security_check: false -# Lock files are sometimes generated by Dependency managers (Gemfile.lock, package-lock.json, etc). -# While applications may want to commit this lock file, libraries most often do not. You can override -# the default below. -engine_package_keep_lock_file: false - # Specifies if you would like to leave the checkout directory intact after CapsuleCD completes. # By default CapsuleCD will delete the temporary checkout directory. Useful for debugging. engine_disable_cleanup: false # The following options let you disable publish and dist steps. Useful for debugging. -engine_disable_dist: false +mgr_disable_dist: false scm_disable_publish: false scm_disable_cleanup: false @@ -115,20 +110,35 @@ compile_step: # - scm_retrieve_payload_step # - scm_checkout_pull_request_step or scm_checkout_push_payload_step # - assemble_step -# - dependencies_step +# - mgr_dependencies_step # - compile_step # - test_step # - package_step -# - dist_step +# - mgr_dist_step # - scm_publish_step # - scm_cleanup_step ############################################################################### # -# Dist Configuration +# Package Manager Configuration # ############################################################################### +# Specify the package manager you would like to use. +# Certain languages have multiple popular package managers, ie. Npm and Yarn for Node, Glide & Dep for Golang, etc. +# If you do not specify a type, CapsuleCD will attempt to detect the package manager to use by looking at the repo filesystem, +# or default to a specific package manager if it cannot detect one to use. +mgr_type: '' + +# The following options let you disable dist step. Useful for debugging. +mgr_disable_dist: false + +# Lock files are sometimes generated by Dependency managers (Gemfile.lock, package-lock.json, etc). +# While applications may want to commit this lock file, libraries most often do not. You can override +# the default below. +mgr_keep_lock_file: false + + # Specifies the Chef Supermarket credentials to use when creating public release for Chef cookbook # found in ~/.chef/knife.rb and ~/.chef/.pem # `chef_supermarket_key` should be the Base64 encoded content of the .pem diff --git a/pkg/config/config.go b/pkg/config/config.go index 85bb692..d66cb8a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -34,7 +34,7 @@ func (c *configuration) Init() error { c.SetDefault("scm", "default") c.SetDefault("runner", "default") c.SetDefault("engine_version_bump_type", "patch") - c.SetDefault("engine_package_keep_lock_file", "false") //delete *.lock files by default. + c.SetDefault("mgr_keep_lock_file", "false") //delete *.lock files by default. //set the default system config file search path. //if you want to load a non-standard location system config file (~/capsule.yml), use ReadConfig diff --git a/pkg/engine/engine_chef.go b/pkg/engine/engine_chef.go index 23bf6fc..ec38f0e 100644 --- a/pkg/engine/engine_chef.go +++ b/pkg/engine/engine_chef.go @@ -13,25 +13,23 @@ import ( "os" "os/exec" "path" + "capsulecd/pkg/metadata" ) -type chefMetadata struct { - Version string `json:"version"` - Name string `json:"name"` -} + type engineChef struct { engineBase Scm scm.Interface //Interface - CurrentMetadata *chefMetadata - NextMetadata *chefMetadata + CurrentMetadata *metadata.ChefMetadata + NextMetadata *metadata.ChefMetadata } func (g *engineChef) Init(pipelineData *pipeline.Data, configData config.Interface, sourceScm scm.Interface) error { g.Config = configData g.Scm = sourceScm g.PipelineData = pipelineData - g.CurrentMetadata = new(chefMetadata) - g.NextMetadata = new(chefMetadata) + g.CurrentMetadata = new(metadata.ChefMetadata) + g.NextMetadata = new(metadata.ChefMetadata) //set command defaults (can be overridden by repo/system configuration) g.Config.SetDefault("chef_supermarket_type", "Other") @@ -44,21 +42,14 @@ func (g *engineChef) Init(pipelineData *pipeline.Data, configData config.Interfa return nil } -func (g *engineChef) ValidateTools() error { - //a chefdk like environment needs to be available for this Engine - if _, kerr := exec.LookPath("knife"); kerr != nil { - return errors.EngineValidateToolError("knife binary is missing") - } - - if _, berr := exec.LookPath("berks"); berr != nil { - return errors.EngineValidateToolError("berkshelf binary is missing") - } - - //TODO: figure out how to validate that "bundle audit" command exists. - if _, berr := exec.LookPath("bundle"); berr != nil { - return errors.EngineValidateToolError("bundler binary is missing") - } +func (g *engineChef) GetCurrentMetadata() interface{} { + return g.CurrentMetadata +} +func (g *engineChef) GetNextMetadata() interface{} { + return g.NextMetadata +} +func (g *engineChef) ValidateTools() error { if _, berr := exec.LookPath("foodcritic"); berr != nil && !g.Config.GetBool("engine_disable_lint") { return errors.EngineValidateToolError("foodcritic binary is missing") } @@ -93,17 +84,7 @@ func (g *engineChef) AssembleStep() error { if !utils.FileExists(rakefilePath) { ioutil.WriteFile(rakefilePath, []byte("task :test"), 0644) } - berksfilePath := path.Join(g.PipelineData.GitLocalPath, "Berksfile") - if !utils.FileExists(berksfilePath) { - ioutil.WriteFile(berksfilePath, []byte(utils.StripIndent( - `source "https://supermarket.chef.io" - metadata - `)), 0644) - } - gemfilePath := path.Join(g.PipelineData.GitLocalPath, "Gemfile") - if !utils.FileExists(gemfilePath) { - ioutil.WriteFile(gemfilePath, []byte(`source "https://rubygems.org"`), 0644) - } + specPath := path.Join(g.PipelineData.GitLocalPath, "spec") if !utils.FileExists(specPath) { os.MkdirAll(specPath, 0777) @@ -120,18 +101,6 @@ func (g *engineChef) AssembleStep() error { return nil } -func (g *engineChef) DependenciesStep() error { - // the cookbook has already been downloaded. lets make sure all its dependencies are available. - if cerr := utils.BashCmdExec("berks install", g.PipelineData.GitLocalPath, nil, ""); cerr != nil { - return errors.EngineTestDependenciesError("berks install failed. Check cookbook dependencies") - } - - //download all its gem dependencies - if berr := utils.BashCmdExec("bundle install", g.PipelineData.GitLocalPath, nil, ""); berr != nil { - return errors.EngineTestDependenciesError("bundle install failed. Check Gem dependencies") - } - return nil -} // Use default compile step.. // func (g *engineChef) CompileStep() error {} @@ -140,10 +109,6 @@ func (g *engineChef) DependenciesStep() error { // func (g *engineChef) TestStep() error {} func (g *engineChef) PackageStep() error { - if !g.Config.GetBool("engine_package_keep_lock_file") { - os.Remove(path.Join(g.PipelineData.GitLocalPath, "Berksfile.lock")) - os.Remove(path.Join(g.PipelineData.GitLocalPath, "Gemfile.lock")) - } if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil { return cerr @@ -158,67 +123,6 @@ func (g *engineChef) PackageStep() error { return nil } -func (g *engineChef) DistStep() error { - if !g.Config.IsSet("chef_supermarket_username") || !g.Config.IsSet("chef_supermarket_key") { - return errors.EngineDistCredentialsMissing("Cannot deploy cookbook to supermarket, credentials missing") - } - - // knife is really sensitive to folder names. The cookbook name MUST match the folder name otherwise knife throws up - // when doing a knife cookbook share. So we're going to make a new tmp directory, create a subdirectory with the EXACT - // cookbook name, and then copy the cookbook contents into it. Yeah yeah, its pretty nasty, but blame Chef. - tmpParentPath, terr := ioutil.TempDir("", "") - if terr != nil { - return terr - } - defer os.RemoveAll(tmpParentPath) - - tmpLocalPath := path.Join(tmpParentPath, g.NextMetadata.Name) - if cerr := utils.CopyDir(g.PipelineData.GitLocalPath, tmpLocalPath); cerr != nil { - return cerr - } - - pemFile, _ := ioutil.TempFile("", "client.pem") - defer os.Remove(pemFile.Name()) - knifeFile, _ := ioutil.TempFile("", "knife.rb") - defer os.Remove(knifeFile.Name()) - - // write the knife.rb config jfile. - knifeContent := fmt.Sprintf(utils.StripIndent( - `node_name "%s" # Replace with the login name you use to login to the Supermarket. - client_key "%s" # Define the path to wherever your client.pem file lives. This is the key you generated when you signed up for a Chef account. - cookbook_path [ '%s' ] # Directory where the cookbook you're uploading resides. - `), - g.Config.GetString("chef_supermarket_username"), - pemFile.Name(), - tmpParentPath, - ) - - _, kerr := knifeFile.Write([]byte(knifeContent)) - if kerr != nil { - return kerr - } - - chefKey, berr := g.Config.GetBase64Decoded("chef_supermarket_key") - if berr != nil { - return berr - } - _, perr := pemFile.Write([]byte(chefKey)) - if perr != nil { - return perr - } - - cookbookDistCmd := fmt.Sprintf("knife cookbook site share %s %s -c %s", - g.NextMetadata.Name, - g.Config.GetString("chef_supermarket_type"), - knifeFile.Name(), - ) - - if derr := utils.BashCmdExec(cookbookDistCmd, "", nil, ""); derr != nil { - return errors.EngineDistPackageError("knife cookbook upload to supermarket failed") - } - return nil -} - //private Helpers func (g *engineChef) retrieveCurrentMetadata(gitLocalPath string) error { diff --git a/pkg/engine/engine_chef_test.go b/pkg/engine/engine_chef_test.go index a840b5a..5a946a9 100644 --- a/pkg/engine/engine_chef_test.go +++ b/pkg/engine/engine_chef_test.go @@ -296,7 +296,7 @@ func (suite *EngineChefTestSuite) TestEngineChef_TestStep_SecurityCheckFailure() func (suite *EngineChefTestSuite) TestEngineChef_PackageStep_WithoutLockFiles() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().GetBool("engine_package_keep_lock_file").MinTimes(1).Return(false) + suite.Config.EXPECT().GetBool("mgr_keep_lock_file").MinTimes(1).Return(false) //copy cookbook fixture into a temp directory. parentPath, err := ioutil.TempDir("", "") diff --git a/pkg/engine/engine_generic.go b/pkg/engine/engine_generic.go index 8ed488e..e77897b 100644 --- a/pkg/engine/engine_generic.go +++ b/pkg/engine/engine_generic.go @@ -11,25 +11,24 @@ import ( "io/ioutil" "path" "strings" + "capsulecd/pkg/metadata" ) -type genericMetadata struct { - Version string -} + type engineGeneric struct { engineBase Scm scm.Interface //Interface - CurrentMetadata *genericMetadata - NextMetadata *genericMetadata + CurrentMetadata *metadata.GenericMetadata + NextMetadata *metadata.GenericMetadata } func (g *engineGeneric) Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error { g.Scm = sourceScm g.Config = config g.PipelineData = pipelineData - g.CurrentMetadata = new(genericMetadata) - g.NextMetadata = new(genericMetadata) + g.CurrentMetadata = new(metadata.GenericMetadata) + g.NextMetadata = new(metadata.GenericMetadata) //set command defaults (can be overridden by repo/system configuration) g.Config.SetDefault("engine_generic_version_template", `version := "%d.%d.%d"`) @@ -42,6 +41,13 @@ func (g *engineGeneric) Init(pipelineData *pipeline.Data, config config.Interfac return nil } +func (g *engineGeneric) GetCurrentMetadata() interface{} { + return g.CurrentMetadata +} +func (g *engineGeneric) GetNextMetadata() interface{} { + return g.NextMetadata +} + func (g *engineGeneric) ValidateTools() error { return nil } @@ -69,10 +75,6 @@ func (g *engineGeneric) AssembleStep() error { return nil } -func (g *engineGeneric) DependenciesStep() error { - return nil -} - func (g *engineGeneric) PackageStep() error { if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil { @@ -88,9 +90,6 @@ func (g *engineGeneric) PackageStep() error { return nil } -func (g *engineGeneric) DistStep() error { - return nil -} //Helpers func (g *engineGeneric) retrieveCurrentMetadata(gitLocalPath string) error { diff --git a/pkg/engine/engine_golang.go b/pkg/engine/engine_golang.go index f0cb4ab..aba8c5d 100644 --- a/pkg/engine/engine_golang.go +++ b/pkg/engine/engine_golang.go @@ -17,17 +17,15 @@ import ( "os/exec" "path" "strings" + "capsulecd/pkg/metadata" ) -type golangMetadata struct { - Version string -} type engineGolang struct { engineBase Scm scm.Interface //Interface - CurrentMetadata *golangMetadata - NextMetadata *golangMetadata + CurrentMetadata *metadata.GolangMetadata + NextMetadata *metadata.GolangMetadata GoPath string } @@ -35,8 +33,8 @@ func (g *engineGolang) Init(pipelineData *pipeline.Data, config config.Interface g.Scm = sourceScm g.Config = config g.PipelineData = pipelineData - g.CurrentMetadata = new(golangMetadata) - g.NextMetadata = new(golangMetadata) + g.CurrentMetadata = new(metadata.GolangMetadata) + g.NextMetadata = new(metadata.GolangMetadata) //TODO: figure out why setting the GOPATH workspace is causing the tools to timeout. // golang recommends that your in-development packages are in the GOPATH and glide requires it to do glide install. @@ -59,6 +57,13 @@ func (g *engineGolang) Init(pipelineData *pipeline.Data, config config.Interface return nil } +func (g *engineGolang) GetCurrentMetadata() interface{} { + return g.CurrentMetadata +} +func (g *engineGolang) GetNextMetadata() interface{} { + return g.NextMetadata +} + func (g *engineGolang) ValidateTools() error { if _, kerr := exec.LookPath("go"); kerr != nil { return errors.EngineValidateToolError("go binary is missing") @@ -78,11 +83,6 @@ func (g *engineGolang) AssembleStep() error { return errors.EngineBuildPackageInvalid("pkg/version/version.go file is required to process Go library") } - //we only support glide as a Go dependency manager right now. Should be easy to add additional ones though. - if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "glide.yaml")) { - return errors.EngineBuildPackageInvalid("glide.yml file is required to process Go library") - } - // bump up the go package version if merr := g.retrieveCurrentMetadata(g.PipelineData.GitLocalPath); merr != nil { return merr @@ -106,16 +106,6 @@ func (g *engineGolang) AssembleStep() error { return nil } -func (g *engineGolang) DependenciesStep() error { - // the library has already been downloaded. lets make sure all its dependencies are available. - //glide will complain if the checkout directory isnt in the GOPATH, so we'll override it. - if cerr := utils.BashCmdExec("glide install", g.PipelineData.GitLocalPath, g.customGopathEnv(), ""); cerr != nil { - return errors.EngineTestDependenciesError("glide install failed. Check dependencies") - } - - return nil -} - func (g *engineGolang) CompileStep() error { //cmd directory is optional. check if it exists first. if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "cmd")) { @@ -194,10 +184,6 @@ func (g *engineGolang) TestStep() error { } func (g *engineGolang) PackageStep() error { - if !g.Config.GetBool("engine_package_keep_lock_file") { - os.Remove(path.Join(g.PipelineData.GitLocalPath, "glide.lock")) - } - if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil { return cerr } @@ -211,13 +197,6 @@ func (g *engineGolang) PackageStep() error { return nil } -func (g *engineGolang) DistStep() error { - - // no real packaging for golang. - // libraries are stored in version control. - return nil -} - //private Helpers func (g *engineGolang) customGopathEnv() []string { diff --git a/pkg/engine/engine_golang_test.go b/pkg/engine/engine_golang_test.go index ec6c008..dcb92f0 100644 --- a/pkg/engine/engine_golang_test.go +++ b/pkg/engine/engine_golang_test.go @@ -289,7 +289,7 @@ func (suite *EngineGolangTestSuite) TestEngineGolang_TestStep_SecurityCheckFailu func (suite *EngineGolangTestSuite) TestEngineGolang_PackageStep_WithoutLockFiles() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().GetBool("engine_package_keep_lock_file").MinTimes(1).Return(false) + suite.Config.EXPECT().GetBool("mgr_keep_lock_file").MinTimes(1).Return(false) //copy cookbook fixture into a temp directory. parentPath, err := ioutil.TempDir("", "") diff --git a/pkg/engine/engine_node.go b/pkg/engine/engine_node.go index 5473d44..24179f1 100644 --- a/pkg/engine/engine_node.go +++ b/pkg/engine/engine_node.go @@ -12,26 +12,23 @@ import ( "os" "os/exec" "path" + "capsulecd/pkg/metadata" ) -type nodeMetadata struct { - Version string `json:"version"` - Name string `json:"name"` -} type engineNode struct { engineBase Scm scm.Interface //Interface - CurrentMetadata *nodeMetadata - NextMetadata *nodeMetadata + CurrentMetadata *metadata.NodeMetadata + NextMetadata *metadata.NodeMetadata } func (g *engineNode) Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error { g.Scm = sourceScm g.Config = config g.PipelineData = pipelineData - g.CurrentMetadata = new(nodeMetadata) - g.NextMetadata = new(nodeMetadata) + g.CurrentMetadata = new(metadata.NodeMetadata) + g.NextMetadata = new(metadata.NodeMetadata) //set command defaults (can be overridden by repo/system configuration) g.Config.SetDefault("engine_cmd_compile", "echo 'skipping compile'") @@ -42,10 +39,14 @@ func (g *engineNode) Init(pipelineData *pipeline.Data, config config.Interface, return nil } +func (g *engineNode) GetCurrentMetadata() interface{} { + return g.CurrentMetadata +} +func (g *engineNode) GetNextMetadata() interface{} { + return g.NextMetadata +} + func (g *engineNode) ValidateTools() error { - if _, kerr := exec.LookPath("npm"); kerr != nil { - return errors.EngineValidateToolError("npm binary is missing") - } if _, kerr := exec.LookPath("node"); kerr != nil { return errors.EngineValidateToolError("node binary is missing") @@ -63,10 +64,6 @@ func (g *engineNode) ValidateTools() error { } func (g *engineNode) AssembleStep() error { - //validate that the npm package.json file exists - if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "package.json")) { - return errors.EngineBuildPackageInvalid("package.json file is required to process Node package") - } // bump up the package version if merr := g.retrieveCurrentMetadata(g.PipelineData.GitLocalPath); merr != nil { @@ -95,19 +92,6 @@ func (g *engineNode) AssembleStep() error { return nil } -func (g *engineNode) DependenciesStep() error { - // the module has already been downloaded. lets make sure all its dependencies are available. - if derr := utils.BashCmdExec("npm install", g.PipelineData.GitLocalPath, nil, ""); derr != nil { - return errors.EngineTestRunnerError("npm install failed. Check module dependencies") - } - - // create a shrinkwrap file. - if derr := utils.BashCmdExec("npm shrinkwrap", g.PipelineData.GitLocalPath, nil, ""); derr != nil { - return errors.EngineTestRunnerError("npm shrinkwrap failed. Check log for exact error") - } - return nil -} - // use default Compile step //func (g *engineNode) CompileStep() error { } @@ -115,9 +99,6 @@ func (g *engineNode) DependenciesStep() error { //func (g *engineNode) TestStep() error { } func (g *engineNode) PackageStep() error { - if !g.Config.GetBool("engine_package_keep_lock_file") { - os.Remove(path.Join(g.PipelineData.GitLocalPath, "npm-shrinkwrap.json")) - } if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil { return cerr @@ -133,32 +114,6 @@ func (g *engineNode) PackageStep() error { return nil } -func (g *engineNode) DistStep() error { - if !g.Config.IsSet("npm_auth_token") { - return errors.EngineDistCredentialsMissing("cannot deploy page to npm, credentials missing") - } - - npmrcFile, _ := ioutil.TempFile("", ".npmrc") - defer os.Remove(npmrcFile.Name()) - - // write the .npmrc config jfile. - npmrcContent := fmt.Sprintf( - "//registry.npmjs.org/:_authToken=%s", - g.Config.GetString("npm_auth_token"), - ) - - if _, werr := npmrcFile.Write([]byte(npmrcContent)); werr != nil { - return werr - } - - npmPublishCmd := fmt.Sprintf("npm --userconfig %s publish .", npmrcFile.Name()) - derr := utils.BashCmdExec(npmPublishCmd, g.PipelineData.GitLocalPath, nil, "") - if derr != nil { - return errors.EngineDistPackageError("npm publish failed. Check log for exact error") - } - return nil -} - //private Helpers func (g *engineNode) retrieveCurrentMetadata(gitLocalPath string) error { diff --git a/pkg/engine/engine_node_test.go b/pkg/engine/engine_node_test.go index bf7741b..dc1d2f8 100644 --- a/pkg/engine/engine_node_test.go +++ b/pkg/engine/engine_node_test.go @@ -266,7 +266,7 @@ func (suite *EngineNodeTestSuite) TestEngineNode_TestStep_SecurityCheckFailure() func (suite *EngineNodeTestSuite) TestEngineNode_PackageStep_WithoutLockFiles() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().GetBool("engine_package_keep_lock_file").MinTimes(1).Return(false) + suite.Config.EXPECT().GetBool("mgr_keep_lock_file").MinTimes(1).Return(false) //copy cookbook fixture into a temp directory. parentPath, err := ioutil.TempDir("", "") diff --git a/pkg/engine/engine_python.go b/pkg/engine/engine_python.go index d75d901..061178e 100644 --- a/pkg/engine/engine_python.go +++ b/pkg/engine/engine_python.go @@ -12,25 +12,23 @@ import ( "os/exec" "path" "strings" + "capsulecd/pkg/metadata" ) -type pythonMetadata struct { - Version string -} type enginePython struct { engineBase Scm scm.Interface //Interface - CurrentMetadata *pythonMetadata - NextMetadata *pythonMetadata + CurrentMetadata *metadata.PythonMetadata + NextMetadata *metadata.PythonMetadata } func (g *enginePython) Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error { g.Scm = sourceScm g.Config = config g.PipelineData = pipelineData - g.CurrentMetadata = new(pythonMetadata) - g.NextMetadata = new(pythonMetadata) + g.CurrentMetadata = new(metadata.PythonMetadata) + g.NextMetadata = new(metadata.PythonMetadata) //set command defaults (can be overridden by repo/system configuration) g.Config.SetDefault("pypi_repository", "https://pypi.python.org/pypi") @@ -42,6 +40,14 @@ func (g *enginePython) Init(pipelineData *pipeline.Data, config config.Interface return nil } +func (g *enginePython) GetCurrentMetadata() interface{} { + return g.CurrentMetadata +} +func (g *enginePython) GetNextMetadata() interface{} { + return g.NextMetadata +} + + func (g *enginePython) ValidateTools() error { if _, kerr := exec.LookPath("tox"); kerr != nil { return errors.EngineValidateToolError("tox binary is missing") @@ -55,9 +61,6 @@ func (g *enginePython) ValidateTools() error { return errors.EngineValidateToolError("python binary is missing") } - if _, berr := exec.LookPath("twine"); berr != nil { - return errors.EngineValidateToolError("twine binary is missing") - } if _, berr := exec.LookPath("safety"); berr != nil && !g.Config.GetBool("engine_disable_security_check") { return errors.EngineValidateToolError("safety binary is missing") @@ -127,14 +130,6 @@ func (g *enginePython) AssembleStep() error { ) } - // check for/create any required missing folders/files - if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "requirements.txt")) { - ioutil.WriteFile(path.Join(g.PipelineData.GitLocalPath, "requirements.txt"), - []byte(""), - 0644, - ) - } - os.MkdirAll(path.Join(g.PipelineData.GitLocalPath, "tests"), 0644) if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "tests", "__init__.py")) { @@ -153,10 +148,6 @@ func (g *enginePython) AssembleStep() error { return nil } -func (n *enginePython) DependenciesStep() error { - return nil //dependencies are installed as part of Tox. -} - // use default Compile step //func (g *enginePython) CompileStep() error { } @@ -166,10 +157,6 @@ func (n *enginePython) DependenciesStep() error { func (g *enginePython) PackageStep() error { os.RemoveAll(path.Join(g.PipelineData.GitLocalPath, ".tox")) //remove .tox folder. - //if !g.Config.GetBool("engine_package_keep_lock_file") { //TODO figure out if theres a good pattern here. - // os.Remove(path.Join(g.PipelineData.GitLocalPath, "npm-shrinkwrap.json")) - //} - if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil { return cerr } @@ -183,49 +170,6 @@ func (g *enginePython) PackageStep() error { return nil } -func (g *enginePython) DistStep() error { - if !g.Config.IsSet("pypi_username") || !g.Config.IsSet("pypi_password") { - return errors.EngineDistCredentialsMissing("Cannot deploy python package to pypi/warehouse, credentials missing") - } - - pypircFile, _ := ioutil.TempFile("", ".pypirc") - defer os.Remove(pypircFile.Name()) - - // write the .pypirc config jfile. - pypircContent := fmt.Sprintf(utils.StripIndent( - `[distutils] - index-servers=pypi - - [pypi] - repository = %s - username = %s - password = %s - `), - g.Config.GetString("pypi_repository"), - g.Config.GetString("pypi_username"), - g.Config.GetString("pypi_password"), - ) - - if _, perr := pypircFile.Write([]byte(pypircContent)); perr != nil { - return perr - } - - pythonDistCmd := "python setup.py sdist" - if derr := utils.BashCmdExec(pythonDistCmd, g.PipelineData.GitLocalPath, nil, ""); derr != nil { - return errors.EngineDistPackageError("python setup.py sdist failed") - } - - // using twine instead of setup.py (it supports HTTPS.)https://python-packaging-user-guide.readthedocs.org/en/latest/distributing/#uploading-your-project-to-pypi - pypiUploadCmd := fmt.Sprintf("twine upload --config-file %s dist/*", - pypircFile.Name(), - ) - - if uerr := utils.BashCmdExec(pypiUploadCmd, g.PipelineData.GitLocalPath, nil, ""); uerr != nil { - return errors.EngineDistPackageError("twine package upload failed. Check log for exact error") - } - return nil -} - //private Helpers func (g *enginePython) retrieveCurrentMetadata(gitLocalPath string) error { diff --git a/pkg/engine/engine_ruby.go b/pkg/engine/engine_ruby.go index b9f02b5..b3a5c28 100644 --- a/pkg/engine/engine_ruby.go +++ b/pkg/engine/engine_ruby.go @@ -14,12 +14,9 @@ import ( "path" "path/filepath" "regexp" + "capsulecd/pkg/metadata" ) -type rubyMetadata struct { - Name string - Version string -} type rubyGemspec struct { Name string `json:"name"` @@ -32,8 +29,8 @@ type engineRuby struct { engineBase Scm scm.Interface //Interface - CurrentMetadata *rubyMetadata - NextMetadata *rubyMetadata + CurrentMetadata *metadata.RubyMetadata + NextMetadata *metadata.RubyMetadata GemspecPath string } @@ -41,8 +38,8 @@ func (g *engineRuby) Init(pipelineData *pipeline.Data, config config.Interface, g.Scm = sourceScm g.Config = config g.PipelineData = pipelineData - g.CurrentMetadata = new(rubyMetadata) - g.NextMetadata = new(rubyMetadata) + g.CurrentMetadata = new(metadata.RubyMetadata) + g.NextMetadata = new(metadata.RubyMetadata) //set command defaults (can be overridden by repo/system configuration) g.Config.SetDefault("engine_cmd_compile", "echo 'skipping compile'") @@ -53,19 +50,19 @@ func (g *engineRuby) Init(pipelineData *pipeline.Data, config config.Interface, return nil } +func (g *engineRuby) GetCurrentMetadata() interface{} { + return g.CurrentMetadata +} +func (g *engineRuby) GetNextMetadata() interface{} { + return g.NextMetadata +} + + func (g *engineRuby) ValidateTools() error { if _, kerr := exec.LookPath("ruby"); kerr != nil { return errors.EngineValidateToolError("ruby binary is missing") } - if _, berr := exec.LookPath("gem"); berr != nil { - return errors.EngineValidateToolError("gem binary is missing") - } - - if _, berr := exec.LookPath("bundle"); berr != nil { - return errors.EngineValidateToolError("bundle binary is missing") - } - if _, berr := exec.LookPath("rake"); berr != nil { return errors.EngineValidateToolError("rake binary is missing") } @@ -103,15 +100,6 @@ func (g *engineRuby) AssembleStep() error { return nerr } - // check for/create any required missing folders/files - if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "Gemfile")) { - ioutil.WriteFile(path.Join(g.PipelineData.GitLocalPath, "Gemfile"), - []byte(utils.StripIndent(`source 'https://rubygems.org' - gemspec`)), - 0644, - ) - } - if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "Rakefile")) { ioutil.WriteFile(path.Join(g.PipelineData.GitLocalPath, "Rakefile"), []byte("task :default => :spec"), @@ -141,23 +129,6 @@ func (g *engineRuby) AssembleStep() error { return nil } -func (g *engineRuby) DependenciesStep() error { - // lets install the gem, and any dependencies - // http://guides.rubygems.org/make-your-own-gem/ - - gemCmd := fmt.Sprintf("gem install %s --ignore-dependencies", - path.Join(g.PipelineData.GitLocalPath, fmt.Sprintf("%s-%s.gem", g.NextMetadata.Name, g.NextMetadata.Version))) - if terr := utils.BashCmdExec(gemCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil { - return errors.EngineTestDependenciesError("gem install failed. Check gemspec and gem dependencies") - } - - // install dependencies - if terr := utils.BashCmdExec("bundle install", g.PipelineData.GitLocalPath, nil, ""); terr != nil { - return errors.EngineBuildPackageFailed("bundle install failed. Check Gemfile") - } - return nil -} - // use default Compile step //func (g *engineRuby) CompileStep() error { } @@ -165,10 +136,6 @@ func (g *engineRuby) DependenciesStep() error { //func (g *engineRuby) TestStep() error { } func (g *engineRuby) PackageStep() error { - if !g.Config.GetBool("engine_package_keep_lock_file") { - os.Remove(path.Join(g.PipelineData.GitLocalPath, "Gemfile.lock")) - } - if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil { return cerr } @@ -182,38 +149,6 @@ func (g *engineRuby) PackageStep() error { return nil } -func (g *engineRuby) DistStep() error { - if !g.Config.IsSet("rubygems_api_key") { - return errors.EngineDistCredentialsMissing("Cannot deploy package to rubygems, credentials missing") - } - - credFile, _ := ioutil.TempFile("", "gem_credentials") - defer os.Remove(credFile.Name()) - - // write the .gem/credentials config jfile. - - credContent := fmt.Sprintf(utils.StripIndent( - `--- - :rubygems_api_key: %s - `), - g.Config.GetString("rubygems_api_key"), - ) - - if _, perr := credFile.Write([]byte(credContent)); perr != nil { - return perr - } - - pushCmd := fmt.Sprintf("gem push %s --config-file %s", - fmt.Sprintf("%s-%s.gem", g.NextMetadata.Name, g.NextMetadata.Version), - credFile.Name(), - ) - if derr := utils.BashCmdExec(pushCmd, g.PipelineData.GitLocalPath, nil, ""); derr != nil { - return errors.EngineDistPackageError("Pushing gem to RubyGems.org using `gem push` failed. Check log for exact error") - } - - return nil -} - //private Helpers func (g *engineRuby) retrieveCurrentMetadata(gitLocalPath string) error { //read Gemspec file. diff --git a/pkg/engine/engine_ruby_test.go b/pkg/engine/engine_ruby_test.go index d00943c..b19cf85 100644 --- a/pkg/engine/engine_ruby_test.go +++ b/pkg/engine/engine_ruby_test.go @@ -298,7 +298,7 @@ func (suite *EngineRubyTestSuite) TestEngineRuby_TestStep_SecurityCheckFailure() func (suite *EngineRubyTestSuite) TestEngineRuby_PackageStep_WithoutLockFiles() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().GetBool("engine_package_keep_lock_file").MinTimes(1).Return(false) + suite.Config.EXPECT().GetBool("mgr_keep_lock_file").MinTimes(1).Return(false) //copy cookbook fixture into a temp directory. parentPath, err := ioutil.TempDir("", "") diff --git a/pkg/engine/factory.go b/pkg/engine/factory.go index 797583a..018541a 100644 --- a/pkg/engine/factory.go +++ b/pkg/engine/factory.go @@ -10,44 +10,27 @@ import ( func Create(engineType string, pipelineData *pipeline.Data, configImpl config.Interface, sourceImpl scm.Interface) (Interface, error) { + var eng Interface + switch engineType { case "chef": - eng := new(engineChef) - if err := eng.Init(pipelineData, configImpl, sourceImpl); err != nil { - return nil, err - } - return eng, nil + eng = new(engineChef) case "generic": - eng := new(engineGeneric) - if err := eng.Init(pipelineData, configImpl, sourceImpl); err != nil { - return nil, err - } - return eng, nil + eng = new(engineGeneric) case "golang": - eng := new(engineGolang) - if err := eng.Init(pipelineData, configImpl, sourceImpl); err != nil { - return nil, err - } - return eng, nil + eng = new(engineGolang) case "node": - eng := new(engineNode) - if err := eng.Init(pipelineData, configImpl, sourceImpl); err != nil { - return nil, err - } - return eng, nil + eng = new(engineNode) case "python": - eng := new(enginePython) - if err := eng.Init(pipelineData, configImpl, sourceImpl); err != nil { - return nil, err - } - return eng, nil + eng = new(enginePython) case "ruby": - eng := new(engineRuby) - if err := eng.Init(pipelineData, configImpl, sourceImpl); err != nil { - return nil, err - } - return eng, nil + eng = new(engineRuby) default: return nil, errors.EngineUnspecifiedError(fmt.Sprintf("Unknown Engine Type: %s", engineType)) } + + if err := eng.Init(pipelineData, configImpl, sourceImpl); err != nil { + return nil, err + } + return eng, nil } diff --git a/pkg/engine/interface.go b/pkg/engine/interface.go index b00aa8d..4f1a7df 100644 --- a/pkg/engine/interface.go +++ b/pkg/engine/interface.go @@ -11,6 +11,9 @@ import ( type Interface interface { Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error + GetCurrentMetadata() interface{} + GetNextMetadata() interface{} + // Validate that required executables are available for the following build/test/package/etc steps ValidateTools() error @@ -24,14 +27,6 @@ type Interface interface { // REQUIRES pipelineData.GitLocalPath AssembleStep() error - // Validate & download dependencies for this package. - // Generate *.lock files for dependencies (should be deleted in PackageStep if necessary) - // CAN override - // REQUIRES pipelineData.GitLocalPath - // REQUIRES CurrentMetadata - // REQUIRES NextMetadata - DependenciesStep() error - // Compile the source for this package (if required) // CAN override // USES engine_disable_compile @@ -60,20 +55,7 @@ type Interface interface { // MUST set ReleaseVersion // REQUIRES pipelineData.GitLocalPath // REQUIRES NextMetadata - // USES engine_package_keep_lock_file + // USES mgr_keep_lock_file PackageStep() error - // Push the release to the package repository (ie. npm, chef supermarket, rubygems) - // Should validate any required credentials are specified. - // CAN override - // REQUIRES pipelineData.GitLocalPath - // REQUIRES NextMetadata - // USES chef_supermarket_username - // USES chef_supermarket_key - // USES npm_auth_token - // USES pypi_repository - // USES pypi_username - // USES pypi_password - // USES rubygems_api_key - DistStep() error } diff --git a/pkg/engine/mock/mock_engine.go b/pkg/engine/mock/mock_engine.go index 207a151..2610f78 100644 --- a/pkg/engine/mock/mock_engine.go +++ b/pkg/engine/mock/mock_engine.go @@ -1,6 +1,7 @@ // Code generated by MockGen. DO NOT EDIT. // Source: pkg/engine/interface.go +// Package mock_engine is a generated GoMock package. package mock_engine import ( @@ -8,6 +9,7 @@ import ( pipeline "capsulecd/pkg/pipeline" scm "capsulecd/pkg/scm" gomock "github.com/golang/mock/gomock" + reflect "reflect" ) // MockInterface is a mock of Interface interface @@ -29,102 +31,102 @@ func NewMockInterface(ctrl *gomock.Controller) *MockInterface { } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockInterface) EXPECT() *MockInterfaceMockRecorder { - return _m.recorder +func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { + return m.recorder } // Init mocks base method -func (_m *MockInterface) Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error { - ret := _m.ctrl.Call(_m, "Init", pipelineData, config, sourceScm) +func (m *MockInterface) Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error { + ret := m.ctrl.Call(m, "Init", pipelineData, config, sourceScm) ret0, _ := ret[0].(error) return ret0 } // Init indicates an expected call of Init -func (_mr *MockInterfaceMockRecorder) Init(arg0, arg1, arg2 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "Init", arg0, arg1, arg2) +func (mr *MockInterfaceMockRecorder) Init(pipelineData, config, sourceScm interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockInterface)(nil).Init), pipelineData, config, sourceScm) } -// ValidateTools mocks base method -func (_m *MockInterface) ValidateTools() error { - ret := _m.ctrl.Call(_m, "ValidateTools") - ret0, _ := ret[0].(error) +// GetCurrentMetadata mocks base method +func (m *MockInterface) GetCurrentMetadata() interface{} { + ret := m.ctrl.Call(m, "GetCurrentMetadata") + ret0, _ := ret[0].(interface{}) return ret0 } -// ValidateTools indicates an expected call of ValidateTools -func (_mr *MockInterfaceMockRecorder) ValidateTools() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "ValidateTools") +// GetCurrentMetadata indicates an expected call of GetCurrentMetadata +func (mr *MockInterfaceMockRecorder) GetCurrentMetadata() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentMetadata", reflect.TypeOf((*MockInterface)(nil).GetCurrentMetadata)) } -// AssembleStep mocks base method -func (_m *MockInterface) AssembleStep() error { - ret := _m.ctrl.Call(_m, "AssembleStep") +// GetNextMetadata mocks base method +func (m *MockInterface) GetNextMetadata() interface{} { + ret := m.ctrl.Call(m, "GetNextMetadata") + ret0, _ := ret[0].(interface{}) + return ret0 +} + +// GetNextMetadata indicates an expected call of GetNextMetadata +func (mr *MockInterfaceMockRecorder) GetNextMetadata() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNextMetadata", reflect.TypeOf((*MockInterface)(nil).GetNextMetadata)) +} + +// ValidateTools mocks base method +func (m *MockInterface) ValidateTools() error { + ret := m.ctrl.Call(m, "ValidateTools") ret0, _ := ret[0].(error) return ret0 } -// AssembleStep indicates an expected call of AssembleStep -func (_mr *MockInterfaceMockRecorder) AssembleStep() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "AssembleStep") +// ValidateTools indicates an expected call of ValidateTools +func (mr *MockInterfaceMockRecorder) ValidateTools() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateTools", reflect.TypeOf((*MockInterface)(nil).ValidateTools)) } -// DependenciesStep mocks base method -func (_m *MockInterface) DependenciesStep() error { - ret := _m.ctrl.Call(_m, "DependenciesStep") +// AssembleStep mocks base method +func (m *MockInterface) AssembleStep() error { + ret := m.ctrl.Call(m, "AssembleStep") ret0, _ := ret[0].(error) return ret0 } -// DependenciesStep indicates an expected call of DependenciesStep -func (_mr *MockInterfaceMockRecorder) DependenciesStep() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "DependenciesStep") +// AssembleStep indicates an expected call of AssembleStep +func (mr *MockInterfaceMockRecorder) AssembleStep() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssembleStep", reflect.TypeOf((*MockInterface)(nil).AssembleStep)) } // CompileStep mocks base method -func (_m *MockInterface) CompileStep() error { - ret := _m.ctrl.Call(_m, "CompileStep") +func (m *MockInterface) CompileStep() error { + ret := m.ctrl.Call(m, "CompileStep") ret0, _ := ret[0].(error) return ret0 } // CompileStep indicates an expected call of CompileStep -func (_mr *MockInterfaceMockRecorder) CompileStep() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "CompileStep") +func (mr *MockInterfaceMockRecorder) CompileStep() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompileStep", reflect.TypeOf((*MockInterface)(nil).CompileStep)) } // TestStep mocks base method -func (_m *MockInterface) TestStep() error { - ret := _m.ctrl.Call(_m, "TestStep") +func (m *MockInterface) TestStep() error { + ret := m.ctrl.Call(m, "TestStep") ret0, _ := ret[0].(error) return ret0 } // TestStep indicates an expected call of TestStep -func (_mr *MockInterfaceMockRecorder) TestStep() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "TestStep") +func (mr *MockInterfaceMockRecorder) TestStep() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TestStep", reflect.TypeOf((*MockInterface)(nil).TestStep)) } // PackageStep mocks base method -func (_m *MockInterface) PackageStep() error { - ret := _m.ctrl.Call(_m, "PackageStep") +func (m *MockInterface) PackageStep() error { + ret := m.ctrl.Call(m, "PackageStep") ret0, _ := ret[0].(error) return ret0 } // PackageStep indicates an expected call of PackageStep -func (_mr *MockInterfaceMockRecorder) PackageStep() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "PackageStep") -} - -// DistStep mocks base method -func (_m *MockInterface) DistStep() error { - ret := _m.ctrl.Call(_m, "DistStep") - ret0, _ := ret[0].(error) - return ret0 -} - -// DistStep indicates an expected call of DistStep -func (_mr *MockInterfaceMockRecorder) DistStep() *gomock.Call { - return _mr.mock.ctrl.RecordCall(_mr.mock, "DistStep") +func (mr *MockInterfaceMockRecorder) PackageStep() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackageStep", reflect.TypeOf((*MockInterface)(nil).PackageStep)) } diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index bc2e7ac..a879b1f 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -102,16 +102,23 @@ func (str EngineTestRunnerError) Error() string { return fmt.Sprintf("EngineTestRunnerError: %q", string(str)) } +// Raised when package manager asseble step fails. +type MgrAssembleError string + +func (str MgrAssembleError) Error() string { + return fmt.Sprintf("MgrAssembleError: %q", string(str)) +} + // Raised when credentials required to upload/deploy new package are missing. -type EngineDistCredentialsMissing string +type MgrDistCredentialsMissing string -func (str EngineDistCredentialsMissing) Error() string { - return fmt.Sprintf("EngineDistCredentialsMissing: %q", string(str)) +func (str MgrDistCredentialsMissing) Error() string { + return fmt.Sprintf("MgrDistCredentialsMissing: %q", string(str)) } // Raised when an error occurs while uploading package. -type EngineDistPackageError string +type MgrDistPackageError string -func (str EngineDistPackageError) Error() string { - return fmt.Sprintf("EngineDistPackageError: %q", string(str)) +func (str MgrDistPackageError) Error() string { + return fmt.Sprintf("MgrDistPackageError: %q", string(str)) } diff --git a/pkg/errors/errors_test.go b/pkg/errors/errors_test.go index 3d0228b..2d6383b 100644 --- a/pkg/errors/errors_test.go +++ b/pkg/errors/errors_test.go @@ -30,8 +30,8 @@ func TestErrors(t *testing.T) { //assert require.Implements(t, (*error)(nil), errors.EngineBuildPackageFailed("test"), "should implement the error interface") require.Implements(t, (*error)(nil), errors.EngineBuildPackageInvalid("test"), "should implement the error interface") - require.Implements(t, (*error)(nil), errors.EngineDistCredentialsMissing("test"), "should implement the error interface") - require.Implements(t, (*error)(nil), errors.EngineDistPackageError("test"), "should implement the error interface") + require.Implements(t, (*error)(nil), errors.MgrDistCredentialsMissing("test"), "should implement the error interface") + require.Implements(t, (*error)(nil), errors.MgrDistPackageError("test"), "should implement the error interface") require.Implements(t, (*error)(nil), errors.EngineTestDependenciesError("test"), "should implement the error interface") require.Implements(t, (*error)(nil), errors.EngineTestRunnerError("test"), "should implement the error interface") require.Implements(t, (*error)(nil), errors.EngineTransformUnavailableStep("test"), "should implement the error interface") diff --git a/pkg/metadata/chef_metadata.go b/pkg/metadata/chef_metadata.go new file mode 100644 index 0000000..5bb84f6 --- /dev/null +++ b/pkg/metadata/chef_metadata.go @@ -0,0 +1,6 @@ +package metadata + +type ChefMetadata struct { + Version string `json:"version"` + Name string `json:"name"` +} diff --git a/pkg/metadata/generic_metadata.go b/pkg/metadata/generic_metadata.go new file mode 100644 index 0000000..546481c --- /dev/null +++ b/pkg/metadata/generic_metadata.go @@ -0,0 +1,5 @@ +package metadata + +type GenericMetadata struct { + Version string +} \ No newline at end of file diff --git a/pkg/metadata/golang_metadata.go b/pkg/metadata/golang_metadata.go new file mode 100644 index 0000000..7aa6a8e --- /dev/null +++ b/pkg/metadata/golang_metadata.go @@ -0,0 +1,5 @@ +package metadata + +type GolangMetadata struct { + Version string +} \ No newline at end of file diff --git a/pkg/metadata/node_metadata.go b/pkg/metadata/node_metadata.go new file mode 100644 index 0000000..f2b9967 --- /dev/null +++ b/pkg/metadata/node_metadata.go @@ -0,0 +1,6 @@ +package metadata + +type NodeMetadata struct { + Version string `json:"version"` + Name string `json:"name"` +} \ No newline at end of file diff --git a/pkg/metadata/python_metadata.go b/pkg/metadata/python_metadata.go new file mode 100644 index 0000000..8996f74 --- /dev/null +++ b/pkg/metadata/python_metadata.go @@ -0,0 +1,5 @@ +package metadata + +type PythonMetadata struct { + Version string +} \ No newline at end of file diff --git a/pkg/metadata/ruby_metadata.go b/pkg/metadata/ruby_metadata.go new file mode 100644 index 0000000..104f1de --- /dev/null +++ b/pkg/metadata/ruby_metadata.go @@ -0,0 +1,6 @@ +package metadata + +type RubyMetadata struct { + Name string + Version string +} diff --git a/pkg/mgr/factory.go b/pkg/mgr/factory.go new file mode 100644 index 0000000..9da9bce --- /dev/null +++ b/pkg/mgr/factory.go @@ -0,0 +1,108 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "capsulecd/pkg/errors" + "fmt" + "capsulecd/pkg/config" +) + +func Create(mgrType string, pipelineData *pipeline.Data, config config.Interface, client *http.Client) (Interface, error) { + + var mgr Interface + + switch mgrType { + //empty/generic package manager. Noop. + case "generic": + mgr = new(mgrGeneric) + + //chef dependency managers + case "berkshelf": + mgr = new(mgrChefBerkshelf) + + //golang dependency managers + case "dep": + mgr = new(mgrGolangDep) + case "glide": + mgr = new(mgrGolangGlide) + + //node dependency managers + case "npm": + mgr = new(mgrNodeNpm) + case "yarn": + mgr = new(mgrNodeYarn) + + //python dependency managers + case "pip": + mgr = new(mgrPythonPip) + + //ruby dependency managers + case "bundler": + mgr = new(mgrRubyBundler) + + default: + return nil, errors.ScmUnspecifiedError(fmt.Sprintf("Unknown Packager Manager Type: %s", mgrType)) + } + + if err := mgr.Init(pipelineData, config, client); err != nil { + return nil, err + } + return mgr, nil +} + +func Detect(packageType string, pipelineData *pipeline.Data, config config.Interface, client *http.Client) (Interface, error) { + + var mgrType string + + switch packageType { + //chef dependency managers + case "chef": + if DetectChefBerkshelf(pipelineData, config, client) { + mgrType = "berkshelf" + } else { //default + mgrType = "berkshelf" + } + + //golang dependency managers + case "golang": + if DetectGolangDep(pipelineData, config, client) { + mgrType = "dep" + } else if DetectGolangGlide(pipelineData, config, client) { + mgrType = "glide" + } else { //default + mgrType = "dep" + } + + //node dependency managers + case "node": + if DetectNodeNpm(pipelineData, config, client) { + mgrType = "npm" + } else if DetectNodeYarn(pipelineData, config, client) { + mgrType = "yarn" + } else { //default + mgrType = "npm" + } + + //python dependency managers + case "python": + if DetectPythonPip(pipelineData, config, client) { + mgrType = "pip" + } else { //default + mgrType = "pip" + } + + //ruby dependency managers + case "ruby": + if DetectRubyBundler(pipelineData, config, client) { + mgrType = "bundler" + } else { //default + mgrType = "bundler" + } + + default: + return nil, errors.ScmUnspecifiedError(fmt.Sprintf("Unknown %s Packager Manager Type: %s", packageType, mgrType)) + } + + return Create(mgrType, pipelineData, config, client ) +} \ No newline at end of file diff --git a/pkg/mgr/interface.go b/pkg/mgr/interface.go new file mode 100644 index 0000000..8bff954 --- /dev/null +++ b/pkg/mgr/interface.go @@ -0,0 +1,54 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "capsulecd/pkg/config" +) + +// Create mock using: +// mockgen -source=pkg/mgr/interface.go -destination=pkg/mgr/mock/mock_mgr.go +type Interface interface { + + Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error + + // Validate that required executables are available for the following build/test/package/etc steps + MgrValidateTools() error + + // Assemble the package contents + // Validate that any required files (like dependency management files) exist + // Create any recommended optional/missing files we can in the structure. + // CAN NOT override + // REQUIRES pipelineData.GitLocalPath + MgrAssembleStep() error + + // Validate & download dependencies for this package. + // Generate *.lock files for dependencies (should be deleted in MgrPackageStep if necessary) + // CAN override + // REQUIRES pipelineData.GitLocalPath + // REQUIRES CurrentMetadata + // REQUIRES NextMetadata + MgrDependenciesStep(currentMetadata interface{}, nextMetadata interface{}) error + + // Commit any local changes and create a git tag. Nothing should be pushed to remote repository yet. + // Make sure you remove any unnecessary files from the repo before making the commit + // CAN NOT override + // REQUIRES pipelineData.GitLocalPath + // REQUIRES NextMetadata + // USES mgr_keep_lock_file + MgrPackageStep(currentMetadata interface{}, nextMetadata interface{}) error + + // Push the release to the package repository (ie. npm, chef supermarket, rubygems) + // Should validate any required credentials are specified. + // CAN override + // REQUIRES pipelineData.GitLocalPath + // REQUIRES NextMetadata + // USES chef_supermarket_username + // USES chef_supermarket_key + // USES npm_auth_token + // USES pypi_repository + // USES pypi_username + // USES pypi_password + // USES rubygems_api_key + MgrDistStep(currentMetadata interface{}, nextMetadata interface{}) error +} \ No newline at end of file diff --git a/pkg/mgr/mgr_chef_berkshelf.go b/pkg/mgr/mgr_chef_berkshelf.go new file mode 100644 index 0000000..c9a55dd --- /dev/null +++ b/pkg/mgr/mgr_chef_berkshelf.go @@ -0,0 +1,156 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "capsulecd/pkg/config" + "os/exec" + "capsulecd/pkg/errors" + "path" + "io/ioutil" + "capsulecd/pkg/utils" + "os" + "fmt" + "capsulecd/pkg/metadata" +) + +func DetectChefBerkshelf(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) bool { + berksfilePath := path.Join(pipelineData.GitLocalPath, "Berksfile") + return utils.FileExists(berksfilePath) +} + + +type mgrChefBerkshelf struct { + Config config.Interface + PipelineData *pipeline.Data + Client *http.Client +} + + +func (m *mgrChefBerkshelf) Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error { + m.PipelineData = pipelineData + m.Config = myconfig + + if client != nil { + //primarily used for testing. + m.Client = client + } + + return nil +} + +func (m *mgrChefBerkshelf) MgrValidateTools() error { + //a chef/berkshelf like environment needs to be available for this Engine + if _, kerr := exec.LookPath("knife"); kerr != nil { + return errors.EngineValidateToolError("knife binary is missing") + } + + if _, berr := exec.LookPath("berks"); berr != nil { + return errors.EngineValidateToolError("berkshelf binary is missing") + } + + //TODO: figure out how to validate that "bundle audit" command exists. + if _, berr := exec.LookPath("bundle"); berr != nil { + return errors.EngineValidateToolError("bundler binary is missing") + } + return nil +} + +func (m *mgrChefBerkshelf) MgrAssembleStep() error { + + berksfilePath := path.Join(m.PipelineData.GitLocalPath, "Berksfile") + if !utils.FileExists(berksfilePath) { + ioutil.WriteFile(berksfilePath, []byte(utils.StripIndent( + `source "https://supermarket.chef.io" + metadata + `)), 0644) + } + gemfilePath := path.Join(m.PipelineData.GitLocalPath, "Gemfile") + if !utils.FileExists(gemfilePath) { + ioutil.WriteFile(gemfilePath, []byte(`source "https://rubygems.org"`), 0644) + } + return nil +} + +func (m *mgrChefBerkshelf) MgrDependenciesStep(currentMetadata interface{}, nextMetadata interface{}) error { + // the cookbook has already been downloaded. lets make sure all its dependencies are available. + if cerr := utils.BashCmdExec("berks install", m.PipelineData.GitLocalPath, nil, ""); cerr != nil { + return errors.EngineTestDependenciesError("berks install failed. Check cookbook dependencies") + } + + //download all its gem dependencies + if berr := utils.BashCmdExec("bundle install", m.PipelineData.GitLocalPath, nil, ""); berr != nil { + return errors.EngineTestDependenciesError("bundle install failed. Check Gem dependencies") + } + return nil +} + +func (m *mgrChefBerkshelf) MgrPackageStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.GetBool("mgr_keep_lock_file") { + os.Remove(path.Join(m.PipelineData.GitLocalPath, "Berksfile.lock")) + os.Remove(path.Join(m.PipelineData.GitLocalPath, "Gemfile.lock")) + } + return nil +} + + +func (m *mgrChefBerkshelf) MgrDistStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.IsSet("chef_supermarket_username") || !m.Config.IsSet("chef_supermarket_key") { + return errors.MgrDistCredentialsMissing("Cannot deploy cookbook to supermarket, credentials missing") + } + + // knife is really sensitive to folder names. The cookbook name MUST match the folder name otherwise knife throws up + // when doing a knife cookbook share. So we're going to make a new tmp directory, create a subdirectory with the EXACT + // cookbook name, and then copy the cookbook contents into it. Yeah yeah, its pretty nasty, but blame Chef. + tmpParentPath, terr := ioutil.TempDir("", "") + if terr != nil { + return terr + } + defer os.RemoveAll(tmpParentPath) + + tmpLocalPath := path.Join(tmpParentPath, nextMetadata.(*metadata.ChefMetadata).Name) + if cerr := utils.CopyDir(m.PipelineData.GitLocalPath, tmpLocalPath); cerr != nil { + return cerr + } + + pemFile, _ := ioutil.TempFile("", "client.pem") + defer os.Remove(pemFile.Name()) + knifeFile, _ := ioutil.TempFile("", "knife.rb") + defer os.Remove(knifeFile.Name()) + + // write the knife.rb config jfile. + knifeContent := fmt.Sprintf(utils.StripIndent( + `node_name "%s" # Replace with the login name you use to login to the Supermarket. + client_key "%s" # Define the path to wherever your client.pem file lives. This is the key you generated when you signed up for a Chef account. + cookbook_path [ '%s' ] # Directory where the cookbook you're uploading resides. + `), + m.Config.GetString("chef_supermarket_username"), + pemFile.Name(), + tmpParentPath, + ) + + _, kerr := knifeFile.Write([]byte(knifeContent)) + if kerr != nil { + return kerr + } + + chefKey, berr := m.Config.GetBase64Decoded("chef_supermarket_key") + if berr != nil { + return berr + } + _, perr := pemFile.Write([]byte(chefKey)) + if perr != nil { + return perr + } + + cookbookDistCmd := fmt.Sprintf("knife cookbook site share %s %s -c %s", + nextMetadata.(*metadata.ChefMetadata).Name, + m.Config.GetString("chef_supermarket_type"), + knifeFile.Name(), + ) + + if derr := utils.BashCmdExec(cookbookDistCmd, "", nil, ""); derr != nil { + return errors.MgrDistPackageError("knife cookbook upload to supermarket failed") + } + return nil +} \ No newline at end of file diff --git a/pkg/mgr/mgr_generic.go b/pkg/mgr/mgr_generic.go new file mode 100644 index 0000000..03f52bd --- /dev/null +++ b/pkg/mgr/mgr_generic.go @@ -0,0 +1,52 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "capsulecd/pkg/config" +) + +func DetectGeneric(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) bool { + return false +} + + +type mgrGeneric struct { + Config config.Interface + PipelineData *pipeline.Data + Client *http.Client +} + + +func (m *mgrGeneric) Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error { + m.PipelineData = pipelineData + m.Config = myconfig + + if client != nil { + //primarily used for testing. + m.Client = client + } + + return nil +} + +func (m *mgrGeneric) MgrValidateTools() error { + return nil +} + +func (m *mgrGeneric) MgrAssembleStep() error { + return nil +} + +func (m *mgrGeneric) MgrDependenciesStep(currentMetadata interface{}, nextMetadata interface{}) error { + return nil +} + +func (m *mgrGeneric) MgrPackageStep(currentMetadata interface{}, nextMetadata interface{}) error { + return nil +} + + +func (m *mgrGeneric) MgrDistStep(currentMetadata interface{}, nextMetadata interface{}) error { + return nil +} \ No newline at end of file diff --git a/pkg/mgr/mgr_golang_dep.go b/pkg/mgr/mgr_golang_dep.go new file mode 100644 index 0000000..6efd070 --- /dev/null +++ b/pkg/mgr/mgr_golang_dep.go @@ -0,0 +1,75 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "path" + "os/exec" + "capsulecd/pkg/errors" + "os" + "capsulecd/pkg/config" + "capsulecd/pkg/utils" +) + +func DetectGolangDep(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) bool { + gopkgPath := path.Join(pipelineData.GitLocalPath, "Gopkg.toml") + return utils.FileExists(gopkgPath) +} + + +type mgrGolangDep struct { + Config config.Interface + PipelineData *pipeline.Data + Client *http.Client +} + + +func (m *mgrGolangDep) Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error { + m.PipelineData = pipelineData + m.Config = myconfig + + if client != nil { + //primarily used for testing. + m.Client = client + } + + return nil +} + +func (m *mgrGolangDep) MgrValidateTools() error { + if _, kerr := exec.LookPath("dep"); kerr != nil { + return errors.EngineValidateToolError("dep binary is missing") + } + return nil +} + +func (m *mgrGolangDep) MgrAssembleStep() error { + if !utils.FileExists(path.Join(m.PipelineData.GitLocalPath, "Gopkg.toml")) { + return errors.EngineBuildPackageInvalid("Gopkg.toml file is required to process Golang/Dep package") + } + + return nil +} + +func (m *mgrGolangDep) MgrDependenciesStep(currentMetadata interface{}, nextMetadata interface{}) error { + // the go source has already been downloaded. lets make sure all its dependencies are available. + if cerr := utils.BashCmdExec("dep ensure", m.PipelineData.GitLocalPath, nil, ""); cerr != nil { + return errors.EngineTestDependenciesError("dep ensure failed. Check dep dependencies") + } + + return nil +} + +func (m *mgrGolangDep) MgrPackageStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.GetBool("mgr_keep_lock_file") { + os.Remove(path.Join(m.PipelineData.GitLocalPath, "Gopkg.lock")) + } + return nil +} + + +func (m *mgrGolangDep) MgrDistStep(currentMetadata interface{}, nextMetadata interface{}) error { + // no real packaging for golang. + // libraries are stored in version control. + return nil +} \ No newline at end of file diff --git a/pkg/mgr/mgr_golang_glide.go b/pkg/mgr/mgr_golang_glide.go new file mode 100644 index 0000000..f2627dc --- /dev/null +++ b/pkg/mgr/mgr_golang_glide.go @@ -0,0 +1,74 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "path" + "os/exec" + "capsulecd/pkg/errors" + "os" + "capsulecd/pkg/utils" + "capsulecd/pkg/config" +) + +func DetectGolangGlide(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) bool { + glideyamlPath := path.Join(pipelineData.GitLocalPath, "glide.yaml") + return utils.FileExists(glideyamlPath) +} + + +type mgrGolangGlide struct { + Config config.Interface + PipelineData *pipeline.Data + Client *http.Client +} + + +func (m *mgrGolangGlide) Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error { + m.PipelineData = pipelineData + m.Config = myconfig + + if client != nil { + //primarily used for testing. + m.Client = client + } + + return nil +} + +func (m *mgrGolangGlide) MgrValidateTools() error { + if _, kerr := exec.LookPath("glide"); kerr != nil { + return errors.EngineValidateToolError("glide binary is missing") + } + return nil +} + +func (m *mgrGolangGlide) MgrAssembleStep() error { + if !utils.FileExists(path.Join(m.PipelineData.GitLocalPath, "glide.yaml")) { + return errors.EngineBuildPackageInvalid("glide.yaml file is required to process Golang/Glide package") + } + return nil +} + +func (m *mgrGolangGlide) MgrDependenciesStep(currentMetadata interface{}, nextMetadata interface{}) error { + // the go source has already been downloaded. lets make sure all its dependencies are available. + if cerr := utils.BashCmdExec("glide install", m.PipelineData.GitLocalPath, nil, ""); cerr != nil { + return errors.EngineTestDependenciesError("glide install failed. Check glide dependencies") + } + + return nil +} + +func (m *mgrGolangGlide) MgrPackageStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.GetBool("mgr_keep_lock_file") { + os.Remove(path.Join(m.PipelineData.GitLocalPath, "glide.lock")) + } + return nil +} + + +func (m *mgrGolangGlide) MgrDistStep(currentMetadata interface{}, nextMetadata interface{}) error { + // no real packaging for golang. + // libraries are stored in version control. + return nil +} \ No newline at end of file diff --git a/pkg/mgr/mgr_node_npm.go b/pkg/mgr/mgr_node_npm.go new file mode 100644 index 0000000..b43c015 --- /dev/null +++ b/pkg/mgr/mgr_node_npm.go @@ -0,0 +1,104 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "path" + "os/exec" + "capsulecd/pkg/errors" + "os" + "capsulecd/pkg/config" + "capsulecd/pkg/utils" + "io/ioutil" + "fmt" +) + +func DetectNodeNpm(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) bool { + npmPath := path.Join(pipelineData.GitLocalPath, "package.json") + return utils.FileExists(npmPath) +} + + +type mgrNodeNpm struct { + Config config.Interface + PipelineData *pipeline.Data + Client *http.Client +} + + +func (m *mgrNodeNpm) Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error { + m.PipelineData = pipelineData + m.Config = myconfig + + if client != nil { + //primarily used for testing. + m.Client = client + } + + return nil +} + +func (m *mgrNodeNpm) MgrValidateTools() error { + if _, kerr := exec.LookPath("npm"); kerr != nil { + return errors.EngineValidateToolError("npm binary is missing") + } + return nil +} + +func (m *mgrNodeNpm) MgrAssembleStep() error { + //validate that the npm package.json file exists + if !utils.FileExists(path.Join(m.PipelineData.GitLocalPath, "package.json")) { + return errors.EngineBuildPackageInvalid("package.json file is required to process Node package") + } + + return nil +} + +func (m *mgrNodeNpm) MgrDependenciesStep(currentMetadata interface{}, nextMetadata interface{}) error { + // the module has already been downloaded. lets make sure all its dependencies are available. + if derr := utils.BashCmdExec("npm install", m.PipelineData.GitLocalPath, nil, ""); derr != nil { + return errors.EngineTestDependenciesError("npm install failed. Check module dependencies") + } + + // create a shrinkwrap file. + if derr := utils.BashCmdExec("npm shrinkwrap", m.PipelineData.GitLocalPath, nil, ""); derr != nil { + return errors.EngineTestDependenciesError("npm shrinkwrap failed. Check log for exact error") + } + return nil +} + +func (m *mgrNodeNpm) MgrPackageStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.GetBool("mgr_keep_lock_file") { + os.Remove(path.Join(m.PipelineData.GitLocalPath, "npm-shrinkwrap.json")) + os.Remove(path.Join(m.PipelineData.GitLocalPath, "package-lock.json")) + os.Remove(path.Join(m.PipelineData.GitLocalPath, "yarn.lock")) + } + return nil +} + + +func (m *mgrNodeNpm) MgrDistStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.IsSet("npm_auth_token") { + return errors.MgrDistCredentialsMissing("cannot deploy page to npm, credentials missing") + } + + npmrcFile, _ := ioutil.TempFile("", ".npmrc") + defer os.Remove(npmrcFile.Name()) + + // write the .npmrc config jfile. + npmrcContent := fmt.Sprintf( + "//registry.npmjs.org/:_authToken=%s", + m.Config.GetString("npm_auth_token"), + ) + + if _, werr := npmrcFile.Write([]byte(npmrcContent)); werr != nil { + return werr + } + + npmPublishCmd := fmt.Sprintf("npm --userconfig %s publish .", npmrcFile.Name()) + derr := utils.BashCmdExec(npmPublishCmd, m.PipelineData.GitLocalPath, nil, "") + if derr != nil { + return errors.MgrDistPackageError("npm publish failed. Check log for exact error") + } + return nil +} diff --git a/pkg/mgr/mgr_node_yarn.go b/pkg/mgr/mgr_node_yarn.go new file mode 100644 index 0000000..27444c9 --- /dev/null +++ b/pkg/mgr/mgr_node_yarn.go @@ -0,0 +1,102 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "path" + "os/exec" + "capsulecd/pkg/errors" + "os" + "io/ioutil" + "fmt" + "capsulecd/pkg/config" + "capsulecd/pkg/utils" +) + +func DetectNodeYarn(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) bool { + //theres no way to automatically determine if a project was created via Yarn (vs NPM) + return false +} + + +type mgrNodeYarn struct { + Config config.Interface + PipelineData *pipeline.Data + Client *http.Client +} + + +func (m *mgrNodeYarn) Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error { + m.PipelineData = pipelineData + m.Config = myconfig + + if client != nil { + //primarily used for testing. + m.Client = client + } + + return nil +} + +func (m *mgrNodeYarn) MgrValidateTools() error { + if _, kerr := exec.LookPath("yarn"); kerr != nil { + return errors.EngineValidateToolError("yarn binary is missing") + } + return nil +} + +func (m *mgrNodeYarn) MgrAssembleStep() error { + //validate that the npm package.json file exists + if !utils.FileExists(path.Join(m.PipelineData.GitLocalPath, "package.json")) { + return errors.EngineBuildPackageInvalid("package.json file is required to process Node package") + } + + return nil +} + +func (m *mgrNodeYarn) MgrDependenciesStep(currentMetadata interface{}, nextMetadata interface{}) error { + // the module has already been downloaded. lets make sure all its dependencies are available. + if derr := utils.BashCmdExec("yarn install --non-interactive", m.PipelineData.GitLocalPath, nil, ""); derr != nil { + return errors.EngineTestDependenciesError("yarn install failed. Check module dependencies") + } + + return nil +} + +func (m *mgrNodeYarn) MgrPackageStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.GetBool("mgr_keep_lock_file") { + os.Remove(path.Join(m.PipelineData.GitLocalPath, "npm-shrinkwrap.json")) + os.Remove(path.Join(m.PipelineData.GitLocalPath, "package-lock.json")) + os.Remove(path.Join(m.PipelineData.GitLocalPath, "yarn.lock")) + } + return nil +} + + +func (m *mgrNodeYarn) MgrDistStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.IsSet("npm_auth_token") { + return errors.MgrDistCredentialsMissing("cannot deploy page to npm, credentials missing") + } + + npmrcFile, _ := ioutil.TempFile("", ".npmrc") + defer os.Remove(npmrcFile.Name()) + + // write the .npmrc config jfile. + npmrcContent := fmt.Sprintf( + "//registry.npmjs.org/:_authToken=%s", + m.Config.GetString("npm_auth_token"), + ) + + if _, werr := npmrcFile.Write([]byte(npmrcContent)); werr != nil { + return werr + } + + //TODO: is it worth using the Yarn publish command as well? + npmPublishCmd := fmt.Sprintf("npm --userconfig %s publish .", npmrcFile.Name()) + derr := utils.BashCmdExec(npmPublishCmd, m.PipelineData.GitLocalPath, nil, "") + if derr != nil { + return errors.MgrDistPackageError("npm publish failed. Check log for exact error") + } + return nil +} + diff --git a/pkg/mgr/mgr_python_pip.go b/pkg/mgr/mgr_python_pip.go new file mode 100644 index 0000000..60c1c03 --- /dev/null +++ b/pkg/mgr/mgr_python_pip.go @@ -0,0 +1,116 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "os/exec" + "capsulecd/pkg/errors" + "path" + "os" + "io/ioutil" + "fmt" + "capsulecd/pkg/config" + "capsulecd/pkg/utils" +) + +func DetectPythonPip(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) bool { + //theres no way to automatically determine if a project was created via Yarn (vs NPM) + return false +} + + +type mgrPythonPip struct { + Config config.Interface + PipelineData *pipeline.Data + Client *http.Client +} + + +func (m *mgrPythonPip) Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error { + m.PipelineData = pipelineData + m.Config = myconfig + + if client != nil { + //primarily used for testing. + m.Client = client + } + + return nil +} + +func (m *mgrPythonPip) MgrValidateTools() error { + if _, berr := exec.LookPath("twine"); berr != nil { + return errors.EngineValidateToolError("twine binary is missing") + } + if _, berr := exec.LookPath("pip"); berr != nil { + return errors.EngineValidateToolError("pip binary is missing") + } + return nil +} + +func (m *mgrPythonPip) MgrAssembleStep() error { + // check for/create any required missing folders/files + if !utils.FileExists(path.Join(m.PipelineData.GitLocalPath, "requirements.txt")) { + ioutil.WriteFile(path.Join(m.PipelineData.GitLocalPath, "requirements.txt"), + []byte(""), + 0644, + ) + } + + return nil +} + +func (m *mgrPythonPip) MgrDependenciesStep(currentMetadata interface{}, nextMetadata interface{}) error { + return nil //dependencies are installed as part of Tox. +} + +func (m *mgrPythonPip) MgrPackageStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.GetBool("mgr_keep_lock_file") { + os.Remove(path.Join(m.PipelineData.GitLocalPath, "requirements.txt")) + } + return nil +} + + +func (m *mgrPythonPip) MgrDistStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.IsSet("pypi_username") || !m.Config.IsSet("pypi_password") { + return errors.MgrDistCredentialsMissing("Cannot deploy python package to pypi/warehouse, credentials missing") + } + + pypircFile, _ := ioutil.TempFile("", ".pypirc") + defer os.Remove(pypircFile.Name()) + + // write the .pypirc config jfile. + pypircContent := fmt.Sprintf(utils.StripIndent( + `[distutils] + index-servers=pypi + + [pypi] + repository = %s + username = %s + password = %s + `), + m.Config.GetString("pypi_repository"), + m.Config.GetString("pypi_username"), + m.Config.GetString("pypi_password"), + ) + + if _, perr := pypircFile.Write([]byte(pypircContent)); perr != nil { + return perr + } + + pythonDistCmd := "python setup.py sdist" + if derr := utils.BashCmdExec(pythonDistCmd, m.PipelineData.GitLocalPath, nil, ""); derr != nil { + return errors.MgrDistPackageError("python setup.py sdist failed") + } + + // using twine instead of setup.py (it supports HTTPS.)https://python-packaging-user-guide.readthedocs.org/en/latest/distributing/#uploading-your-project-to-pypi + pypiUploadCmd := fmt.Sprintf("twine upload --config-file %s dist/*", + pypircFile.Name(), + ) + + if uerr := utils.BashCmdExec(pypiUploadCmd, m.PipelineData.GitLocalPath, nil, ""); uerr != nil { + return errors.MgrDistPackageError("twine package upload failed. Check log for exact error") + } + return nil +} diff --git a/pkg/mgr/mgr_ruby_bundler.go b/pkg/mgr/mgr_ruby_bundler.go new file mode 100644 index 0000000..9a0e1c0 --- /dev/null +++ b/pkg/mgr/mgr_ruby_bundler.go @@ -0,0 +1,122 @@ +package mgr + +import ( + "capsulecd/pkg/pipeline" + "net/http" + "os/exec" + "capsulecd/pkg/errors" + "path" + "io/ioutil" + "os" + "fmt" + "capsulecd/pkg/config" + "capsulecd/pkg/utils" + "capsulecd/pkg/metadata" +) + +func DetectRubyBundler(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) bool { + //theres no way to automatically determine if a project was created via Yarn (vs NPM) + return false +} + + +type mgrRubyBundler struct { + Config config.Interface + PipelineData *pipeline.Data + Client *http.Client +} + + +func (m *mgrRubyBundler) Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error { + m.PipelineData = pipelineData + m.Config = myconfig + + if client != nil { + //primarily used for testing. + m.Client = client + } + + return nil +} + +func (m *mgrRubyBundler) MgrValidateTools() error { + if _, berr := exec.LookPath("gem"); berr != nil { + return errors.EngineValidateToolError("gem binary is missing") + } + + if _, berr := exec.LookPath("bundle"); berr != nil { + return errors.EngineValidateToolError("bundle binary is missing") + } + return nil +} + +func (m *mgrRubyBundler) MgrAssembleStep() error { + // check for/create any required missing folders/files + if !utils.FileExists(path.Join(m.PipelineData.GitLocalPath, "Gemfile")) { + ioutil.WriteFile(path.Join(m.PipelineData.GitLocalPath, "Gemfile"), + []byte(utils.StripIndent(`source 'https://rubygems.org' + gemspec`)), + 0644, + ) + } + + + return nil +} + +func (m *mgrRubyBundler) MgrDependenciesStep(currentMetadata interface{}, nextMetadata interface{}) error { + // lets install the gem, and any dependencies + // http://guides.rubygems.org/make-your-own-gem/ + + gemCmd := fmt.Sprintf("gem install %s --ignore-dependencies", + path.Join(m.PipelineData.GitLocalPath, fmt.Sprintf("%s-%s.gem", nextMetadata.(*metadata.RubyMetadata).Name, nextMetadata.(*metadata.RubyMetadata).Version))) + if terr := utils.BashCmdExec(gemCmd, m.PipelineData.GitLocalPath, nil, ""); terr != nil { + return errors.EngineTestDependenciesError("gem install failed. Check gemspec and gem dependencies") + } + + // install dependencies + if terr := utils.BashCmdExec("bundle install", m.PipelineData.GitLocalPath, nil, ""); terr != nil { + return errors.EngineTestDependenciesError("bundle install failed. Check Gemfile") + } + return nil +} + +func (m *mgrRubyBundler) MgrPackageStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.GetBool("mgr_keep_lock_file") { + os.Remove(path.Join(m.PipelineData.GitLocalPath, "Gemfile.lock")) + } + return nil +} + + +func (m *mgrRubyBundler) MgrDistStep(currentMetadata interface{}, nextMetadata interface{}) error { + if !m.Config.IsSet("rubygems_api_key") { + return errors.MgrDistCredentialsMissing("Cannot deploy package to rubygems, credentials missing") + } + + credFile, _ := ioutil.TempFile("", "gem_credentials") + defer os.Remove(credFile.Name()) + + // write the .gem/credentials config jfile. + + credContent := fmt.Sprintf(utils.StripIndent( + `--- + :rubygems_api_key: %s + `), + m.Config.GetString("rubygems_api_key"), + ) + + if _, perr := credFile.Write([]byte(credContent)); perr != nil { + return perr + } + + pushCmd := fmt.Sprintf("gem push %s --config-file %s", + fmt.Sprintf("%s-%s.gem", nextMetadata.(*metadata.RubyMetadata).Name, nextMetadata.(*metadata.RubyMetadata).Version), + credFile.Name(), + ) + if derr := utils.BashCmdExec(pushCmd, m.PipelineData.GitLocalPath, nil, ""); derr != nil { + return errors.MgrDistPackageError("Pushing gem to RubyGems.org using `gem push` failed. Check log for exact error") + } + + return nil +} \ No newline at end of file diff --git a/pkg/mgr/mock/mock_mgr.go b/pkg/mgr/mock/mock_mgr.go new file mode 100644 index 0000000..360d5c4 --- /dev/null +++ b/pkg/mgr/mock/mock_mgr.go @@ -0,0 +1,108 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg/mgr/interface.go + +// Package mock_mgr is a generated GoMock package. +package mock_mgr + +import ( + config "capsulecd/pkg/config" + pipeline "capsulecd/pkg/pipeline" + gomock "github.com/golang/mock/gomock" + http "net/http" + reflect "reflect" +) + +// MockInterface is a mock of Interface interface +type MockInterface struct { + ctrl *gomock.Controller + recorder *MockInterfaceMockRecorder +} + +// MockInterfaceMockRecorder is the mock recorder for MockInterface +type MockInterfaceMockRecorder struct { + mock *MockInterface +} + +// NewMockInterface creates a new mock instance +func NewMockInterface(ctrl *gomock.Controller) *MockInterface { + mock := &MockInterface{ctrl: ctrl} + mock.recorder = &MockInterfaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { + return m.recorder +} + +// Init mocks base method +func (m *MockInterface) Init(pipelineData *pipeline.Data, myconfig config.Interface, client *http.Client) error { + ret := m.ctrl.Call(m, "Init", pipelineData, myconfig, client) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init +func (mr *MockInterfaceMockRecorder) Init(pipelineData, myconfig, client interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockInterface)(nil).Init), pipelineData, myconfig, client) +} + +// MgrValidateTools mocks base method +func (m *MockInterface) MgrValidateTools() error { + ret := m.ctrl.Call(m, "MgrValidateTools") + ret0, _ := ret[0].(error) + return ret0 +} + +// MgrValidateTools indicates an expected call of MgrValidateTools +func (mr *MockInterfaceMockRecorder) MgrValidateTools() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MgrValidateTools", reflect.TypeOf((*MockInterface)(nil).MgrValidateTools)) +} + +// MgrAssembleStep mocks base method +func (m *MockInterface) MgrAssembleStep() error { + ret := m.ctrl.Call(m, "MgrAssembleStep") + ret0, _ := ret[0].(error) + return ret0 +} + +// MgrAssembleStep indicates an expected call of MgrAssembleStep +func (mr *MockInterfaceMockRecorder) MgrAssembleStep() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MgrAssembleStep", reflect.TypeOf((*MockInterface)(nil).MgrAssembleStep)) +} + +// MgrDependenciesStep mocks base method +func (m *MockInterface) MgrDependenciesStep() error { + ret := m.ctrl.Call(m, "MgrDependenciesStep") + ret0, _ := ret[0].(error) + return ret0 +} + +// MgrDependenciesStep indicates an expected call of MgrDependenciesStep +func (mr *MockInterfaceMockRecorder) MgrDependenciesStep() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MgrDependenciesStep", reflect.TypeOf((*MockInterface)(nil).MgrDependenciesStep)) +} + +// MgrPackageStep mocks base method +func (m *MockInterface) MgrPackageStep(currentMetadata, nextMetadata interface{}) error { + ret := m.ctrl.Call(m, "MgrPackageStep", currentMetadata, nextMetadata) + ret0, _ := ret[0].(error) + return ret0 +} + +// MgrPackageStep indicates an expected call of MgrPackageStep +func (mr *MockInterfaceMockRecorder) MgrPackageStep(currentMetadata, nextMetadata interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MgrPackageStep", reflect.TypeOf((*MockInterface)(nil).MgrPackageStep), currentMetadata, nextMetadata) +} + +// MgrDistStep mocks base method +func (m *MockInterface) MgrDistStep(currentMetadata, nextMetadata interface{}) error { + ret := m.ctrl.Call(m, "MgrDistStep", currentMetadata, nextMetadata) + ret0, _ := ret[0].(error) + return ret0 +} + +// MgrDistStep indicates an expected call of MgrDistStep +func (mr *MockInterfaceMockRecorder) MgrDistStep(currentMetadata, nextMetadata interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MgrDistStep", reflect.TypeOf((*MockInterface)(nil).MgrDistStep), currentMetadata, nextMetadata) +} diff --git a/pkg/pipeline.go b/pkg/pipeline.go index b4a3a13..35bca02 100644 --- a/pkg/pipeline.go +++ b/pkg/pipeline.go @@ -11,6 +11,7 @@ import ( "log" "os" "path" + "capsulecd/pkg/mgr" ) type Pipeline struct { @@ -18,6 +19,7 @@ type Pipeline struct { Config config.Interface Scm scm.Interface Engine engine.Interface + PackageManager mgr.Interface } func (p *Pipeline) Start(config config.Interface) error { @@ -49,15 +51,24 @@ func (p *Pipeline) Start(config config.Interface) error { return err } + if err := p.StepExecNotify("mgr_init_step", p.MgrInitStep); err != nil { + return err + } + + if err := p.StepExecNotify("validate_tools", p.ValidateTools); err != nil { return err } - if err := p.StepExecNotify("assemble_step", p.AssembleStep); err != nil { + if err := p.StepExecNotify("mgr_validate_tools", p.MgrValidateTools); err != nil { + return err + } + + if err := p.StepExecNotify("assemble_step", p.AssembleStep); err != nil { //this step includes Mgr work. return err } - if err := p.StepExecNotify("dependencies_step", p.DependenciesStep); err != nil { + if err := p.StepExecNotify("mgr_dependencies_step", p.MgrDependenciesStep); err != nil { return err } @@ -69,11 +80,11 @@ func (p *Pipeline) Start(config config.Interface) error { return err } - if err := p.StepExecNotify("package_step", p.PackageStep); err != nil { + if err := p.StepExecNotify("package_step", p.PackageStep); err != nil { //this step includes Mgr work return err } - if err := p.StepExecNotify("dist_step", p.DistStep); err != nil { + if err := p.StepExecNotify("mgr_dist_step", p.MgrDistStep); err != nil { return err } @@ -228,12 +239,36 @@ func (p *Pipeline) ParseRepoConfig() error { return nil } +func (p *Pipeline) MgrInitStep() error { + log.Println("mgr_init_step") + if p.Config.IsSet("mgr_type") { + mgr, merr := mgr.Create(p.Config.GetString("mgr_type"), p.Data, p.Config, nil) + if merr != nil { + return merr + } + p.PackageManager = mgr + } else { + mgr, merr := mgr.Detect(p.Config.GetString("package_type"), p.Data, p.Config, nil) + if merr != nil { + return merr + } + p.PackageManager = mgr + } + return nil +} + // validate that required executables are available for the following build/test/package/etc steps func (p *Pipeline) ValidateTools() error { log.Println("validate_tools") return p.Engine.ValidateTools() } +func (p *Pipeline) MgrValidateTools() error { + log.Println("mgr_validate_tools") + return p.PackageManager.MgrValidateTools() +} + + // now that the payload has been processed we can begin by building the code. // this may be creating missing files/default structure, compilation, version bumping, etc. func (p *Pipeline) AssembleStep() error { @@ -250,7 +285,10 @@ func (p *Pipeline) AssembleStep() error { if err := p.Engine.AssembleStep(); err != nil { return err } - + log.Println("mgr_assemble_step") + if err := p.PackageManager.MgrAssembleStep(); err != nil { + return err + } // POST HOOK if err := p.RunHook("assemble_step.post"); err != nil { return err @@ -259,25 +297,25 @@ func (p *Pipeline) AssembleStep() error { } // this step should download dependencies -func (p *Pipeline) DependenciesStep() error { +func (p *Pipeline) MgrDependenciesStep() error { // PRE HOOK - if err := p.RunHook("dependencies_step.pre"); err != nil { + if err := p.RunHook("mgr_dependencies_step.pre"); err != nil { return err } - if p.Config.IsSet("dependencies_step.override") { - if err := p.RunHook("dependencies_step.override"); err != nil { + if p.Config.IsSet("mgr_dependencies_step.override") { + if err := p.RunHook("mgr_dependencies_step.override"); err != nil { return err } } else { - log.Println("dependencies_step") - if err := p.Engine.DependenciesStep(); err != nil { + log.Println("mgr_dependencies_step") + if err := p.PackageManager.MgrDependenciesStep(p.Engine.GetCurrentMetadata(), p.Engine.GetNextMetadata()); err != nil { return err } } // POST HOOK - if err := p.RunHook("dependencies_step.post"); err != nil { + if err := p.RunHook("mgr_dependencies_step.post"); err != nil { return err } return nil @@ -351,7 +389,11 @@ func (p *Pipeline) PackageStep() error { } if p.Config.IsSet("package_step.override") { - log.Println("Cannot override the assemble_step, ignoring.") + log.Println("Cannot override the package_step, ignoring.") + } + log.Println("mgr_package_step") + if err := p.PackageManager.MgrPackageStep(p.Engine.GetCurrentMetadata(), p.Engine.GetNextMetadata()); err != nil { + return err } log.Println("package_step") if err := p.Engine.PackageStep(); err != nil { @@ -366,30 +408,30 @@ func (p *Pipeline) PackageStep() error { } // this step should push the release to the package repository (ie. npm, chef supermarket, rubygems) -func (p *Pipeline) DistStep() error { - if p.Config.GetBool("engine_disable_dist") { - log.Println("skipping dist_step.pre, dist_step, dist_step.post") +func (p *Pipeline) MgrDistStep() error { + if p.Config.GetBool("mgr_disable_dist") { + log.Println("skipping mgr_dist_step.pre, mgr_dist_step, mgr_dist_step.post") return nil } // PRE HOOK - if err := p.RunHook("dist_step.pre"); err != nil { + if err := p.RunHook("mgr_dist_step.pre"); err != nil { return err } - if p.Config.IsSet("dist_step.override") { - if err := p.RunHook("dist_step.override"); err != nil { + if p.Config.IsSet("mgr_dist_step.override") { + if err := p.RunHook("mgr_dist_step.override"); err != nil { return err } } else { - log.Println("dist_step") - if err := p.Engine.DistStep(); err != nil { + log.Println("mgr_dist_step") + if err := p.PackageManager.MgrDistStep(p.Engine.GetCurrentMetadata(), p.Engine.GetNextMetadata()); err != nil { return err } } // POST HOOK - if err := p.RunHook("dist_step.post"); err != nil { + if err := p.RunHook("mgr_dist_step.post"); err != nil { return err } return nil diff --git a/pkg/scm/factory.go b/pkg/scm/factory.go index b66df44..052b4ab 100644 --- a/pkg/scm/factory.go +++ b/pkg/scm/factory.go @@ -10,20 +10,18 @@ import ( func Create(scmType string, pipelineData *pipeline.Data, config config.Interface, client *http.Client) (Interface, error) { + var scm Interface switch scmType { case "bitbucket": - scm := new(scmBitbucket) - if err := scm.Init(pipelineData, config, client); err != nil { - return nil, err - } - return scm, nil + scm = new(scmBitbucket) case "github": - scm := new(scmGithub) - if err := scm.Init(pipelineData, config, client); err != nil { - return nil, err - } - return scm, nil + scm = new(scmGithub) default: return nil, errors.ScmUnspecifiedError(fmt.Sprintf("Unknown Scm Type: %s", scmType)) } + + if err := scm.Init(pipelineData, config, client); err != nil { + return nil, err + } + return scm, nil } From c7df690543a1141029448152f0aa88ae04f35a22 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Sun, 26 Aug 2018 19:11:49 -0700 Subject: [PATCH 02/27] using https://upload.pypi.org/legacy/ instead of pypi.python.org. Added a test suite, including a live test integrating with pypi test site. --- example.capsule.yml | 2 +- pkg/engine/engine_python.go | 2 +- pkg/engine/engine_python_test.go | 42 +---- pkg/mgr/mgr_python_pip_test.go | 146 ++++++++++++++++++ .../python/pip_analogj_test/.gitignore | 0 .../testdata/python/pip_analogj_test/LICENSE | 21 +++ .../python/pip_analogj_test/MANIFEST.in | 1 + .../python/pip_analogj_test/README.md | 1 + .../testdata/python/pip_analogj_test/VERSION | 1 + .../python/pip_analogj_test/requirements.txt | 0 .../python/pip_analogj_test/setup.cfg | 5 + .../testdata/python/pip_analogj_test/setup.py | 80 ++++++++++ .../python/pip_analogj_test/tests/__init__.py | 0 .../testdata/python/pip_analogj_test/tox.ini | 14 ++ 14 files changed, 272 insertions(+), 43 deletions(-) create mode 100644 pkg/mgr/mgr_python_pip_test.go create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/.gitignore create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/LICENSE create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/MANIFEST.in create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/README.md create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/VERSION create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/requirements.txt create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/setup.cfg create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/setup.py create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/tests/__init__.py create mode 100644 pkg/mgr/testdata/python/pip_analogj_test/tox.ini diff --git a/example.capsule.yml b/example.capsule.yml index df50dd2..bd18e9f 100644 --- a/example.capsule.yml +++ b/example.capsule.yml @@ -153,7 +153,7 @@ npm_auth_token: '' # Specifies the PYPI credentials to use when creating public release for Pypi package # found in ~/.pypirc on developer machine -pypi_repository: 'https://pypi.python.org/pypi' +pypi_repository: 'https://upload.pypi.org/legacy/' pypi_username: '' pypi_password: '' diff --git a/pkg/engine/engine_python.go b/pkg/engine/engine_python.go index 061178e..576397c 100644 --- a/pkg/engine/engine_python.go +++ b/pkg/engine/engine_python.go @@ -31,7 +31,7 @@ func (g *enginePython) Init(pipelineData *pipeline.Data, config config.Interface g.NextMetadata = new(metadata.PythonMetadata) //set command defaults (can be overridden by repo/system configuration) - g.Config.SetDefault("pypi_repository", "https://pypi.python.org/pypi") + g.Config.SetDefault("pypi_repository", "https://upload.pypi.org/legacy/") g.Config.SetDefault("engine_cmd_compile", "echo 'skipping compile'") g.Config.SetDefault("engine_cmd_lint", "find . -name '*.py' -exec pylint -E '{}' +") g.Config.SetDefault("engine_cmd_fmt", "find . -name '*.py' -exec pylint -E '{}' +") //TODO: replace with pycodestyle/pep8 diff --git a/pkg/engine/engine_python_test.go b/pkg/engine/engine_python_test.go index 385bb03..c1d3bf4 100644 --- a/pkg/engine/engine_python_test.go +++ b/pkg/engine/engine_python_test.go @@ -38,7 +38,7 @@ func TestEnginePython_Create(t *testing.T) { //assert require.NoError(t, err) require.NotNil(t, pythonEngine) - require.Equal(t, "https://pypi.python.org/pypi", testConfig.GetString("pypi_repository"), "should load engine defaults") + require.Equal(t, "https://upload.pypi.org/legacy/", testConfig.GetString("pypi_repository"), "should load engine defaults") } // Define the suite, and absorb the built-in basic suite @@ -168,31 +168,6 @@ func (suite *EnginePythonTestSuite) TestEnginePython_AssembleStep_WithoutSetupPy require.Error(suite.T(), berr, "should return an error") } -func (suite *EnginePythonTestSuite) TestEnginePython_DependenciesStep() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - - //copy cookbook fixture into a temp directory. - parentPath, err := ioutil.TempDir("", "") - require.NoError(suite.T(), err) - defer os.RemoveAll(parentPath) - suite.PipelineData.GitParentPath = parentPath - suite.PipelineData.GitLocalPath = path.Join(parentPath, "pip_analogj_test") - cerr := utils.CopyDir(path.Join("testdata", "python", "pip_analogj_test"), suite.PipelineData.GitLocalPath) - require.NoError(suite.T(), cerr) - - pythonEngine, err := engine.Create("python", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := pythonEngine.DependenciesStep() - - //assert - require.NoError(suite.T(), berr) - //require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Berksfile.lock"))) - //no lock files created by Python engine, and dependencies are installed by Tox in TestStep - //should be a noop -} func (suite *EnginePythonTestSuite) TestEnginePython_TestStep_AllDisabled() { //setup @@ -318,18 +293,3 @@ func (suite *EnginePythonTestSuite) TestEnginePython_PackageStep() { //assert require.NoError(suite.T(), berr) } - -func (suite *EnginePythonTestSuite) TestEnginePython_DistStep_WithoutCredentials() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().IsSet("pypi_username").MinTimes(1).Return(false) - - pythonEngine, err := engine.Create("python", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := pythonEngine.DistStep() - - //assert - require.Error(suite.T(), berr) -} diff --git a/pkg/mgr/mgr_python_pip_test.go b/pkg/mgr/mgr_python_pip_test.go new file mode 100644 index 0000000..1204b40 --- /dev/null +++ b/pkg/mgr/mgr_python_pip_test.go @@ -0,0 +1,146 @@ +package mgr_test + +import ( + "github.com/stretchr/testify/suite" + "github.com/golang/mock/gomock" + "capsulecd/pkg/config/mock" + "capsulecd/pkg/pipeline" + "capsulecd/pkg/mgr/mock" + "io/ioutil" + "github.com/stretchr/testify/require" + "os" + "path" + "capsulecd/pkg/utils" + "capsulecd/pkg/mgr" + "capsulecd/pkg/metadata" + "testing" + "time" +) + +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including a T() method which +// returns the current testing context +type MgrPythonPipTestSuite struct { + suite.Suite + MockCtrl *gomock.Controller + Mgr *mock_mgr.MockInterface + Config *mock_config.MockInterface + PipelineData *pipeline.Data +} + +// Make sure that VariableThatShouldStartAtFive is set to five +// before each test +func (suite *MgrPythonPipTestSuite) SetupTest() { + suite.MockCtrl = gomock.NewController(suite.T()) + + suite.PipelineData = new(pipeline.Data) + + suite.Config = mock_config.NewMockInterface(suite.MockCtrl) + suite.Mgr = mock_mgr.NewMockInterface(suite.MockCtrl) + +} + +func (suite *MgrPythonPipTestSuite) TearDownTest() { + suite.MockCtrl.Finish() +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestEnginePython_TestSuite(t *testing.T) { + suite.Run(t, new(MgrPythonPipTestSuite)) +} + +func (suite *MgrPythonPipTestSuite) TestMgrPythonPip_DependenciesStep() { + //setup + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + + //copy cookbook fixture into a temp directory. + parentPath, err := ioutil.TempDir("", "") + require.NoError(suite.T(), err) + defer os.RemoveAll(parentPath) + suite.PipelineData.GitParentPath = parentPath + suite.PipelineData.GitLocalPath = path.Join(parentPath, "pip_analogj_test") + cerr := utils.CopyDir(path.Join("testdata", "python", "pip_analogj_test"), suite.PipelineData.GitLocalPath) + require.NoError(suite.T(), cerr) + + mgrPythonPip, err := mgr.Create("pip", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + + currentVersion := new(metadata.PythonMetadata) + nextVersion := new(metadata.PythonMetadata) + + //test + berr := mgrPythonPip.MgrDependenciesStep(currentVersion, nextVersion) + + //assert + require.NoError(suite.T(), berr) + //require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Berksfile.lock"))) + //no lock files created by Python engine, and dependencies are installed by Tox in TestStep + //should be a noop +} + + +func (suite *MgrPythonPipTestSuite) TestMgrPythonPip_WithoutCredentials() { + //setup + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + suite.Config.EXPECT().IsSet("pypi_username").MinTimes(1).Return(false) + + mgrPythonPip, err := mgr.Create("pip", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + + currentVersion := new(metadata.PythonMetadata) + nextVersion := new(metadata.PythonMetadata) + + //test + berr := mgrPythonPip.MgrDistStep(currentVersion, nextVersion) + + //assert + require.Error(suite.T(), berr) +} + + +// junk username/password only for use on test.pypi.org +// username: capsulecd +// password: capsulecd$23$ +// we're not going to mock out this test, as we want to ensure that package manager integration works correctly, so we'll just +// communicate with the test pypi server. +func (suite *MgrPythonPipTestSuite) TestMgrPythonPip_DistStep_WithCredentials() { + //setup + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + suite.Config.EXPECT().IsSet("pypi_username").MinTimes(1).Return(true) + suite.Config.EXPECT().IsSet("pypi_password").MinTimes(1).Return(true) + suite.Config.EXPECT().GetString("pypi_username").MinTimes(1).Return("capsulecd") + suite.Config.EXPECT().GetString("pypi_password").MinTimes(1).Return("capsulecd$23$") + suite.Config.EXPECT().GetString("pypi_repository").MinTimes(1).Return("https://test.pypi.org/legacy/") //using test repo + + + //copy cookbook fixture into a temp directory. + parentPath, err := ioutil.TempDir("", "") + require.NoError(suite.T(), err) + defer os.RemoveAll(parentPath) + suite.PipelineData.GitParentPath = parentPath + suite.PipelineData.GitLocalPath = path.Join(parentPath, "pip_analogj_test") + cerr := utils.CopyDir(path.Join("testdata", "python", "pip_analogj_test"), suite.PipelineData.GitLocalPath) + require.NoError(suite.T(), cerr) + + + //using current date/time as a pseudo version number + t := time.Now() + dateVersion := t.Format("20060102.1504.05") //yyyymmdd.HHMM.SS + werr := ioutil.WriteFile(path.Join(suite.PipelineData.GitLocalPath, "VERSION"), []byte(dateVersion), 0644) + require.NoError(suite.T(), werr) + + + mgrPythonPip, err := mgr.Create("pip", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + + currentVersion := new(metadata.PythonMetadata) + nextVersion := new(metadata.PythonMetadata) + + //test + berr := mgrPythonPip.MgrDistStep(currentVersion, nextVersion) + + //assert + require.NoError(suite.T(), berr) +} + diff --git a/pkg/mgr/testdata/python/pip_analogj_test/.gitignore b/pkg/mgr/testdata/python/pip_analogj_test/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/pkg/mgr/testdata/python/pip_analogj_test/LICENSE b/pkg/mgr/testdata/python/pip_analogj_test/LICENSE new file mode 100644 index 0000000..e7f25d8 --- /dev/null +++ b/pkg/mgr/testdata/python/pip_analogj_test/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jason Kulatunga + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/pkg/mgr/testdata/python/pip_analogj_test/MANIFEST.in b/pkg/mgr/testdata/python/pip_analogj_test/MANIFEST.in new file mode 100644 index 0000000..cb1200e --- /dev/null +++ b/pkg/mgr/testdata/python/pip_analogj_test/MANIFEST.in @@ -0,0 +1 @@ +include README.md LICENSE requirements.txt VERSION \ No newline at end of file diff --git a/pkg/mgr/testdata/python/pip_analogj_test/README.md b/pkg/mgr/testdata/python/pip_analogj_test/README.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pkg/mgr/testdata/python/pip_analogj_test/README.md @@ -0,0 +1 @@ + diff --git a/pkg/mgr/testdata/python/pip_analogj_test/VERSION b/pkg/mgr/testdata/python/pip_analogj_test/VERSION new file mode 100644 index 0000000..ece61c6 --- /dev/null +++ b/pkg/mgr/testdata/python/pip_analogj_test/VERSION @@ -0,0 +1 @@ +1.0.6 \ No newline at end of file diff --git a/pkg/mgr/testdata/python/pip_analogj_test/requirements.txt b/pkg/mgr/testdata/python/pip_analogj_test/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/pkg/mgr/testdata/python/pip_analogj_test/setup.cfg b/pkg/mgr/testdata/python/pip_analogj_test/setup.cfg new file mode 100644 index 0000000..c34b498 --- /dev/null +++ b/pkg/mgr/testdata/python/pip_analogj_test/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +# This flag says that the code is written to work on both Python 2 and Python +# 3. If at all possible, it is good practice to do this. If you cannot, you +# will need to generate wheels for each Python version that you support. +universal=1 \ No newline at end of file diff --git a/pkg/mgr/testdata/python/pip_analogj_test/setup.py b/pkg/mgr/testdata/python/pip_analogj_test/setup.py new file mode 100644 index 0000000..6cd920c --- /dev/null +++ b/pkg/mgr/testdata/python/pip_analogj_test/setup.py @@ -0,0 +1,80 @@ +"""A setuptools based setup module. + +See: +https://packaging.python.org/en/latest/distributing.html +https://github.com/pypa/sampleproject +""" + +# Always prefer setuptools over distutils +from setuptools import setup, find_packages +# To use a consistent encoding +from codecs import open +from os import path, listdir + +version = 'unknown' +with open(path.join(path.dirname(path.abspath(__file__)), 'VERSION')) as version_file: + version = version_file.read().strip() + +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the README file +with open(path.join(here, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name='pip_analogj_test', + + # Versions should comply with PEP440. For a discussion on single-sourcing + # the version across setup.py and the project code, see + # https://packaging.python.org/en/latest/single_source_version.html + version=version, + + description='test package', + long_description=long_description, + + # The project's main homepage. + url='https://github.com/AnalogJ/pip_analogj_test', + + # Author details + author='Jason Kulatunga', + author_email='jason@thesparktree.com', + + # Choose your license + license='MIT', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 5 - Production/Stable' + ], + + # What does your project relate to? + keywords='pip_analogj_test', + + # You can just specify the packages manually here if your project is + # simple. Or you can use find_packages(). + packages=find_packages(exclude=['contrib', 'docs', 'tests']), + + # Alternatively, if you want to distribute just a my_module.py, uncomment + # this: + # py_modules=["my_module"], + + # List run-time dependencies here. These will be installed by pip when + # your project is installed. For an analysis of "install_requires" vs pip's + # requirements files see: + # https://packaging.python.org/en/latest/requirements.html + install_requires=['pip_analogj_test'] + + # Although 'package_data' is the preferred approach, in some case you may + # need to place data files outside of your packages. See: + # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa + # In this case, 'data_file' will be installed into '/my_data' + #data_files=[('my_data', ['data/data_file'])], + + # To provide executable scripts, use entry points in preference to the + # "scripts" keyword. Entry points provide cross-platform support and allow + # pip to create the appropriate form of executable for the target platform. +) \ No newline at end of file diff --git a/pkg/mgr/testdata/python/pip_analogj_test/tests/__init__.py b/pkg/mgr/testdata/python/pip_analogj_test/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pkg/mgr/testdata/python/pip_analogj_test/tox.ini b/pkg/mgr/testdata/python/pip_analogj_test/tox.ini new file mode 100644 index 0000000..62823a1 --- /dev/null +++ b/pkg/mgr/testdata/python/pip_analogj_test/tox.ini @@ -0,0 +1,14 @@ +# Tox (http://tox.testrun.org/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py27 +usedevelop = True + +[testenv] +# commands = py.test tests # we're not using py.test here because when running py.test with no test cases causes an error exit code. +commands = echo "success" +deps = + pytest \ No newline at end of file From 9d93ac1f15801fd3d8b22e1cc9c38a03adde888f Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Sun, 26 Aug 2018 22:17:48 -0700 Subject: [PATCH 03/27] adding test suite. --- README.md | 1 + pkg/engine/engine_chef_test.go | 35 ----- pkg/engine/engine_golang_test.go | 24 --- pkg/engine/engine_ruby_test.go | 26 ---- pkg/mgr/mgr_chef_berkshelf_test.go | 145 ++++++++++++++++++ pkg/mgr/mgr_golang_dep_test.go | 93 +++++++++++ pkg/mgr/mgr_golang_glide_test.go | 93 +++++++++++ pkg/mgr/mgr_node_npm_test.go | 93 +++++++++++ pkg/mgr/mgr_python_pip_test.go | 4 +- pkg/mgr/mgr_ruby_bundler_test.go | 98 ++++++++++++ .../chef/cookbook_analogj_test/.gitignore | 53 +++++++ .../chef/cookbook_analogj_test/Berksfile | 2 + .../chef/cookbook_analogj_test/CHANGELOG.md | 3 + .../chef/cookbook_analogj_test/Gemfile | 13 ++ .../chef/cookbook_analogj_test/LICENSE | 21 +++ .../chef/cookbook_analogj_test/README.md | 13 ++ .../chef/cookbook_analogj_test/Rakefile | 1 + .../chef/cookbook_analogj_test/Thorfile | 12 ++ .../chef/cookbook_analogj_test/chefignore | 94 ++++++++++++ .../chef/cookbook_analogj_test/metadata.rb | 9 ++ .../cookbook_analogj_test/recipes/default.rb | 6 + .../golang/dep_analogj_test/.gitignore | 0 .../golang/dep_analogj_test/Gopkg.toml | 3 + .../dep_analogj_test/pkg/version/version.go | 5 + .../golang/glide_analogj_test/.gitignore | 0 .../golang/glide_analogj_test/glide.yaml | 3 + .../glide_analogj_test/pkg/version/version.go | 5 + .../testdata/node/npm_analogj_test/LICENSE | 21 +++ .../testdata/node/npm_analogj_test/README.md | 2 + .../node/npm_analogj_test/package.json | 19 +++ .../testdata/ruby/gem_analogj_test-0.1.4.gem | Bin 0 -> 7168 bytes .../testdata/ruby/gem_analogj_test/Gemfile | 4 + .../ruby/gem_analogj_test/LICENSE.txt | 21 +++ .../testdata/ruby/gem_analogj_test/README.md | 41 +++++ .../testdata/ruby/gem_analogj_test/Rakefile | 6 + .../ruby/gem_analogj_test/bin/console | 14 ++ .../testdata/ruby/gem_analogj_test/bin/setup | 8 + .../gem_analogj_test/gem_analogj_test.gemspec | 25 +++ .../gem_analogj_test/lib/gem_analogj_test.rb | 5 + .../lib/gem_analogj_test/version.rb | 3 + .../spec/gem_analogj_test_spec.rb | 7 + .../ruby/gem_analogj_test/spec/spec_helper.rb | 2 + 42 files changed, 946 insertions(+), 87 deletions(-) create mode 100644 pkg/mgr/mgr_chef_berkshelf_test.go create mode 100644 pkg/mgr/mgr_golang_dep_test.go create mode 100644 pkg/mgr/mgr_golang_glide_test.go create mode 100644 pkg/mgr/mgr_node_npm_test.go create mode 100644 pkg/mgr/mgr_ruby_bundler_test.go create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/.gitignore create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/Berksfile create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/CHANGELOG.md create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/Gemfile create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/LICENSE create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/README.md create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/Rakefile create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/Thorfile create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/chefignore create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/metadata.rb create mode 100644 pkg/mgr/testdata/chef/cookbook_analogj_test/recipes/default.rb create mode 100644 pkg/mgr/testdata/golang/dep_analogj_test/.gitignore create mode 100644 pkg/mgr/testdata/golang/dep_analogj_test/Gopkg.toml create mode 100644 pkg/mgr/testdata/golang/dep_analogj_test/pkg/version/version.go create mode 100644 pkg/mgr/testdata/golang/glide_analogj_test/.gitignore create mode 100644 pkg/mgr/testdata/golang/glide_analogj_test/glide.yaml create mode 100644 pkg/mgr/testdata/golang/glide_analogj_test/pkg/version/version.go create mode 100644 pkg/mgr/testdata/node/npm_analogj_test/LICENSE create mode 100644 pkg/mgr/testdata/node/npm_analogj_test/README.md create mode 100644 pkg/mgr/testdata/node/npm_analogj_test/package.json create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test-0.1.4.gem create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test/Gemfile create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test/LICENSE.txt create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test/README.md create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test/Rakefile create mode 100755 pkg/mgr/testdata/ruby/gem_analogj_test/bin/console create mode 100755 pkg/mgr/testdata/ruby/gem_analogj_test/bin/setup create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test/gem_analogj_test.gemspec create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test/lib/gem_analogj_test.rb create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test/lib/gem_analogj_test/version.rb create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test/spec/gem_analogj_test_spec.rb create mode 100644 pkg/mgr/testdata/ruby/gem_analogj_test/spec/spec_helper.rb diff --git a/README.md b/README.md index fd06f65..f4e8068 100644 --- a/README.md +++ b/README.md @@ -292,3 +292,4 @@ CapsuleCD is licensed under the MIT License - see the - http://craigwickesser.com/2015/02/golang-cmd-with-custom-environment/ - https://opencredo.com/why-i-dont-like-error-handling-in-go/ - https://godoc.org/github.com/pkg/errors +- https://blog.strapi.io/testing-npm-package-before-releasing-it-using-verdaccio-and-ngrok/ \ No newline at end of file diff --git a/pkg/engine/engine_chef_test.go b/pkg/engine/engine_chef_test.go index 5a946a9..ec877c7 100644 --- a/pkg/engine/engine_chef_test.go +++ b/pkg/engine/engine_chef_test.go @@ -166,30 +166,6 @@ func (suite *EngineChefTestSuite) TestEngineChef_AssembleStep_WithoutMetadata() require.Error(suite.T(), berr, "should return an error") } -func (suite *EngineChefTestSuite) TestEngineChef_DependenciesStep() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - - //copy cookbook fixture into a temp directory. - parentPath, err := ioutil.TempDir("", "") - require.NoError(suite.T(), err) - defer os.RemoveAll(parentPath) - suite.PipelineData.GitParentPath = parentPath - suite.PipelineData.GitLocalPath = path.Join(parentPath, "cookbook_analogj_test") - cerr := utils.CopyDir(path.Join("testdata", "chef", "cookbook_analogj_test"), suite.PipelineData.GitLocalPath) - require.NoError(suite.T(), cerr) - - chefEngine, err := engine.Create("chef", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := chefEngine.DependenciesStep() - - //assert - require.NoError(suite.T(), berr) - require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Berksfile.lock"))) - require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile.lock"))) -} func (suite *EngineChefTestSuite) TestEngineChef_TestStep_AllDisabled() { //setup @@ -320,16 +296,5 @@ func (suite *EngineChefTestSuite) TestEngineChef_PackageStep_WithoutLockFiles() } func (suite *EngineChefTestSuite) TestEngineChef_DistStep_WithoutCredentials() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().IsSet("chef_supermarket_username").MinTimes(1).Return(false) - chefEngine, err := engine.Create("chef", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := chefEngine.DistStep() - - //assert - require.Error(suite.T(), berr) } diff --git a/pkg/engine/engine_golang_test.go b/pkg/engine/engine_golang_test.go index dcb92f0..6f6512f 100644 --- a/pkg/engine/engine_golang_test.go +++ b/pkg/engine/engine_golang_test.go @@ -160,30 +160,6 @@ func (suite *EngineGolangTestSuite) TestEngineGolang_AssembleStep_WithoutVersion require.Error(suite.T(), berr, "should return an error") } -func (suite *EngineGolangTestSuite) TestEngineGolang_DependenciesStep() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - - //copy cookbook fixture into a temp directory. - parentPath, err := ioutil.TempDir("", "") - require.NoError(suite.T(), err) - defer os.RemoveAll(parentPath) - suite.PipelineData.GitParentPath = parentPath - suite.PipelineData.GitLocalPath = path.Join(parentPath, "golang_analogj_test") - cerr := utils.CopyDir(path.Join("testdata", "golang", "golang_analogj_test"), suite.PipelineData.GitLocalPath) - require.NoError(suite.T(), cerr) - - golangEngine, err := engine.Create("golang", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := golangEngine.DependenciesStep() - - //assert - require.NoError(suite.T(), berr) - require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "glide.lock"))) -} - func (suite *EngineGolangTestSuite) TestEngineGolang_TestStep_AllDisabled() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) diff --git a/pkg/engine/engine_ruby_test.go b/pkg/engine/engine_ruby_test.go index b19cf85..cdf4119 100644 --- a/pkg/engine/engine_ruby_test.go +++ b/pkg/engine/engine_ruby_test.go @@ -167,32 +167,6 @@ func (suite *EngineRubyTestSuite) TestEngineRuby_AssembleStep_WithoutGemspec() { require.Error(suite.T(), berr, "should return an error") } -func (suite *EngineRubyTestSuite) TestEngineRuby_DependenciesStep() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - - //copy cookbook fixture into a temp directory. - parentPath, err := ioutil.TempDir("", "") - require.NoError(suite.T(), err) - defer os.RemoveAll(parentPath) - suite.PipelineData.GitParentPath = parentPath - suite.PipelineData.GitLocalPath = path.Join(parentPath, "gem_analogj_test") - cerr := utils.CopyDir(path.Join("testdata", "ruby", "gem_analogj_test"), suite.PipelineData.GitLocalPath) - require.NoError(suite.T(), cerr) - cperr := utils.CopyFile(path.Join("testdata", "ruby", "gem_analogj_test-0.1.4.gem"), path.Join(suite.PipelineData.GitLocalPath, "-.gem")) - require.NoError(suite.T(), cperr) - - rubyEngine, err := engine.Create("ruby", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := rubyEngine.DependenciesStep() - - //assert - require.NoError(suite.T(), berr) - require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile.lock"))) -} - func (suite *EngineRubyTestSuite) TestEngineRuby_TestStep_AllDisabled() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) diff --git a/pkg/mgr/mgr_chef_berkshelf_test.go b/pkg/mgr/mgr_chef_berkshelf_test.go new file mode 100644 index 0000000..e60b78f --- /dev/null +++ b/pkg/mgr/mgr_chef_berkshelf_test.go @@ -0,0 +1,145 @@ +package mgr_test + +import ( + "github.com/stretchr/testify/suite" + "github.com/golang/mock/gomock" + "capsulecd/pkg/mgr/mock" + "capsulecd/pkg/config/mock" + "capsulecd/pkg/pipeline" + "testing" + "io/ioutil" + "github.com/stretchr/testify/require" + "os" + "path" + "capsulecd/pkg/metadata" + "capsulecd/pkg/mgr" + "capsulecd/pkg/utils" +) + +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including a T() method which +// returns the current testing context +type MgrChefBerkshelfTestSuite struct { + suite.Suite + MockCtrl *gomock.Controller + Mgr *mock_mgr.MockInterface + Config *mock_config.MockInterface + PipelineData *pipeline.Data +} + +// Make sure that VariableThatShouldStartAtFive is set to five +// before each test +func (suite *MgrChefBerkshelfTestSuite) SetupTest() { + suite.MockCtrl = gomock.NewController(suite.T()) + + suite.PipelineData = new(pipeline.Data) + + suite.Config = mock_config.NewMockInterface(suite.MockCtrl) + suite.Mgr = mock_mgr.NewMockInterface(suite.MockCtrl) + +} + +func (suite *MgrChefBerkshelfTestSuite) TearDownTest() { + suite.MockCtrl.Finish() +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestMgrChefBerkshelf_TestSuite(t *testing.T) { + suite.Run(t, new(MgrChefBerkshelfTestSuite)) +} + +func (suite *MgrChefBerkshelfTestSuite) TestMgrChefBerkshelfTestSuite_DependenciesStep() { + //setup + suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + + //copy cookbook fixture into a temp directory. + parentPath, err := ioutil.TempDir("", "") + require.NoError(suite.T(), err) + defer os.RemoveAll(parentPath) + suite.PipelineData.GitParentPath = parentPath + suite.PipelineData.GitLocalPath = path.Join(parentPath, "cookbook_analogj_test") + cerr := utils.CopyDir(path.Join("testdata", "chef", "cookbook_analogj_test"), suite.PipelineData.GitLocalPath) + require.NoError(suite.T(), cerr) + + mgrChefBerkshelf, err := mgr.Create("berkshelf", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + + currentVersion := new(metadata.ChefMetadata) + nextVersion := new(metadata.ChefMetadata) + + //test + berr := mgrChefBerkshelf.MgrDependenciesStep(currentVersion, nextVersion) + + //assert + require.NoError(suite.T(), berr) + require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Berksfile.lock"))) + require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile.lock"))) + +} + + +func (suite *MgrChefBerkshelfTestSuite) TestMgrChefBerkshelfTestSuite_WithoutCredentials() { + //setup + suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + suite.Config.EXPECT().IsSet("chef_supermarket_username").MinTimes(1).Return(false) + + mgrChefBerkshelf, err := mgr.Create("berkshelf", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + + currentVersion := new(metadata.ChefMetadata) + nextVersion := new(metadata.ChefMetadata) + + //test + berr := mgrChefBerkshelf.MgrDistStep(currentVersion, nextVersion) + + //assert + require.Error(suite.T(), berr) +} + +// +//// junk username/password only for use on test.pypi.org +//// username: capsulecd +//// password: capsulecd$23$ +//// we're not going to mock out this test, as we want to ensure that package manager integration works correctly, so we'll just +//// communicate with the test pypi server. +//func (suite *MgrPythonPipTestSuite) TestMgrChefBerkshelfTestSuite_DistStep_WithCredentials() { +// //setup +// //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) +// suite.Config.EXPECT().IsSet("pypi_username").MinTimes(1).Return(true) +// suite.Config.EXPECT().IsSet("pypi_password").MinTimes(1).Return(true) +// suite.Config.EXPECT().GetString("pypi_username").MinTimes(1).Return("capsulecd") +// suite.Config.EXPECT().GetString("pypi_password").MinTimes(1).Return("capsulecd$23$") +// suite.Config.EXPECT().GetString("pypi_repository").MinTimes(1).Return("https://test.pypi.org/legacy/") //using test repo +// +// +// //copy cookbook fixture into a temp directory. +// parentPath, err := ioutil.TempDir("", "") +// require.NoError(suite.T(), err) +// defer os.RemoveAll(parentPath) +// suite.PipelineData.GitParentPath = parentPath +// suite.PipelineData.GitLocalPath = path.Join(parentPath, "pip_analogj_test") +// cerr := utils.CopyDir(path.Join("testdata", "python", "pip_analogj_test"), suite.PipelineData.GitLocalPath) +// require.NoError(suite.T(), cerr) +// +// +// //using current date/time as a pseudo version number +// t := time.Now() +// dateVersion := t.Format("20060102.1504.05") //yyyymmdd.HHMM.SS +// werr := ioutil.WriteFile(path.Join(suite.PipelineData.GitLocalPath, "VERSION"), []byte(dateVersion), 0644) +// require.NoError(suite.T(), werr) +// +// +// mgrPythonPip, err := mgr.Create("pip", suite.PipelineData, suite.Config, nil) +// require.NoError(suite.T(), err) +// +// currentVersion := new(metadata.PythonMetadata) +// nextVersion := new(metadata.PythonMetadata) +// +// //test +// berr := mgrPythonPip.MgrDistStep(currentVersion, nextVersion) +// +// //assert +// require.NoError(suite.T(), berr) +//} + diff --git a/pkg/mgr/mgr_golang_dep_test.go b/pkg/mgr/mgr_golang_dep_test.go new file mode 100644 index 0000000..829bda0 --- /dev/null +++ b/pkg/mgr/mgr_golang_dep_test.go @@ -0,0 +1,93 @@ +package mgr_test + +import ( + "github.com/stretchr/testify/suite" + "github.com/golang/mock/gomock" + "capsulecd/pkg/mgr/mock" + "capsulecd/pkg/config/mock" + "capsulecd/pkg/pipeline" + "testing" + "io/ioutil" + "github.com/stretchr/testify/require" + "os" + "path" + "capsulecd/pkg/metadata" + "capsulecd/pkg/utils" + "capsulecd/pkg/mgr" +) + +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including a T() method which +// returns the current testing context +type MgrGolangDepTestSuite struct { + suite.Suite + MockCtrl *gomock.Controller + Mgr *mock_mgr.MockInterface + Config *mock_config.MockInterface + PipelineData *pipeline.Data +} + +// Make sure that VariableThatShouldStartAtFive is set to five +// before each test +func (suite *MgrGolangDepTestSuite) SetupTest() { + suite.MockCtrl = gomock.NewController(suite.T()) + + suite.PipelineData = new(pipeline.Data) + + suite.Config = mock_config.NewMockInterface(suite.MockCtrl) + suite.Mgr = mock_mgr.NewMockInterface(suite.MockCtrl) + +} + +func (suite *MgrGolangDepTestSuite) TearDownTest() { + suite.MockCtrl.Finish() +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestMgrGolangDep_TestSuite(t *testing.T) { + suite.Run(t, new(MgrGolangDepTestSuite)) +} + +func (suite *MgrGolangDepTestSuite) TestMgrGolangDepTestSuite_DependenciesStep() { + //setup + suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + + //copy cookbook fixture into a temp directory. + parentPath, err := ioutil.TempDir("", "") + require.NoError(suite.T(), err) + defer os.RemoveAll(parentPath) + suite.PipelineData.GitParentPath = parentPath + suite.PipelineData.GitLocalPath = path.Join(parentPath, "dep_analogj_test") + cerr := utils.CopyDir(path.Join("testdata", "golang", "dep_analogj_test"), suite.PipelineData.GitLocalPath) + require.NoError(suite.T(), cerr) + + mgrGolangDeg, err := mgr.Create("dep", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + currentVersion := new(metadata.GolangMetadata) + nextVersion := new(metadata.GolangMetadata) + + //test + berr := mgrGolangDeg.MgrDependenciesStep(currentVersion, nextVersion) + + //assert + require.NoError(suite.T(), berr) + require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gopkg.toml"))) + +} + + +func (suite *MgrGolangDepTestSuite) TestMgrGolangDepTestSuite_MgrDistStep_WithoutCredentials() { + //setup + suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + mgrGolangDeg, err := mgr.Create("dep", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + currentVersion := new(metadata.ChefMetadata) + nextVersion := new(metadata.ChefMetadata) + + //test + berr := mgrGolangDeg.MgrDistStep(currentVersion, nextVersion) + + //assert + require.Error(suite.T(), berr) +} \ No newline at end of file diff --git a/pkg/mgr/mgr_golang_glide_test.go b/pkg/mgr/mgr_golang_glide_test.go new file mode 100644 index 0000000..da0aafc --- /dev/null +++ b/pkg/mgr/mgr_golang_glide_test.go @@ -0,0 +1,93 @@ +package mgr_test + +import ( + "github.com/stretchr/testify/suite" + "github.com/golang/mock/gomock" + "capsulecd/pkg/mgr/mock" + "capsulecd/pkg/config/mock" + "capsulecd/pkg/pipeline" + "testing" + "io/ioutil" + "github.com/stretchr/testify/require" + "os" + "path" + "capsulecd/pkg/metadata" + "capsulecd/pkg/utils" + "capsulecd/pkg/mgr" +) + +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including a T() method which +// returns the current testing context +type MgrGolangGlideTestSuite struct { + suite.Suite + MockCtrl *gomock.Controller + Mgr *mock_mgr.MockInterface + Config *mock_config.MockInterface + PipelineData *pipeline.Data +} + +// Make sure that VariableThatShouldStartAtFive is set to five +// before each test +func (suite *MgrGolangGlideTestSuite) SetupTest() { + suite.MockCtrl = gomock.NewController(suite.T()) + + suite.PipelineData = new(pipeline.Data) + + suite.Config = mock_config.NewMockInterface(suite.MockCtrl) + suite.Mgr = mock_mgr.NewMockInterface(suite.MockCtrl) + +} + +func (suite *MgrGolangGlideTestSuite) TearDownTest() { + suite.MockCtrl.Finish() +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestMgrGolangGlide_TestSuite(t *testing.T) { + suite.Run(t, new(MgrGolangGlideTestSuite)) +} + +func (suite *MgrGolangGlideTestSuite) TestMgrGolangGlideTestSuite_DependenciesStep() { + //setup + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + + //copy cookbook fixture into a temp directory. + parentPath, err := ioutil.TempDir("", "") + require.NoError(suite.T(), err) + defer os.RemoveAll(parentPath) + suite.PipelineData.GitParentPath = parentPath + suite.PipelineData.GitLocalPath = path.Join(parentPath, "glide_analogj_test") + cerr := utils.CopyDir(path.Join("testdata", "golang", "glide_analogj_test"), suite.PipelineData.GitLocalPath) + require.NoError(suite.T(), cerr) + + mgrGolangDeg, err := mgr.Create("glide", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + currentVersion := new(metadata.GolangMetadata) + nextVersion := new(metadata.GolangMetadata) + + //test + berr := mgrGolangDeg.MgrDependenciesStep(currentVersion, nextVersion) + + //assert + require.NoError(suite.T(), berr) + require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "glide.yaml"))) + +} + + +func (suite *MgrGolangGlideTestSuite) TestMgrGolangGlideTestSuite_MgrDistStep_WithoutCredentials() { + //setup + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + mgrGolangDeg, err := mgr.Create("glide", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + currentVersion := new(metadata.GolangMetadata) + nextVersion := new(metadata.GolangMetadata) + + //test + berr := mgrGolangDeg.MgrDistStep(currentVersion, nextVersion) + + //assert + require.Error(suite.T(), berr) +} \ No newline at end of file diff --git a/pkg/mgr/mgr_node_npm_test.go b/pkg/mgr/mgr_node_npm_test.go new file mode 100644 index 0000000..515567b --- /dev/null +++ b/pkg/mgr/mgr_node_npm_test.go @@ -0,0 +1,93 @@ +package mgr_test + +import ( + "github.com/stretchr/testify/suite" + "github.com/golang/mock/gomock" + "capsulecd/pkg/mgr/mock" + "capsulecd/pkg/config/mock" + "capsulecd/pkg/pipeline" + "testing" + "io/ioutil" + "github.com/stretchr/testify/require" + "os" + "path" + "capsulecd/pkg/metadata" + "capsulecd/pkg/utils" + "capsulecd/pkg/mgr" +) + +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including a T() method which +// returns the current testing context +type MgrNodeNpmTestSuite struct { + suite.Suite + MockCtrl *gomock.Controller + Mgr *mock_mgr.MockInterface + Config *mock_config.MockInterface + PipelineData *pipeline.Data +} + +// Make sure that VariableThatShouldStartAtFive is set to five +// before each test +func (suite *MgrNodeNpmTestSuite) SetupTest() { + suite.MockCtrl = gomock.NewController(suite.T()) + + suite.PipelineData = new(pipeline.Data) + + suite.Config = mock_config.NewMockInterface(suite.MockCtrl) + suite.Mgr = mock_mgr.NewMockInterface(suite.MockCtrl) + +} + +func (suite *MgrNodeNpmTestSuite) TearDownTest() { + suite.MockCtrl.Finish() +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestMgrNodeNpm_TestSuite(t *testing.T) { + suite.Run(t, new(MgrNodeNpmTestSuite)) +} + +func (suite *MgrNodeNpmTestSuite) TestMgrNodeNpmTestSuite_DependenciesStep() { + //setup + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + + //copy cookbook fixture into a temp directory. + parentPath, err := ioutil.TempDir("", "") + require.NoError(suite.T(), err) + defer os.RemoveAll(parentPath) + suite.PipelineData.GitParentPath = parentPath + suite.PipelineData.GitLocalPath = path.Join(parentPath, "npm_analogj_test") + cerr := utils.CopyDir(path.Join("testdata", "node", "npm_analogj_test"), suite.PipelineData.GitLocalPath) + require.NoError(suite.T(), cerr) + + mgrNodeNpm, err := mgr.Create("npm", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + currentVersion := new(metadata.NodeMetadata) + nextVersion := new(metadata.NodeMetadata) + + //test + berr := mgrNodeNpm.MgrDependenciesStep(currentVersion, nextVersion) + + //assert + require.NoError(suite.T(), berr) + require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "package.json"))) + +} + + +func (suite *MgrNodeNpmTestSuite) TestMgrNodeNpmTestSuite_MgrDistStep_WithoutCredentials() { + //setup + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + mgrNodeNpm, err := mgr.Create("npm", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + currentVersion := new(metadata.NodeMetadata) + nextVersion := new(metadata.NodeMetadata) + + //test + berr := mgrNodeNpm.MgrDistStep(currentVersion, nextVersion) + + //assert + require.Error(suite.T(), berr) +} \ No newline at end of file diff --git a/pkg/mgr/mgr_python_pip_test.go b/pkg/mgr/mgr_python_pip_test.go index 1204b40..994ea29 100644 --- a/pkg/mgr/mgr_python_pip_test.go +++ b/pkg/mgr/mgr_python_pip_test.go @@ -80,7 +80,7 @@ func (suite *MgrPythonPipTestSuite) TestMgrPythonPip_DependenciesStep() { } -func (suite *MgrPythonPipTestSuite) TestMgrPythonPip_WithoutCredentials() { +func (suite *MgrPythonPipTestSuite) TestMgrPythonPip_MgrDistStep_WithoutCredentials() { //setup //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) suite.Config.EXPECT().IsSet("pypi_username").MinTimes(1).Return(false) @@ -104,7 +104,7 @@ func (suite *MgrPythonPipTestSuite) TestMgrPythonPip_WithoutCredentials() { // password: capsulecd$23$ // we're not going to mock out this test, as we want to ensure that package manager integration works correctly, so we'll just // communicate with the test pypi server. -func (suite *MgrPythonPipTestSuite) TestMgrPythonPip_DistStep_WithCredentials() { +func (suite *MgrPythonPipTestSuite) TestMgrPythonPip_MgrDistStep_WithCredentials() { //setup //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) suite.Config.EXPECT().IsSet("pypi_username").MinTimes(1).Return(true) diff --git a/pkg/mgr/mgr_ruby_bundler_test.go b/pkg/mgr/mgr_ruby_bundler_test.go new file mode 100644 index 0000000..db1924c --- /dev/null +++ b/pkg/mgr/mgr_ruby_bundler_test.go @@ -0,0 +1,98 @@ +package mgr + +import ( + "github.com/stretchr/testify/suite" + "github.com/golang/mock/gomock" + "capsulecd/pkg/mgr/mock" + "capsulecd/pkg/config/mock" + "capsulecd/pkg/pipeline" + "testing" + "io/ioutil" + "github.com/stretchr/testify/require" + "os" + "path" + "capsulecd/pkg/metadata" + "capsulecd/pkg/utils" + "capsulecd/pkg/mgr" +) + +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including a T() method which +// returns the current testing context +type MgrRubyBundlerTestSuite struct { + suite.Suite + MockCtrl *gomock.Controller + Mgr *mock_mgr.MockInterface + Config *mock_config.MockInterface + PipelineData *pipeline.Data +} + +// Make sure that VariableThatShouldStartAtFive is set to five +// before each test +func (suite *MgrRubyBundlerTestSuite) SetupTest() { + suite.MockCtrl = gomock.NewController(suite.T()) + + suite.PipelineData = new(pipeline.Data) + + suite.Config = mock_config.NewMockInterface(suite.MockCtrl) + suite.Mgr = mock_mgr.NewMockInterface(suite.MockCtrl) + +} + +func (suite *MgrRubyBundlerTestSuite) TearDownTest() { + suite.MockCtrl.Finish() +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestMgrRubyBundler_TestSuite(t *testing.T) { + suite.Run(t, new(MgrRubyBundlerTestSuite)) +} + +func (suite *MgrRubyBundlerTestSuite) TestMgrRubyBundlerTestSuite_DependenciesStep() { + //setup + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + + //copy cookbook fixture into a temp directory. + parentPath, err := ioutil.TempDir("", "") + require.NoError(suite.T(), err) + defer os.RemoveAll(parentPath) + suite.PipelineData.GitParentPath = parentPath + suite.PipelineData.GitLocalPath = path.Join(parentPath, "gem_analogj_test") + cerr := utils.CopyDir(path.Join("testdata", "ruby", "gem_analogj_test"), suite.PipelineData.GitLocalPath) + require.NoError(suite.T(), cerr) + cperr := utils.CopyFile(path.Join("testdata", "ruby", "gem_analogj_test-0.1.4.gem"), path.Join(suite.PipelineData.GitLocalPath, "-.gem")) + require.NoError(suite.T(), cperr) + + + mgrRubyBundler, err := mgr.Create("bundler", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + currentVersion := new(metadata.RubyMetadata) + nextVersion := new(metadata.RubyMetadata) + + //test + berr := mgrRubyBundler.MgrDependenciesStep(currentVersion, nextVersion) + + //assert + require.NoError(suite.T(), berr) + require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile.lock"))) + +} + + +func (suite *MgrRubyBundlerTestSuite) TestMgrRubyBundlerTestSuite_MgrDistStep_WithoutCredentials() { + //setup + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + suite.Config.EXPECT().IsSet("rubygems_api_key").MinTimes(1).Return(false) + + mgrRubyBundler, err := mgr.Create("bundler", suite.PipelineData, suite.Config, nil) + require.NoError(suite.T(), err) + currentVersion := new(metadata.RubyMetadata) + nextVersion := new(metadata.RubyMetadata) + + //test + berr := mgrRubyBundler.MgrDistStep(currentVersion, nextVersion) + + //assert + require.Error(suite.T(), berr) +} \ No newline at end of file diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/.gitignore b/pkg/mgr/testdata/chef/cookbook_analogj_test/.gitignore new file mode 100644 index 0000000..27ccd58 --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/.gitignore @@ -0,0 +1,53 @@ +# Created by .ignore support plugin (hsz.mobi) +### Ruby template +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/spec/examples.txt +/test/tmp/ +/test/version_tmp/ +/tmp/ + +# Used by dotenv library to load environment variables. +# .env + +## Specific to RubyMotion: +.dat* +.repl_history +build/ +*.bridgesupport +build-iPhoneOS/ +build-iPhoneSimulator/ + +## Specific to RubyMotion (use of CocoaPods): +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# vendor/Pods/ + +## Documentation cache and generated files: +/.yardoc/ +/_yardoc/ +/doc/ +/rdoc/ + +## Environment normalization: +/.bundle/ +/vendor/bundle +/lib/bundler/man/ + +# for a library or gem, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# Gemfile.lock +# .ruby-version +# .ruby-gemset + +# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: +.rvmrc + diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/Berksfile b/pkg/mgr/testdata/chef/cookbook_analogj_test/Berksfile new file mode 100644 index 0000000..529caeb --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/Berksfile @@ -0,0 +1,2 @@ +source "https://supermarket.chef.io" +metadata \ No newline at end of file diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/CHANGELOG.md b/pkg/mgr/testdata/chef/cookbook_analogj_test/CHANGELOG.md new file mode 100644 index 0000000..c950ebf --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0 + +Initial release of test_cookbook diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/Gemfile b/pkg/mgr/testdata/chef/cookbook_analogj_test/Gemfile new file mode 100644 index 0000000..d566b85 --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/Gemfile @@ -0,0 +1,13 @@ +source 'https://rubygems.org' + +# Uncomment these lines if you want to live on the Edge: +# +# group :development do +# gem "berkshelf", github: "berkshelf/berkshelf" +# gem "vagrant", github: "mitchellh/vagrant", tag: "v1.6.3" +# end +# +# group :plugins do +# gem "vagrant-berkshelf", github: "berkshelf/vagrant-berkshelf" +# gem "vagrant-omnibus", github: "schisamo/vagrant-omnibus" +# end \ No newline at end of file diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/LICENSE b/pkg/mgr/testdata/chef/cookbook_analogj_test/LICENSE new file mode 100644 index 0000000..e7f25d8 --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jason Kulatunga + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/README.md b/pkg/mgr/testdata/chef/cookbook_analogj_test/README.md new file mode 100644 index 0000000..b186a93 --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/README.md @@ -0,0 +1,13 @@ + +test contenst +sd +f +sd +test PR 1sd +f +sdf +s +ds +f + +touch diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/Rakefile b/pkg/mgr/testdata/chef/cookbook_analogj_test/Rakefile new file mode 100644 index 0000000..1c804f8 --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/Rakefile @@ -0,0 +1 @@ +task :test \ No newline at end of file diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/Thorfile b/pkg/mgr/testdata/chef/cookbook_analogj_test/Thorfile new file mode 100644 index 0000000..7a0c0ff --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/Thorfile @@ -0,0 +1,12 @@ +# encoding: utf-8 + +require 'bundler' +require 'bundler/setup' +require 'berkshelf/thor' + +begin + require "kitchen/thor_tasks" + Kitchen::ThorTasks.new +rescue LoadError + puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV["CI"] +end diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/chefignore b/pkg/mgr/testdata/chef/cookbook_analogj_test/chefignore new file mode 100644 index 0000000..138a808 --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/chefignore @@ -0,0 +1,94 @@ +# Put files/directories that should be ignored in this file when uploading +# or sharing to the community site. +# Lines that start with '# ' are comments. + +# OS generated files # +###################### +.DS_Store +Icon? +nohup.out +ehthumbs.db +Thumbs.db + +# SASS # +######## +.sass-cache + +# EDITORS # +########### +\#* +.#* +*~ +*.sw[a-z] +*.bak +REVISION +TAGS* +tmtags +*_flymake.* +*_flymake +*.tmproj +.project +.settings +mkmf.log + +## COMPILED ## +############## +a.out +*.o +*.pyc +*.so +*.com +*.class +*.dll +*.exe +*/rdoc/ + +# Testing # +########### +.watchr +.rspec +spec/* +spec/fixtures/* +test/* +features/* +Guardfile +Procfile + +# SCM # +####### +.git +*/.git +.gitignore +.gitmodules +.gitconfig +.gitattributes +.svn +*/.bzr/* +*/.hg/* +*/.svn/* + +# Berkshelf # +############# +cookbooks/* +tmp + +# Cookbooks # +############# +CONTRIBUTING +CHANGELOG* + +# Strainer # +############ +Colanderfile +Strainerfile +.colander +.strainer + +# Vagrant # +########### +.vagrant +Vagrantfile + +# Travis # +########## +.travis.yml diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/metadata.rb b/pkg/mgr/testdata/chef/cookbook_analogj_test/metadata.rb new file mode 100644 index 0000000..fb03d5f --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/metadata.rb @@ -0,0 +1,9 @@ +name 'cookbook_analogj_test' +license 'All rights reserved' +description 'Installs/Configures cookbook_analogj_test' +long_description 'Installs/Configures cookbook_analogj_test' +version '0.1.11' +maintainer 'Test User' +maintainer_email 'test@test.com' +issues_url 'http://www.example.com' +source_url 'http://www.example.com' diff --git a/pkg/mgr/testdata/chef/cookbook_analogj_test/recipes/default.rb b/pkg/mgr/testdata/chef/cookbook_analogj_test/recipes/default.rb new file mode 100644 index 0000000..23a7f42 --- /dev/null +++ b/pkg/mgr/testdata/chef/cookbook_analogj_test/recipes/default.rb @@ -0,0 +1,6 @@ +# +# Cookbook Name:: test_cookbook +# Recipe:: default +# # +# All rights reserved - Do Not Redistribute +# diff --git a/pkg/mgr/testdata/golang/dep_analogj_test/.gitignore b/pkg/mgr/testdata/golang/dep_analogj_test/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/pkg/mgr/testdata/golang/dep_analogj_test/Gopkg.toml b/pkg/mgr/testdata/golang/dep_analogj_test/Gopkg.toml new file mode 100644 index 0000000..1ce1040 --- /dev/null +++ b/pkg/mgr/testdata/golang/dep_analogj_test/Gopkg.toml @@ -0,0 +1,3 @@ +required = [ + "github.com/spf13/viper" +] \ No newline at end of file diff --git a/pkg/mgr/testdata/golang/dep_analogj_test/pkg/version/version.go b/pkg/mgr/testdata/golang/dep_analogj_test/pkg/version/version.go new file mode 100644 index 0000000..e64fc31 --- /dev/null +++ b/pkg/mgr/testdata/golang/dep_analogj_test/pkg/version/version.go @@ -0,0 +1,5 @@ +package version + +// VERSION is the app-global version string, which will be replaced with a +// new value during packaging +const VERSION = "1.0.0" diff --git a/pkg/mgr/testdata/golang/glide_analogj_test/.gitignore b/pkg/mgr/testdata/golang/glide_analogj_test/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/pkg/mgr/testdata/golang/glide_analogj_test/glide.yaml b/pkg/mgr/testdata/golang/glide_analogj_test/glide.yaml new file mode 100644 index 0000000..ef1ef42 --- /dev/null +++ b/pkg/mgr/testdata/golang/glide_analogj_test/glide.yaml @@ -0,0 +1,3 @@ +package: go_analogj_test +import: +- package: github.com/spf13/viper \ No newline at end of file diff --git a/pkg/mgr/testdata/golang/glide_analogj_test/pkg/version/version.go b/pkg/mgr/testdata/golang/glide_analogj_test/pkg/version/version.go new file mode 100644 index 0000000..e64fc31 --- /dev/null +++ b/pkg/mgr/testdata/golang/glide_analogj_test/pkg/version/version.go @@ -0,0 +1,5 @@ +package version + +// VERSION is the app-global version string, which will be replaced with a +// new value during packaging +const VERSION = "1.0.0" diff --git a/pkg/mgr/testdata/node/npm_analogj_test/LICENSE b/pkg/mgr/testdata/node/npm_analogj_test/LICENSE new file mode 100644 index 0000000..e7f25d8 --- /dev/null +++ b/pkg/mgr/testdata/node/npm_analogj_test/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jason Kulatunga + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/pkg/mgr/testdata/node/npm_analogj_test/README.md b/pkg/mgr/testdata/node/npm_analogj_test/README.md new file mode 100644 index 0000000..92e7ad0 --- /dev/null +++ b/pkg/mgr/testdata/node/npm_analogj_test/README.md @@ -0,0 +1,2 @@ +# test_npm +Test npm package for use with capsulecd. diff --git a/pkg/mgr/testdata/node/npm_analogj_test/package.json b/pkg/mgr/testdata/node/npm_analogj_test/package.json new file mode 100644 index 0000000..3bfe4a3 --- /dev/null +++ b/pkg/mgr/testdata/node/npm_analogj_test/package.json @@ -0,0 +1,19 @@ +{ + "name": "npm_analogj_test", + "version": "1.0.8", + "description": "test javascript package for capsulecd", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\"" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/AnalogJ/npm_analogj_test.git" + }, + "author": "", + "license": "MIT", + "bugs": { + "url": "https://github.com/AnalogJ/npm_analogj_test/issues" + }, + "homepage": "https://github.com/AnalogJ/npm_analogj_test#readme" +} diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test-0.1.4.gem b/pkg/mgr/testdata/ruby/gem_analogj_test-0.1.4.gem new file mode 100644 index 0000000000000000000000000000000000000000..3b614a6a6a5bb1dff0d2ac078028a386f6949940 GIT binary patch literal 7168 zcmeHLS5(wX(kBNcgD~WH1&1_)AVE&gleYksk`0mTz{r2p!UaI@l>F%mucmGaRVXfWFt<2rbC2W0&{wjg| z5NT;?qQC1O^0S6YK_Ns?DOm{g8bk&v3nhX;uS#Eq5b;6&Du@1cUw53Fx$6&?JngKl z9sbqu)A-Nq|BUUA;C@>E-#X=uq$2XZK#3$F9Yw&LCLuHp8%Vp_Z%eUm>arl?Hx{&^ zjvI7a~GEzWZBqwxU#g_LSvG!&D7p zJ$a{t-{scXrJZge(99$4%6XKIXFv~(-g#zU{A~}oh9YMYipDet5ZMgN*tWK6>_3X^ zZ1lQy$Du8`+&}NlLo@vQC*N_;-g(u3m)PG(dF7UE`b3=8$@IA^Z8gjNPj_b7))a+_ zyKBCj*}O?_^qz$pJlL~v>8A2{`xaNs@Gzk{heUR4C%P#%g zWtTj_lds1=C*2 z9#1A8uafiocy5_a<<4Tz5^2?mjR3q740K}cDua_p51(0*g}rL?WMASE6?96)v})h> zltC>wUfvbnuGv!XHMY(n=*_^o_P`Ez?i-t;S?c?I^PoJ^Wo#6S%cd_aG@kw@@+r>? z#Ji~O^hTk!Xz#agVQ;?!`VQHe?H-0wBRf2~!^-t8J7lM%MY1evWmNyIzvF-3uYYj= z4+i{y6^+7DHGS;>G!7Urmui|Wk!a5u7D$vqC%vaLruI^ zpNGe(knRUUY?Ch_8*yJ}Yf62-tpiv&e|S1OJADY;yVtdd3WYY1gI`QR-MOnk&2*az zu+=yi*$mx+A)yctacI$1gFjM=;bhq~Q5~_rmNDIL{gw6|$%dlcsna%hF#%|^!7TzC zb_(4|lGzHfmq$x{G8~D&xo!zPL|AW9(=hV%tZ`5A^B?&esqz^zQVJa84S8|Hck&n~ z_Fz!BTdP}RL>d&62D!aEto zExeYIP*M9t+@I6qtKHm=i7@muJ!H!2Yt&T_iMv8o1UF)QXf52rmGWwbrC=b3?lXos z-Igqz46_V``pY4mVNMuK3Ti@-$|R=bwT9xQaaJ!AU)9cQ^SKb2Y_z9v9X)=YNfaVR zi>6?^&(3-kJc+o?=hgMaF!zlsl_Ne?IF&f0Iw|4i*TVV!<)pM?gf4G>Ss2I@O5um60Kt4)D}Hh1|mylLQ~NoTOA~|q=d|$-Z)zq zqZL>DEhJ4vJd(q56I{hh1lyNgz525%~*6nZ=a}gvVs;*~CgelgMg|k~VTY;e)J)Ix4 zt8WTA(ASWhBxS4TPp3bYtdE&|qU{?t`;4Rhn?}J|BqLu(_B3Zh;5l`rZ`qt}+4!p` zC$W1^u5+pQH`iC}m9AQ3jbGn3bE}`hd4=xzJeIbm$AVI0j+IC*A~LTF+1PH+9kSe- z9IP*u^)a#(o+;G4p?^$j*XHdD*^QB`+agZCPcv|$%GTi-haKF=pn*sDt{iC4X?NUo zzCTAvqdz$<5pJ<{(c~;(jI#4=4@tUnv__?OG943{F=%3fIzf&1tIy}h{EpozrpP!z zoz!A%_v3p57*UN1Uf%15Y~7f}ti|~8o#AUSCk?|yw=gxmEq2iTJXtr)5RaFtVOMvy zKf)5Pmp)vr>ufgIUmI66AH=N8bleE4yBIp;GHJRL(m8T6cbE=y1|%k$v7Q4a!WVex zfSqk#9=~9K^XZGjrkDdV2q~AjvDIcC)tL|zi>lbi*bZ9dlg_ygZ#yb{D=1MU%I`M3 zg9`HOnnRcXpQYqL59vs-3e2uHZ>nkGDlsbUD$j$MCvQA*YwFsJRk{Mmkw_FDU139G8zX4&3lfU?Hq+-p-Gl%{#bfRYe4LGIB3Fvc&fMBcG8ufY?LVb7r3Wic$ zn%4SJ89Xze>Y^C6je+_~w7K^Up2>xaM#!S&^P&&Y>AOblHFxF(Ym{7@4^$2O%!|ud zFR`a$=yM1xIU|9twRU%0t1*p3Uf|`i`{3~FgN40@ulokuvO(P^bBTCe3+m()y08Ovp@nPhY4Q(N{dcGmXhu$oDxkj*$gE`pbD z8I#Iul%S}hSX(|;I8b!Fe$c{;eAV70rC?ZISYP7|XCBOx@4p=I)M&ReWS}BEh-`m6 z>^4QiLF$Re{Fz9c5GzV2#-Suba1|q=>bvjUo^6MPe~^Pmt6W0l<&xig+>ms>G^x5K zEt#cp@|w-gZ2Zb~l~1z6Xea3V*uT)B7mFQaRIo;Aukl_UdXb1I@MOw`2Ny6 zP=!;w*EHCm;9^${62u9|mX9hB&JmF`_Q7X#W7kY)nYytK=k;kayjDNYG|8%FI;rqkKwaj#T{p!6m7~33qfg zw1b5qnw$B=P;x<=^KeR^QH$O`!*}UFe zd1vs+BdOdev8nZB&G)$ih~@NXEOvd|-!Z=LRH+~k-CSb|U^)Fn$<2)~;O{Azxrnl2 z`b2Z@t_^#9@v|S$4ga96#9{F~@*N_YQ7>-P0hY4Fa@ihhI+>`ZW2$hY{b@?e|4mMei)qSZ}mp;`iM;CJ%J6|F8%AbMsgH^+R*2?;s zzjLKGfWn;>6c;C_Kzw|Y$k<8hcb`x-uXuq6XKcc|W+}Kjk8XXO&sWqvNdm@qcm>B6 zfatSP0C1^{jdNZEFm}90Uc^sDK3?OBvUwJbbDGGE#0VeZSLluY8`wMb^R#@Zzwc=(!$_1a}AJy^qT`Q$0_QmqZX1;5v^9H`wL5;kjLY z)0J*-@V7FfxbwYtcZbZKu8xpUZo%HN>hPs_P=#epwp-WW$3D%B%6?e+Xw3e5=s>P+ zfqaOAeW#PG!AWb#c0Hv$1M3d=X3H9Bc;$0oS`dMufni^7+TZR1wXZn08RRZ-v|6RT zM+>fdyTR?VtJh0P7#GWZFOqLEYu1~>3u%JiEf{6@bw@PpSQE+zCNl7+hos_mzJs~M Ue}dF6^DhK`A@B=<|1AXm2A88su>b%7 literal 0 HcmV?d00001 diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/Gemfile b/pkg/mgr/testdata/ruby/gem_analogj_test/Gemfile new file mode 100644 index 0000000..ec2aaad --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in gem_test.gemspec +gemspec diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/LICENSE.txt b/pkg/mgr/testdata/ruby/gem_analogj_test/LICENSE.txt new file mode 100644 index 0000000..de2524e --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jason Kulatunga + +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: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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. diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/README.md b/pkg/mgr/testdata/ruby/gem_analogj_test/README.md new file mode 100644 index 0000000..52402d1 --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/README.md @@ -0,0 +1,41 @@ +# GemTest + +Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/gem_test`. To experiment with that code, run `bin/console` for an interactive prompt. + +TODO: Delete this and the text above, and describe your gem + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'gem_test' +``` + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install gem_test + +## Usage + +TODO: Write usage instructions here + +## Development + +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/gem_test. + + +## License + +The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). + diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/Rakefile b/pkg/mgr/testdata/ruby/gem_analogj_test/Rakefile new file mode 100644 index 0000000..b7e9ed5 --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/Rakefile @@ -0,0 +1,6 @@ +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +task :default => :spec diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/bin/console b/pkg/mgr/testdata/ruby/gem_analogj_test/bin/console new file mode 100755 index 0000000..6ff4627 --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require "bundler/setup" +require "gem_analogj_test" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require "irb" +IRB.start diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/bin/setup b/pkg/mgr/testdata/ruby/gem_analogj_test/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/gem_analogj_test.gemspec b/pkg/mgr/testdata/ruby/gem_analogj_test/gem_analogj_test.gemspec new file mode 100644 index 0000000..f161cf5 --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/gem_analogj_test.gemspec @@ -0,0 +1,25 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'gem_analogj_test/version' + +Gem::Specification.new do |spec| + spec.name = "gem_analogj_test" + spec.version = GemTest::VERSION + spec.authors = ["Jason Kulatunga"] + spec.email = ["jk17@ualberta.ca"] + + spec.summary = 'this is my test summary' + spec.description = 'this is my test description' + spec.homepage = "http://www.github.com/Analogj/gem_analogj_test" + spec.license = "MIT" + + spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler", "~> 1.11" + spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "rspec", "~> 3.0" +end diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/lib/gem_analogj_test.rb b/pkg/mgr/testdata/ruby/gem_analogj_test/lib/gem_analogj_test.rb new file mode 100644 index 0000000..4279afa --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/lib/gem_analogj_test.rb @@ -0,0 +1,5 @@ +require "gem_analogj_test/version" + +module GemTest + # Your code goes here... +end diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/lib/gem_analogj_test/version.rb b/pkg/mgr/testdata/ruby/gem_analogj_test/lib/gem_analogj_test/version.rb new file mode 100644 index 0000000..fed23a1 --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/lib/gem_analogj_test/version.rb @@ -0,0 +1,3 @@ +module GemTest + VERSION = "0.1.3" +end diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/spec/gem_analogj_test_spec.rb b/pkg/mgr/testdata/ruby/gem_analogj_test/spec/gem_analogj_test_spec.rb new file mode 100644 index 0000000..8b11f3e --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/spec/gem_analogj_test_spec.rb @@ -0,0 +1,7 @@ +require 'spec_helper' + +describe 'GemTest' do + it 'does something useful' do + expect(true).to eq(true) + end +end diff --git a/pkg/mgr/testdata/ruby/gem_analogj_test/spec/spec_helper.rb b/pkg/mgr/testdata/ruby/gem_analogj_test/spec/spec_helper.rb new file mode 100644 index 0000000..6b3b79a --- /dev/null +++ b/pkg/mgr/testdata/ruby/gem_analogj_test/spec/spec_helper.rb @@ -0,0 +1,2 @@ +$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +require 'gem_analogj_test' From d5c5e604ad5a62bb6496005983953a606b16d549 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Sun, 26 Aug 2018 22:19:32 -0700 Subject: [PATCH 04/27] cleanup of old test suite. --- pkg/engine/engine_golang_test.go | 13 ----------- pkg/engine/engine_node_test.go | 39 -------------------------------- pkg/engine/engine_ruby_test.go | 15 ------------ 3 files changed, 67 deletions(-) diff --git a/pkg/engine/engine_golang_test.go b/pkg/engine/engine_golang_test.go index 6f6512f..ed8abc7 100644 --- a/pkg/engine/engine_golang_test.go +++ b/pkg/engine/engine_golang_test.go @@ -286,16 +286,3 @@ func (suite *EngineGolangTestSuite) TestEngineGolang_PackageStep_WithoutLockFile require.NoError(suite.T(), berr) require.False(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "glide.lock"))) } - -func (suite *EngineGolangTestSuite) TestEngineGolang_DistStep() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - golangEngine, err := engine.Create("golang", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := golangEngine.DistStep() - - //assert - require.NoError(suite.T(), berr) -} diff --git a/pkg/engine/engine_node_test.go b/pkg/engine/engine_node_test.go index dc1d2f8..0481398 100644 --- a/pkg/engine/engine_node_test.go +++ b/pkg/engine/engine_node_test.go @@ -135,30 +135,6 @@ func (suite *EngineNodeTestSuite) TestEngineNode_AssembleStep_WithoutPackageJson require.Error(suite.T(), berr, "should return an error") } -func (suite *EngineNodeTestSuite) TestEngineNode_DependenciesStep() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - - //copy cookbook fixture into a temp directory. - parentPath, err := ioutil.TempDir("", "") - require.NoError(suite.T(), err) - defer os.RemoveAll(parentPath) - suite.PipelineData.GitParentPath = parentPath - suite.PipelineData.GitLocalPath = path.Join(parentPath, "npm_analogj_test") - cerr := utils.CopyDir(path.Join("testdata", "node", "npm_analogj_test"), suite.PipelineData.GitLocalPath) - require.NoError(suite.T(), cerr) - - nodeEngine, err := engine.Create("node", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := nodeEngine.DependenciesStep() - - //assert - require.NoError(suite.T(), berr) - require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "npm-shrinkwrap.json"))) -} - func (suite *EngineNodeTestSuite) TestEngineNode_TestStep_AllDisabled() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) @@ -287,18 +263,3 @@ func (suite *EngineNodeTestSuite) TestEngineNode_PackageStep_WithoutLockFiles() require.NoError(suite.T(), berr) require.False(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "npm-shrinkwrap.json"))) } - -func (suite *EngineNodeTestSuite) TestEngineNode_DistStep_WithoutCredentials() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().IsSet("npm_auth_token").MinTimes(1).Return(false) - - nodeEngine, err := engine.Create("node", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := nodeEngine.DistStep() - - //assert - require.Error(suite.T(), berr) -} diff --git a/pkg/engine/engine_ruby_test.go b/pkg/engine/engine_ruby_test.go index cdf4119..ced37d2 100644 --- a/pkg/engine/engine_ruby_test.go +++ b/pkg/engine/engine_ruby_test.go @@ -293,18 +293,3 @@ func (suite *EngineRubyTestSuite) TestEngineRuby_PackageStep_WithoutLockFiles() require.NoError(suite.T(), berr) require.False(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile.lock"))) } - -func (suite *EngineRubyTestSuite) TestEngineRuby_DistStep_WithoutCredentials() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().IsSet("rubygems_api_key").MinTimes(1).Return(false) - - rubyEngine, err := engine.Create("ruby", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := rubyEngine.DistStep() - - //assert - require.Error(suite.T(), berr) -} From d8b4c219e4495e3b4b7e341a66e04cca8f656b26 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 07:46:24 -0700 Subject: [PATCH 05/27] print exit codes. --- circle.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index a373644..de1d159 100644 --- a/circle.yml +++ b/circle.yml @@ -48,6 +48,8 @@ jobs: analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV + echo "exit code $?" + docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Chef Docker containers with coverage @@ -60,6 +62,8 @@ jobs: analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV + echo "exit code $?" + docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Node Docker containers with coverage @@ -72,6 +76,7 @@ jobs: analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV + echo "exit code $?" docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Python Docker containers with coverage @@ -83,7 +88,7 @@ jobs: --name $CAPSULECD_ENV \ analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV - + echo "exit code $?" docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Ruby Docker containers with coverage @@ -95,7 +100,7 @@ jobs: --name $CAPSULECD_ENV \ analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV - + echo "exit code $?" docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Golang Docker containers with coverage @@ -107,7 +112,7 @@ jobs: --name $CAPSULECD_ENV \ analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV - + echo "exit code $?" docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: @@ -118,7 +123,7 @@ jobs: # upload to codecov. bash <(curl -s https://codecov.io/bash) -f "*.txt" -s /coverage - + echo "exit code $?" # - run: # name: Merge generated coverage reports using webhook. # command: | From 6a70228a031dc0402273890697a479af87247e2d Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 08:00:30 -0700 Subject: [PATCH 06/27] dont override default shell. --- circle.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/circle.yml b/circle.yml index de1d159..51a5ed7 100644 --- a/circle.yml +++ b/circle.yml @@ -39,7 +39,7 @@ jobs: docker build -f ci/Dockerfile.golang --tag analogj/capsulecd-build:golang . - run: name: Run Base Docker containers with coverage - shell: bash +# shell: bash command: | CAPSULECD_ENV=base echo "#################################################### $CAPSULECD_ENV" @@ -53,7 +53,7 @@ jobs: docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Chef Docker containers with coverage - shell: bash +# shell: bash command: | CAPSULECD_ENV=chef echo "#################################################### $CAPSULECD_ENV" @@ -67,7 +67,7 @@ jobs: docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Node Docker containers with coverage - shell: bash +# shell: bash command: | CAPSULECD_ENV=node echo "#################################################### $CAPSULECD_ENV" @@ -80,7 +80,7 @@ jobs: docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Python Docker containers with coverage - shell: bash +# shell: bash command: | CAPSULECD_ENV=python echo "#################################################### $CAPSULECD_ENV" @@ -92,7 +92,7 @@ jobs: docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Ruby Docker containers with coverage - shell: bash +# shell: bash command: | CAPSULECD_ENV=ruby echo "#################################################### $CAPSULECD_ENV" @@ -104,7 +104,7 @@ jobs: docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Golang Docker containers with coverage - shell: bash +# shell: bash command: | CAPSULECD_ENV=golang echo "#################################################### $CAPSULECD_ENV" @@ -117,13 +117,14 @@ jobs: - run: name: Merge coverage reports and submit - shell: bash +# shell: bash command: | ls -alt /coverage # upload to codecov. bash <(curl -s https://codecov.io/bash) -f "*.txt" -s /coverage echo "exit code $?" + when: always # - run: # name: Merge generated coverage reports using webhook. # command: | From ceee48ea1ccc04b1b1fc341ecd362e704211a6f3 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 08:01:24 -0700 Subject: [PATCH 07/27] dont override default shell. --- circle.yml | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/circle.yml b/circle.yml index 51a5ed7..f36ca00 100644 --- a/circle.yml +++ b/circle.yml @@ -39,7 +39,6 @@ jobs: docker build -f ci/Dockerfile.golang --tag analogj/capsulecd-build:golang . - run: name: Run Base Docker containers with coverage -# shell: bash command: | CAPSULECD_ENV=base echo "#################################################### $CAPSULECD_ENV" @@ -48,12 +47,9 @@ jobs: analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV - echo "exit code $?" - docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Chef Docker containers with coverage -# shell: bash command: | CAPSULECD_ENV=chef echo "#################################################### $CAPSULECD_ENV" @@ -62,12 +58,9 @@ jobs: analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV - echo "exit code $?" - docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Node Docker containers with coverage -# shell: bash command: | CAPSULECD_ENV=node echo "#################################################### $CAPSULECD_ENV" @@ -76,11 +69,9 @@ jobs: analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV - echo "exit code $?" docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Python Docker containers with coverage -# shell: bash command: | CAPSULECD_ENV=python echo "#################################################### $CAPSULECD_ENV" @@ -88,11 +79,10 @@ jobs: --name $CAPSULECD_ENV \ analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV - echo "exit code $?" + docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Ruby Docker containers with coverage -# shell: bash command: | CAPSULECD_ENV=ruby echo "#################################################### $CAPSULECD_ENV" @@ -100,11 +90,10 @@ jobs: --name $CAPSULECD_ENV \ analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV - echo "exit code $?" + docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Run Golang Docker containers with coverage -# shell: bash command: | CAPSULECD_ENV=golang echo "#################################################### $CAPSULECD_ENV" @@ -112,18 +101,16 @@ jobs: --name $CAPSULECD_ENV \ analogj/capsulecd-build:$CAPSULECD_ENV \ ./ci/coverage.sh $CAPSULECD_ENV - echo "exit code $?" + docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - run: name: Merge coverage reports and submit -# shell: bash command: | ls -alt /coverage # upload to codecov. bash <(curl -s https://codecov.io/bash) -f "*.txt" -s /coverage - echo "exit code $?" when: always # - run: # name: Merge generated coverage reports using webhook. From d1db6d99de257a7fa115c1d473162e9ee05718d2 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 08:19:52 -0700 Subject: [PATCH 08/27] always run all test suites before returning. --- circle.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index f36ca00..f1cc4d4 100644 --- a/circle.yml +++ b/circle.yml @@ -48,6 +48,7 @@ jobs: ./ci/coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage + when: always - run: name: Run Chef Docker containers with coverage command: | @@ -59,6 +60,7 @@ jobs: ./ci/coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage + when: always - run: name: Run Node Docker containers with coverage command: | @@ -70,6 +72,7 @@ jobs: ./ci/coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage + when: always - run: name: Run Python Docker containers with coverage command: | @@ -81,6 +84,7 @@ jobs: ./ci/coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage + when: always - run: name: Run Ruby Docker containers with coverage command: | @@ -92,6 +96,7 @@ jobs: ./ci/coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage + when: always - run: name: Run Golang Docker containers with coverage command: | @@ -103,7 +108,7 @@ jobs: ./ci/coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage - + when: always - run: name: Merge coverage reports and submit command: | From fa601cd2995bf8338e0ba0bcb9e642facd6e9a7a Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 08:34:25 -0700 Subject: [PATCH 09/27] fix import cycle. --- pkg/mgr/mgr_ruby_bundler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mgr/mgr_ruby_bundler_test.go b/pkg/mgr/mgr_ruby_bundler_test.go index db1924c..07fd6cb 100644 --- a/pkg/mgr/mgr_ruby_bundler_test.go +++ b/pkg/mgr/mgr_ruby_bundler_test.go @@ -1,4 +1,4 @@ -package mgr +package mgr_test import ( "github.com/stretchr/testify/suite" From 1833ebf1d069580c7167406db0b0ca7bed67c0b5 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 08:50:48 -0700 Subject: [PATCH 10/27] make sure that all test suites are targeted to only run on their language. --- circle.yml | 16 +++++++++++++--- pkg/mgr/mgr_chef_berkshelf_test.go | 1 + pkg/mgr/mgr_golang_dep_test.go | 1 + pkg/mgr/mgr_golang_glide_test.go | 1 + pkg/mgr/mgr_node_npm_test.go | 1 + pkg/mgr/mgr_python_pip_test.go | 2 ++ pkg/mgr/mgr_ruby_bundler_test.go | 2 ++ 7 files changed, 21 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index f1cc4d4..5201aad 100644 --- a/circle.yml +++ b/circle.yml @@ -121,6 +121,16 @@ jobs: # name: Merge generated coverage reports using webhook. # command: | - - -# +# build capsulecd every night, to ensure that language/dependency breaking changes are caught early. +workflows: + version: 2 + nightly: + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - master + jobs: + - build diff --git a/pkg/mgr/mgr_chef_berkshelf_test.go b/pkg/mgr/mgr_chef_berkshelf_test.go index e60b78f..126b576 100644 --- a/pkg/mgr/mgr_chef_berkshelf_test.go +++ b/pkg/mgr/mgr_chef_berkshelf_test.go @@ -1,3 +1,4 @@ +// +build chef package mgr_test import ( diff --git a/pkg/mgr/mgr_golang_dep_test.go b/pkg/mgr/mgr_golang_dep_test.go index 829bda0..8a5e0ba 100644 --- a/pkg/mgr/mgr_golang_dep_test.go +++ b/pkg/mgr/mgr_golang_dep_test.go @@ -1,3 +1,4 @@ +// +build golang package mgr_test import ( diff --git a/pkg/mgr/mgr_golang_glide_test.go b/pkg/mgr/mgr_golang_glide_test.go index da0aafc..3abd3de 100644 --- a/pkg/mgr/mgr_golang_glide_test.go +++ b/pkg/mgr/mgr_golang_glide_test.go @@ -1,3 +1,4 @@ +// +build golang package mgr_test import ( diff --git a/pkg/mgr/mgr_node_npm_test.go b/pkg/mgr/mgr_node_npm_test.go index 515567b..9538006 100644 --- a/pkg/mgr/mgr_node_npm_test.go +++ b/pkg/mgr/mgr_node_npm_test.go @@ -1,3 +1,4 @@ +// +build node package mgr_test import ( diff --git a/pkg/mgr/mgr_python_pip_test.go b/pkg/mgr/mgr_python_pip_test.go index 994ea29..7e83ad7 100644 --- a/pkg/mgr/mgr_python_pip_test.go +++ b/pkg/mgr/mgr_python_pip_test.go @@ -1,3 +1,5 @@ +// +build python + package mgr_test import ( diff --git a/pkg/mgr/mgr_ruby_bundler_test.go b/pkg/mgr/mgr_ruby_bundler_test.go index 07fd6cb..37c2fb7 100644 --- a/pkg/mgr/mgr_ruby_bundler_test.go +++ b/pkg/mgr/mgr_ruby_bundler_test.go @@ -1,3 +1,5 @@ +// +build ruby + package mgr_test import ( From 71602a55b605a1c0847a115dd776b98166e0bec1 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 09:00:02 -0700 Subject: [PATCH 11/27] build every week. --- circle.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 5201aad..e89ac80 100644 --- a/circle.yml +++ b/circle.yml @@ -121,13 +121,14 @@ jobs: # name: Merge generated coverage reports using webhook. # command: | -# build capsulecd every night, to ensure that language/dependency breaking changes are caught early. +# build capsulecd every week, to ensure that language/dependency breaking changes are caught early. +# https://crontab.guru/every-week workflows: version: 2 nightly: triggers: - schedule: - cron: "0 0 * * *" + cron: "0 0 * * 0" filters: branches: only: From 665bb1e547c6e954523e49005a4ff8a95107f262 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 09:01:31 -0700 Subject: [PATCH 12/27] comment out workflows.. --- circle.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/circle.yml b/circle.yml index e89ac80..c052e44 100644 --- a/circle.yml +++ b/circle.yml @@ -123,15 +123,15 @@ jobs: # build capsulecd every week, to ensure that language/dependency breaking changes are caught early. # https://crontab.guru/every-week -workflows: - version: 2 - nightly: - triggers: - - schedule: - cron: "0 0 * * 0" - filters: - branches: - only: - - master - jobs: - - build +#workflows: +# version: 2 +# nightly: +# triggers: +# - schedule: +# cron: "0 0 * * 0" +# filters: +# branches: +# only: +# - master +# jobs: +# - build From 6b74e94c2a6168bb2603344373128aadb0024572 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 09:04:30 -0700 Subject: [PATCH 13/27] re-add workflows. --- circle.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/circle.yml b/circle.yml index c052e44..ca8f9bd 100644 --- a/circle.yml +++ b/circle.yml @@ -123,15 +123,15 @@ jobs: # build capsulecd every week, to ensure that language/dependency breaking changes are caught early. # https://crontab.guru/every-week -#workflows: -# version: 2 -# nightly: -# triggers: -# - schedule: -# cron: "0 0 * * 0" -# filters: -# branches: -# only: -# - master -# jobs: -# - build +workflows: + version: 2 + nightly: + triggers: + - schedule: + cron: "0 0 * * 0" + filters: + branches: + only: + - master + jobs: + - build From a5fdf4ee80268b17c17a17b254aa992d5ee72a04 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 09:06:40 -0700 Subject: [PATCH 14/27] rename config.yaml in circleci dir. --- circle.yml => .circleci/config.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename circle.yml => .circleci/config.yml (100%) diff --git a/circle.yml b/.circleci/config.yml similarity index 100% rename from circle.yml rename to .circleci/config.yml From 9c0456f002c39387966dfe11acb180635d43efb4 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 09:09:11 -0700 Subject: [PATCH 15/27] workflows only work on master. --- .circleci/config.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ca8f9bd..1cab95e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -123,15 +123,15 @@ jobs: # build capsulecd every week, to ensure that language/dependency breaking changes are caught early. # https://crontab.guru/every-week -workflows: - version: 2 - nightly: - triggers: - - schedule: - cron: "0 0 * * 0" - filters: - branches: - only: - - master - jobs: - - build +#workflows: +# version: 2 +# nightly: +# triggers: +# - schedule: +# cron: "0 0 * * 0" +# filters: +# branches: +# only: +# - master +# jobs: +# - build From 4a7a6289591700f2c63752e1160618f3cbfb1837 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 09:39:59 -0700 Subject: [PATCH 16/27] fixes for broken tests. --- ci/Dockerfile.golang | 3 +- ci/Dockerfile.node | 2 +- pkg/engine/engine_chef_test.go | 56 ++++++++++++++------------------ pkg/engine/engine_python_test.go | 5 +-- pkg/engine/engine_ruby_test.go | 50 ++++++++++++++-------------- 5 files changed, 56 insertions(+), 60 deletions(-) diff --git a/ci/Dockerfile.golang b/ci/Dockerfile.golang index 926a2b5..4c95c62 100644 --- a/ci/Dockerfile.golang +++ b/ci/Dockerfile.golang @@ -17,7 +17,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* \ && go get -u gopkg.in/alecthomas/gometalinter.v1 \ && gometalinter.v1 --install \ - && go get github.com/Masterminds/glide + && go get github.com/Masterminds/glide \ + && curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh ENV PKG_CONFIG_PATH="/usr/lib/pkgconfig/:/usr/local/lib/pkgconfig/:/usr/local/lib/libgit2/lib/pkgconfig:/usr/local/lib/openssl/lib/pkgconfig:/usr/local/lib/libssh2" diff --git a/ci/Dockerfile.node b/ci/Dockerfile.node index a2aa5f5..45665e2 100644 --- a/ci/Dockerfile.node +++ b/ci/Dockerfile.node @@ -4,7 +4,7 @@ ## ################################################## FROM analogj/libgit2-crossbuild:linux-amd64 AS base -FROM node:8.2 AS node +FROM node:8.11 AS node MAINTAINER Jason Kulatunga RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/pkg/engine/engine_chef_test.go b/pkg/engine/engine_chef_test.go index ec877c7..0e2e74d 100644 --- a/pkg/engine/engine_chef_test.go +++ b/pkg/engine/engine_chef_test.go @@ -137,9 +137,7 @@ func (suite *EngineChefTestSuite) TestEngineChef_AssembleStep_WithMinimalCookboo //assert require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Rakefile"))) - require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Berksfile"))) require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, ".gitignore"))) - require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile"))) } func (suite *EngineChefTestSuite) TestEngineChef_AssembleStep_WithoutMetadata() { @@ -269,32 +267,28 @@ func (suite *EngineChefTestSuite) TestEngineChef_TestStep_SecurityCheckFailure() require.Error(suite.T(), berr) } -func (suite *EngineChefTestSuite) TestEngineChef_PackageStep_WithoutLockFiles() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().GetBool("mgr_keep_lock_file").MinTimes(1).Return(false) - - //copy cookbook fixture into a temp directory. - parentPath, err := ioutil.TempDir("", "") - require.NoError(suite.T(), err) - defer os.RemoveAll(parentPath) - suite.PipelineData.GitParentPath = parentPath - cpath, cerr := utils.GitClone(parentPath, "cookbook_analogj_test", "https://github.com/AnalogJ/cookbook_analogj_test.git") - require.NoError(suite.T(), cerr) - suite.PipelineData.GitLocalPath = cpath - - chefEngine, err := engine.Create("chef", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := chefEngine.PackageStep() - - //assert - require.NoError(suite.T(), berr) - require.False(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Berksfile.lock"))) - require.False(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile.lock"))) -} - -func (suite *EngineChefTestSuite) TestEngineChef_DistStep_WithoutCredentials() { - -} +//func (suite *EngineChefTestSuite) TestEngineChef_PackageStep_WithoutLockFiles() { +// //setup +// suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) +// suite.Config.EXPECT().GetBool("mgr_keep_lock_file").MinTimes(1).Return(false) +// +// //copy cookbook fixture into a temp directory. +// parentPath, err := ioutil.TempDir("", "") +// require.NoError(suite.T(), err) +// defer os.RemoveAll(parentPath) +// suite.PipelineData.GitParentPath = parentPath +// cpath, cerr := utils.GitClone(parentPath, "cookbook_analogj_test", "https://github.com/AnalogJ/cookbook_analogj_test.git") +// require.NoError(suite.T(), cerr) +// suite.PipelineData.GitLocalPath = cpath +// +// chefEngine, err := engine.Create("chef", suite.PipelineData, suite.Config, suite.Scm) +// require.NoError(suite.T(), err) +// +// //test +// berr := chefEngine.PackageStep() +// +// //assert +// require.NoError(suite.T(), berr) +// require.False(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Berksfile.lock"))) +// require.False(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile.lock"))) +//} diff --git a/pkg/engine/engine_python_test.go b/pkg/engine/engine_python_test.go index c1d3bf4..1643c64 100644 --- a/pkg/engine/engine_python_test.go +++ b/pkg/engine/engine_python_test.go @@ -77,8 +77,8 @@ func TestEnginePython_TestSuite(t *testing.T) { func (suite *EnginePythonTestSuite) TestEnginePython_ValidateTools() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().GetBool("engine_disable_lint").Return(false) - suite.Config.EXPECT().GetBool("engine_disable_security_check").Return(false) + suite.Config.EXPECT().GetBool("engine_disable_lint").MinTimes(1).Return(false) + suite.Config.EXPECT().GetBool("engine_disable_security_check").MinTimes(1).Return(false) pythonEngine, err := engine.Create("python", suite.PipelineData, suite.Config, suite.Scm) require.NoError(suite.T(), err) @@ -173,6 +173,7 @@ func (suite *EnginePythonTestSuite) TestEnginePython_TestStep_AllDisabled() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) suite.Config.EXPECT().GetBool(gomock.Any()).MinTimes(1).Return(true) + suite.Config.EXPECT().GetString("engine_cmd_test").MinTimes(1).Return("exit 0") //copy cookbook fixture into a temp directory. parentPath, err := ioutil.TempDir("", "") diff --git a/pkg/engine/engine_ruby_test.go b/pkg/engine/engine_ruby_test.go index ced37d2..485b3b4 100644 --- a/pkg/engine/engine_ruby_test.go +++ b/pkg/engine/engine_ruby_test.go @@ -139,7 +139,6 @@ func (suite *EngineRubyTestSuite) TestEngineRuby_AssembleStep_WithMinimalGem() { require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Rakefile"))) require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "spec"))) require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, ".gitignore"))) - require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile"))) require.True(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "gem_analogj_test-0.1.4.gem"))) } @@ -171,6 +170,7 @@ func (suite *EngineRubyTestSuite) TestEngineRuby_TestStep_AllDisabled() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) suite.Config.EXPECT().GetBool(gomock.Any()).MinTimes(1).Return(true) + suite.Config.EXPECT().GetString("engine_cmd_test").MinTimes(1).Return("exit 0") //copy cookbook fixture into a temp directory. parentPath, err := ioutil.TempDir("", "") @@ -269,27 +269,27 @@ func (suite *EngineRubyTestSuite) TestEngineRuby_TestStep_SecurityCheckFailure() require.Error(suite.T(), berr) } -func (suite *EngineRubyTestSuite) TestEngineRuby_PackageStep_WithoutLockFiles() { - //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().GetBool("mgr_keep_lock_file").MinTimes(1).Return(false) - - //copy cookbook fixture into a temp directory. - parentPath, err := ioutil.TempDir("", "") - require.NoError(suite.T(), err) - defer os.RemoveAll(parentPath) - suite.PipelineData.GitParentPath = parentPath - cpath, cerr := utils.GitClone(parentPath, "gem_analogj_test", "https://github.com/AnalogJ/gem_analogj_test.git") - require.NoError(suite.T(), cerr) - suite.PipelineData.GitLocalPath = cpath - - rubyEngine, err := engine.Create("ruby", suite.PipelineData, suite.Config, suite.Scm) - require.NoError(suite.T(), err) - - //test - berr := rubyEngine.PackageStep() - - //assert - require.NoError(suite.T(), berr) - require.False(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile.lock"))) -} +//func (suite *EngineRubyTestSuite) TestEngineRuby_PackageStep_WithoutLockFiles() { +// //setup +// suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) +// suite.Config.EXPECT().GetBool("mgr_keep_lock_file").MinTimes(1).Return(false) +// +// //copy cookbook fixture into a temp directory. +// parentPath, err := ioutil.TempDir("", "") +// require.NoError(suite.T(), err) +// defer os.RemoveAll(parentPath) +// suite.PipelineData.GitParentPath = parentPath +// cpath, cerr := utils.GitClone(parentPath, "gem_analogj_test", "https://github.com/AnalogJ/gem_analogj_test.git") +// require.NoError(suite.T(), cerr) +// suite.PipelineData.GitLocalPath = cpath +// +// rubyEngine, err := engine.Create("ruby", suite.PipelineData, suite.Config, suite.Scm) +// require.NoError(suite.T(), err) +// +// //test +// berr := rubyEngine.PackageStep() +// +// //assert +// require.NoError(suite.T(), berr) +// require.False(suite.T(), utils.FileExists(path.Join(suite.PipelineData.GitLocalPath, "Gemfile.lock"))) +//} From 8be3cf09c745435ef6d7d7f229133e929bdd51ea Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 10:00:32 -0700 Subject: [PATCH 17/27] ensure blank line after tag. --- pkg/mgr/mgr_chef_berkshelf_test.go | 1 + pkg/mgr/mgr_golang_dep_test.go | 1 + pkg/mgr/mgr_golang_glide_test.go | 1 + pkg/mgr/mgr_node_npm_test.go | 1 + 4 files changed, 4 insertions(+) diff --git a/pkg/mgr/mgr_chef_berkshelf_test.go b/pkg/mgr/mgr_chef_berkshelf_test.go index 126b576..d81a5ed 100644 --- a/pkg/mgr/mgr_chef_berkshelf_test.go +++ b/pkg/mgr/mgr_chef_berkshelf_test.go @@ -1,4 +1,5 @@ // +build chef + package mgr_test import ( diff --git a/pkg/mgr/mgr_golang_dep_test.go b/pkg/mgr/mgr_golang_dep_test.go index 8a5e0ba..f5151a0 100644 --- a/pkg/mgr/mgr_golang_dep_test.go +++ b/pkg/mgr/mgr_golang_dep_test.go @@ -1,4 +1,5 @@ // +build golang + package mgr_test import ( diff --git a/pkg/mgr/mgr_golang_glide_test.go b/pkg/mgr/mgr_golang_glide_test.go index 3abd3de..37c605a 100644 --- a/pkg/mgr/mgr_golang_glide_test.go +++ b/pkg/mgr/mgr_golang_glide_test.go @@ -1,4 +1,5 @@ // +build golang + package mgr_test import ( diff --git a/pkg/mgr/mgr_node_npm_test.go b/pkg/mgr/mgr_node_npm_test.go index 9538006..1af3e7f 100644 --- a/pkg/mgr/mgr_node_npm_test.go +++ b/pkg/mgr/mgr_node_npm_test.go @@ -1,4 +1,5 @@ // +build node + package mgr_test import ( From 70daa35922748da82c3c6848cfae7938106fa7fc Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 10:02:47 -0700 Subject: [PATCH 18/27] fixes for engine. --- pkg/engine/engine_chef_test.go | 1 + pkg/engine/engine_python_test.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/engine/engine_chef_test.go b/pkg/engine/engine_chef_test.go index 0e2e74d..7f0d1a6 100644 --- a/pkg/engine/engine_chef_test.go +++ b/pkg/engine/engine_chef_test.go @@ -169,6 +169,7 @@ func (suite *EngineChefTestSuite) TestEngineChef_TestStep_AllDisabled() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) suite.Config.EXPECT().GetBool(gomock.Any()).MinTimes(1).Return(true) + suite.Config.EXPECT().GetString("engine_cmd_test").Return("exit 0") //copy cookbook fixture into a temp directory. parentPath, err := ioutil.TempDir("", "") diff --git a/pkg/engine/engine_python_test.go b/pkg/engine/engine_python_test.go index 1643c64..7a5fa8e 100644 --- a/pkg/engine/engine_python_test.go +++ b/pkg/engine/engine_python_test.go @@ -77,8 +77,8 @@ func TestEnginePython_TestSuite(t *testing.T) { func (suite *EnginePythonTestSuite) TestEnginePython_ValidateTools() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().GetBool("engine_disable_lint").MinTimes(1).Return(false) - suite.Config.EXPECT().GetBool("engine_disable_security_check").MinTimes(1).Return(false) + suite.Config.EXPECT().GetBool("engine_disable_lint").Return(false) + suite.Config.EXPECT().GetBool("engine_disable_security_check").Return(false) pythonEngine, err := engine.Create("python", suite.PipelineData, suite.Config, suite.Scm) require.NoError(suite.T(), err) From 4553dc9e1ae9245b6eed26863180384f4d23065b Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 10:32:55 -0700 Subject: [PATCH 19/27] fixes for engine. --- pkg/engine/engine_python_test.go | 4 ++-- pkg/mgr/mgr_chef_berkshelf_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/engine/engine_python_test.go b/pkg/engine/engine_python_test.go index 7a5fa8e..620d5a7 100644 --- a/pkg/engine/engine_python_test.go +++ b/pkg/engine/engine_python_test.go @@ -77,8 +77,8 @@ func TestEnginePython_TestSuite(t *testing.T) { func (suite *EnginePythonTestSuite) TestEnginePython_ValidateTools() { //setup suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) - suite.Config.EXPECT().GetBool("engine_disable_lint").Return(false) - suite.Config.EXPECT().GetBool("engine_disable_security_check").Return(false) + //suite.Config.EXPECT().GetBool("engine_disable_lint").Return(false) + //suite.Config.EXPECT().GetBool("engine_disable_security_check").Return(false) pythonEngine, err := engine.Create("python", suite.PipelineData, suite.Config, suite.Scm) require.NoError(suite.T(), err) diff --git a/pkg/mgr/mgr_chef_berkshelf_test.go b/pkg/mgr/mgr_chef_berkshelf_test.go index d81a5ed..77ee83f 100644 --- a/pkg/mgr/mgr_chef_berkshelf_test.go +++ b/pkg/mgr/mgr_chef_berkshelf_test.go @@ -53,7 +53,7 @@ func TestMgrChefBerkshelf_TestSuite(t *testing.T) { func (suite *MgrChefBerkshelfTestSuite) TestMgrChefBerkshelfTestSuite_DependenciesStep() { //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) //copy cookbook fixture into a temp directory. parentPath, err := ioutil.TempDir("", "") @@ -83,7 +83,7 @@ func (suite *MgrChefBerkshelfTestSuite) TestMgrChefBerkshelfTestSuite_Dependenci func (suite *MgrChefBerkshelfTestSuite) TestMgrChefBerkshelfTestSuite_WithoutCredentials() { //setup - suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) + //suite.Config.EXPECT().SetDefault(gomock.Any(), gomock.Any()).MinTimes(1) suite.Config.EXPECT().IsSet("chef_supermarket_username").MinTimes(1).Return(false) mgrChefBerkshelf, err := mgr.Create("berkshelf", suite.PipelineData, suite.Config, nil) From f481ee9bc33b3d3772225f953ac64a4ce31b6810 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 12:46:07 -0700 Subject: [PATCH 20/27] print debug information about build env to figure out why go test is failing. --- ci/coverage.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ci/coverage.sh b/ci/coverage.sh index 2cc33b3..b926fa4 100755 --- a/ci/coverage.sh +++ b/ci/coverage.sh @@ -1,7 +1,13 @@ #!/usr/bin/env bash +# print out information about env. +uname -a +ld --version +pkg-config --version + set -e mkdir -p /coverage + echo "" > /coverage/coverage-$1.txt for d in $(go list ./... | grep -v vendor); do From 301f26e4ae4c664451c112b18c2191a6783969ac Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 17:32:16 -0700 Subject: [PATCH 21/27] refactoring to build test binaries seperately from testing. --- .circleci/config.yml | 36 +++++++++++++-------------- .gitignore | 4 ++- ci/Dockerfile.base | 10 ++++++-- ci/Dockerfile.chef | 58 ++++++++++++++++++++++++++++++-------------- ci/Dockerfile.golang | 54 +++++++++++++++++++++++++++++------------ ci/Dockerfile.node | 54 +++++++++++++++++++++++++++++------------ ci/Dockerfile.python | 53 ++++++++++++++++++++++++++++------------ ci/Dockerfile.ruby | 53 ++++++++++++++++++++++++++++------------ ci/coverage.sh | 21 ---------------- ci/test-build.sh | 17 +++++++++++++ ci/test-coverage.sh | 32 ++++++++++++++++++++++++ 11 files changed, 268 insertions(+), 124 deletions(-) delete mode 100755 ci/coverage.sh create mode 100755 ci/test-build.sh create mode 100755 ci/test-coverage.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 1cab95e..0695458 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,12 +31,12 @@ jobs: - run: name: Build application Docker image command: | - docker build -f ci/Dockerfile.base --tag analogj/capsulecd-build:base . - docker build -f ci/Dockerfile.chef --tag analogj/capsulecd-build:chef . - docker build -f ci/Dockerfile.node --tag analogj/capsulecd-build:node . - docker build -f ci/Dockerfile.python --tag analogj/capsulecd-build:python . - docker build -f ci/Dockerfile.ruby --tag analogj/capsulecd-build:ruby . - docker build -f ci/Dockerfile.golang --tag analogj/capsulecd-build:golang . + docker build -f ci/Dockerfile.base --tag analogj/capsulecd-ci:base . + docker build -f ci/Dockerfile.chef --tag analogj/capsulecd-ci:chef . + docker build -f ci/Dockerfile.node --tag analogj/capsulecd-ci:node . + docker build -f ci/Dockerfile.python --tag analogj/capsulecd-ci:python . + docker build -f ci/Dockerfile.ruby --tag analogj/capsulecd-ci:ruby . + docker build -f ci/Dockerfile.golang --tag analogj/capsulecd-ci:golang . - run: name: Run Base Docker containers with coverage command: | @@ -44,8 +44,8 @@ jobs: echo "#################################################### $CAPSULECD_ENV" docker run -e "CI=true" \ --name $CAPSULECD_ENV \ - analogj/capsulecd-build:$CAPSULECD_ENV \ - ./ci/coverage.sh $CAPSULECD_ENV + analogj/capsulecd-ci:$CAPSULECD_ENV \ + ./ci/test-coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage when: always @@ -56,8 +56,8 @@ jobs: echo "#################################################### $CAPSULECD_ENV" docker run -e "CI=true" \ --name $CAPSULECD_ENV \ - analogj/capsulecd-build:$CAPSULECD_ENV \ - ./ci/coverage.sh $CAPSULECD_ENV + analogj/capsulecd-ci:$CAPSULECD_ENV \ + ./ci/test-coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage when: always @@ -68,8 +68,8 @@ jobs: echo "#################################################### $CAPSULECD_ENV" docker run -e "CI=true" \ --name $CAPSULECD_ENV \ - analogj/capsulecd-build:$CAPSULECD_ENV \ - ./ci/coverage.sh $CAPSULECD_ENV + analogj/capsulecd-ci:$CAPSULECD_ENV \ + ./ci/test-coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage when: always @@ -80,8 +80,8 @@ jobs: echo "#################################################### $CAPSULECD_ENV" docker run -e "CI=true" \ --name $CAPSULECD_ENV \ - analogj/capsulecd-build:$CAPSULECD_ENV \ - ./ci/coverage.sh $CAPSULECD_ENV + analogj/capsulecd-ci:$CAPSULECD_ENV \ + ./ci/test-coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage when: always @@ -92,8 +92,8 @@ jobs: echo "#################################################### $CAPSULECD_ENV" docker run -e "CI=true" \ --name $CAPSULECD_ENV \ - analogj/capsulecd-build:$CAPSULECD_ENV \ - ./ci/coverage.sh $CAPSULECD_ENV + analogj/capsulecd-ci:$CAPSULECD_ENV \ + ./ci/test-coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage when: always @@ -104,8 +104,8 @@ jobs: echo "#################################################### $CAPSULECD_ENV" docker run -e "CI=true" \ --name $CAPSULECD_ENV \ - analogj/capsulecd-build:$CAPSULECD_ENV \ - ./ci/coverage.sh $CAPSULECD_ENV + analogj/capsulecd-ci:$CAPSULECD_ENV \ + ./ci/test-coverage.sh $CAPSULECD_ENV docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage when: always diff --git a/.gitignore b/.gitignore index a195367..2596dfb 100644 --- a/.gitignore +++ b/.gitignore @@ -75,4 +75,6 @@ _testmain.go vendor glide.lock /capsulecd-darwin-amd64 -/capsulecd-linux-amd64 \ No newline at end of file +/capsulecd-linux-amd64 + +test_binary \ No newline at end of file diff --git a/ci/Dockerfile.base b/ci/Dockerfile.base index 5d13c5e..3e543f0 100644 --- a/ci/Dockerfile.base +++ b/ci/Dockerfile.base @@ -1,3 +1,5 @@ +ARG PACKAGE_TYPE=base + ################################################# # # Base @@ -6,9 +8,11 @@ # It does not actually build the capsulecd executable # It runs unit tests for each supported engine type. # -# Use the docker containers in dist as an example of what a proper runtime-environment for CapsuleCD looks like. +# Use the docker containers in https://github.com/AnalogJ/capsulecd-docker as an example of what a +# proper runtime-environment for CapsuleCD looks like. # ################################################# + FROM analogj/libgit2-crossbuild:linux-amd64 AS base MAINTAINER Jason Kulatunga WORKDIR /go/src/capsulecd @@ -28,4 +32,6 @@ RUN glide install \ ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt -CMD ci/coverage.sh \ No newline at end of file +RUN ci/test-build.sh ${PACKAGE_TYPE} + +CMD ci/test-coverage.sh \ No newline at end of file diff --git a/ci/Dockerfile.chef b/ci/Dockerfile.chef index c23b06f..1ccabe6 100644 --- a/ci/Dockerfile.chef +++ b/ci/Dockerfile.chef @@ -1,19 +1,52 @@ +ARG PACKAGE_TYPE=chef + +################################################# +# +# Base +# This container should not be used as a runtime environment. +# It is based off a massive build image (crossbuild) which has lots of unnecessary build tools +# It does not actually build the capsulecd executable +# It runs unit tests for each supported engine type. +# +# Use the docker containers in https://github.com/AnalogJ/capsulecd-docker as an example of what a +# proper runtime-environment for CapsuleCD looks like. +# +################################################# + +FROM analogj/libgit2-crossbuild:linux-amd64 AS base +MAINTAINER Jason Kulatunga +WORKDIR /go/src/capsulecd + +RUN apt-get update && apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* \ + && go get github.com/Masterminds/glide + +COPY . . + +## download glide deps & move libgit2 library into expected location. +RUN glide install \ + && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ + && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ + +ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + +RUN ci/test-build.sh ${PACKAGE_TYPE} + ################################################## ## ## ChefDK ## ################################################## -FROM analogj/libgit2-crossbuild:linux-amd64 AS base FROM chef/chefdk AS chef MAINTAINER Jason Kulatunga RUN apt-get update && apt-get install -y --no-install-recommends \ - apt-transport-https \ - ca-certificates \ git \ curl \ - gcc \ - g++ \ + apt-transport-https \ + ca-certificates \ && rm -rf /var/lib/apt/lists/* \ && gem install bundler-audit @@ -25,21 +58,10 @@ RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd6 ENV PATH="/go/bin:/usr/local/go/bin:/root/.chefdk/gem/ruby/2.4.0/bin:${PATH}" \ GOPATH="/go" -ENV PKG_CONFIG_PATH="/usr/lib/pkgconfig/:/usr/local/lib/pkgconfig/:/usr/local/lib/libgit2/lib/pkgconfig:/usr/local/lib/openssl/lib/pkgconfig:/usr/local/lib/libssh2" -RUN go get github.com/Masterminds/glide -COPY --from=base /usr/local/lib/libgit2/ /usr/local/lib/libgit2/ -COPY --from=base /usr/local/lib/libssh2/ /usr/local/lib/libssh2/ -COPY --from=base /usr/local/lib/openssl/ /usr/local/lib/openssl/ +COPY --from=base /go/src/capsulecd /go/src/capsulecd WORKDIR /go/src/capsulecd -COPY . . - -## download glide deps & move libgit2 library into expected location. -RUN glide install \ - && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ - && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ - ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt -CMD ci/coverage.sh \ No newline at end of file +CMD ci/test-coverage.sh \ No newline at end of file diff --git a/ci/Dockerfile.golang b/ci/Dockerfile.golang index 4c95c62..ae039ce 100644 --- a/ci/Dockerfile.golang +++ b/ci/Dockerfile.golang @@ -1,9 +1,44 @@ +ARG PACKAGE_TYPE=golang + +################################################# +# +# Base +# This container should not be used as a runtime environment. +# It is based off a massive build image (crossbuild) which has lots of unnecessary build tools +# It does not actually build the capsulecd executable +# It runs unit tests for each supported engine type. +# +# Use the docker containers in https://github.com/AnalogJ/capsulecd-docker as an example of what a +# proper runtime-environment for CapsuleCD looks like. +# +################################################# + +FROM analogj/libgit2-crossbuild:linux-amd64 AS base +MAINTAINER Jason Kulatunga +WORKDIR /go/src/capsulecd + +RUN apt-get update && apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* \ + && go get github.com/Masterminds/glide + +COPY . . + +## download glide deps & move libgit2 library into expected location. +RUN glide install \ + && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ + && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ + +ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + +RUN ci/test-build.sh ${PACKAGE_TYPE} + ################################################## ## ## Golang ## ################################################## -FROM analogj/libgit2-crossbuild:linux-amd64 AS base FROM golang:1.8 AS golang MAINTAINER Jason Kulatunga @@ -12,29 +47,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ git \ curl \ - gcc \ - g++ \ && rm -rf /var/lib/apt/lists/* \ && go get -u gopkg.in/alecthomas/gometalinter.v1 \ && gometalinter.v1 --install \ && go get github.com/Masterminds/glide \ && curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh -ENV PKG_CONFIG_PATH="/usr/lib/pkgconfig/:/usr/local/lib/pkgconfig/:/usr/local/lib/libgit2/lib/pkgconfig:/usr/local/lib/openssl/lib/pkgconfig:/usr/local/lib/libssh2" - -COPY --from=base /usr/local/lib/libgit2/ /usr/local/lib/libgit2/ -COPY --from=base /usr/local/lib/libssh2/ /usr/local/lib/libssh2/ -COPY --from=base /usr/local/lib/openssl/ /usr/local/lib/openssl/ +COPY --from=base /go/src/capsulecd /go/src/capsulecd WORKDIR /go/src/capsulecd -COPY . . - -## download glide deps & move libgit2 library into expected location. -RUN glide install \ - && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ - && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ - ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt -CMD ci/coverage.sh \ No newline at end of file +CMD ci/test-coverage.sh \ No newline at end of file diff --git a/ci/Dockerfile.node b/ci/Dockerfile.node index 45665e2..1cc2b0c 100644 --- a/ci/Dockerfile.node +++ b/ci/Dockerfile.node @@ -1,9 +1,44 @@ +ARG PACKAGE_TYPE=node + +################################################# +# +# Base +# This container should not be used as a runtime environment. +# It is based off a massive build image (crossbuild) which has lots of unnecessary build tools +# It does not actually build the capsulecd executable +# It runs unit tests for each supported engine type. +# +# Use the docker containers in https://github.com/AnalogJ/capsulecd-docker as an example of what a +# proper runtime-environment for CapsuleCD looks like. +# +################################################# + +FROM analogj/libgit2-crossbuild:linux-amd64 AS base +MAINTAINER Jason Kulatunga +WORKDIR /go/src/capsulecd + +RUN apt-get update && apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* \ + && go get github.com/Masterminds/glide + +COPY . . + +## download glide deps & move libgit2 library into expected location. +RUN glide install \ + && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ + && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ + +ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + +RUN ci/test-build.sh ${PACKAGE_TYPE} + ################################################## ## ## Node ## ################################################## -FROM analogj/libgit2-crossbuild:linux-amd64 AS base FROM node:8.11 AS node MAINTAINER Jason Kulatunga @@ -12,8 +47,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ git \ curl \ - gcc \ - g++ \ && rm -rf /var/lib/apt/lists/* \ && npm install -g eslint \ && npm install -g nsp @@ -26,21 +59,10 @@ RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd6 ENV PATH="/go/bin:/usr/local/go/bin:${PATH}" \ GOPATH="/go" -ENV PKG_CONFIG_PATH="/usr/lib/pkgconfig/:/usr/local/lib/pkgconfig/:/usr/local/lib/libgit2/lib/pkgconfig:/usr/local/lib/openssl/lib/pkgconfig:/usr/local/lib/libssh2" -RUN go get github.com/Masterminds/glide -COPY --from=base /usr/local/lib/libgit2/ /usr/local/lib/libgit2/ -COPY --from=base /usr/local/lib/libssh2/ /usr/local/lib/libssh2/ -COPY --from=base /usr/local/lib/openssl/ /usr/local/lib/openssl/ +COPY --from=base /go/src/capsulecd /go/src/capsulecd WORKDIR /go/src/capsulecd -COPY . . - -## download glide deps & move libgit2 library into expected location. -RUN glide install \ - && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ - && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ - ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt -CMD ci/coverage.sh \ No newline at end of file +CMD ci/test-coverage.sh \ No newline at end of file diff --git a/ci/Dockerfile.python b/ci/Dockerfile.python index d119000..345e81e 100644 --- a/ci/Dockerfile.python +++ b/ci/Dockerfile.python @@ -1,9 +1,43 @@ +ARG PACKAGE_TYPE=python + +################################################# +# +# Base +# This container should not be used as a runtime environment. +# It is based off a massive build image (crossbuild) which has lots of unnecessary build tools +# It does not actually build the capsulecd executable +# It runs unit tests for each supported engine type. +# +# Use the docker containers in https://github.com/AnalogJ/capsulecd-docker as an example of what a +# proper runtime-environment for CapsuleCD looks like. +# +################################################# +FROM analogj/libgit2-crossbuild:linux-amd64 AS base +MAINTAINER Jason Kulatunga +WORKDIR /go/src/capsulecd + +RUN apt-get update && apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* \ + && go get github.com/Masterminds/glide + +COPY . . + +## download glide deps & move libgit2 library into expected location. +RUN glide install \ + && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ + && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ + +ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + +RUN ci/test-build.sh ${PACKAGE_TYPE} + ################################################## ## ## Python ## ################################################## -FROM analogj/libgit2-crossbuild:linux-amd64 AS base FROM python:2.7 AS python MAINTAINER Jason Kulatunga @@ -12,8 +46,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ git \ curl \ - gcc \ - g++ \ pylint \ && rm -rf /var/lib/apt/lists/* \ && pip install tox \ @@ -28,21 +60,10 @@ RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd6 ENV PATH="/go/bin:/usr/local/go/bin:${PATH}" \ GOPATH="/go" -ENV PKG_CONFIG_PATH="/usr/lib/pkgconfig/:/usr/local/lib/pkgconfig/:/usr/local/lib/libgit2/lib/pkgconfig:/usr/local/lib/openssl/lib/pkgconfig:/usr/local/lib/libssh2" -RUN go get github.com/Masterminds/glide -COPY --from=base /usr/local/lib/libgit2/ /usr/local/lib/libgit2/ -COPY --from=base /usr/local/lib/libssh2/ /usr/local/lib/libssh2/ -COPY --from=base /usr/local/lib/openssl/ /usr/local/lib/openssl/ +COPY --from=base /go/src/capsulecd /go/src/capsulecd WORKDIR /go/src/capsulecd -COPY . . - -## download glide deps & move libgit2 library into expected location. -RUN glide install \ - && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ - && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ - ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt -CMD ci/coverage.sh \ No newline at end of file +CMD ci/test-coverage.sh \ No newline at end of file diff --git a/ci/Dockerfile.ruby b/ci/Dockerfile.ruby index 43d81b0..a5e9ef3 100644 --- a/ci/Dockerfile.ruby +++ b/ci/Dockerfile.ruby @@ -1,9 +1,43 @@ +ARG PACKAGE_TYPE=ruby + +################################################# +# +# Base +# This container should not be used as a runtime environment. +# It is based off a massive build image (crossbuild) which has lots of unnecessary build tools +# It does not actually build the capsulecd executable +# It runs unit tests for each supported engine type. +# +# Use the docker containers in https://github.com/AnalogJ/capsulecd-docker as an example of what a +# proper runtime-environment for CapsuleCD looks like. +# +################################################# +FROM analogj/libgit2-crossbuild:linux-amd64 AS base +MAINTAINER Jason Kulatunga +WORKDIR /go/src/capsulecd + +RUN apt-get update && apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* \ + && go get github.com/Masterminds/glide + +COPY . . + +## download glide deps & move libgit2 library into expected location. +RUN glide install \ + && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ + && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ + +ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + +RUN ci/test-build.sh ${PACKAGE_TYPE} + ################################################## ## ## Ruby ## ################################################## -FROM analogj/libgit2-crossbuild:linux-amd64 AS base FROM ruby:2.4 AS ruby MAINTAINER Jason Kulatunga @@ -12,8 +46,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ git \ curl \ - gcc \ - g++ \ && rm -rf /var/lib/apt/lists/* \ && gem install rubocop \ && gem install rake \ @@ -28,21 +60,10 @@ RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd6 ENV PATH="/go/bin:/usr/local/go/bin:${PATH}" \ GOPATH="/go" -ENV PKG_CONFIG_PATH="/usr/lib/pkgconfig/:/usr/local/lib/pkgconfig/:/usr/local/lib/libgit2/lib/pkgconfig:/usr/local/lib/openssl/lib/pkgconfig:/usr/local/lib/libssh2" -RUN go get github.com/Masterminds/glide -COPY --from=base /usr/local/lib/libgit2/ /usr/local/lib/libgit2/ -COPY --from=base /usr/local/lib/libssh2/ /usr/local/lib/libssh2/ -COPY --from=base /usr/local/lib/openssl/ /usr/local/lib/openssl/ +COPY --from=base /go/src/capsulecd /go/src/capsulecd WORKDIR /go/src/capsulecd -COPY . . - -## download glide deps & move libgit2 library into expected location. -RUN glide install \ - && mkdir -p /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build \ - && cp -r /usr/local/lib/libgit2/lib/pkgconfig/. /go/src/capsulecd/vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/ - ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt -CMD ci/coverage.sh \ No newline at end of file +CMD ci/test-coverage.sh \ No newline at end of file diff --git a/ci/coverage.sh b/ci/coverage.sh deleted file mode 100755 index b926fa4..0000000 --- a/ci/coverage.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -# print out information about env. -uname -a -ld --version -pkg-config --version - -set -e -mkdir -p /coverage - -echo "" > /coverage/coverage-$1.txt - -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic -tags="static $1" $d - if [ -f profile.out ]; then - cat profile.out >> /coverage/coverage-$1.txt - rm profile.out - fi -done - -ls /coverage \ No newline at end of file diff --git a/ci/test-build.sh b/ci/test-build.sh new file mode 100755 index 0000000..08ff22a --- /dev/null +++ b/ci/test-build.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# because capsulecd depends on compiled binaries (even for testing) we'll be building the test binaries first in the "build container" and then +# executing them in a "runtime container" to get coverage/profiling data. +# +# this script generates the test binaries in the "build container" + +set -e + +PACKAGE_NAME="capsulecd" + +for d in $(go list ./... | grep -v vendor); do + # determine the output path + OUTPUT_PATH=$(echo "$d" | sed -e "s/^${PACKAGE_NAME}\///") + + go test -race -covermode=atomic -tags="static $1" -c -o=${OUTPUT_PATH}/test_binary $d +done \ No newline at end of file diff --git a/ci/test-coverage.sh b/ci/test-coverage.sh new file mode 100755 index 0000000..58ebe18 --- /dev/null +++ b/ci/test-coverage.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + + +# because capsulecd depends on compiled binaries (even for testing) we'll be building the test binaries first in the "build container" and then +# executing them in a "runtime container" to get coverage/profiling data. +# +# this script executes the test binaries in the "runtime container" + +set -e +mkdir -p /coverage + +echo "" > /coverage/coverage-$1.txt + +PACKAGE_NAME="capsulecd" + +for d in $(go list ./... | grep -v vendor); do + # determine the output path + TEST_BINARY_PATH=$(echo "$d" | sed -e "s/^${PACKAGE_NAME}\///") + TEST_BINARY="${TEST_BINARY_PATH}/test_binary" + + if [ -f "${TEST_BINARY}" ]; then + pushd ${TEST_BINARY_PATH} + ./test_binary -test.coverprofile=profile.out + if [ -f profile.out ]; then + cat profile.out >> /coverage/coverage-$1.txt + rm profile.out + fi + popd + fi +done + +ls /coverage \ No newline at end of file From a71fa91ed5886f2c827d0dcdcd653f940f3ccb11 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 18:13:38 -0700 Subject: [PATCH 22/27] archive coverage data. --- .circleci/config.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0695458..6f89483 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -109,6 +109,10 @@ jobs: docker cp $CAPSULECD_ENV:/coverage/coverage-$CAPSULECD_ENV.txt /coverage when: always + - store_artifacts: + path: /coverage + destination: coverage + - run: name: Merge coverage reports and submit command: | From 5f0191efb63401434116e0201d648164823950bb Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 27 Aug 2018 18:46:31 -0700 Subject: [PATCH 23/27] ignore codecov upload. --- .circleci/config.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6f89483..9aaf225 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -113,17 +113,15 @@ jobs: path: /coverage destination: coverage - - run: - name: Merge coverage reports and submit - command: | - ls -alt /coverage - - # upload to codecov. - bash <(curl -s https://codecov.io/bash) -f "*.txt" -s /coverage - when: always # - run: -# name: Merge generated coverage reports using webhook. +# name: Merge coverage reports and submit # command: | +# ls -alt /coverage +# +# # upload to codecov. +# bash <(curl -s https://codecov.io/bash) -f "*.txt" -s /coverage +# when: always + # build capsulecd every week, to ensure that language/dependency breaking changes are caught early. # https://crontab.guru/every-week From 98adabee02cfd045f1caaf1c0d10342226239e83 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Tue, 28 Aug 2018 08:28:42 -0700 Subject: [PATCH 24/27] only enable fast linters. --- gometalinter.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/gometalinter.json b/gometalinter.json index 72d1b63..d175953 100644 --- a/gometalinter.json +++ b/gometalinter.json @@ -2,11 +2,5 @@ "Errors": true, "Debug": true, "Deadline": "30m", - "Enable": [ - "golint", - "goimports", - "goconst", - "gosimple", - "staticcheck" - ] + "Fast": true } \ No newline at end of file From d33b24ba60b4f65689b57df3eb424d4eb3b06c08 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Tue, 28 Aug 2018 08:41:17 -0700 Subject: [PATCH 25/27] disbale linter. --- Dockerfile.build | 4 ++-- capsule.yml | 3 ++- ci/Dockerfile.golang | 4 ++-- example.capsule.yml | 2 +- pkg/engine/engine_golang.go | 6 +++--- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Dockerfile.build b/Dockerfile.build index 31189b2..585df74 100644 --- a/Dockerfile.build +++ b/Dockerfile.build @@ -25,8 +25,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ git \ curl \ && rm -rf /var/lib/apt/lists/* \ - && go get -u gopkg.in/alecthomas/gometalinter.v1 \ - && gometalinter.v1 --install \ + && go get -u gopkg.in/alecthomas/gometalinter.v2 \ + && gometalinter.v2 --install \ && go get github.com/Masterminds/glide COPY ./ci/capsulecd.sh /scripts/capsulecd.sh diff --git a/capsule.yml b/capsule.yml index af0adcc..41e56b2 100644 --- a/capsule.yml +++ b/capsule.yml @@ -7,7 +7,8 @@ engine_cmd_compile: - cp /usr/local/linux/lib/pkgconfig/libgit2.pc vendor/gopkg.in/libgit2/git2go.v25/vendor/libgit2/build/libgit2.pc - '. /scripts/toolchains/linux/linux-build-env.sh && go build -ldflags "-X main.goos=linux -X main.goarch=amd64" -o capsulecd-linux-amd64 -tags "static" $(go list ./cmd/...)' engine_cmd_test: 'go test -v -tags "static" $(glide novendor)' -engine_cmd_lint: 'gometalinter.v1 --vendor --config=gometalinter.json ./...' +engine_cmd_lint: 'gometalinter.v2 --vendor --config=gometalinter.json ./...' +engine_disable_lint: true scm_enable_branch_cleanup: true scm_release_assets: - local_path: capsulecd-linux-amd64 diff --git a/ci/Dockerfile.golang b/ci/Dockerfile.golang index ae039ce..a103058 100644 --- a/ci/Dockerfile.golang +++ b/ci/Dockerfile.golang @@ -48,8 +48,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ git \ curl \ && rm -rf /var/lib/apt/lists/* \ - && go get -u gopkg.in/alecthomas/gometalinter.v1 \ - && gometalinter.v1 --install \ + && go get -u gopkg.in/alecthomas/gometalinter.v2 \ + && gometalinter.v2 --install \ && go get github.com/Masterminds/glide \ && curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh diff --git a/example.capsule.yml b/example.capsule.yml index bd18e9f..ada48fd 100644 --- a/example.capsule.yml +++ b/example.capsule.yml @@ -53,7 +53,7 @@ engine_cmd_compile: '' #eg. 'go build $(go list ./cmd/...)' engine_disable_compile: false # Specifies the linter command to validate source -engine_cmd_lint: '' # eg. 'gometalinter.v1 --errors --vendor --deadline=3m ./...' +engine_cmd_lint: '' # eg. 'gometalinter.v2 --errors --vendor --deadline=3m ./...' engine_disable_lint: false # Specifies the test command to validate source diff --git a/pkg/engine/engine_golang.go b/pkg/engine/engine_golang.go index aba8c5d..73bfe43 100644 --- a/pkg/engine/engine_golang.go +++ b/pkg/engine/engine_golang.go @@ -49,7 +49,7 @@ func (g *engineGolang) Init(pipelineData *pipeline.Data, config config.Interface //set command defaults (can be overridden by repo/system configuration) g.Config.SetDefault("engine_cmd_compile", "go build $(go list ./cmd/...)") - g.Config.SetDefault("engine_cmd_lint", "gometalinter.v1 --errors --vendor --deadline=3m ./...") + g.Config.SetDefault("engine_cmd_lint", "gometalinter.v2 --errors --vendor --deadline=3m ./...") g.Config.SetDefault("engine_cmd_fmt", "go fmt $(go list ./... | grep -v /vendor/)") g.Config.SetDefault("engine_cmd_test", "go test $(glide novendor)") g.Config.SetDefault("engine_cmd_security_check", "exit 0") //TODO: update when there's a dependency checker for Golang/Glide @@ -69,8 +69,8 @@ func (g *engineGolang) ValidateTools() error { return errors.EngineValidateToolError("go binary is missing") } - if _, kerr := exec.LookPath("gometalinter.v1"); kerr != nil { - return errors.EngineValidateToolError("gometalinter.v1 binary is missing") + if _, kerr := exec.LookPath("gometalinter.v2"); kerr != nil { + return errors.EngineValidateToolError("gometalinter.v2 binary is missing") } return nil From e53a2e967dd5da0102d6c0f7bcbff0cb3cca8120 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Tue, 28 Aug 2018 08:50:11 -0700 Subject: [PATCH 26/27] disable gometalinter completely in v1. --- gometalinter.json | 1 + 1 file changed, 1 insertion(+) diff --git a/gometalinter.json b/gometalinter.json index d175953..3aa1995 100644 --- a/gometalinter.json +++ b/gometalinter.json @@ -1,4 +1,5 @@ { + "DisableAll": true, "Errors": true, "Debug": true, "Deadline": "30m", From ac9b44441227f3e4c606462785ec5929da83719e Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Tue, 28 Aug 2018 09:04:37 -0700 Subject: [PATCH 27/27] ensure that tests use a mocked config. --- pkg/engine/engine_base_impl_test.go | 36 ++++++++++++++++++----------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/pkg/engine/engine_base_impl_test.go b/pkg/engine/engine_base_impl_test.go index 9684f5c..55d7a33 100644 --- a/pkg/engine/engine_base_impl_test.go +++ b/pkg/engine/engine_base_impl_test.go @@ -1,17 +1,20 @@ package engine import ( - "capsulecd/pkg/config" "github.com/stretchr/testify/require" "testing" + "capsulecd/pkg/config/mock" + "github.com/golang/mock/gomock" ) func TestEngineBase_BumpVersion_Patch(t *testing.T) { //setup - testConfig, _ := config.Create() + mockCtrl := gomock.NewController(t) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("engine_version_bump_type").MinTimes(1).Return("patch") eng := engineBase{ - Config: testConfig, + Config: fakeConfig, } //test @@ -29,12 +32,14 @@ func TestEngineBase_BumpVersion_Patch(t *testing.T) { func TestEngineBase_BumpVersion_Minor(t *testing.T) { //setup - testConfig, _ := config.Create() - testConfig.Set("engine_version_bump_type", "minor") + mockCtrl := gomock.NewController(t) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("engine_version_bump_type").MinTimes(1).Return("minor") eng := engineBase{ - Config: testConfig, + Config: fakeConfig, } + //test ver, err := eng.BumpVersion("1.2.2") require.Nil(t, err) @@ -46,10 +51,11 @@ func TestEngineBase_BumpVersion_Minor(t *testing.T) { func TestEngineBase_BumpVersion_Major(t *testing.T) { //setup - testConfig, _ := config.Create() - testConfig.Set("engine_version_bump_type", "major") + mockCtrl := gomock.NewController(t) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("engine_version_bump_type").MinTimes(1).Return("major") eng := engineBase{ - Config: testConfig, + Config: fakeConfig, } //test @@ -63,9 +69,11 @@ func TestEngineBase_BumpVersion_Major(t *testing.T) { func TestEngineBase_BumpVersion_InvalidCurrentVersion(t *testing.T) { //setup - testConfig, _ := config.Create() + mockCtrl := gomock.NewController(t) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("engine_version_bump_type").MinTimes(1).Return("patch") eng := engineBase{ - Config: testConfig, + Config: fakeConfig, } //test @@ -79,9 +87,11 @@ func TestEngineBase_BumpVersion_InvalidCurrentVersion(t *testing.T) { func TestEngineBase_BumpVersion_WithVPrefix(t *testing.T) { //setup - testConfig, _ := config.Create() + mockCtrl := gomock.NewController(t) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("engine_version_bump_type").MinTimes(1).Return("patch") eng := engineBase{ - Config: testConfig, + Config: fakeConfig, } //test