Skip to content

Commit 228dd15

Browse files
authored
feat: version updater support (#213)
1 parent a1502c4 commit 228dd15

File tree

7 files changed

+275
-2
lines changed

7 files changed

+275
-2
lines changed

.github/workflows/release.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,15 @@ jobs:
3636
npm run build:prod
3737
cd ..
3838
cp -r dashboard/dist/* swiftwave_service/dashboard/www/
39+
- name: Set Version
40+
run: |
41+
echo "${{ github.event.release.tag_name }}" >> swiftwave_service/cmd/.version
3942
- name: Build and publish
4043
uses: wangyoucao577/go-release-action@v1
4144
with:
4245
github_token: ${{ secrets.GITHUB_TOKEN }}
4346
goos: ${{ matrix.goos }}
4447
goarch: ${{ matrix.goarch }}
45-
goversion: "https://dl.google.com/go/go1.21.3.linux-amd64.tar.gz"
48+
goversion: "https://dl.google.com/go/go1.21.6.linux-amd64.tar.gz"
4649
project_path: "."
4750
binary_name: "swiftwave"

cmd/.version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
develop

cmd/root.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
_ "embed"
45
"fmt"
56
"os"
67

@@ -12,6 +13,9 @@ var systemConfig *system_config.Config
1213

1314
var configFilePath = "/etc/swiftwave/config.yml"
1415

16+
//go:embed .version
17+
var swiftwaveVersion string
18+
1519
func init() {
1620
rootCmd.PersistentFlags().Bool("dev", false, "Run in development mode")
1721
rootCmd.AddCommand(initCmd)
@@ -26,6 +30,8 @@ func init() {
2630
rootCmd.AddCommand(generateTLSCommand)
2731
rootCmd.AddCommand(dbMigrateCmd)
2832
rootCmd.AddCommand(serviceCmd)
33+
rootCmd.AddCommand(versionCmd)
34+
rootCmd.AddCommand(updateCmd)
2935
}
3036

3137
var rootCmd = &cobra.Command{

cmd/swiftwave.service

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ After=multi-user.target
55
[Service]
66
ExecStart=/bin/sh -c "/usr/bin/swiftwave postgres auto-run-local && /usr/bin/swiftwave start"
77
Type=simple
8+
Restart=always
89

910
[Install]
1011
WantedBy=multi-user.target

cmd/update.go

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
package cmd
2+
3+
import (
4+
"archive/tar"
5+
"compress/gzip"
6+
"errors"
7+
"github.com/spf13/cobra"
8+
"io"
9+
"net/http"
10+
"os"
11+
"os/exec"
12+
"runtime"
13+
"strings"
14+
)
15+
16+
var updateCmd = &cobra.Command{
17+
Use: "update",
18+
Short: "Update Swiftwave to the latest minor patch version",
19+
Long: `Update Swiftwave to the latest minor patch version`,
20+
Run: func(cmd *cobra.Command, args []string) {
21+
if swiftwaveVersion == "develop" {
22+
printError("You should use a stable version of Swiftwave to avail this feature")
23+
return
24+
}
25+
// fetch latest tag commit
26+
latestTag, err := fetchLatestTag(swiftwaveVersion)
27+
if err != nil {
28+
printError("Failed to fetch latest tag")
29+
return
30+
}
31+
// fetch package download url
32+
downloadUrl, err := fetchPackageDownloadURL(latestTag)
33+
if err != nil {
34+
printError("Failed to fetch package download url")
35+
return
36+
}
37+
// download the package in tmpfs and extract it
38+
downloadedPackagePath, err := downloadPackage(downloadUrl)
39+
if err != nil {
40+
printError("Failed to download and extract the package")
41+
return
42+
}
43+
// extract the package
44+
err = extractTarGz(downloadedPackagePath, "/tmp/swiftwave-update")
45+
if err != nil {
46+
printError("Failed to extract the package")
47+
return
48+
}
49+
// new swiftwave binary path
50+
newBinaryPath := "/tmp/swiftwave-update/swiftwave"
51+
// check if new binary exists
52+
if _, err := os.Stat(newBinaryPath); os.IsNotExist(err) {
53+
printError("Failed to find new binary")
54+
return
55+
}
56+
// make new binary executable
57+
err = os.Chmod(newBinaryPath, 0755)
58+
if err != nil {
59+
printError("Failed to make new binary executable")
60+
return
61+
}
62+
// replace it at /usr/bin/swiftwave
63+
err = os.Rename(newBinaryPath, "/usr/bin/swiftwave")
64+
if err != nil {
65+
printError("Failed to replace binary")
66+
return
67+
}
68+
// daemon-reload
69+
runCommand := exec.Command("systemctl", "daemon-reload")
70+
err = runCommand.Run()
71+
if err != nil {
72+
printError("Failed to reload systemd daemon")
73+
return
74+
}
75+
// restart swiftwave service
76+
runCommand = exec.Command("systemctl", "restart", "swiftwave.service")
77+
err = runCommand.Run()
78+
if err != nil {
79+
printError("Failed to restart swiftwave service")
80+
return
81+
}
82+
printSuccess("Updated Swiftwave to " + latestTag)
83+
},
84+
}
85+
86+
func getMajorVersion(version string) string {
87+
splitVersion := strings.Split(version, ".")
88+
return splitVersion[0]
89+
}
90+
91+
func fetchLatestTag(currentVersion string) (string, error) {
92+
tagPrefix := getMajorVersion(currentVersion)
93+
tagListUrl := "https://api.github.com/repos/swiftwave-org/swiftwave/git/refs/tags/" + tagPrefix
94+
res, err := http.Get(tagListUrl)
95+
if err != nil || res.StatusCode != 200 {
96+
return "", err
97+
}
98+
resBody, err := io.ReadAll(res.Body)
99+
if err != nil {
100+
return "", err
101+
}
102+
// fetch last tag
103+
lastTag := ""
104+
for _, tag := range strings.Split(string(resBody), "refs/tags/") {
105+
if tag != "" {
106+
lastTag = strings.Split(tag, "\"")[0]
107+
}
108+
}
109+
if lastTag == "" {
110+
return "", err
111+
}
112+
return lastTag, nil
113+
}
114+
115+
func fetchPackageDownloadURL(tag string) (string, error) {
116+
packageFileName := "swiftwave-" + tag + "-" + runtime.GOOS + "-" + runtime.GOARCH + ".tar.gz"
117+
packageDownloadUrl := ""
118+
// fetch release info
119+
releaseInfoUrl := "https://api.github.com/repos/swiftwave-org/swiftwave/releases/tags/" + tag
120+
res, err := http.Get(releaseInfoUrl)
121+
if err != nil || res.StatusCode != 200 {
122+
return "", err
123+
}
124+
resBody, err := io.ReadAll(res.Body)
125+
if err != nil {
126+
return "", err
127+
}
128+
// fetch download url
129+
for _, asset := range strings.Split(string(resBody), "browser_download_url") {
130+
if asset != "" && len(strings.Split(asset, "\"")) > 2 {
131+
assetUrl := strings.Split(asset, "\"")[2]
132+
if strings.Contains(assetUrl, packageFileName) &&
133+
!strings.Contains(assetUrl, packageFileName+".md5") {
134+
packageDownloadUrl = assetUrl
135+
}
136+
}
137+
}
138+
if packageDownloadUrl == "" {
139+
return "", errors.New("failed to fetch package download url")
140+
}
141+
return packageDownloadUrl, nil
142+
}
143+
144+
func downloadPackage(url string) (string, error) {
145+
filename := url[strings.LastIndex(url, "/")+1:]
146+
// download the package
147+
file, err := os.Create("/tmp/" + filename)
148+
if err != nil {
149+
return "", err
150+
}
151+
defer func(file *os.File) {
152+
err := file.Close()
153+
if err != nil {
154+
return
155+
}
156+
}(file)
157+
resp, err := http.Get(url)
158+
if err != nil {
159+
return "", err
160+
}
161+
defer func(resp *http.Response) {
162+
err := resp.Body.Close()
163+
if err != nil {
164+
return
165+
}
166+
}(resp)
167+
_, err = io.Copy(file, resp.Body)
168+
if err != nil {
169+
return "", err
170+
}
171+
return "/tmp/" + filename, nil
172+
}
173+
174+
func extractTarGz(downloadedPackagePath, destFolder string) error {
175+
_ = os.RemoveAll(destFolder)
176+
err := os.MkdirAll(destFolder, 0755)
177+
if err != nil {
178+
return err
179+
}
180+
// open downloaded package
181+
file, err := os.Open(downloadedPackagePath)
182+
if err != nil {
183+
return err
184+
}
185+
defer func(file *os.File) {
186+
err := file.Close()
187+
if err != nil {
188+
return
189+
}
190+
}(file)
191+
// read gzip file
192+
gzipReader, err := gzip.NewReader(file)
193+
if err != nil {
194+
return err
195+
}
196+
defer func(gzipReader *gzip.Reader) {
197+
err := gzipReader.Close()
198+
if err != nil {
199+
return
200+
}
201+
}(gzipReader)
202+
// extract tar file
203+
tarReader := tar.NewReader(gzipReader)
204+
for {
205+
header, err := tarReader.Next()
206+
if err != nil {
207+
if err == io.EOF {
208+
break
209+
}
210+
return err
211+
}
212+
// get filename from header
213+
filename := header.Name
214+
// check if file is directory
215+
if header.Typeflag == tar.TypeDir {
216+
err = os.MkdirAll(destFolder+"/"+filename, 0755)
217+
if err != nil {
218+
return err
219+
}
220+
continue
221+
}
222+
// create file
223+
file, err := os.Create(destFolder + "/" + filename)
224+
if err != nil {
225+
return err
226+
}
227+
defer func(file *os.File) {
228+
err := file.Close()
229+
if err != nil {
230+
return
231+
}
232+
}(file)
233+
// copy file data
234+
_, err = io.Copy(file, tarReader)
235+
if err != nil {
236+
return err
237+
}
238+
}
239+
return nil
240+
}

cmd/version.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package cmd
2+
3+
import (
4+
_ "embed"
5+
"github.com/spf13/cobra"
6+
)
7+
8+
func init() {
9+
versionCmd.Flags().BoolP("short", "s", false, "Show only the swiftwaveVersion number")
10+
}
11+
12+
var versionCmd = &cobra.Command{
13+
Use: "version",
14+
Short: "Print the swiftwaveVersion number of Swiftwave",
15+
Long: "Print the swiftwaveVersion number of Swiftwave",
16+
Run: func(cmd *cobra.Command, args []string) {
17+
if short, _ := cmd.Flags().GetBool("short"); short {
18+
cmd.Println(swiftwaveVersion)
19+
return
20+
}
21+
cmd.Println("Swiftwave swiftwaveVersion " + swiftwaveVersion)
22+
},
23+
}

container_manager/constructor.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"github.com/docker/docker/client"
77
)
88

9-
109
func NewDockerManager(unixSocketPath string) (*Manager, error) {
1110
manager := &Manager{}
1211
client, err := client.NewClientWithOpts(client.WithHost("unix://"+unixSocketPath), client.WithAPIVersionNegotiation())

0 commit comments

Comments
 (0)