File: http.go

package info (click to toggle)
gh 2.46.0-4
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 15,548 kB
  • sloc: sh: 227; makefile: 117
file content (156 lines) | stat: -rw-r--r-- 5,261 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package set

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"

	"github.com/cli/cli/v2/api"
	"github.com/cli/cli/v2/internal/ghrepo"
	"github.com/cli/cli/v2/pkg/cmd/variable/shared"
)

const (
	createdOperation = "Created"
	updatedOperation = "Updated"
)

type setPayload struct {
	Name         string  `json:"name,omitempty"`
	Repositories []int64 `json:"selected_repository_ids,omitempty"`
	Value        string  `json:"value,omitempty"`
	Visibility   string  `json:"visibility,omitempty"`
}

type setOptions struct {
	Entity        shared.VariableEntity
	Environment   string
	Key           string
	Organization  string
	Repository    ghrepo.Interface
	RepositoryIDs []int64
	Value         string
	Visibility    string
}

type setResult struct {
	Err       error
	Key       string
	Operation string
}

func setVariable(client *api.Client, host string, opts setOptions) setResult {
	var err error
	var postErr api.HTTPError
	result := setResult{Operation: createdOperation, Key: opts.Key}
	switch opts.Entity {
	case shared.Organization:
		if err = postOrgVariable(client, host, opts.Organization, opts.Visibility, opts.Key, opts.Value, opts.RepositoryIDs); err == nil {
			return result
		} else if errors.As(err, &postErr) && postErr.StatusCode == 409 {
			// Server will return a 409 if variable already exists
			result.Operation = updatedOperation
			err = patchOrgVariable(client, host, opts.Organization, opts.Visibility, opts.Key, opts.Value, opts.RepositoryIDs)
		}
	case shared.Environment:
		var ids []int64
		ids, err = api.GetRepoIDs(client, opts.Repository.RepoHost(), []ghrepo.Interface{opts.Repository})
		if err != nil || len(ids) != 1 {
			err = fmt.Errorf("failed to look up repository %s: %w", ghrepo.FullName(opts.Repository), err)
			break
		}
		if err = postEnvVariable(client, opts.Repository.RepoHost(), ids[0], opts.Environment, opts.Key, opts.Value); err == nil {
			return result
		} else if errors.As(err, &postErr) && postErr.StatusCode == 409 {
			// Server will return a 409 if variable already exists
			result.Operation = updatedOperation
			err = patchEnvVariable(client, opts.Repository.RepoHost(), ids[0], opts.Environment, opts.Key, opts.Value)
		}
	default:
		if err = postRepoVariable(client, opts.Repository, opts.Key, opts.Value); err == nil {
			return result
		} else if errors.As(err, &postErr) && postErr.StatusCode == 409 {
			// Server will return a 409 if variable already exists
			result.Operation = updatedOperation
			err = patchRepoVariable(client, opts.Repository, opts.Key, opts.Value)
		}
	}
	if err != nil {
		result.Err = fmt.Errorf("failed to set variable %q: %w", opts.Key, err)
	}
	return result
}

func postVariable(client *api.Client, host, path string, payload interface{}) error {
	payloadBytes, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("failed to serialize: %w", err)
	}
	requestBody := bytes.NewReader(payloadBytes)
	return client.REST(host, "POST", path, requestBody, nil)
}

func postOrgVariable(client *api.Client, host, orgName, visibility, variableName, value string, repositoryIDs []int64) error {
	payload := setPayload{
		Name:         variableName,
		Value:        value,
		Visibility:   visibility,
		Repositories: repositoryIDs,
	}
	path := fmt.Sprintf(`orgs/%s/actions/variables`, orgName)
	return postVariable(client, host, path, payload)
}

func postEnvVariable(client *api.Client, host string, repoID int64, envName, variableName, value string) error {
	payload := setPayload{
		Name:  variableName,
		Value: value,
	}
	path := fmt.Sprintf(`repositories/%d/environments/%s/variables`, repoID, envName)
	return postVariable(client, host, path, payload)
}

func postRepoVariable(client *api.Client, repo ghrepo.Interface, variableName, value string) error {
	payload := setPayload{
		Name:  variableName,
		Value: value,
	}
	path := fmt.Sprintf(`repos/%s/actions/variables`, ghrepo.FullName(repo))
	return postVariable(client, repo.RepoHost(), path, payload)
}

func patchVariable(client *api.Client, host, path string, payload interface{}) error {
	payloadBytes, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("failed to serialize: %w", err)
	}
	requestBody := bytes.NewReader(payloadBytes)
	return client.REST(host, "PATCH", path, requestBody, nil)
}

func patchOrgVariable(client *api.Client, host, orgName, visibility, variableName, value string, repositoryIDs []int64) error {
	payload := setPayload{
		Value:        value,
		Visibility:   visibility,
		Repositories: repositoryIDs,
	}
	path := fmt.Sprintf(`orgs/%s/actions/variables/%s`, orgName, variableName)
	return patchVariable(client, host, path, payload)
}

func patchEnvVariable(client *api.Client, host string, repoID int64, envName, variableName, value string) error {
	payload := setPayload{
		Value: value,
	}
	path := fmt.Sprintf(`repositories/%d/environments/%s/variables/%s`, repoID, envName, variableName)
	return patchVariable(client, host, path, payload)
}

func patchRepoVariable(client *api.Client, repo ghrepo.Interface, variableName, value string) error {
	payload := setPayload{
		Value: value,
	}
	path := fmt.Sprintf(`repos/%s/actions/variables/%s`, ghrepo.FullName(repo), variableName)
	return patchVariable(client, repo.RepoHost(), path, payload)
}