You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

245 lines
8.5 KiB
Go

package main
import (
"bufio"
"bytes"
"fmt"
"hash/crc32"
"log"
"math/rand"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"time"
"github.com/PumpkinSeed/cage"
"github.com/gohugoio/hugo/commands"
"github.com/spf13/viper"
)
func check(e error) {
if e != nil {
log.Fatal(e)
panic(e)
}
}
func isPublished(gitRepoPath, gitIndexPath, prevcommit, lastcommit string) bool {
var sout bytes.Buffer
//var serr bytes.Buffer
commits := exec.Command("git", "-C", gitRepoPath, "rev-list", fmt.Sprintf("%s..%s", prevcommit, lastcommit))
commits.Env = os.Environ()
commits.Env = append(commits.Env, fmt.Sprintf("GIT_INDEX_FILE=%s%s", gitRepoPath, gitIndexPath))
commits.Stdout = &sout
//commits.Stderr = &serr
err := commits.Run()
check(err)
for _, commit := range strings.Fields(sout.String()) {
committedFiles, err := exec.Command("git", "-C", gitRepoPath, "diff-tree", "--no-commit-id", "--name-only", commit).Output()
check(err)
for _, commitFile := range strings.Fields(string(committedFiles)) {
if commitFile == "PUBLISH.trigger.md" {
return true
}
}
commitMessages, err := exec.Command("git", "-C", gitRepoPath, "show", "-s", "--format=%B", commit).Output()
check(err)
if strings.Contains(string(commitMessages), "!publish!") {
return true
}
}
return false
}
func hugoLogs(logLines string, lines []string, hugoInstance string) string {
logLines = logLines + fmt.Sprintf("~~~~~~~~~~ Hugo's logs %s ~~~~~~~~~~\n", hugoInstance)
for _, d := range lines {
logLines = logLines + d + "\n"
}
logLines = logLines + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
return logLines
}
func getOutdir(tmpRepoPath, gitRepoPath string) string {
viper.SetConfigName("config")
viper.SetConfigType("toml")
viper.AddConfigPath(filepath.Join(tmpRepoPath))
err := viper.ReadInConfig()
check(err)
//outdirBasePath := viper.GetString("params.sandpointsOutputDirectory")
outdirChecksum := strconv.FormatUint(uint64(crc32.ChecksumIEEE([]byte(gitRepoPath))), 16)
reg, err := regexp.Compile("[^a-zA-Z0-9]+")
check(err)
outdirRepoName := reg.ReplaceAllString(viper.GetString("title"), "")
return strings.ToLower(outdirRepoName) + "-" + outdirChecksum
}
func main() {
defer os.Exit(0)
// outDir is where the output of hugo will end up
// make sure the last part of the path has the right permissions
// os.TempDir() as first argument in filepath.Join is probably good choice for testing
outDir := filepath.Join("/", "var", "www", "html", "sandpoints")
startTime := time.Now()
logLines := startTime.Format(time.RFC822) + "\n"
logGiteaLines := ""
ex, err := os.Executable()
check(err)
var gitRepoPath, prevCommit, lastCommit, branchName, gitIndexPath string
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
if scanner.Text() == "" {
// if it has no output it comes as post-commit hook
gitRepoPath = strings.Replace(filepath.Dir(ex), filepath.Join("/", ".git", "hooks"), "", 1)
//gitRepoPath = "/home/m/gitea-repositories/sandpoints/dev.git"
gitLastCommit, err := exec.Command("git", "-C", gitRepoPath, "rev-parse", "HEAD").Output()
check(err)
gitPrevCommit, err := exec.Command("git", "-C", gitRepoPath, "rev-parse", "HEAD^").Output()
check(err)
gitBranchName, err := exec.Command("git", "-C", gitRepoPath, "branch", "-a", "--contains", "HEAD").Output()
check(err)
branchName = strings.Replace(strings.TrimSpace(string(gitBranchName)), "* ", "", 1)
prevCommit, lastCommit, gitIndexPath = strings.TrimSpace(string(gitPrevCommit)), strings.TrimSpace(string(gitLastCommit)), filepath.Join("/", ".git", "index")
} else {
// if it has output it comes as post-receive.d/sphook from gitea repo
revs := strings.Fields(scanner.Text())
gitRepoPath = strings.Replace(filepath.Dir(ex), filepath.Join("/", "hooks", "post-receive.d"), "", 1)
prevCommit, lastCommit, branchName, gitIndexPath = revs[0], revs[1], strings.Split(revs[2], "/")[2], filepath.Join("/", "index")
}
// there's third one here, revs[2], usually saying refs/heads/master
// branchName := fmt.Sprintf(strings.Split(revs[2])[2])
logLines = logLines + "\n" + fmt.Sprintf("Repo: %s\n", gitRepoPath)
logLines = logLines + fmt.Sprintf("Repo's branch: %s\n", branchName)
rand.Seed(time.Now().UnixNano())
worktreeName := fmt.Sprintf("sandpoints_repo_%06d", rand.Intn(100001))
tmpRepoPath := filepath.Join(os.TempDir(), worktreeName)
logLines = logLines + fmt.Sprintf("cloned into temporary directory: %s\n", tmpRepoPath)
if _, err := os.Stat(tmpRepoPath); err == nil {
err := os.RemoveAll(tmpRepoPath)
check(err)
}
err = os.MkdirAll(tmpRepoPath, 0755)
check(err)
//var sout bytes.Buffer
//var serr bytes.Buffer
gitAddWorktree := exec.Command("git", "-C", gitRepoPath, "worktree", "add", "--detach", tmpRepoPath)
gitRemoveWorktree := exec.Command("git", "-C", gitRepoPath, "worktree", "remove", worktreeName)
gitAddWorktree.Env = os.Environ()
gitAddWorktree.Env = append(gitAddWorktree.Env, fmt.Sprintf("GIT_INDEX_FILE=%s%s", gitRepoPath, gitIndexPath))
//gitAddWorktree.Stdout = &sout
//gitAddWorktree.Stderr = &serr
err = gitAddWorktree.Run()
check(err)
gitRemoveWorktree.Env = os.Environ()
gitRemoveWorktree.Env = append(gitRemoveWorktree.Env, fmt.Sprintf("GIT_INDEX_FILE=%s%s", gitRepoPath, gitIndexPath))
defer gitRemoveWorktree.Run()
outSandpointsDir := getOutdir(tmpRepoPath, gitRepoPath)
logLines = logLines + fmt.Sprintf("to be moved to: %s\n", filepath.Join(outDir, outSandpointsDir))
tmpHugoGiteaPath := filepath.Join(os.TempDir(), fmt.Sprintf("sandpoints_hugo_%06d", rand.Intn(100001)))
tmpHugoPreviewPath := filepath.Join(tmpHugoGiteaPath, "_preview")
err = os.RemoveAll(tmpHugoGiteaPath)
check(err)
err = os.MkdirAll(tmpHugoPreviewPath, 0755)
check(err)
err = os.MkdirAll(filepath.Join(outDir, outSandpointsDir, "_preview"), 0755)
check(err)
published := isPublished(gitRepoPath, gitIndexPath, prevCommit, lastCommit)
defer func() {
lastPreviewCommitLog, err := os.Create(filepath.Join(outDir, outSandpointsDir, "_preview", "last-commit-log.txt"))
check(err)
defer lastPreviewCommitLog.Close()
fmt.Fprintln(lastPreviewCommitLog, logLines)
log.Printf(logLines)
}()
defer func() {
if published {
lastGiteaCommitLog, err := os.Create(filepath.Join(outDir, outSandpointsDir, "last-commit-log.txt"))
check(err)
defer lastGiteaCommitLog.Close()
fmt.Fprintln(lastGiteaCommitLog, logGiteaLines)
}
}()
if published {
hugoGiteaLogs := cage.Start()
respGitea := commands.Execute([]string{"-s", tmpRepoPath, "-d", tmpHugoGiteaPath, "-e", "gitea"})
cage.Stop(hugoGiteaLogs)
if respGitea.Err != nil {
logGiteaLines = fmt.Sprintf("Attempt to publish web site ended up with an Error! Check out the last commit to capture possible errors.\n\n%s\n~~~~~~~~~~\n\n%s", respGitea.Err, logLines)
logLines = fmt.Sprintf("Rendering _preview of the web site ended up with an ERROR! Check out the last commit to capture possible errors.\n\n%s\n~~~~~~~~~~\n\n%s", respGitea.Err, logLines)
runtime.Goexit()
}
logGiteaLines = hugoLogs(logLines, hugoGiteaLogs.Data, "for published instance")
}
hugoPreviewLogs := cage.Start()
respPreview := commands.Execute([]string{"-s", tmpRepoPath, "-d", tmpHugoPreviewPath, "-e", "preview", "--templateMetrics"})
cage.Stop(hugoPreviewLogs)
if respPreview.Err != nil {
logLines = fmt.Sprintf("Rendering _preview of the web site ended up with an ERROR! Check out the last commit to capture possible errors.\n\n%s\n~~~~~~~~~~\n\n%s", respPreview.Err, logLines)
runtime.Goexit()
}
logLines = hugoLogs(logLines, hugoPreviewLogs.Data, "for _preview instance")
if published {
if _, err := os.Stat(filepath.Join(outDir, outSandpointsDir)); err == nil {
err := os.RemoveAll(filepath.Join(outDir, outSandpointsDir))
check(err)
}
} else if _, err := os.Stat(filepath.Join(outDir, outSandpointsDir, "_preview")); err == nil {
err := os.RemoveAll(filepath.Join(outDir, outSandpointsDir, "_preview"))
check(err)
}
err = os.MkdirAll(outDir, 0755)
check(err)
if published {
err = os.Rename(tmpHugoGiteaPath, filepath.Join(outDir, outSandpointsDir))
check(err)
} else {
err = os.Rename(tmpHugoPreviewPath, filepath.Join(outDir, outSandpointsDir, "_preview"))
check(err)
}
publishedMessage := ""
if published {
publishedMessage = " + published instance"
}
durationMillseconds := int64(time.Since(startTime) / time.Millisecond)
logLines = logLines + fmt.Sprintf("Total processing time (_preview%s): %d ms", publishedMessage, durationMillseconds)
logGiteaLines = logGiteaLines + fmt.Sprintf("Total processing time (published + _preview instance): %d ms", durationMillseconds)
}