supabase-cli/tools/publish/main.go

158 lines
4.2 KiB
Go

package main
import (
"bufio"
"bytes"
"context"
_ "embed"
"encoding/base64"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"strings"
"text/template"
"github.com/google/go-github/v62/github"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/pkg/fetcher"
"github.com/supabase/cli/tools/shared"
)
const (
SUPABASE_OWNER = "supabase"
HOMEBREW_REPO = "homebrew-tap"
SCOOP_REPO = "scoop-bucket"
)
var (
//go:embed templates/supabase.rb
brewFormula string
brewFormulaTemplate = template.Must(template.New(HOMEBREW_REPO).Parse(brewFormula))
//go:embed templates/supabase.json
scoopBucket string
scoopBucketTemplate = template.Must(template.New(SCOOP_REPO).Parse(scoopBucket))
)
func main() {
beta := flag.Bool("beta", false, "Updates the beta release channel.")
flag.Parse()
semver := flag.Arg(0)
if len(semver) == 0 {
log.Fatalln("Missing required arg: version")
} else if semver[0] == 'v' {
semver = semver[1:]
}
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
if err := publishPackages(ctx, semver, *beta); err != nil {
log.Fatalln(err)
}
}
func publishPackages(ctx context.Context, version string, beta bool) error {
config, err := fetchConfig(ctx, version)
if err != nil {
return err
}
config.FormulaName = "Supabase"
config.Description = "Supabase CLI"
filename := "supabase"
if beta {
config.FormulaName += "Beta"
config.Description += " (Beta)"
filename += "-beta"
}
client := utils.GetGitHubClient(ctx)
if err := updatePackage(ctx, client, HOMEBREW_REPO, filename+".rb", brewFormulaTemplate, config); err != nil {
return err
}
return updatePackage(ctx, client, SCOOP_REPO, filename+".json", scoopBucketTemplate, config)
}
type PackageConfig struct {
Version string
Checksum map[string]string
Description string
FormulaName string
}
func fetchConfig(ctx context.Context, version string) (PackageConfig, error) {
client := fetcher.NewFetcher("https://github.com", fetcher.WithExpectedStatus(http.StatusOK))
checkPath := fmt.Sprintf("/%s/%s/releases/download/v%[3]s/supabase_%[3]s_checksums.txt",
utils.CLI_OWNER,
utils.CLI_REPO,
version,
)
log.Println("Downloading checksum:", checkPath)
config := PackageConfig{Version: version}
resp, err := client.Send(ctx, http.MethodGet, checkPath, nil)
if err != nil {
return config, err
}
defer resp.Body.Close()
// Read checksums into map: filename -> sha256
config.Checksum = make(map[string]string)
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
tokens := strings.Fields(scanner.Text())
key := strings.TrimSuffix(tokens[1], ".tar.gz")
config.Checksum[key] = tokens[0]
}
if err := scanner.Err(); err != nil {
return config, err
}
return config, nil
}
func updatePackage(ctx context.Context, client *github.Client, repo, path string, tmpl *template.Template, config PackageConfig) error {
fmt.Fprintf(os.Stderr, "Updating %s: %s\n", repo, path)
// Render formula from template
var buf bytes.Buffer
if err := tmpl.Option("missingkey=error").Execute(&buf, config); err != nil {
return err
}
branch := "release/cli"
master := "main"
if err := shared.CreateGitBranch(ctx, client, SUPABASE_OWNER, repo, branch, master); err != nil {
return err
}
// Get file SHA
opts := github.RepositoryContentGetOptions{Ref: "heads/" + branch}
file, _, _, err := client.Repositories.GetContents(ctx, SUPABASE_OWNER, repo, path, &opts)
if err != nil {
return err
}
content, err := base64.StdEncoding.DecodeString(*file.Content)
if err != nil {
return err
}
if bytes.Equal(content, buf.Bytes()) {
fmt.Fprintln(os.Stderr, "All versions are up to date.")
return nil
}
// Update file content
message := "Release " + config.Description
commit := github.RepositoryContentFileOptions{
Message: &message,
Content: buf.Bytes(),
SHA: file.SHA,
Branch: &branch,
}
resp, _, err := client.Repositories.UpdateFile(ctx, SUPABASE_OWNER, repo, path, &commit)
if err != nil {
return err
}
fmt.Fprintln(os.Stderr, "Committed changes to", *resp.Commit.SHA)
// Create pull request
pr := github.NewPullRequest{
Title: &message,
Head: &branch,
Base: &master,
}
return shared.CreatePullRequest(ctx, client, SUPABASE_OWNER, repo, pr)
}