/ src / update.go
update.go
  1  package git_pages
  2  
  3  import (
  4  	"context"
  5  	"errors"
  6  	"fmt"
  7  	"io"
  8  	"log"
  9  	"strings"
 10  )
 11  
 12  type UpdateOutcome int
 13  
 14  const (
 15  	UpdateError UpdateOutcome = iota
 16  	UpdateTimeout
 17  	UpdateCreated
 18  	UpdateReplaced
 19  	UpdateDeleted
 20  	UpdateNoChange
 21  )
 22  
 23  type UpdateResult struct {
 24  	outcome  UpdateOutcome
 25  	manifest *Manifest
 26  	err      error
 27  }
 28  
 29  func Update(ctx context.Context, webRoot string, manifest *Manifest) UpdateResult {
 30  	var oldManifest, newManifest *Manifest
 31  	var err error
 32  
 33  	outcome := UpdateError
 34  	oldManifest, _ = backend.GetManifest(ctx, webRoot, GetManifestOptions{})
 35  	if IsManifestEmpty(manifest) {
 36  		newManifest, err = manifest, backend.DeleteManifest(ctx, webRoot)
 37  		if err == nil {
 38  			if oldManifest == nil {
 39  				outcome = UpdateNoChange
 40  			} else {
 41  				outcome = UpdateDeleted
 42  			}
 43  		}
 44  	} else if err = PrepareManifest(ctx, manifest); err == nil {
 45  		newManifest, err = StoreManifest(ctx, webRoot, manifest)
 46  		if err == nil {
 47  			domain, _, _ := strings.Cut(webRoot, "/")
 48  			err = backend.CreateDomain(ctx, domain)
 49  		}
 50  		if err == nil {
 51  			if oldManifest == nil {
 52  				outcome = UpdateCreated
 53  			} else if CompareManifest(oldManifest, newManifest) {
 54  				outcome = UpdateNoChange
 55  			} else {
 56  				outcome = UpdateReplaced
 57  			}
 58  		}
 59  	}
 60  
 61  	if err == nil {
 62  		status := ""
 63  		switch outcome {
 64  		case UpdateCreated:
 65  			status = "created"
 66  		case UpdateReplaced:
 67  			status = "replaced"
 68  		case UpdateDeleted:
 69  			status = "deleted"
 70  		case UpdateNoChange:
 71  			status = "unchanged"
 72  		}
 73  		if newManifest.Commit != nil {
 74  			log.Printf("update %s ok: %s %s", webRoot, status, *newManifest.Commit)
 75  		} else {
 76  			log.Printf("update %s ok: %s", webRoot, status)
 77  		}
 78  	} else {
 79  		log.Printf("update %s err: %s", webRoot, err)
 80  	}
 81  
 82  	return UpdateResult{outcome, newManifest, err}
 83  }
 84  
 85  func UpdateFromRepository(
 86  	ctx context.Context,
 87  	webRoot string,
 88  	repoURL string,
 89  	branch string,
 90  ) (result UpdateResult) {
 91  	span, ctx := ObserveFunction(ctx, "UpdateFromRepository", "repo.url", repoURL)
 92  	defer span.Finish()
 93  
 94  	log.Printf("update %s: %s %s\n", webRoot, repoURL, branch)
 95  
 96  	manifest, err := FetchRepository(ctx, repoURL, branch)
 97  	if errors.Is(err, context.DeadlineExceeded) {
 98  		result = UpdateResult{UpdateTimeout, nil, fmt.Errorf("update timeout")}
 99  	} else if err != nil {
100  		result = UpdateResult{UpdateError, nil, err}
101  	} else {
102  		result = Update(ctx, webRoot, manifest)
103  	}
104  
105  	observeUpdateResult(result)
106  	return result
107  }
108  
109  var errArchiveFormat = errors.New("unsupported archive format")
110  
111  func UpdateFromArchive(
112  	ctx context.Context,
113  	webRoot string,
114  	contentType string,
115  	reader io.Reader,
116  ) (result UpdateResult) {
117  	var manifest *Manifest
118  	var err error
119  
120  	switch contentType {
121  	case "application/x-tar":
122  		log.Printf("update %s: (tar)", webRoot)
123  		manifest, err = ExtractTar(reader) // yellow?
124  	case "application/x-tar+gzip":
125  		log.Printf("update %s: (tar.gz)", webRoot)
126  		manifest, err = ExtractTarGzip(reader) // definitely yellow.
127  	case "application/x-tar+zstd":
128  		log.Printf("update %s: (tar.zst)", webRoot)
129  		manifest, err = ExtractTarZstd(reader)
130  	case "application/zip":
131  		log.Printf("update %s: (zip)", webRoot)
132  		manifest, err = ExtractZip(reader)
133  	default:
134  		err = errArchiveFormat
135  	}
136  
137  	if err != nil {
138  		log.Printf("update %s err: %s", webRoot, err)
139  		result = UpdateResult{UpdateError, nil, err}
140  	} else {
141  		result = Update(ctx, webRoot, manifest)
142  	}
143  
144  	observeUpdateResult(result)
145  	return
146  }
147  
148  func observeUpdateResult(result UpdateResult) {
149  	if result.err != nil {
150  		ObserveError(result.err)
151  	}
152  }