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 a588835 commit 0f878f4
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 @@ -1746,3 +1794,29 @@ func orbInformThatOrbCannotBeDeletedMessage() {
fmt.Println("Once an orb is created it cannot be deleted. Orbs are semver compliant, and each published version is immutable. Publicly released orbs are potential dependencies for other projects.")
fmt.Println("Therefore, allowing orb deletion would make users susceptible to unexpected loss of functionality.")
}

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 0f878f4

Please sign in to comment.