-
Notifications
You must be signed in to change notification settings - Fork 0
/
latest.go
128 lines (107 loc) · 3.66 KB
/
latest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main
import (
"os"
"encoding/json"
"fmt"
"path/filepath"
"time"
"strings"
)
type LatestMetadata struct {
Latest string `json:"latest"`
}
const LatestFileName = "..latest"
func ReadLatest(path string) (*LatestMetadata, error) {
latest_path := filepath.Join(path, LatestFileName)
latest_raw, err := os.ReadFile(latest_path)
if err != nil {
return nil, fmt.Errorf("failed to read '" + latest_path + "'; %w", err)
}
var output LatestMetadata
err = json.Unmarshal(latest_raw, &output)
if err != nil {
return nil, fmt.Errorf("failed to parse JSON in '" + latest_path + "'; %w", err)
}
return &output, nil
}
func refreshLatest(asset_dir string) error {
entries, err := os.ReadDir(asset_dir)
if err != nil {
return fmt.Errorf("failed to list versions in %q; %w", asset_dir, err)
}
found := false
var most_recent time.Time
var most_recent_name string
for _, entry := range entries {
if entry.IsDir() && !strings.HasPrefix(entry.Name(), ".") {
full_path := filepath.Join(asset_dir, entry.Name())
summ, err := ReadSummary(full_path)
if err != nil {
return fmt.Errorf("failed to read summary from %q; %w", full_path, err)
}
if summ.IsProbational() {
continue
}
as_time, err := time.Parse(time.RFC3339, summ.UploadFinish)
if err != nil {
return fmt.Errorf("could not parse 'upload_finish' from %q; %w", full_path, err)
}
if !found || most_recent.Before(as_time) {
most_recent = as_time
most_recent_name = entry.Name()
found = true
}
}
}
latest_path := filepath.Join(asset_dir, LatestFileName)
if found {
output := LatestMetadata { Latest: most_recent_name }
err := dump_json(latest_path, &output)
if err != nil {
return fmt.Errorf("failed to save '..latest' to %q; %w", asset_dir, err)
}
} else {
err := os.Remove(latest_path)
if err != nil {
return fmt.Errorf("failed to remove %q; %w", latest_path, err)
}
}
return nil
}
func refreshLatestHandler(reqpath, registry string, administrators []string) error {
source_user, err := IdentifyUser(reqpath)
if err != nil {
return fmt.Errorf("failed to find owner of %q; %w", reqpath, err)
}
if !IsAuthorizedToAdmin(source_user, administrators) {
return fmt.Errorf("user %q is not authorized to refreseh the latest version (%q)", source_user, reqpath)
}
incoming := struct {
Project *string `json:"project"`
Asset *string `json:"asset"`
}{}
{
handle, err := os.ReadFile(reqpath)
if err != nil {
return fmt.Errorf("failed to read %q; %w", reqpath, err)
}
err = json.Unmarshal(handle, &incoming)
if err != nil {
return fmt.Errorf("failed to parse JSON from %q; %w", reqpath, err)
}
}
if incoming.Project == nil {
return fmt.Errorf("expected a 'project' property in %q", reqpath)
}
if incoming.Asset == nil {
return fmt.Errorf("expected an 'asset' property in %q", reqpath)
}
asset_dir := filepath.Join(registry, *(incoming.Project), *(incoming.Asset))
lock_path := filepath.Join(asset_dir, LockFileName)
handle, err := Lock(lock_path, 1000 * time.Second)
if err != nil {
return fmt.Errorf("failed to acquire the lock on the asset directory %q; %w", asset_dir, err)
}
defer Unlock(handle)
return refreshLatest(asset_dir)
}