Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.20
FROM golang:1.21

RUN go install gotest.tools/gotestsum@v1.12.2

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ test:
docker-compose run --rm cli gotestsum --format short-verbose --junitfile results.xml --packages="./..." -- -p 1

build:
docker-compose run --rm cli env GOOS=$(OS) GOARCH=$(ARCH) go build -ldflags "-s -w -X main.version=$(shell git describe --tags --abbrev=0)" -o sem
docker-compose run --rm cli env GOOS=$(OS) GOARCH=$(ARCH) go build -buildvcs=false -ldflags "-s -w -X main.version=$(shell git describe --tags --abbrev=0)" -o sem
Comment thread
hamir-suspect marked this conversation as resolved.
Outdated
tar -czvf /tmp/sem.tar.gz sem

# Automation of CLI tagging.
Expand Down
27 changes: 27 additions & 0 deletions cmd/get.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"encoding/json"
"fmt"
"log"
"os"
Expand Down Expand Up @@ -283,6 +284,29 @@ func getAllAgents(client client.AgentApiV1AlphaApi, agentType string) ([]models.
return agents, nil
}

var GetCurrentProjectCmd = &cobra.Command{
Use: "current_project",
Short: "Get project for current directory",
Aliases: []string{"cur"},
Long: ``,
Comment thread
danudey marked this conversation as resolved.
Outdated
Comment thread
danudey marked this conversation as resolved.
Outdated

Run: func(cmd *cobra.Command, args []string) {
project, err := utils.InferProject()
utils.Check(err)

doJSON, err := cmd.Flags().GetBool("json")
utils.Check(err)
if doJSON {
jsonBody, err := json.MarshalIndent(project, "", " ")
utils.Check(err)
fmt.Println(string(jsonBody))
} else {
fmt.Println(project.Metadata.Id, project.Metadata.Name, project.Spec.Repository.Url)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we could format this in some way that is easy to split the string

}

},
}

var GetProjectCmd = &cobra.Command{
Use: "projects [name]",
Short: "Get projects.",
Expand Down Expand Up @@ -531,6 +555,9 @@ func init() {
getCmd.AddCommand(GetProjectCmd)
getCmd.AddCommand(GetAgentTypeCmd)

getCmd.AddCommand(GetCurrentProjectCmd)
GetCurrentProjectCmd.Flags().Bool("json", false, "print project information as json")

GetAgentsCmd.Flags().StringP("agent-type", "t", "",
"agent type; if specified, returns only agents for this agent type")
getCmd.AddCommand(GetAgentsCmd)
Expand Down
77 changes: 61 additions & 16 deletions cmd/utils/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package utils

import (
"fmt"
"slices"

"log"
"os/exec"
"strings"

"github.com/semaphoreci/cli/api/client"
"github.com/semaphoreci/cli/api/models"
)

func GetProjectId(name string) string {
Expand All @@ -20,46 +22,68 @@ func GetProjectId(name string) string {
}

func InferProjectName() (string, error) {
originUrl, err := getGitOriginUrl()
project, err := InferProject()
if err != nil {
return "", err
}
return project.Metadata.Name, nil
}

log.Printf("Origin url: '%s'\n", originUrl)
func InferProject() (models.ProjectV1Alpha, error) {
originURLs, err := getAllGitRemoteURLs()
if err != nil {
return models.ProjectV1Alpha{}, err
}

projectName, err := getProjectIdFromUrl(originUrl)
project, err := getProjectFromUrls(originURLs)
if err != nil {
return "", err
log.Printf("no project found for any configured remotes (%s)", strings.Join(originURLs, ", "))
return models.ProjectV1Alpha{}, fmt.Errorf("no project found for any configured remotes: %w", err)
}

return projectName, nil
return project, nil
}

func getProjectIdFromUrl(url string) (string, error) {
project, err := getProjectFromUrls([]string{url})
if err != nil {
return "", fmt.Errorf("project with url %s not found in this org", url)
}
return project.Metadata.Id, nil

}

func getProjectFromUrls(urls []string) (models.ProjectV1Alpha, error) {
projectClient := client.NewProjectV1AlphaApi()
projects, err := projectClient.ListProjects()

if err != nil {
return "", fmt.Errorf("getting project list failed '%s'", err)
return models.ProjectV1Alpha{}, fmt.Errorf("getting project list failed '%s'", err)
}
Comment on lines +111 to 116

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is to remove duplicates right? This current implementation is O(n²) we could use a map here very easily


projectName := ""
for _, p := range projects.Projects {
if p.Spec.Repository.Url == url {
projectName = p.Metadata.Name
break
if slices.Contains(urls, p.Spec.Repository.Url) {
return p, nil
}
}

if projectName == "" {
return "", fmt.Errorf("project with url '%s' not found in this org", url)
}
return models.ProjectV1Alpha{}, fmt.Errorf("no project found in this org with a url matching any of this repository's remotes")
}

return projectName, nil
func getGitRemotes() ([]string, error) {
args := []string{"remote"}
cmd := exec.Command("git", args...)
out, err := cmd.CombinedOutput()
if err != nil {
cmd_string := fmt.Sprintf("'%s %s'", "git", strings.Join(args, " "))
user_msg := "You are probably not in a git directory?"
return []string{}, fmt.Errorf("%s failed with message: '%s'\n%s", cmd_string, err, user_msg)
}
return strings.Split(strings.TrimSpace(string(out)), "\n"), nil
}

func getGitOriginUrl() (string, error) {
args := []string{"config", "remote.origin.url"}
func getGitRemoteUrl(remote string) (string, error) {
args := []string{"config", fmt.Sprintf("remote.%s.url", remote)}

cmd := exec.Command("git", args...)
out, err := cmd.CombinedOutput()
Expand All @@ -71,3 +95,24 @@ func getGitOriginUrl() (string, error) {

return strings.TrimSpace(string(out)), nil
}

func getAllGitRemoteURLs() ([]string, error) {
gitRemotes, err := getGitRemotes()
if err != nil {
return gitRemotes, err
}
var gitRemoteURLs []string
for _, remote := range gitRemotes {
remoteURL, err := getGitRemoteUrl(remote)
if err != nil {
log.Printf("could not get URL for remote %s", remote)
} else {
gitRemoteURLs = append(gitRemoteURLs, remoteURL)
}
}
Comment on lines +170 to +183

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the code is O(n*m) because of the nested loops, you could use maps to improve the complexity to O(n+m).

I think this is not that much about performance but for readability. But I can see some cases that a few ms can compound in the long run.

return gitRemoteURLs, nil
}

func getGitOriginUrl() (string, error) {
return getGitRemoteUrl("origin")
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/semaphoreci/cli

go 1.20
go 1.21

require (
github.com/ghodss/yaml v1.0.0
Expand Down