@@ -25,75 +25,151 @@ import (
2525
2626const (
2727 defaultGitHubApi = "https://api.github.com"
28- defaultGitHub = "https://github.com"
29- repo = "googleapis/googleapis"
28+ defaultGitHubDn = "https://github.com"
3029 branch = "master"
30+ defaultRoot = "googleapis"
3131)
3232
33+ // githubEndpoints defines the endpoints used to access GitHub.
34+ type githubEndpoints struct {
35+ // Api defines the endpoint used to make API calls.
36+ Api string
37+ // Download defines the endpoint to download tarballs.
38+ Download string
39+ }
40+
41+ // githubRepo represents a GitHub repository name.
42+ type githubRepo struct {
43+ // Org defines the GitHub organization (or user), that owns the repository.
44+ Org string
45+ // Repo is the name of the repository, such as `googleapis` or `google-cloud-rust`.
46+ Repo string
47+ }
48+
3349// UpdateRootConfig updates the root configuration file with the latest SHA from GitHub.
3450func 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
51+ endpoints := githubConfig (rootConfig )
52+ repo , err := githubRepoFromTarballLink (rootConfig , defaultRoot )
53+ if err != nil {
54+ return err
4255 }
4356
44- query := fmt .Sprintf ("%s/repos/%s/commits/%s" , gitHubApi , repo , branch )
57+ query := fmt .Sprintf ("%s/repos/%s/%s/ commits/%s" , endpoints . Api , repo . Org , repo . Repo , branch )
4558 fmt .Printf ("getting latest SHA from %q\n " , query )
4659 latestSha , err := getLatestSha (query )
4760 if err != nil {
4861 return err
4962 }
5063
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 )
64+ newLink := newTarballLink ( endpoints , repo , latestSha )
65+ fmt .Printf ("computing SHA256 for %q\n " , newLink )
66+ newSha256 , err := getSha256 (newLink )
5467 if err != nil {
5568 return err
5669 }
57- fmt .Printf ("updating .sidekick.toml \n " )
70+ fmt .Printf ("updating %s \n " , configName )
5871
59- contents , err := os .ReadFile (".sidekick.toml" )
72+ contents , err := os .ReadFile (configName )
6073 if err != nil {
6174 return err
6275 }
63- var newContents []string
64- for _ , line := range strings .Split (string (contents ), "\n " ) {
76+ newContents , err := updateRootConfigContents (defaultRoot , contents , endpoints , repo , latestSha , newSha256 )
77+ if err != nil {
78+ return err
79+ }
80+ return os .WriteFile (configName , newContents , 0644 )
81+ }
82+
83+ // githubConfig returns the API endpoint the browser endpoint for GitHub.
84+ // In tests, these are replaced with a fake.
85+ func githubConfig (rootConfig * Config ) * githubEndpoints {
86+ api , ok := rootConfig .Source ["github-api" ]
87+ if ! ok {
88+ api = defaultGitHubApi
89+ }
90+ download , ok := rootConfig .Source ["github" ]
91+ if ! ok {
92+ download = defaultGitHubDn
93+ }
94+ return & githubEndpoints {
95+ Api : api ,
96+ Download : download ,
97+ }
98+ }
99+
100+ // githubRepoFromRoot extracts the gitHub account and repository (such as
101+ // `googleapis/googleapis`, or `googleapis/google-cloud-rust`) from the tarball
102+ // link.
103+ func githubRepoFromTarballLink (rootConfig * Config , rootName string ) (* githubRepo , error ) {
104+ config := githubConfig (rootConfig )
105+ root , ok := rootConfig .Source [fmt .Sprintf ("%s-root" , rootName )]
106+ if ! ok {
107+ return nil , fmt .Errorf ("missing %s root configuration" , rootName )
108+ }
109+ urlPath := strings .TrimPrefix (root , config .Download )
110+ urlPath = strings .TrimPrefix (urlPath , "/" )
111+ components := strings .Split (urlPath , "/" )
112+ if len (components ) < 2 {
113+ return nil , fmt .Errorf ("url path for %s root configuration is missing components" , rootName )
114+ }
115+ repo := & githubRepo {
116+ Org : components [0 ],
117+ Repo : components [1 ],
118+ }
119+ return repo , nil
120+ }
121+
122+ func newTarballLink (endpoints * githubEndpoints , repo * githubRepo , latestSha string ) string {
123+ return fmt .Sprintf ("%s/%s/%s/archive/%s.tar.gz" , endpoints .Download , repo .Org , repo .Repo , latestSha )
124+ }
125+
126+ func updateRootConfigContents (rootName string , contents []byte , endpoints * githubEndpoints , repo * githubRepo , latestSha , newSha256 string ) ([]byte , error ) {
127+ newLink := newTarballLink (endpoints , repo , latestSha )
128+
129+ var output strings.Builder
130+ updatedRoot := 0
131+ updatedSha256 := 0
132+ updatedExtractedName := 0
133+ lines := strings .Split (string (contents ), "\n " )
134+ for idx , line := range lines {
65135 switch {
66- case strings .HasPrefix (line , "googleapis -root " ):
136+ case strings .HasPrefix (line , fmt . Sprintf ( "%s -root ", rootName ) ):
67137 s := strings .SplitN (line , "=" , 2 )
68138 if len (s ) != 2 {
69- return fmt .Errorf ("invalid googleapis -root line, expected = separator, got=%q" , line )
139+ return nil , fmt .Errorf ("invalid %s -root line, expected = separator, got=%q" , rootName , line )
70140 }
71- newContents = append (newContents , fmt .Sprintf ("%s= '%s'" , s [0 ], newRoot ))
72- case strings .HasPrefix (line , "googleapis-sha256 " ):
141+ fmt .Fprintf (& output , "%s= '%s'\n " , s [0 ], newLink )
142+ updatedRoot += 1
143+ case strings .HasPrefix (line , fmt .Sprintf ("%s-sha256 " , rootName )):
73144 s := strings .SplitN (line , "=" , 2 )
74145 if len (s ) != 2 {
75- return fmt .Errorf ("invalid googleapis -sha256 line, expected = separator, got=%q" , line )
146+ return nil , fmt .Errorf ("invalid %s -sha256 line, expected = separator, got=%q" , rootName , line )
76147 }
77- newContents = append (newContents , fmt .Sprintf ("%s= '%s'" , s [0 ], newSha256 ))
148+ fmt .Fprintf (& output , "%s= '%s'\n " , s [0 ], newSha256 )
149+ updatedSha256 += 1
150+ case strings .HasPrefix (line , fmt .Sprintf ("%s-extracted-name " , rootName )):
151+ s := strings .SplitN (line , "=" , 2 )
152+ if len (s ) != 2 {
153+ return nil , fmt .Errorf ("invalid %s-extracted-name line, expected = separator, got=%q" , rootName , line )
154+ }
155+ fmt .Fprintf (& output , "%s= '%s-%s'\n " , s [0 ], repo .Repo , latestSha )
156+ updatedExtractedName += 1
78157 default :
79- newContents = append (newContents , line )
158+ if idx != len (lines )- 1 {
159+ fmt .Fprintf (& output , "%s\n " , line )
160+ } else {
161+ fmt .Fprintf (& output , "%s" , line )
162+ }
80163 }
81164 }
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
165+ newContents := output .String ()
166+ if updatedRoot == 0 && updatedSha256 == 0 {
167+ return []byte (newContents ), nil
88168 }
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- }
169+ if updatedRoot != 1 || updatedSha256 != 1 || updatedExtractedName > 1 {
170+ return nil , fmt .Errorf ("too many changes to Root or Sha256 for %s" , rootName )
95171 }
96- return f . Close ()
172+ return [] byte ( newContents ), nil
97173}
98174
99175func getSha256 (query string ) (string , error ) {
0 commit comments