docs/_releaser/netlify.go

144 lines
3.6 KiB
Go

package main
import (
"context"
"fmt"
"io"
"log"
"os"
"regexp"
"strings"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
netlify "github.com/netlify/open-api/go/models"
"github.com/netlify/open-api/go/plumbing/operations"
"github.com/netlify/open-api/go/porcelain"
netlifyctx "github.com/netlify/open-api/go/porcelain/context"
)
type NetlifyCmd struct {
Remove NetlifyRemoveCmd `kong:"cmd,name=remove"`
Deploy NetlifyDeployCmd `kong:"cmd,name=deploy"`
}
type netlifyGlobalFlags struct {
SiteName string `kong:"name='site-name',env='NETLIFY_SITE_NAME'"`
}
type NetlifyRemoveCmd struct {
netlifyGlobalFlags
}
func (s *NetlifyRemoveCmd) Run() error {
siteName := cleanSiteName(s.SiteName)
c := newNetlifyClient(getEnvOrSecret("NETLIFY_AUTH_TOKEN"))
site, err := c.getSite(siteName)
if err != nil {
return fmt.Errorf("failed to get site %q: %w", siteName, err)
}
if site == nil {
log.Printf("INFO: site %s already removed", siteName)
return nil
}
return c.DeleteSite(c.ctx, site.ID)
}
type NetlifyDeployCmd struct {
netlifyGlobalFlags
PublishDir string `kong:"name='publish-dir',env='NETLIFY_PUBLISH_DIR'"`
}
func (s *NetlifyDeployCmd) Run() error {
if ok, err := isDirEmpty(s.PublishDir); err != nil {
return fmt.Errorf("cannot read publish dir %q: %w", s.PublishDir, err)
} else if !ok {
return fmt.Errorf("publish dir %q empty", s.PublishDir)
}
siteName := cleanSiteName(s.SiteName)
c := newNetlifyClient(getEnvOrSecret("NETLIFY_AUTH_TOKEN"))
site, err := c.CreateSite(c.ctx, &netlify.SiteSetup{
Site: netlify.Site{
AccountSlug: getEnvOrSecret("NETLIFY_ACCOUNT_SLUG"),
Name: siteName,
},
}, false)
if err != nil {
return fmt.Errorf("failed to create site %q: %w", siteName, err)
}
deploy, err := c.DeploySite(c.ctx, porcelain.DeployOptions{
SiteID: site.ID,
Dir: s.PublishDir,
})
if err != nil {
return fmt.Errorf("failed to deploy site %q (%s): %w", siteName, site.ID, err)
}
log.Printf("INFO: site %q (%s) deployed at %s\n", siteName, site.ID, deploy.URL)
return nil
}
type netlifyClient struct {
ctx netlifyctx.Context
*porcelain.Netlify
}
func newNetlifyClient(authToken string) *netlifyClient {
return &netlifyClient{
ctx: netlifyctx.WithAuthInfo(context.Background(), authInfo(authToken)),
Netlify: porcelain.NewRetryableHTTPClient(strfmt.Default, 5),
}
}
func (c *netlifyClient) getSite(siteName string) (*netlify.Site, error) {
sites, err := c.ListSites(c.ctx, &operations.ListSitesParams{
Context: c.ctx,
Name: &siteName,
})
if err != nil {
return nil, err
}
for _, site := range sites {
if site.Name == siteName {
return site, nil
}
}
return nil, nil
}
func authInfo(authToken string) runtime.ClientAuthInfoWriter {
return runtime.ClientAuthInfoWriterFunc(func(r runtime.ClientRequest, _ strfmt.Registry) error {
err := r.SetHeaderParam("User-Agent", "User-Agent: releaser/"+version)
if err != nil {
return fmt.Errorf("unable to set useragent header: %w", err)
}
err = r.SetHeaderParam("Authorization", "Bearer "+authToken)
if err != nil {
return fmt.Errorf("unable to set authorization header: %w", err)
}
return nil
})
}
func cleanSiteName(siteName string) string {
siteName = regexp.MustCompile(`[^a-zA-Z0-9]+`).ReplaceAllString(siteName, "-")
siteName = regexp.MustCompile(`^-+\|-+$`).ReplaceAllString(siteName, "")
return strings.ToLower(strings.TrimSuffix(siteName, "-"))
}
func isDirEmpty(name string) (bool, error) {
f, err := os.Open(name)
if err != nil {
return false, err
}
defer f.Close()
_, err = f.Readdirnames(1)
if err == io.EOF {
return true, nil
}
return false, err
}