Skip to content

Commit

Permalink
dont template formula (#3680)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
justinsb committed Dec 1, 2022
1 parent 72a67f1 commit 43c578c
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 64 deletions.
109 changes: 45 additions & 64 deletions release/formula/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,103 +15,84 @@
package main

import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"hash"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"text/template"
)

func main() {
if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "must specify new version\n")
os.Exit(1)
}
input := Input{Version: os.Args[1]}
var err error
input.Sha, err = getSha(input.Version)
err := run(context.Background())
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}

// generate the formula text
t, err := template.New("formula").Parse(formula)
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
func run(ctx context.Context) error {
if len(os.Args) < 2 {
return fmt.Errorf("must specify new version")
}

// write the new formula
b := &bytes.Buffer{}
if err = t.Execute(b, input); err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
version := os.Args[1]
url := "https://github.com/GoogleContainerTools/kpt/archive/" + version + ".tar.gz"

formula, err := buildFormula(http.DefaultClient, url)
if err != nil {
return err
}

err = os.WriteFile(filepath.Join("Formula", "kpt.rb"), b.Bytes(), 0644)
err = os.WriteFile(filepath.Join("Formula", "kpt.rb"), []byte(formula), 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
return err
}
return nil
}

func getSha(version string) (string, error) {
// create the dir for the data
d, err := os.MkdirTemp("", "kpt-bin")
func buildFormula(httpClient *http.Client, url string) (string, error) {
sha256, err := hashURL(httpClient, url, sha256.New())
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
return "", err
}
defer os.RemoveAll(d)

fmt.Println(
"fetching https://github.com/GoogleContainerTools/kpt/archive/" + version + ".tar.gz")
// generate the formula text
formula := formulaTemplate
formula = strings.ReplaceAll(formula, "{{url}}", url)
formula = strings.ReplaceAll(formula, "{{sha256}}", sha256)

return formula, nil
}

func hashURL(httpClient *http.Client, url string, hasher hash.Hash) (string, error) {
fmt.Printf("fetching %q\n", url)

// get the content
resp, err := http.Get(
"https://github.com/GoogleContainerTools/kpt/archive/" + version + ".tar.gz")
resp, err := httpClient.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
return "", err
return "", fmt.Errorf("error getting %q: %w", url, err)
}
defer resp.Body.Close()

// write the file
func() {
out, err := os.Create(filepath.Join(d, version+".tar.gz"))
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
}

if _, err = io.Copy(out, resp.Body); err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
}
out.Close()
}()
if resp.StatusCode != 200 {
return "", fmt.Errorf("unexpected response from %q: %v", url, resp.Status)
}

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

type Input struct {
Version string
Sha string
// calculate the sha
hash := hasher.Sum(nil)
return hex.EncodeToString(hash), nil
}

const formula = `# Copyright 2019 Google LLC
const formulaTemplate = `# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -128,8 +109,8 @@ const formula = `# Copyright 2019 Google LLC
class Kpt < Formula
desc "Toolkit to manage,and apply Kubernetes Resource config data files"
homepage "https://googlecontainertools.github.io/kpt"
url "https://github.com/GoogleContainerTools/kpt/archive/{{.Version}}.tar.gz"
sha256 "{{.Sha}}"
url "{{url}}"
sha256 "{{sha256}}"
depends_on "go" => :build
Expand Down
87 changes: 87 additions & 0 deletions release/formula/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"bytes"
"io"
"net/http"
"testing"

"github.com/google/go-cmp/cmp"
)

func TestFormula(t *testing.T) {
want := `# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class Kpt < Formula
desc "Toolkit to manage,and apply Kubernetes Resource config data files"
homepage "https://googlecontainertools.github.io/kpt"
url "https://github.com/GoogleContainerTools/kpt/archive/v0.0.0-fake.tar.gz"
sha256 "4e42c5ce1a23511405beb5f51cfe07885fa953db448265fe74ee9b81e0def277"
depends_on "go" => :build
def install
ENV["GO111MODULE"] = "on"
system "go", "build", "-ldflags", "-X github.com/GoogleContainerTools/kpt/run.version=#{version}", *std_go_args
end
test do
assert_match version.to_s, shell_output("#{bin}/kpt version")
end
end
`

httpClient := &http.Client{
Transport: &fakeServer{t: t},
}
url := "https://github.com/GoogleContainerTools/kpt/archive/v0.0.0-fake.tar.gz"
got, err := buildFormula(httpClient, url)
if err != nil {
t.Fatalf("error from buildFormula(%q): %v", url, err)
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("buildFormula(%q) returned unexpected diff (-want +got):\n%s", url, diff)
}
}

type fakeServer struct {
t *testing.T
}

func (s *fakeServer) RoundTrip(req *http.Request) (*http.Response, error) {
if req.URL.Path != "/GoogleContainerTools/kpt/archive/v0.0.0-fake.tar.gz" {
s.t.Errorf("Expected to request '/GoogleContainerTools/kpt/archive/v0.0.0-fake.tar.gz', got: %s", req.URL.Path)
}
body := bytes.NewReader([]byte(`This is fake content so that our tests don't download big files`))
return &http.Response{
Status: http.StatusText(http.StatusOK),
StatusCode: http.StatusOK,
Body: io.NopCloser(body),
}, nil
}

0 comments on commit 43c578c

Please sign in to comment.