Skip to content

Commit 43c578c

Browse files
authored
dont template formula (#3680)
* brew formula: replace text/template Also add a test while we're at it. * brew formula: use a fake http server in tests Avoids downloading big files.
1 parent 72a67f1 commit 43c578c

File tree

2 files changed

+132
-64
lines changed

2 files changed

+132
-64
lines changed

release/formula/main.go

Lines changed: 45 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -15,103 +15,84 @@
1515
package main
1616

1717
import (
18-
"bytes"
18+
"context"
19+
"crypto/sha256"
20+
"encoding/hex"
1921
"fmt"
22+
"hash"
2023
"io"
2124
"net/http"
2225
"os"
23-
"os/exec"
2426
"path/filepath"
2527
"strings"
26-
"text/template"
2728
)
2829

2930
func main() {
30-
if len(os.Args) < 2 {
31-
fmt.Fprintf(os.Stderr, "must specify new version\n")
32-
os.Exit(1)
33-
}
34-
input := Input{Version: os.Args[1]}
35-
var err error
36-
input.Sha, err = getSha(input.Version)
31+
err := run(context.Background())
3732
if err != nil {
33+
fmt.Fprintf(os.Stderr, "%v\n", err)
3834
os.Exit(1)
3935
}
36+
}
4037

41-
// generate the formula text
42-
t, err := template.New("formula").Parse(formula)
43-
if err != nil {
44-
fmt.Fprintf(os.Stderr, "%v", err)
45-
os.Exit(1)
38+
func run(ctx context.Context) error {
39+
if len(os.Args) < 2 {
40+
return fmt.Errorf("must specify new version")
4641
}
4742

48-
// write the new formula
49-
b := &bytes.Buffer{}
50-
if err = t.Execute(b, input); err != nil {
51-
fmt.Fprintf(os.Stderr, "%v", err)
52-
os.Exit(1)
43+
version := os.Args[1]
44+
url := "https://github.com/GoogleContainerTools/kpt/archive/" + version + ".tar.gz"
45+
46+
formula, err := buildFormula(http.DefaultClient, url)
47+
if err != nil {
48+
return err
5349
}
5450

55-
err = os.WriteFile(filepath.Join("Formula", "kpt.rb"), b.Bytes(), 0644)
51+
err = os.WriteFile(filepath.Join("Formula", "kpt.rb"), []byte(formula), 0644)
5652
if err != nil {
57-
fmt.Fprintf(os.Stderr, "%v", err)
58-
os.Exit(1)
53+
return err
5954
}
55+
return nil
6056
}
6157

62-
func getSha(version string) (string, error) {
63-
// create the dir for the data
64-
d, err := os.MkdirTemp("", "kpt-bin")
58+
func buildFormula(httpClient *http.Client, url string) (string, error) {
59+
sha256, err := hashURL(httpClient, url, sha256.New())
6560
if err != nil {
66-
fmt.Fprintf(os.Stderr, "%v", err)
6761
return "", err
6862
}
69-
defer os.RemoveAll(d)
7063

71-
fmt.Println(
72-
"fetching https://github.com/GoogleContainerTools/kpt/archive/" + version + ".tar.gz")
64+
// generate the formula text
65+
formula := formulaTemplate
66+
formula = strings.ReplaceAll(formula, "{{url}}", url)
67+
formula = strings.ReplaceAll(formula, "{{sha256}}", sha256)
68+
69+
return formula, nil
70+
}
71+
72+
func hashURL(httpClient *http.Client, url string, hasher hash.Hash) (string, error) {
73+
fmt.Printf("fetching %q\n", url)
74+
7375
// get the content
74-
resp, err := http.Get(
75-
"https://github.com/GoogleContainerTools/kpt/archive/" + version + ".tar.gz")
76+
resp, err := httpClient.Get(url)
7677
if err != nil {
77-
fmt.Fprintf(os.Stderr, "%v", err)
78-
return "", err
78+
return "", fmt.Errorf("error getting %q: %w", url, err)
7979
}
8080
defer resp.Body.Close()
8181

82-
// write the file
83-
func() {
84-
out, err := os.Create(filepath.Join(d, version+".tar.gz"))
85-
if err != nil {
86-
fmt.Fprintf(os.Stderr, "%v", err)
87-
os.Exit(1)
88-
}
89-
90-
if _, err = io.Copy(out, resp.Body); err != nil {
91-
fmt.Fprintf(os.Stderr, "%v", err)
92-
os.Exit(1)
93-
}
94-
out.Close()
95-
}()
82+
if resp.StatusCode != 200 {
83+
return "", fmt.Errorf("unexpected response from %q: %v", url, resp.Status)
84+
}
9685

97-
// calculate the sha
98-
e := exec.Command("sha256sum", filepath.Join(d, version+".tar.gz"))
99-
o, err := e.Output()
100-
if err != nil {
101-
fmt.Fprintf(os.Stderr, "%v", err)
102-
return "", err
86+
if _, err := io.Copy(hasher, resp.Body); err != nil {
87+
return "", fmt.Errorf("error hashing response from %q: %w", url, err)
10388
}
104-
parts := strings.Split(string(o), " ")
105-
fmt.Println("new sha: " + parts[0])
106-
return parts[0], nil
107-
}
10889

109-
type Input struct {
110-
Version string
111-
Sha string
90+
// calculate the sha
91+
hash := hasher.Sum(nil)
92+
return hex.EncodeToString(hash), nil
11293
}
11394

114-
const formula = `# Copyright 2019 Google LLC
95+
const formulaTemplate = `# Copyright 2019 Google LLC
11596
#
11697
# Licensed under the Apache License, Version 2.0 (the "License");
11798
# you may not use this file except in compliance with the License.
@@ -128,8 +109,8 @@ const formula = `# Copyright 2019 Google LLC
128109
class Kpt < Formula
129110
desc "Toolkit to manage,and apply Kubernetes Resource config data files"
130111
homepage "https://googlecontainertools.github.io/kpt"
131-
url "https://github.com/GoogleContainerTools/kpt/archive/{{.Version}}.tar.gz"
132-
sha256 "{{.Sha}}"
112+
url "{{url}}"
113+
sha256 "{{sha256}}"
133114
134115
depends_on "go" => :build
135116

release/formula/main_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2022 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+
// http://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 main
16+
17+
import (
18+
"bytes"
19+
"io"
20+
"net/http"
21+
"testing"
22+
23+
"github.com/google/go-cmp/cmp"
24+
)
25+
26+
func TestFormula(t *testing.T) {
27+
want := `# Copyright 2019 Google LLC
28+
#
29+
# Licensed under the Apache License, Version 2.0 (the "License");
30+
# you may not use this file except in compliance with the License.
31+
# You may obtain a copy of the License at
32+
#
33+
# http://www.apache.org/licenses/LICENSE-2.0
34+
#
35+
# Unless required by applicable law or agreed to in writing, software
36+
# distributed under the License is distributed on an "AS IS" BASIS,
37+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
38+
# See the License for the specific language governing permissions and
39+
# limitations under the License.
40+
41+
class Kpt < Formula
42+
desc "Toolkit to manage,and apply Kubernetes Resource config data files"
43+
homepage "https://googlecontainertools.github.io/kpt"
44+
url "https://github.com/GoogleContainerTools/kpt/archive/v0.0.0-fake.tar.gz"
45+
sha256 "4e42c5ce1a23511405beb5f51cfe07885fa953db448265fe74ee9b81e0def277"
46+
47+
depends_on "go" => :build
48+
49+
def install
50+
ENV["GO111MODULE"] = "on"
51+
system "go", "build", "-ldflags", "-X github.com/GoogleContainerTools/kpt/run.version=#{version}", *std_go_args
52+
end
53+
54+
test do
55+
assert_match version.to_s, shell_output("#{bin}/kpt version")
56+
end
57+
end
58+
`
59+
60+
httpClient := &http.Client{
61+
Transport: &fakeServer{t: t},
62+
}
63+
url := "https://github.com/GoogleContainerTools/kpt/archive/v0.0.0-fake.tar.gz"
64+
got, err := buildFormula(httpClient, url)
65+
if err != nil {
66+
t.Fatalf("error from buildFormula(%q): %v", url, err)
67+
}
68+
if diff := cmp.Diff(want, got); diff != "" {
69+
t.Errorf("buildFormula(%q) returned unexpected diff (-want +got):\n%s", url, diff)
70+
}
71+
}
72+
73+
type fakeServer struct {
74+
t *testing.T
75+
}
76+
77+
func (s *fakeServer) RoundTrip(req *http.Request) (*http.Response, error) {
78+
if req.URL.Path != "/GoogleContainerTools/kpt/archive/v0.0.0-fake.tar.gz" {
79+
s.t.Errorf("Expected to request '/GoogleContainerTools/kpt/archive/v0.0.0-fake.tar.gz', got: %s", req.URL.Path)
80+
}
81+
body := bytes.NewReader([]byte(`This is fake content so that our tests don't download big files`))
82+
return &http.Response{
83+
Status: http.StatusText(http.StatusOK),
84+
StatusCode: http.StatusOK,
85+
Body: io.NopCloser(body),
86+
}, nil
87+
}

0 commit comments

Comments
 (0)