Skip to content

Commit 2554e7d

Browse files
committed
wip: check status
1 parent 6451fcc commit 2554e7d

8 files changed

Lines changed: 545 additions & 5 deletions

File tree

cmds/checkcmd/check.go

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,64 @@
11
package checkcmd
22

3-
import "github.com/alecthomas/kingpin"
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
"regexp"
8+
"simple-relmgt/core"
49

5-
// CheckCmd control the check command
10+
"github.com/alecthomas/kingpin"
11+
version "github.com/hashicorp/go-version"
12+
)
13+
14+
// Check control the check command
615
type Check struct {
716
cmd *kingpin.CmdClause
17+
18+
config *core.Config
19+
github *core.Github
20+
git *core.Git
21+
22+
versionFile string
23+
extractVersionRE *regexp.Regexp
24+
25+
releaseVersion string
826
}
927

1028
const (
11-
CheckCmd = "check"
29+
CheckCmd = "check"
30+
defaultVersionFile = "version.go"
31+
defaultExtractVersion = ` *VERSION *= *[\"'](%s)["']`
1232
)
1333

1434
// Action execute the `check` command
1535
func (c *Check) Action([]string) {
36+
c.config = core.NewConfig("release-mgt.yaml")
37+
38+
c.github = core.NewGithub()
39+
40+
err := c.github.CheckGithub()
41+
kingpin.FatalIfError(err, "Unable to get github-release")
42+
43+
c.git = core.NewGit()
44+
45+
err = c.git.OpenRepo()
46+
kingpin.FatalIfError(err, "Unable to open the local repository.")
47+
48+
var data []byte
49+
data, err = ioutil.ReadFile(c.versionFile)
50+
if err != nil {
51+
fmt.Printf("Unable to read release version file %s. %s", c.versionFile, err)
52+
os.Exit(3)
53+
}
54+
55+
result := c.extractVersionRE.FindStringSubmatch(string(data))
56+
if result == nil {
57+
fmt.Printf("Release version file (%s) found, but version string has not been detected from '%s'.", c.versionFile, defaultExtractVersion)
58+
os.Exit(2)
59+
}
60+
c.releaseVersion = result[1]
61+
fmt.Printf("Release version detected: %s (in %s)", c.releaseVersion, c.versionFile)
1662

1763
}
1864

@@ -22,4 +68,11 @@ func (c *Check) Init(app *kingpin.Application) {
2268
return
2369
}
2470
c.cmd = app.Command(CheckCmd, "Provide a return code on the release status")
71+
72+
c.versionFile = defaultVersionFile
73+
74+
var err error
75+
c.extractVersionRE, err = regexp.Compile(fmt.Sprintf(defaultExtractVersion, version.SemverRegexpRaw))
76+
kingpin.FatalIfError(err, "Unable to initialize check command")
77+
2578
}

core/config.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package core
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
8+
"gopkg.in/yaml.v2"
9+
)
10+
11+
// Config is the top Configuration object.
12+
type Config struct {
13+
yaml yamlConfig
14+
file string
15+
}
16+
17+
// NewConfig creates a Config object
18+
func NewConfig(file string) (ret *Config) {
19+
ret = new(Config)
20+
21+
ret.file = file
22+
return
23+
}
24+
25+
// Load the configuration file
26+
func (c *Config) Load() (err error) {
27+
if c == nil {
28+
return errors.New("Config object is nil. Unable to load")
29+
}
30+
31+
var fd *os.File
32+
fd, err = os.Open(c.file)
33+
if err != nil {
34+
return fmt.Errorf("Unable to load '%s'. %s", c.file, err)
35+
}
36+
37+
decoder := yaml.NewDecoder(fd)
38+
39+
if decoder == nil {
40+
return fmt.Errorf("Unable to load '%s'. yaml decoder object not created", c.file)
41+
}
42+
43+
err = decoder.Decode(&c.yaml)
44+
if err != nil {
45+
return fmt.Errorf("Unable to read yaml file '%s'. %s", c.file, err)
46+
}
47+
return
48+
}

