golang 어플리케이션 self update 적용하기(github latest version 기반)
메모 차원에서 작성합니다.
dalfox 1.1 버전대 업데이트에서 큰 부분 중 하나가 self-update입니다. 이전에 xspear, a2sv 등에서도 여러가지 방법으로 self-update를 지원했었는데 이번 dalfox는 바이너리로 컴파일되서 배포되기 때문에 고민이 좀 많았습니다. (이전까진 git command 기반으로 update 시켰거든요 😊)
그래서 git api로 release 버전 체크 후 바이너리를 교체할 방법으로 만들려고 하던 중 혹시나 해서 찾아봤더니 정말 좋은 모듈이 있어서 쉽게 해결했습니다.
Self update with go-github-selfupdate
go-get으로 모듈을 설치하신 후 ..
go get -u https://github.com/rhysd/go-github-selfupdate
아래 코드같은 형태로 self update가 가능합니다. 재미있는 점이라면, github의 주소를 기반으로 latest 버전을 체크하고, OS 타입에 맞는 바이너리를 받아 현재 실행중인 바이너리와 바꿔주는겁니다. (만들려고 했던 모든게 다있었네요)
import (
"bufio"
"github.com/blang/semver"
"github.com/rhysd/go-github-selfupdate/selfupdate"
"log"
"os"
)
const version = "1.2.3"
func confirmAndSelfUpdate() {
latest, found, err := selfupdate.DetectLatest("owner/repo")
if err != nil {
log.Println("Error occurred while detecting version:", err)
return
}
v := semver.MustParse(version)
if !found || latest.Version.LTE(v) {
log.Println("Current version is the latest")
return
}
fmt.Print("Do you want to update to", latest.Version, "? (y/n): ")
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil || (input != "y\n" && input != "n\n") {
log.Println("Invalid input")
return
}
if input == "n\n" {
return
}
exe, err := os.Executable()
if err != nil {
log.Println("Could not locate executable path")
return
}
if err := selfupdate.UpdateTo(latest.AssetURL, exe); err != nil {
log.Println("Error occurred while updating binary:", err)
return
}
log.Println("Successfully updated to version", latest.Version)
}
Using on DalFox
샘플코드 참조해서 아래와 같이 구성했습니다. 앱 내부에서 version 정보를 가지고 있는데, 이를 selfupdate의 DetectLatest 함수를 통해 얻은 마지막 버전과 비교 후 낮은 버전이라면 패치를 진행합니다.
package cmd
import (
"bufio"
"fmt"
"github.com/blang/semver"
"github.com/hahwul/dalfox/pkg/printing"
"github.com/rhysd/go-github-selfupdate/selfupdate"
"github.com/spf13/cobra"
"os"
)
// updateCmd represents the update command
var updateCmd = &cobra.Command{
Use: "update",
Short: "Update DalFox (Binary patch)",
Run: func(cmd *cobra.Command, args []string) {
confirmAndSelfUpdate()
},
}
func init() {
rootCmd.AddCommand(updateCmd)
}
func confirmAndSelfUpdate() {
version := printing.VERSION[1:]
latest, found, err := selfupdate.DetectLatest("hahwul/dalfox")
if err != nil {
printing.DalLog("ERROR", "Error occurred while detecting version", optionsStr)
return
}
v := semver.MustParse(version)
if !found || latest.Version.LTE(v) {
printing.DalLog("SYSTEM", "Current version is the latest", optionsStr)
return
}
fmt.Print("Do you want to update to", latest.Version, "? (y/n): ")
input, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil || (input != "y\n" && input != "n\n") {
printing.DalLog("ERROR", "Invalid input", optionsStr)
return
}
if input == "n\n" {
return
}
exe, err := os.Executable()
if err != nil {
printing.DalLog("SYSTEM", "Could not locate executable path", optionsStr)
return
}
if err := selfupdate.UpdateTo(latest.AssetURL, exe); err != nil {
printing.DalLog("SYSTEM", "Error occurred while updating binary", optionsStr)
return
}
printing.DalLog("SYSTEM", "Successfully updated to latest version", optionsStr)
}
덕분에 매우 쉽게 해결했습니다 :D