package main import ( "context" "fmt" "log" "os" "path/filepath" "strings" "text/template" "github.com/google/go-github/v54/github" "golang.org/x/oauth2" "golang.org/x/sync/errgroup" "github.com/make-go-great/netrc-go" ) const ( postFilesPath = "posts" templatePostPath = "templates/post.html" generatedPath = "docs" extHTML = ".html" netrcPath = "~/.netrc" netrcMachineGitHub = "github.com" ) type templatePostData struct { Index string Body string } func main() { ctx := context.Background() // Cleanup generated path if err := os.RemoveAll(generatedPath); err != nil { log.Fatalln("Failed to remove all", generatedPath, err) } if err := os.MkdirAll(generatedPath, 0o777); err != nil { log.Fatalln("Failed to mkdir all", generatedPath) } // Read post files directory postFiles, err := os.ReadDir(postFilesPath) if err != nil { log.Fatalln("Failed to read dir", postFilesPath) } // Prepare template templatePostBytes, err := os.ReadFile(templatePostPath) if err != nil { log.Fatalln("Failed to read file", templatePostPath, err) } templatePost, err := template.New("post").Parse(string(templatePostBytes)) if err != nil { log.Fatalln("Failed to parse template", err) } // Prepare GitHub netrcData, err := netrc.ParseFile(netrcPath) if err != nil { log.Fatalln("Failed to read file netrc", err) } var ghAccessToken string for _, machine := range netrcData.Machines { if machine.Name == netrcMachineGitHub { ghAccessToken = machine.Password break } } if ghAccessToken == "" { log.Fatalln("Empty GitHub token in netrc") } ghTokenSrc := oauth2.StaticTokenSource( &oauth2.Token{ AccessToken: strings.TrimSpace(ghAccessToken), }, ) ghHTTPClient := oauth2.NewClient(ctx, ghTokenSrc) ghClient := github.NewClient(ghHTTPClient) eg := new(errgroup.Group) // Generate post files for _, postFile := range postFiles { if postFile.IsDir() { continue } postFilename := postFile.Name() eg.Go(func() error { // Prepare post file mdFilename := filepath.Join(postFilesPath, postFilename) mdFileBytes, err := os.ReadFile(mdFilename) if err != nil { return fmt.Errorf("os: failed to read file %s: %w", mdFilename, err) } ghMarkdown, _, err := ghClient.Markdown(ctx, string(mdFileBytes), nil) if err != nil { return fmt.Errorf("github: failed to markdown: %w", err) } // Prepare html file htmlFilename := strings.TrimSuffix(postFilename, filepath.Ext(postFilename)) + extHTML htmlFilepath := filepath.Join(generatedPath, htmlFilename) htmlFile, err := os.OpenFile(htmlFilepath, os.O_RDWR|os.O_CREATE, 0o600) if err != nil { return fmt.Errorf("os: failed to open file %s: %w", htmlFilepath, err) } defer htmlFile.Close() // Ignore index in index file indexHTML := `

~

` if strings.Contains(postFilename, "index") { indexHTML = "" } if err := templatePost.Execute(htmlFile, templatePostData{ Index: indexHTML, Body: ghMarkdown, }); err != nil { return fmt.Errorf("template: failed to execute: %w", err) } return nil }) } // Wait for all HTTP fetches to complete. if err := eg.Wait(); err != nil { log.Fatalln("I am sorry :(", err) } else { log.Println("Build successfully. Are you happy now?") } }