core/git.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package core
2+
3+
import (
4+
"fmt"
5+
6+
git "gopkg.in/src-d/go-git.v4"
7+
"gopkg.in/src-d/go-git.v4/plumbing"
8+
)
9+
10+
type Git struct {
11+
repoPath string
12+
repo *git.Repository
13+
}
14+
15+
const (
16+
defaultRepo = "."
17+
)
18+
19+
// NewGit creates the internal GIT object
20+
func NewGit() (ret *Git) {
21+
ret = new(Git)
22+
23+
ret.repoPath = defaultRepo
24+
25+
return
26+
}
27+
28+
// OpenRepo open the GIT repo
29+
func (g *Git) OpenRepo() (err error) {
30+
31+
g.repo, err = git.PlainOpen(g.repoPath)
32+
if err != nil {
33+
return fmt.Errorf("%s is not a valid GIT repository. %s", g.repoPath, err)
34+
}
35+
36+
if _, err := g.repo.Worktree(); err != nil {
37+
return fmt.Errorf("Unable to open %s. %s", g.repoPath, err)
38+
}
39+
return
40+
}
41+
42+
// CheckTag verify if the tag `name` exist in the default remote.
43+
func (g *Git) CheckTag(name string) (found bool, _ error) {
44+
if g == nil {
45+
return
46+
}
47+
48+
var fetchOptions git.FetchOptions
49+
fetchOptions.Validate()
50+
g.repo.Fetch(&fetchOptions)
51+
52+
tagrefs, err := g.repo.Tags()
53+
if err != nil {
54+
return false, err
55+
}
56+
57+
err = tagrefs.ForEach(func(t *plumbing.Reference) (_ error) {
58+
if t.Name().String() == "refs/tags/"+name {
59+
found = true
60+
}
61+
return
62+
})
63+
return
64+
}

