github.com/supabase/cli@v1.168.1/tools/selfhost/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/base64"
     7  	"fmt"
     8  	"log"
     9  	"os"
    10  	"os/signal"
    11  	"strings"
    12  
    13  	"github.com/google/go-github/v62/github"
    14  	"github.com/supabase/cli/internal/utils"
    15  	"github.com/supabase/cli/tools/shared"
    16  	"gopkg.in/yaml.v3"
    17  )
    18  
    19  const (
    20  	SUPABASE_REPO  = "supabase"
    21  	SUPABASE_OWNER = "supabase"
    22  )
    23  
    24  func main() {
    25  	branch := "cli/latest"
    26  	if len(os.Args) > 1 {
    27  		branch = os.Args[1]
    28  	}
    29  
    30  	ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
    31  	if err := updateSelfHosted(ctx, branch); err != nil {
    32  		log.Fatalln(err)
    33  	}
    34  }
    35  
    36  type ComposeService struct {
    37  	Image string `yaml:"image,omitempty"`
    38  }
    39  
    40  type ComposeFile struct {
    41  	Services map[string]ComposeService `yaml:"services,omitempty"`
    42  }
    43  
    44  func updateSelfHosted(ctx context.Context, branch string) error {
    45  	client := utils.GetGtihubClient(ctx)
    46  	master := "master"
    47  	if err := shared.CreateGitBranch(ctx, client, SUPABASE_OWNER, SUPABASE_REPO, branch, master); err != nil {
    48  		return err
    49  	}
    50  	stable := getStableVersions()
    51  	if err := updateComposeVersion(ctx, client, "docker/docker-compose.yml", branch, stable); err != nil {
    52  		return err
    53  	}
    54  	pr := github.NewPullRequest{
    55  		Title: github.String("chore: update self-hosted image versions"),
    56  		Head:  &branch,
    57  		Base:  &master,
    58  	}
    59  	return shared.CreatePullRequest(ctx, client, SUPABASE_OWNER, SUPABASE_REPO, pr)
    60  }
    61  
    62  func getStableVersions() map[string]string {
    63  	images := append([]string{utils.Pg15Image}, utils.ServiceImages...)
    64  	result := make(map[string]string, len(images))
    65  	for _, img := range images {
    66  		parts := strings.Split(img, ":")
    67  		key := strings.TrimPrefix(parts[0], "library/")
    68  		result[key] = parts[1]
    69  	}
    70  	return result
    71  }
    72  
    73  func updateComposeVersion(ctx context.Context, client *github.Client, path, ref string, stable map[string]string) error {
    74  	fmt.Fprintln(os.Stderr, "Parsing file:", path)
    75  	opts := github.RepositoryContentGetOptions{Ref: "heads/" + ref}
    76  	file, _, _, err := client.Repositories.GetContents(ctx, SUPABASE_OWNER, SUPABASE_REPO, path, &opts)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	content, err := base64.StdEncoding.DecodeString(*file.Content)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	var data ComposeFile
    85  	if err := yaml.Unmarshal(content, &data); err != nil {
    86  		return err
    87  	}
    88  	updated := false
    89  	for _, v := range data.Services {
    90  		parts := strings.Split(v.Image, ":")
    91  		if version, ok := stable[parts[0]]; ok && parts[1] != version {
    92  			fmt.Fprintf(os.Stderr, "Updating %s: %s => %s\n", parts[0], parts[1], version)
    93  			image := strings.Join([]string{parts[0], version}, ":")
    94  			content = bytes.ReplaceAll(content, []byte(v.Image), []byte(image))
    95  			updated = true
    96  		}
    97  	}
    98  	if !updated {
    99  		fmt.Fprintln(os.Stderr, "All images are up to date.")
   100  		return nil
   101  	}
   102  	message := "chore: update image versions for " + path
   103  	commit := github.RepositoryContentFileOptions{
   104  		Message: &message,
   105  		Content: content,
   106  		SHA:     file.SHA,
   107  		Branch:  &ref,
   108  	}
   109  	resp, _, err := client.Repositories.UpdateFile(ctx, SUPABASE_OWNER, SUPABASE_REPO, path, &commit)
   110  	if err != nil {
   111  		return err
   112  	}
   113  	fmt.Fprintln(os.Stderr, "Committed changes to", *resp.Commit.SHA)
   114  	return nil
   115  }