Skip to content

Commit a7d0203

Browse files
authored
refactor(sidekick): split update root config (#2448)
1 parent 91c0189 commit a7d0203

File tree

4 files changed

+243
-196
lines changed

4 files changed

+243
-196
lines changed

internal/sidekick/internal/config/config.go

Lines changed: 1 addition & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,18 @@
1717
package config
1818

1919
import (
20-
"crypto/sha256"
2120
"fmt"
22-
"io"
2321
"maps"
24-
"net/http"
2522
"os"
2623
"path"
27-
"strings"
2824

2925
"github.com/googleapis/librarian/internal/sidekick/internal/config/gcloudyaml"
3026
"github.com/googleapis/librarian/internal/sidekick/internal/license"
3127
toml "github.com/pelletier/go-toml/v2"
3228
)
3329

3430
const (
35-
defaultGitHubApi = "https://api.github.com"
36-
defaultGitHub = "https://github.com"
37-
repo = "googleapis/googleapis"
38-
branch = "master"
39-
configName = ".sidekick.toml"
31+
configName = ".sidekick.toml"
4032
)
4133

4234
// DocumentationOverride describes overrides for the documentation of a single element.
@@ -174,112 +166,6 @@ func mergeConfigs(rootConfig, local *Config) *Config {
174166
return &merged
175167
}
176168

177-
// UpdateRootConfig updates the root configuration file with the latest SHA from GitHub.
178-
func UpdateRootConfig(rootConfig *Config) error {
179-
gitHubApi, ok := rootConfig.Source["github-api"]
180-
if !ok {
181-
gitHubApi = defaultGitHubApi
182-
}
183-
gitHub, ok := rootConfig.Source["github"]
184-
if !ok {
185-
gitHub = defaultGitHub
186-
}
187-
188-
query := fmt.Sprintf("%s/repos/%s/commits/%s", gitHubApi, repo, branch)
189-
fmt.Printf("getting latest SHA from %q\n", query)
190-
latestSha, err := getLatestSha(query)
191-
if err != nil {
192-
return err
193-
}
194-
195-
newRoot := fmt.Sprintf("%s/%s/archive/%s.tar.gz", gitHub, repo, latestSha)
196-
fmt.Printf("computing SHA256 for %q\n", newRoot)
197-
newSha256, err := getSha256(newRoot)
198-
if err != nil {
199-
return err
200-
}
201-
fmt.Printf("updating .sidekick.toml\n")
202-
203-
contents, err := os.ReadFile(".sidekick.toml")
204-
if err != nil {
205-
return err
206-
}
207-
var newContents []string
208-
for _, line := range strings.Split(string(contents), "\n") {
209-
switch {
210-
case strings.HasPrefix(line, "googleapis-root "):
211-
s := strings.SplitN(line, "=", 2)
212-
if len(s) != 2 {
213-
return fmt.Errorf("invalid googleapis-root line, expected = separator, got=%q", line)
214-
}
215-
newContents = append(newContents, fmt.Sprintf("%s= '%s'", s[0], newRoot))
216-
case strings.HasPrefix(line, "googleapis-sha256 "):
217-
s := strings.SplitN(line, "=", 2)
218-
if len(s) != 2 {
219-
return fmt.Errorf("invalid googleapis-sha256 line, expected = separator, got=%q", line)
220-
}
221-
newContents = append(newContents, fmt.Sprintf("%s= '%s'", s[0], newSha256))
222-
default:
223-
newContents = append(newContents, line)
224-
}
225-
}
226-
227-
cwd, _ := os.Getwd()
228-
fmt.Printf("%s\n", cwd)
229-
f, err := os.Create(".sidekick.toml")
230-
if err != nil {
231-
return err
232-
}
233-
defer f.Close()
234-
for i, line := range newContents {
235-
f.Write([]byte(line))
236-
if i != len(newContents)-1 {
237-
f.Write([]byte("\n"))
238-
}
239-
}
240-
return f.Close()
241-
}
242-
243-
func getSha256(query string) (string, error) {
244-
response, err := http.Get(query)
245-
if err != nil {
246-
return "", err
247-
}
248-
if response.StatusCode >= 300 {
249-
return "", fmt.Errorf("http error in download %s", response.Status)
250-
}
251-
defer response.Body.Close()
252-
253-
hasher := sha256.New()
254-
if _, err := io.Copy(hasher, response.Body); err != nil {
255-
return "", err
256-
}
257-
got := fmt.Sprintf("%x", hasher.Sum(nil))
258-
return got, nil
259-
}
260-
261-
func getLatestSha(query string) (string, error) {
262-
client := &http.Client{}
263-
request, err := http.NewRequest(http.MethodGet, query, nil)
264-
if err != nil {
265-
return "", err
266-
}
267-
request.Header.Set("Accept", "application/vnd.github.VERSION.sha")
268-
response, err := client.Do(request)
269-
if err != nil {
270-
return "", err
271-
}
272-
if response.StatusCode >= 300 {
273-
return "", fmt.Errorf("http error in download %s", response.Status)
274-
}
275-
defer response.Body.Close()
276-
contents, err := io.ReadAll(response.Body)
277-
if err != nil {
278-
return "", err
279-
}
280-
return string(contents), nil
281-
}
282-
283169
// WriteSidekickToml writes the configuration to a .sidekick.toml file.
284170
func WriteSidekickToml(outDir string, config *Config) error {
285171
if err := os.MkdirAll(outDir, 0777); err != nil {

internal/sidekick/internal/config/config_test.go

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ package config
1616

1717
import (
1818
"bytes"
19-
"fmt"
20-
"net/http"
21-
"net/http/httptest"
2219
"os"
2320
"path"
2421
"testing"
@@ -485,81 +482,3 @@ func mergeTestConfigs(t *testing.T, root, local *Config) (*Config, error) {
485482
tempFile.Close()
486483
return MergeConfigAndFile(root, tempFile.Name())
487484
}
488-
489-
func TestUpdateRootConfig(t *testing.T) {
490-
// update() normally writes `.sidekick.toml` to cwd. We need to change to a
491-
// temporary directory to avoid changing the actual configuration, and any
492-
// conflicts with other tests running at the same time.
493-
tempDir := t.TempDir()
494-
t.Chdir(tempDir)
495-
496-
const (
497-
getLatestShaPath = "/repos/googleapis/googleapis/commits/master"
498-
latestSha = "5d5b1bf126485b0e2c972bac41b376438601e266"
499-
tarballPath = "/googleapis/googleapis/archive/5d5b1bf126485b0e2c972bac41b376438601e266.tar.gz"
500-
latestShaContents = "The quick brown fox jumps over the lazy dog"
501-
latestShaContentsHash = "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
502-
)
503-
504-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
505-
switch r.URL.Path {
506-
case getLatestShaPath:
507-
got := r.Header.Get("Accept")
508-
want := "application/vnd.github.VERSION.sha"
509-
if got != want {
510-
t.Fatalf("mismatched Accept header for %q, got=%q, want=%s", r.URL.Path, got, want)
511-
}
512-
w.WriteHeader(http.StatusOK)
513-
w.Write([]byte(latestSha))
514-
case tarballPath:
515-
w.WriteHeader(http.StatusOK)
516-
w.Write([]byte(latestShaContents))
517-
default:
518-
t.Fatalf("unexpected request path %q", r.URL.Path)
519-
}
520-
}))
521-
defer server.Close()
522-
523-
rootConfig := &Config{
524-
General: GeneralConfig{
525-
Language: "rust",
526-
SpecificationFormat: "protobuf",
527-
},
528-
Source: map[string]string{
529-
"github-api": server.URL,
530-
"github": server.URL,
531-
"googleapis-root": fmt.Sprintf("%s/googleapis/googleapis/archive/old.tar.gz", server.URL),
532-
"googleapis-sha256": "old-sha-unused",
533-
},
534-
Codec: map[string]string{},
535-
}
536-
if err := WriteSidekickToml(".", rootConfig); err != nil {
537-
t.Fatal(err)
538-
}
539-
540-
if err := UpdateRootConfig(rootConfig); err != nil {
541-
t.Fatal(err)
542-
}
543-
544-
got := &Config{}
545-
contents, err := os.ReadFile(path.Join(tempDir, ".sidekick.toml"))
546-
if err != nil {
547-
t.Fatal(err)
548-
}
549-
if err := toml.Unmarshal(contents, got); err != nil {
550-
t.Fatal("error reading top-level configuration: %w", err)
551-
}
552-
want := &Config{
553-
General: rootConfig.General,
554-
Source: map[string]string{
555-
"github-api": server.URL,
556-
"github": server.URL,
557-
"googleapis-root": fmt.Sprintf("%s/googleapis/googleapis/archive/%s.tar.gz", server.URL, latestSha),
558-
"googleapis-sha256": latestShaContentsHash,
559-
},
560-
}
561-
562-
if diff := cmp.Diff(want, got); diff != "" {
563-
t.Errorf("mismatch in loaded root config (-want, +got)\n:%s", diff)
564-
}
565-
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package config
16+
17+
import (
18+
"crypto/sha256"
19+
"fmt"
20+
"io"
21+
"net/http"
22+
"os"
23+
"strings"
24+
)
25+
26+
const (
27+
defaultGitHubApi = "https://api.github.com"
28+
defaultGitHub = "https://github.com"
29+
repo = "googleapis/googleapis"
30+
branch = "master"
31+
)
32+
33+
// UpdateRootConfig updates the root configuration file with the latest SHA from GitHub.
34+
func UpdateRootConfig(rootConfig *Config) error {
35+
gitHubApi, ok := rootConfig.Source["github-api"]
36+
if !ok {
37+
gitHubApi = defaultGitHubApi
38+
}
39+
gitHub, ok := rootConfig.Source["github"]
40+
if !ok {
41+
gitHub = defaultGitHub
42+
}
43+
44+
query := fmt.Sprintf("%s/repos/%s/commits/%s", gitHubApi, repo, branch)
45+
fmt.Printf("getting latest SHA from %q\n", query)
46+
latestSha, err := getLatestSha(query)
47+
if err != nil {
48+
return err
49+
}
50+
51+
newRoot := fmt.Sprintf("%s/%s/archive/%s.tar.gz", gitHub, repo, latestSha)
52+
fmt.Printf("computing SHA256 for %q\n", newRoot)
53+
newSha256, err := getSha256(newRoot)
54+
if err != nil {
55+
return err
56+
}
57+
fmt.Printf("updating .sidekick.toml\n")
58+
59+
contents, err := os.ReadFile(".sidekick.toml")
60+
if err != nil {
61+
return err
62+
}
63+
var newContents []string
64+
for _, line := range strings.Split(string(contents), "\n") {
65+
switch {
66+
case strings.HasPrefix(line, "googleapis-root "):
67+
s := strings.SplitN(line, "=", 2)
68+
if len(s) != 2 {
69+
return fmt.Errorf("invalid googleapis-root line, expected = separator, got=%q", line)
70+
}
71+
newContents = append(newContents, fmt.Sprintf("%s= '%s'", s[0], newRoot))
72+
case strings.HasPrefix(line, "googleapis-sha256 "):
73+
s := strings.SplitN(line, "=", 2)
74+
if len(s) != 2 {
75+
return fmt.Errorf("invalid googleapis-sha256 line, expected = separator, got=%q", line)
76+
}
77+
newContents = append(newContents, fmt.Sprintf("%s= '%s'", s[0], newSha256))
78+
default:
79+
newContents = append(newContents, line)
80+
}
81+
}
82+
83+
cwd, _ := os.Getwd()
84+
fmt.Printf("%s\n", cwd)
85+
f, err := os.Create(".sidekick.toml")
86+
if err != nil {
87+
return err
88+
}
89+
defer f.Close()
90+
for i, line := range newContents {
91+
f.Write([]byte(line))
92+
if i != len(newContents)-1 {
93+
f.Write([]byte("\n"))
94+
}
95+
}
96+
return f.Close()
97+
}
98+
99+
func getSha256(query string) (string, error) {
100+
response, err := http.Get(query)
101+
if err != nil {
102+
return "", err
103+
}
104+
if response.StatusCode >= 300 {
105+
return "", fmt.Errorf("http error in download %s", response.Status)
106+
}
107+
defer response.Body.Close()
108+
109+
hasher := sha256.New()
110+
if _, err := io.Copy(hasher, response.Body); err != nil {
111+
return "", err
112+
}
113+
got := fmt.Sprintf("%x", hasher.Sum(nil))
114+
return got, nil
115+
}
116+
117+
func getLatestSha(query string) (string, error) {
118+
client := &http.Client{}
119+
request, err := http.NewRequest(http.MethodGet, query, nil)
120+
if err != nil {
121+
return "", err
122+
}
123+
request.Header.Set("Accept", "application/vnd.github.VERSION.sha")
124+
response, err := client.Do(request)
125+
if err != nil {
126+
return "", err
127+
}
128+
if response.StatusCode >= 300 {
129+
return "", fmt.Errorf("http error in download %s", response.Status)
130+
}
131+
defer response.Body.Close()
132+
contents, err := io.ReadAll(response.Body)
133+
if err != nil {
134+
return "", err
135+
}
136+
return string(contents), nil
137+
}

0 commit comments

Comments
 (0)