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