Skip to content

Commit

Permalink
Add options for partial builds
Browse files Browse the repository at this point in the history
  • Loading branch information
georgemblack committed Feb 25, 2024
1 parent f0dfc0f commit 3f63c1a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 27 deletions.
5 changes: 4 additions & 1 deletion cmd/build/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (
)

func main() {
_, err := web.Build()
_, err := web.Build(web.Options{
Archive: true,
ReplaceRemoteAssets: true,
})
if err != nil {
slog.Error(err.Error())
os.Exit(1)
Expand Down
5 changes: 4 additions & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ func main() {
port := getEnv("PORT", "9002")

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
buildID, err := web.Build()
buildID, err := web.Build(web.Options{
Archive: true,
ReplaceRemoteAssets: false,
})
if err != nil {
slog.Error(err.Error())
http.Error(w, "Build failed", http.StatusInternalServerError)
Expand Down
9 changes: 9 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,12 @@ func IsIndex(path string) bool {
fileName := filepath.Base(path)
return strings.HasPrefix(fileName, "index.html")
}

func Contains(slice []string, val string) bool {
for _, item := range slice {
if item == val {
return true
}
}
return false
}
69 changes: 44 additions & 25 deletions pkg/web/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ import (
"github.com/georgemblack/web/pkg/repo"
"github.com/georgemblack/web/pkg/static"
"github.com/georgemblack/web/pkg/types"
"github.com/georgemblack/web/pkg/util"
"golang.org/x/sync/errgroup"
)

type Options struct {
Archive bool // Create a snapshot of the site and store it in GCS
ReplaceRemoteAssets bool // Replace all assets (i.e. images) in R2, instead of just adding new ones
}

// Build starts build process
func Build() (string, error) {
func Build(options Options) (string, error) {
logger := slog.Default()
buildID := time.Now().UTC().Format("2006-01-02-15-04-05")

Expand All @@ -29,7 +35,7 @@ func Build() (string, error) {
return "", fmt.Errorf("failed to load configuration; %w", err)
}

logger.Info("initializing services...")
logger.Info("initializing services")

r2 := repo.R2Service{
Config: config,
Expand All @@ -44,7 +50,7 @@ func Build() (string, error) {
}

logger.Info("starting build: " + buildID)
logger.Info("collecting web data...")
logger.Info("collecting web data")

// Fetch 'posts' and 'likes' from API in parallel
var posts types.Posts
Expand Down Expand Up @@ -83,7 +89,7 @@ func Build() (string, error) {
// Execute build steps in parallel.
// Each builder returns a set of files to write to the destination.
// Use a mutex to ensure only one build step can add to the output files at one time.
logger.Info("executing build steps...")
logger.Info("executing build steps")

var files []types.SiteFile
mutex := sync.Mutex{}
Expand Down Expand Up @@ -199,7 +205,8 @@ func Build() (string, error) {
return "", types.WrapErr(err, "failed one or more build steps")
}

// Cleanup any unused files in the destination
// Find difference between new and existing files
// Mark unused files for deletion
logger.Info("finding diff between new and existing files")
existingKeys, err := r2.List()
if err != nil {
Expand All @@ -219,10 +226,10 @@ func Build() (string, error) {
keysToDelete = append(keysToDelete, existingKey)
}
}
logger.Info("found the following files to delete: " + fmt.Sprint(keysToDelete))
logger.Info("marking the following files for deletion: " + fmt.Sprint(keysToDelete))

// Write all site files to destination (as well as backup location)
logger.Info("writing files to destination...")
logger.Info("writing files to destination")

maxParallel := 35
if len(files) < maxParallel {
Expand All @@ -234,6 +241,15 @@ func Build() (string, error) {

for _, file := range files {
file := file // https://go.dev/doc/faq#closures_and_goroutines
key := file.GetKey()

writeToDestination := options.ReplaceRemoteAssets || !util.Contains(existingKeys.Keys, key)
writeToArchive := options.Archive

// Skip fetching contents of file if we don't need to write it
if !writeToDestination && !writeToArchive {
continue
}

// Fetch contents of file
contents, err := file.GetContents()
Expand All @@ -242,32 +258,35 @@ func Build() (string, error) {
}

// Write file to R2
group.Go(func() error {
logger.Info("writing file to r2: " + file.GetKey())
err = r2.Write(file.GetKey(), bytes.NewReader(contents))
if err != nil {
return types.WrapErr(err, "failed to write file to r2")
}
return nil
})
if writeToDestination {
group.Go(func() error {
logger.Info("writing file to r2: " + key)
err = r2.Write(file.GetKey(), bytes.NewReader(contents))
if err != nil {
return types.WrapErr(err, "failed to write file to r2")
}
return nil
})
}

// Write file to GCS backups bucket
group.Go(func() error {
logger.Info("writing file to gcs: " + file.GetKey())
err = gcs.PutToBackup(file.GetKey(), buildID, contents)
if err != nil {
return types.WrapErr(err, "failed to write file to gcs sbackup")
}
return nil
})
if writeToArchive {
group.Go(func() error {
logger.Info("writing file to gcs: " + key)
err = gcs.PutToBackup(key, buildID, contents)
if err != nil {
return types.WrapErr(err, "failed to write file to gcs sbackup")
}
return nil
})
}
}

if err := group.Wait(); err != nil {
return "", types.WrapErr(err, "failed to write file(s)")
}

logger.Info("deleting files from destination...")

logger.Info("cleaning up unused files from destination")
for _, key := range keysToDelete {
logger.Info("deleting file from r2: " + key)
err = r2.Delete(key)
Expand Down

0 comments on commit 3f63c1a

Please sign in to comment.