5/17/2020

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





HAHWUL

Security engineer, Gopher and H4cker!

Share: | Coffee Me:

3 comments:

  1. what is the name of the terminal in dalfox pict

    ReplyDelete
    Replies
    1. Terminal applications? Or Shell? Well, let me tell you both...

      "zsh" on "iterm"

      Delete
    2. and zsh is decorated using "oh-my-zsh."

      Delete