CLI/TUI for drafting, repeating, and publishing daily standup updates as GitHub issues
github go cli golang management project tui daily
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(internal): add version checking against GitHub releases

+145
+145
internal/version/check.go
··· 1 + package version 2 + 3 + import ( 4 + "encoding/json" 5 + "fmt" 6 + "net/http" 7 + "strings" 8 + "time" 9 + 10 + "github.com/prefapp/pad/internal/appfs" 11 + ) 12 + 13 + const ( 14 + githubRepo = "prefapp/pad" 15 + checkInterval = 24 * time.Hour 16 + ) 17 + 18 + type ReleaseInfo struct { 19 + TagName string `json:"tag_name"` 20 + PublishedAt time.Time `json:"published_at"` 21 + HTMLURL string `json:"html_url"` 22 + } 23 + 24 + type VersionState struct { 25 + LatestVersion string `json:"latest_version"` 26 + LastCheck time.Time `json:"last_check"` 27 + Skipped bool `json:"skipped"` 28 + } 29 + 30 + func Current() string { 31 + return currentVersion 32 + } 33 + 34 + func SetCurrent(v string) { 35 + currentVersion = v 36 + } 37 + 38 + var currentVersion = "dev" 39 + 40 + func CheckUpdate() (*ReleaseInfo, bool) { 41 + if currentVersion == "dev" || strings.HasSuffix(currentVersion, "-snapshot") { 42 + return nil, false 43 + } 44 + 45 + state, err := loadState() 46 + if err == nil && time.Since(state.LastCheck) < checkInterval { 47 + if state.LatestVersion != "" && isNewer(state.LatestVersion, currentVersion) { 48 + return &ReleaseInfo{TagName: state.LatestVersion}, true 49 + } 50 + return nil, false 51 + } 52 + 53 + release, err := fetchLatestRelease() 54 + if err != nil { 55 + return nil, false 56 + } 57 + 58 + _ = saveState(VersionState{ 59 + LatestVersion: release.TagName, 60 + LastCheck: time.Now(), 61 + }) 62 + 63 + if isNewer(release.TagName, currentVersion) { 64 + return release, true 65 + } 66 + 67 + return nil, false 68 + } 69 + 70 + func fetchLatestRelease() (*ReleaseInfo, error) { 71 + url := fmt.Sprintf("https://api.github.com/repos/%s/releases/latest", githubRepo) 72 + 73 + resp, err := http.Get(url) 74 + if err != nil { 75 + return nil, err 76 + } 77 + defer resp.Body.Close() 78 + 79 + if resp.StatusCode != http.StatusOK { 80 + return nil, fmt.Errorf("github API returned %d", resp.StatusCode) 81 + } 82 + 83 + var release ReleaseInfo 84 + if err := json.NewDecoder(resp.Body).Decode(&release); err != nil { 85 + return nil, err 86 + } 87 + 88 + return &release, nil 89 + } 90 + 91 + func isNewer(latest, current string) bool { 92 + latest = strings.TrimPrefix(latest, "v") 93 + current = strings.TrimPrefix(current, "v") 94 + 95 + return latest != current && latest != "" 96 + } 97 + 98 + func SkipThisVersion(version string) error { 99 + state, _ := loadState() 100 + state.LatestVersion = version 101 + state.LastCheck = time.Now() 102 + state.Skipped = true 103 + return saveState(state) 104 + } 105 + 106 + func statePath() (string, error) { 107 + paths, err := appfs.Discover() 108 + if err != nil { 109 + return "", err 110 + } 111 + return paths.ConfigFile + ".version", nil 112 + } 113 + 114 + func loadState() (VersionState, error) { 115 + path, err := statePath() 116 + if err != nil { 117 + return VersionState{}, err 118 + } 119 + 120 + data, err := appfs.ReadFile(path) 121 + if err != nil { 122 + return VersionState{}, err 123 + } 124 + 125 + var state VersionState 126 + if err := json.Unmarshal(data, &state); err != nil { 127 + return VersionState{}, err 128 + } 129 + 130 + return state, nil 131 + } 132 + 133 + func saveState(state VersionState) error { 134 + path, err := statePath() 135 + if err != nil { 136 + return err 137 + } 138 + 139 + data, err := json.Marshal(state) 140 + if err != nil { 141 + return err 142 + } 143 + 144 + return appfs.WriteFile(path, data, 0644) 145 + }