core/github.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package core
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"net/http"
7+
"net/url"
8+
"os"
9+
"os/exec"
10+
"regexp"
11+
"strings"
12+
13+
"github.com/forj-oss/forjj-modules/trace"
14+
)
15+
16+
// This file manage the relationship between simple-relmgt and github through github-release (https://github.com/aktau/github-release)
17+
18+
// It download the binary from https://github.com/aktau/github-release/releases/download/v{version}/linux-amd64-github-release.tar.bz2
19+
20+
// Github represents the github-release command used by simple-relmgt
21+
type Github struct {
22+
url string
23+
file string
24+
version string
25+
untarCmd string
26+
packageName string
27+
packageExtract string
28+
}
29+
30+
const (
31+
defaultVersion = "v0.7.2"
32+
defaultURLPath = "https://github.com/aktau/github-release/releases/download/%s/%s"
33+
defaultFilePath = "bin/linux/amd64/github-release"
34+
defaultFileName = "linux-amd64-github-release.tar.bz2"
35+
defaultPackageExtract = "tar -xvjf -"
36+
)
37+
38+
// NewGithub creates the Github object
39+
func NewGithub() (ret *Github) {
40+
ret = new(Github)
41+
42+
ret.url = defaultURLPath
43+
ret.file = defaultFilePath
44+
ret.version = defaultVersion
45+
ret.packageName = defaultFileName
46+
ret.packageExtract = defaultPackageExtract
47+
48+
return
49+
}
50+
51+
// SetAppVersion define the version of github-release to use.
52+
func (g *Github) SetAppVersion(version string) {
53+
if g == nil {
54+
return
55+
}
56+
g.version = version
57+
}
58+
59+
// SetURLPath define the URL path where versioned package are stored.
60+
func (g *Github) SetURLPath(urlPath string) (err error) {
61+
if g == nil {
62+
return errors.New("Github object is nil")
63+
}
64+
65+
if found, _ := regexp.Match("%s.*%s", []byte(urlPath)); !found {
66+
return fmt.Errorf("%s is an invalid package URL base. It must contains '%%s' twice. The first one will get package version, the second will get package file name", urlPath)
67+
}
68+
finalURL := fmt.Sprintf(urlPath, g.version, g.packageName)
69+
70+
urlTest := new(url.URL)
71+
if urlTest == nil {
72+
return fmt.Errorf("Cannot test the URL %s. Unable to allocate url.URL", urlPath)
73+
} else if _, err := urlTest.Parse(finalURL); err != nil {
74+
return fmt.Errorf("The URL '%s' is invalid. %s", finalURL, err)
75+
}
76+
77+
g.url = urlPath
78+
return
79+
}
80+
81+
// CheckGithub verify the binary existence and its version.
82+
func (g *Github) CheckGithub() error {
83+
if g == nil {
84+
return errors.New("Github object is nil")
85+
}
86+
if ok, _ := g.checkGithub(); !ok {
87+
return g.download()
88+
}
89+
return nil
90+
}
91+
92+
// Internal github-release check
93+
// - file found and executable
94+
// - returning version requested.
95+
func (g *Github) checkGithub() (bool, error) {
96+
if g == nil {
97+
return false, errors.New("Github object is nil")
98+
}
99+
100+
gotrace.Trace("Checking %s ...", g.file)
101+
info, err := os.Stat(g.file)
102+
if err != nil {
103+
return false, err
104+
}
105+
106+
mode := info.Mode().Perm()
107+
if (mode & 0100) == 0 {
108+
return false, fmt.Errorf("%s is not executable", g.file)
109+
}
110+
111+
command := exec.Command(g.file, "--version")
112+
output, err := command.Output()
113+
if err != nil {
114+
return false, err
115+
}
116+
117+
if !strings.Contains(string(output), g.version) {
118+
return false, fmt.Errorf("Version %s not detected. Got %s", g.version, string(output))
119+
}
120+
gotrace.Info("OK: Found %s version %s", g.file, g.version)
121+
return true, nil
122+
}
123+
124+
// Download the github-release file
125+
func (g *Github) download() (err error) {
126+
if g == nil {
127+
return errors.New("Github object is nil")
128+
}
129+
130+
finalURL := fmt.Sprintf(g.url, g.version, g.packageName)
131+
gotrace.Trace("Downloading %s...", finalURL)
132+
133+
// Get the data
134+
resp, err := http.Get(finalURL)
135+
if err != nil {
136+
return err
137+
}
138+
defer resp.Body.Close()
139+
140+
// Check server response
141+
if resp.StatusCode != http.StatusOK {
142+
return fmt.Errorf("Unable to download '%s'. bad status: %s", finalURL, resp.Status)
143+
}
144+
145+
cmds := strings.Split(g.packageExtract, " ")
146+
cmd := cmds[0]
147+
params := cmds[1:]
148+
command := exec.Command(cmd, params...)
149+
150+
command.Stdin = resp.Body
151+
152+
if err = command.Start(); err != nil {
153+
return fmt.Errorf("Unable to run '%s'. %s", g.packageExtract, err)
154+
}
155+
156+
if err = command.Wait(); err != nil {
157+
return fmt.Errorf("Issue to run 'curl %s | %s'. %s", finalURL, g.packageExtract, err)
158+
}
159+
160+
if info, err := os.Stat(g.file); err != nil {
161+
return fmt.Errorf("Unable to find '%s' from '%s'. %s", g.file, g.packageName, err)
162+
} else {
163+
mode := info.Mode().Perm()
164+
if (mode & 0100) == 0 {
165+
if err = os.Chmod(g.file, 0755); err != nil {
166+
return fmt.Errorf("Unable to set '%s' as executable. %s", g.file, err)
167+
}
168+
}
169+
}
170+
171+
if _, err := g.checkGithub(); err != nil {
172+
return fmt.Errorf("Something is wrong. Expected to have an executable %s version %s. %s", g.file, g.version, err)
173+
}
174+
175+
return
176+
}

0 commit comments

Comments
 (0)