package main import ( "archive/tar" "compress/gzip" "context" "errors" "fmt" "io" "log/slog" "net/http" "os" "path" "path/filepath" "strings" "github.com/magefile/mage/mg" "gopkg.in/yaml.v3" "code.icb4dc0.de/prskr/supabase-operator/internal/errx" ) const ( composeFileUrl = "https://raw.githubusercontent.com/supabase/supabase/refs/heads/master/docker/docker-compose.yml" ) func GenerateAll(ctx context.Context) { mg.CtxDeps(ctx, FetchImageMeta, FetchMigrations, CRDs, CRDDocs) } func CRDs() error { return errors.Join( RunTool( tools[ControllerGen], "rbac:roleName=manager-role", "crd", "webhook", `paths="./..."`, "output:crd:artifacts:config=config/crd/bases", ), RunTool(tools[ControllerGen], `object:headerFile="hack/boilerplate.go.txt"`, `paths="./..."`), ) } func CRDDocs() error { return RunTool( tools[CRDRefDocs], "--source-path=./api/", "--renderer=markdown", "--config=crd-docs.yaml", "--output-path=./docs/api/", "--output-mode=group", ) } func FetchImageMeta(ctx context.Context) (err error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, composeFileUrl, nil) if err != nil { return err } resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer errx.Close(resp.Body, &err) var composeFile struct { Services map[string]struct { Image string `yaml:"image"` } } if err := yaml.NewDecoder(resp.Body).Decode(&composeFile); err != nil { return err } f, err := os.Create(filepath.Join("internal", "supabase", "images.go")) if err != nil { return err } defer errx.Close(f, &err) type imageRef struct { Repository string Tag string } serviceMappings := map[string]string{ "auth": "Gotrue", "functions": "EdgeRuntime", "imgproxy": "ImgProxy", "meta": "PostgresMeta", "realtime": "Realtime", "rest": "Postgrest", "storage": "Storage", "studio": "Studio", } templateData := struct { Images map[string]imageRef }{ Images: make(map[string]imageRef), } for name, service := range composeFile.Services { splitIdx := strings.LastIndex(service.Image, ":") repo := service.Image[:splitIdx] tag := service.Image[splitIdx+1:] mapping, ok := serviceMappings[name] if !ok { continue } templateData.Images[mapping] = imageRef{ Repository: repo, Tag: tag, } } latestEnvoyTag, err := latestReleaseVersion(ctx, "envoyproxy", "envoy") if err != nil { return err } templateData.Images["Envoy"] = imageRef{ Repository: "envoyproxy/envoy", Tag: fmt.Sprintf("distroless-%s", latestEnvoyTag), } if err := templates.ExecuteTemplate(f, "images.go.tmpl", templateData); err != nil { return err } if err := f.Sync(); err != nil { return err } return RunTool(tools[Gofumpt], "-l", "-w", f.Name()) } func FetchMigrations(ctx context.Context) (err error) { latestRelease, err := latestReleaseVersion(ctx, "supabase", "postgres") if err != nil { return err } releaseArtifactURL := fmt.Sprintf("https://github.com/supabase/postgres/archive/refs/tags/%s.tar.gz", latestRelease) req, err := http.NewRequestWithContext(ctx, http.MethodGet, releaseArtifactURL, nil) if err != nil { return err } resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer errx.Close(resp.Body, &err) gzipReader, err := gzip.NewReader(resp.Body) if err != nil { return err } defer errx.Close(gzipReader, &err) migrationsDirPath := path.Join(fmt.Sprintf("postgres-%s", latestRelease), ".", "migrations", "db") + "/" tarReader := tar.NewReader(gzipReader) var header *tar.Header for header, err = tarReader.Next(); err == nil; header, err = tarReader.Next() { fileInfo := header.FileInfo() if fileInfo.IsDir() || path.Ext(fileInfo.Name()) != ".sql" { continue } fileName := header.Name if strings.HasPrefix(fileName, migrationsDirPath) { fileName = strings.TrimPrefix(fileName, migrationsDirPath) dir, _ := path.Split(fileName) outDir := filepath.Join(workingDir, "assets", "migrations", filepath.FromSlash(dir)) if err := os.MkdirAll(outDir, 0o750); err != nil { return err } slog.Info("Copying file", slog.String("file", fileName)) outFile, err := os.Create(filepath.Join(workingDir, "assets", "migrations", filepath.FromSlash(fileName))) if err != nil { return err } if _, err := io.Copy(outFile, tarReader); err != nil { return err } if err := outFile.Close(); err != nil { return err } } else { slog.Debug("skipping file", slog.String("file", fileName)) } } if errors.Is(err, io.EOF) { return nil } return err }