| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- package buildpacks
- import (
- "context"
- "fmt"
- "os"
- "strings"
- "sync"
- "github.com/google/go-github/github"
- "github.com/pelletier/go-toml"
- )
- type apiPythonRuntime struct {
- wg sync.WaitGroup
- packs map[string]*BuildpackInfo
- }
- // FIXME: should be called once at the top-level somewhere in the backend
- func populatePythonPacks(client *github.Client) map[string]*BuildpackInfo {
- packs := make(map[string]*BuildpackInfo)
- repoRelease, _, err := client.Repositories.GetLatestRelease(context.Background(), "paketo-buildpacks", "python")
- if err != nil {
- fmt.Printf("Error fetching latest release for paketo-buildpacks/python: %v\n", err)
- return nil
- }
- fileContent, _, _, err := client.Repositories.GetContents(
- context.Background(), "paketo-buildpacks", "python", "buildpack.toml",
- &github.RepositoryContentGetOptions{
- Ref: *repoRelease.TagName,
- },
- )
- if err != nil {
- fmt.Printf("Error fetching contents of buildpack.toml for paketo-buildpacks/python: %v\n", err)
- return nil
- }
- data, err := fileContent.GetContent()
- if err != nil {
- fmt.Printf("Error calling GetContent() on buildpack.toml for paketo-buildpacks/python: %v\n", err)
- return nil
- }
- buildpackToml, err := toml.Load(data)
- if err != nil {
- fmt.Printf("Error while reading buildpack.toml from paketo-buildpacks/python: %v\n", err)
- os.Exit(1)
- }
- order := buildpackToml.Get("order").([]*toml.Tree)
- // pipenv
- packs[pipenv] = newBuildpackInfo()
- pipenvGroup := order[0].GetArray("group").([]*toml.Tree)
- for i := 0; i < len(pipenvGroup); i++ {
- packs[pipenv].addPack(
- buildpackOrderGroupInfo{
- ID: pipenvGroup[i].Get("id").(string),
- Optional: pipenvGroup[i].GetDefault("optional", false).(bool),
- Version: pipenvGroup[i].Get("version").(string),
- },
- )
- }
- // pip
- packs[pip] = newBuildpackInfo()
- pipGroup := order[1].GetArray("group").([]*toml.Tree)
- for i := 0; i < len(pipGroup); i++ {
- packs[pip].addPack(
- buildpackOrderGroupInfo{
- ID: pipGroup[i].Get("id").(string),
- Optional: pipGroup[i].GetDefault("optional", false).(bool),
- Version: pipGroup[i].Get("version").(string),
- },
- )
- }
- // conda
- packs[conda] = newBuildpackInfo()
- condaGroup := order[2].GetArray("group").([]*toml.Tree)
- for i := 0; i < len(condaGroup); i++ {
- packs[pip].addPack(
- buildpackOrderGroupInfo{
- ID: condaGroup[i].Get("id").(string),
- Optional: condaGroup[i].GetDefault("optional", false).(bool),
- Version: condaGroup[i].Get("version").(string),
- },
- )
- }
- // no package manager
- packs[standalone] = newBuildpackInfo()
- standaloneGroup := order[3].GetArray("group").([]*toml.Tree)
- for i := 0; i < len(standaloneGroup); i++ {
- packs[standalone].addPack(
- buildpackOrderGroupInfo{
- ID: standaloneGroup[i].Get("id").(string),
- Optional: standaloneGroup[i].GetDefault("optional", false).(bool),
- Version: standaloneGroup[i].Get("version").(string),
- },
- )
- }
- return packs
- }
- func NewAPIPythonRuntime() APIRuntime {
- return &apiPythonRuntime{}
- }
- func (runtime *apiPythonRuntime) detectPipenv(results chan struct {
- string
- bool
- }, directoryContent []*github.RepositoryContent) {
- pipfileFound := false
- pipfileLockFound := false
- for i := 0; i < len(directoryContent); i++ {
- name := directoryContent[i].GetName()
- if name == "Pipfile" {
- pipfileFound = true
- } else if name == "Pipfile.lock" {
- pipfileLockFound = true
- }
- if pipfileFound && pipfileLockFound {
- break
- }
- }
- if pipfileFound && pipfileLockFound {
- results <- struct {
- string
- bool
- }{pipenv, true}
- } else {
- results <- struct {
- string
- bool
- }{pipenv, false}
- }
- runtime.wg.Done()
- }
- func (runtime *apiPythonRuntime) detectPip(results chan struct {
- string
- bool
- }, directoryContent []*github.RepositoryContent) {
- requirementsTxtFound := false
- for i := 0; i < len(directoryContent); i++ {
- name := directoryContent[i].GetName()
- if name == "requirements.txt" {
- requirementsTxtFound = true
- }
- }
- if requirementsTxtFound {
- results <- struct {
- string
- bool
- }{pip, true}
- } else {
- results <- struct {
- string
- bool
- }{pip, false}
- }
- runtime.wg.Done()
- }
- func (runtime *apiPythonRuntime) detectConda(results chan struct {
- string
- bool
- }, directoryContent []*github.RepositoryContent) {
- environmentFound := false
- packageListFound := false
- for i := 0; i < len(directoryContent); i++ {
- name := directoryContent[i].GetName()
- if name == "environment.yml" {
- environmentFound = true
- break
- } else if name == "package-list.txt" {
- packageListFound = true
- break
- }
- }
- if environmentFound || packageListFound {
- results <- struct {
- string
- bool
- }{conda, true}
- } else {
- results <- struct {
- string
- bool
- }{conda, false}
- }
- runtime.wg.Done()
- }
- func (runtime *apiPythonRuntime) detectStandalone(results chan struct {
- string
- bool
- }, directoryContent []*github.RepositoryContent) {
- pyFound := false
- for i := 0; i < len(directoryContent); i++ {
- name := directoryContent[i].GetName()
- if strings.HasSuffix(name, ".py") {
- pyFound = true
- break
- }
- }
- if pyFound {
- results <- struct {
- string
- bool
- }{standalone, true}
- } else {
- results <- struct {
- string
- bool
- }{standalone, false}
- }
- runtime.wg.Done()
- }
- func (runtime *apiPythonRuntime) Detect(
- client *github.Client,
- directoryContent []*github.RepositoryContent,
- owner, name, path string,
- repoContentOptions github.RepositoryContentGetOptions,
- ) *RuntimeResponse {
- runtime.packs = populatePythonPacks(client)
- results := make(chan struct {
- string
- bool
- }, 4)
- fmt.Printf("Starting detection for a Python runtime for %s/%s\n", owner, name)
- runtime.wg.Add(4)
- fmt.Println("Checking for pipenv")
- go runtime.detectPipenv(results, directoryContent)
- fmt.Println("Checking for pip")
- go runtime.detectPip(results, directoryContent)
- fmt.Println("Checking for conda")
- go runtime.detectConda(results, directoryContent)
- fmt.Println("Checking for Python standalone")
- go runtime.detectStandalone(results, directoryContent)
- runtime.wg.Wait()
- close(results)
- detected := make(map[string]bool)
- for result := range results {
- detected[result.string] = result.bool
- }
- // TODO: how to access config values for Python projects
- if detected[pipenv] {
- fmt.Printf("Python pipenv runtime detected for %s/%s\n", owner, name)
- return &RuntimeResponse{
- Name: "Python",
- Runtime: pipenv,
- Buildpacks: runtime.packs[pipenv],
- }
- } else if detected[pip] {
- fmt.Printf("Python pip runtime detected for %s/%s\n", owner, name)
- return &RuntimeResponse{
- Name: "Python",
- Runtime: pip,
- Buildpacks: runtime.packs[pip],
- }
- } else if detected[conda] {
- fmt.Printf("Python conda runtime detected for %s/%s\n", owner, name)
- return &RuntimeResponse{
- Name: "Python",
- Runtime: conda,
- Buildpacks: runtime.packs[conda],
- }
- } else if detected[standalone] {
- fmt.Printf("Python standalone runtime detected for %s/%s\n", owner, name)
- return &RuntimeResponse{
- Name: "Python",
- Runtime: standalone,
- Buildpacks: runtime.packs[standalone],
- }
- }
- fmt.Printf("No Python runtime detected for %s/%s\n", owner, name)
- return nil
- }
|