Repository: jwplayer/buildpipe-buildkite-plugin (language: python)

Issue:
Allow manual build of specific project label/path without code push
Unless I'm missing something (and that's really possible), it's impossible to trigger a build without a code change that flags which paths/labels to build. (There was a pull request - #13 - when this project was still in Python to that regard, and it looks like it was merged, so this may be a regression.) 

Use case: Our Ansible roles are in a monorepo. We would like to trigger a Packer run for two of those roles weekly so that we pick up changes to packages upstream from us. To do that, we'd need to be able to add to the project paths that will be built by getAffectedProjects. 

This feature is similar to #74 . 


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2868029..ce66a40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+0.10.0 (boyntoni)
+-----------------
+
+1. Add support for manually specifying build projects.
+
 0.9.4 (TBoshoven)
 -----------------
 
diff --git a/README.md b/README.md
index ed1f03c..118bbc5 100644
--- a/README.md
+++ b/README.md
@@ -161,6 +161,23 @@ steps:
     ...
 ```
 
+### Specifying build projects
+
+Since version 0.10.0, you can manually specify which projects you wish to build using the `BUILDKITE_PLUGIN_BUILDPIPE_BUILD_PROJECTS` environment variable.
+If this environemnt variable is provided, it will take precedence over any existing `git` diff. You can specify the build projects in one of two ways:
+
+1. Provide a comma-separated list of build projects (identified by the project label attribute). For example:
+
+```bash
+BUILDKITE_PLUGIN_BUILDPIPE_BUILD_PROJECTS="project1,project2"
+```
+
+2. Build all projects in a Buildpipe configuration:
+
+```bash
+BUILDKITE_PLUGIN_BUILDPIPE_BUILD_PROJECTS="*"
+```
+
 `diff_` commands
 ----------------
 
diff --git a/main.go b/main.go
index 0dc3076..a63c090 100644
--- a/main.go
+++ b/main.go
@@ -3,6 +3,7 @@ package main
 import (
 	"io/ioutil"
 	"os"
+	"strings"
 
 	log "github.com/sirupsen/logrus"
 	"gopkg.in/yaml.v2"
@@ -42,6 +43,24 @@ func getAffectedProjects(projects []Project, changedFiles []string) []Project {
 	return affectedProjects
 }
 
+func projectsFromBuildProjects(buildProjects string, projects []Project) []Project {
+	if buildProjects == "*" {
+		return projects
+	}
+
+	projectNames := strings.Split(buildProjects, ",")
+
+	affectedProjects := make([]Project, 0)
+	for _, projectName := range projectNames {
+		for _, configProject := range projects {
+			if projectName == configProject.Label {
+				affectedProjects  = append(affectedProjects, configProject)
+			}
+		}
+	}
+	return affectedProjects
+}
+
 func main() {
 	logLevel := getEnv(pluginPrefix+"LOG_LEVEL", "info")
 	ll, err := log.ParseLevel(logLevel)
@@ -52,16 +71,23 @@ func main() {
 	log.SetLevel(ll)
 
 	config := NewConfig(os.Getenv(pluginPrefix + "DYNAMIC_PIPELINE"))
-	changedFiles := getChangedFiles()
-	if len(changedFiles) == 0 {
-		log.Info("No files were changed")
-		os.Exit(0)
-	}
+	buildProjects := os.Getenv(pluginPrefix+"BUILD_PROJECTS")
+
+	var affectedProjects []Project
+	if len(buildProjects) > 0 {
+		affectedProjects = projectsFromBuildProjects(buildProjects, config.Projects)
+	} else {
+		changedFiles := getChangedFiles()
+		if len(changedFiles) == 0 {
+			log.Info("No files were changed")
+			os.Exit(0)
+		}
 
-	affectedProjects := getAffectedProjects(config.Projects, changedFiles)
-	if len(affectedProjects) == 0 {
-		log.Info("No project was affected from git changes")
-		os.Exit(0)
+		affectedProjects = getAffectedProjects(config.Projects, changedFiles)
+		if len(affectedProjects) == 0 {
+			log.Info("No project was affected from git changes")
+			os.Exit(0)
+		}
 	}
 
 	pipeline := generatePipeline(config.Steps, config.Env, affectedProjects)


Test patch:
diff --git a/main_test.go b/main_test.go
new file mode 100644
index 0000000..ad46f99
--- /dev/null
+++ b/main_test.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestProjectsFromBuildProjects(t *testing.T) {
+	assert := assert.New(t)
+	p1 := Project{Label: "project1", Path: []string{"project1/"}, Skip: []string{}}
+	p2 := Project{Label: "project2", Path: []string{"project2/"}, Skip: []string{}}
+	p3 := Project{Label: "project3", Path: []string{"project3/"}, Skip: []string{}}
+
+	projects := []Project{p1, p2, p3}
+	projectNames := "project1,project3"
+
+	filteredList := projectsFromBuildProjects(projectNames, projects)
+
+	assert.Equal(filteredList, []Project{p1, p3})
+}
+
+func TestAllProjectsFromBuildProjects(t *testing.T) {
+	assert := assert.New(t)
+	p1 := Project{Label: "project1", Path: []string{"project1/"}, Skip: []string{}}
+	p2 := Project{Label: "project2", Path: []string{"project2/"}, Skip: []string{}}
+	p3 := Project{Label: "project3", Path: []string{"project3/"}, Skip: []string{}}
+
+	projects := []Project{p1, p2, p3}
+	projectNames := "*"
+	filteredList := projectsFromBuildProjects(projectNames, projects)
+
+	assert.Equal(filteredList, []Project{p1, p2, p3})
+}