Skip to content

Commit

Permalink
Add org-slug and org-id flags to orb validate & process commands
Browse files Browse the repository at this point in the history
  • Loading branch information
zbenhadi committed Apr 25, 2023
1 parent 66c2bfe commit 7b4e701
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 23 deletions.
11 changes: 8 additions & 3 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ func WhoamiQuery(cl *graphql.Client) (*WhoamiResponse, error) {
}

// OrbQuery validated and processes an orb.
func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) {
func OrbQuery(cl *graphql.Client, configPath string, ownerId string) (*ConfigResponse, error) {
var response OrbConfigResponse

config, err := loadYaml(configPath)
Expand All @@ -522,8 +522,8 @@ func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) {
}

query := `
query ValidateOrb ($config: String!) {
orbConfig(orbYaml: $config) {
query ValidateOrb ($config: String!, $owner: UUID) {
orbConfig(orbYaml: $config, ownerId: $owner) {
valid,
errors { message },
sourceYaml,
Expand All @@ -533,6 +533,11 @@ func OrbQuery(cl *graphql.Client, configPath string) (*ConfigResponse, error) {

request := graphql.NewRequest(query)
request.Var("config", config)

if ownerId != "" {
request.Var("owner", ownerId)
}

request.SetToken(cl.Token)

err = cl.Run(request, &response)
Expand Down
23 changes: 23 additions & 0 deletions api/collaborators/collaborators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package collaborators

type CollaborationResult struct {
VcsTye string `json:"vcs_type"`
OrgSlug string `json:"slug"`
OrgName string `json:"name"`
OrgId string `json:"id"`
AvatarUrl string `json:"avatar_url"`
}

type CollaboratorsClient interface {
GetOrgCollaborations() ([]CollaborationResult, error)
}

// GetOrgIdFromSlug - converts a slug into an orgID.
func GetOrgIdFromSlug(slug string, collaborations []CollaborationResult) string {
for _, v := range collaborations {
if v.OrgSlug == slug {
return v.OrgId
}
}
return ""
}
36 changes: 36 additions & 0 deletions api/collaborators/collaborators_rest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package collaborators

import (
"net/url"

"github.com/CircleCI-Public/circleci-cli/api/rest"
"github.com/CircleCI-Public/circleci-cli/settings"
)

var (
CollaborationsPath = "me/collaborations"
)

type collaboratorsRestClient struct {
client *rest.Client
}

// NewCollaboratorsRestClient returns a new collaboratorsRestClient satisfying the api.CollaboratorsClient
// interface via the REST API.
func NewCollaboratorsRestClient(config settings.Config) (*collaboratorsRestClient, error) {
client := &collaboratorsRestClient{
client: rest.NewFromConfig(config.Host, &config),
}
return client, nil
}

func (c *collaboratorsRestClient) GetOrgCollaborations() ([]CollaborationResult, error) {
req, err := c.client.NewRequest("GET", &url.URL{Path: CollaborationsPath}, nil)
if err != nil {
return nil, err
}

var resp []CollaborationResult
_, err = c.client.DoRequest(req, &resp)
return resp, err
}
100 changes: 87 additions & 13 deletions cmd/orb.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"time"

"github.com/CircleCI-Public/circleci-cli/api"
"github.com/CircleCI-Public/circleci-cli/api/collaborators"
"github.com/CircleCI-Public/circleci-cli/api/graphql"
"github.com/CircleCI-Public/circleci-cli/filetree"
"github.com/CircleCI-Public/circleci-cli/process"
Expand All @@ -43,9 +44,10 @@ import (
)

type orbOptions struct {
cfg *settings.Config
cl *graphql.Client
args []string
cfg *settings.Config
cl *graphql.Client
args []string
collaborators collaborators.CollaboratorsClient

color string

Expand All @@ -62,6 +64,11 @@ type orbOptions struct {
integrationTesting bool
}

type orbOrgOptions struct {
OrgID string
OrgSlug string
}

var orbAnnotations = map[string]string{
"<path>": "The path to your orb (use \"-\" for STDIN)",
"<namespace>": "The namespace used for the orb (i.e. circleci)",
Expand Down Expand Up @@ -93,9 +100,16 @@ func (ui createOrbTestUI) askUserToConfirm(message string) bool {
}

func newOrbCommand(config *settings.Config) *cobra.Command {
collaborators, err := collaborators.NewCollaboratorsRestClient(*config)

if err != nil {
panic(err)
}

opts := orbOptions{
cfg: config,
tty: createOrbInteractiveUI{},
cfg: config,
tty: createOrbInteractiveUI{},
collaborators: collaborators,
}

listCommand := &cobra.Command{
Expand All @@ -121,13 +135,23 @@ func newOrbCommand(config *settings.Config) *cobra.Command {
validateCommand := &cobra.Command{
Use: "validate <path>",
Short: "Validate an orb.yml",
RunE: func(_ *cobra.Command, _ []string) error {
return validateOrb(opts)
RunE: func(cmd *cobra.Command, _ []string) error {
orgID, _ := cmd.Flags().GetString("org-id")
orgSlug, _ := cmd.Flags().GetString("org-slug")

org := orbOrgOptions{
OrgID: orgID,
OrgSlug: orgSlug,
}

return validateOrb(opts, org)
},
Args: cobra.ExactArgs(1),
Annotations: make(map[string]string),
}
validateCommand.Annotations["<path>"] = orbAnnotations["<path>"]
validateCommand.Flags().String("org-slug", "", "organization slug (for example: github/example-org), used when an orb depends on private orbs belonging to that org")
validateCommand.Flags().String("org-id", "", "organization id used when an orb depends on private orbs belonging to that org")

processCommand := &cobra.Command{
Use: "process <path>",
Expand All @@ -141,14 +165,24 @@ func newOrbCommand(config *settings.Config) *cobra.Command {
opts.args = args
opts.cl = graphql.NewClient(config.HTTPClient, config.Host, config.Endpoint, config.Token, config.Debug)
},
RunE: func(_ *cobra.Command, _ []string) error {
return processOrb(opts)
RunE: func(cmd *cobra.Command, _ []string) error {
orgID, _ := cmd.Flags().GetString("org-id")
orgSlug, _ := cmd.Flags().GetString("org-slug")

org := orbOrgOptions{
OrgID: orgID,
OrgSlug: orgSlug,
}

return processOrb(opts, org)
},
Args: cobra.ExactArgs(1),
Annotations: make(map[string]string),
}
processCommand.Example = ` circleci orb process src/my-orb/@orb.yml`
processCommand.Annotations["<path>"] = orbAnnotations["<path>"]
processCommand.Flags().String("org-slug", "", "organization slug (for example: github/example-org), used when an orb depends on private orbs belonging to that org")
processCommand.Flags().String("org-id", "", "organization id used when an orb depends on private orbs belonging to that org")

publishCommand := &cobra.Command{
Use: "publish <path> <orb>",
Expand Down Expand Up @@ -691,8 +725,14 @@ func listNamespaceOrbs(opts orbOptions) error {
return logOrbs(*orbs, opts)
}

func validateOrb(opts orbOptions) error {
_, err := api.OrbQuery(opts.cl, opts.args[0])
func validateOrb(opts orbOptions, org orbOrgOptions) error {
orgId, err := (&opts).getOrgId(org)

if err != nil {
return fmt.Errorf("failed to get the appropriate org-id: %s", err.Error())
}

_, err = api.OrbQuery(opts.cl, opts.args[0], orgId)

if err != nil {
return err
Expand All @@ -707,8 +747,16 @@ func validateOrb(opts orbOptions) error {
return nil
}

func processOrb(opts orbOptions) error {
response, err := api.OrbQuery(opts.cl, opts.args[0])
func processOrb(opts orbOptions, org orbOrgOptions) error {
orgId, err := (&opts).getOrgId(org)

if err != nil {
return fmt.Errorf("failed to get the appropriate org-id: %s", err.Error())
}

_, err = api.OrbQuery(opts.cl, opts.args[0], orgId)

response, err := api.OrbQuery(opts.cl, opts.args[0], orgId)

if err != nil {
return err
Expand Down Expand Up @@ -1734,3 +1782,29 @@ func stringifyDiff(diff gotextdiff.Unified, colorOpt string) string {
color.NoColor = oldNoColor
return strings.Join(lines, "\n")
}

func (o *orbOptions) getOrgId(orgInfo orbOrgOptions) (string, error) {
if orgInfo.OrgID == "" && orgInfo.OrgSlug == "" {
return "", nil
}

var orgID string
if strings.TrimSpace(orgInfo.OrgID) != "" {
orgID = orgInfo.OrgID
} else if strings.TrimSpace(orgInfo.OrgSlug) != "" {
orgs, err := o.collaborators.GetOrgCollaborations()
if err != nil {
return "", err
}

orgID = collaborators.GetOrgIdFromSlug(orgInfo.OrgSlug, orgs)

if orgID == "" {
fmt.Println("Could not fetch a valid org-id from collaborators endpoint.")
fmt.Println("Check if you have access to this org by hitting https://circleci.com/api/v2/me/collaborations")
fmt.Println("Continuing on - private orb resolution will not work as intended")
}
}

return orgID, nil
}
14 changes: 7 additions & 7 deletions cmd/orb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ See a full explanation and documentation on orbs here: https://circleci.com/docs
} `json:"variables"`
}{
Query: `
query ValidateOrb ($config: String!) {
orbConfig(orbYaml: $config) {
query ValidateOrb ($config: String!, $owner: UUID) {
orbConfig(orbYaml: $config, ownerId: $owner) {
valid,
errors { message },
sourceYaml,
Expand Down Expand Up @@ -184,7 +184,7 @@ See a full explanation and documentation on orbs here: https://circleci.com/docs
}`

expectedRequestJson := ` {
"query": "\n\t\tquery ValidateOrb ($config: String!) {\n\t\t\torbConfig(orbYaml: $config) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"query": "\n\t\tquery ValidateOrb ($config: String!, $owner: UUID) {\n\t\t\torbConfig(orbYaml: $config, ownerId: $owner) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "{}"
}
Expand Down Expand Up @@ -231,7 +231,7 @@ See a full explanation and documentation on orbs here: https://circleci.com/docs
}`

expectedRequestJson := ` {
"query": "\n\t\tquery ValidateOrb ($config: String!) {\n\t\t\torbConfig(orbYaml: $config) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"query": "\n\t\tquery ValidateOrb ($config: String!, $owner: UUID) {\n\t\t\torbConfig(orbYaml: $config, ownerId: $owner) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "some orb"
}
Expand Down Expand Up @@ -266,7 +266,7 @@ See a full explanation and documentation on orbs here: https://circleci.com/docs
}`

expectedRequestJson := ` {
"query": "\n\t\tquery ValidateOrb ($config: String!) {\n\t\t\torbConfig(orbYaml: $config) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"query": "\n\t\tquery ValidateOrb ($config: String!, $owner: UUID) {\n\t\t\torbConfig(orbYaml: $config, ownerId: $owner) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "some orb"
}
Expand Down Expand Up @@ -309,7 +309,7 @@ See a full explanation and documentation on orbs here: https://circleci.com/docs
}`

expectedRequestJson := ` {
"query": "\n\t\tquery ValidateOrb ($config: String!) {\n\t\t\torbConfig(orbYaml: $config) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"query": "\n\t\tquery ValidateOrb ($config: String!, $owner: UUID) {\n\t\t\torbConfig(orbYaml: $config, ownerId: $owner) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "some orb"
}
Expand Down Expand Up @@ -344,7 +344,7 @@ See a full explanation and documentation on orbs here: https://circleci.com/docs
}`

expectedRequestJson := ` {
"query": "\n\t\tquery ValidateOrb ($config: String!) {\n\t\t\torbConfig(orbYaml: $config) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"query": "\n\t\tquery ValidateOrb ($config: String!, $owner: UUID) {\n\t\t\torbConfig(orbYaml: $config, ownerId: $owner) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "some orb"
}
Expand Down

0 comments on commit 7b4e701

Please sign in to comment.