Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
54104e61cf | |||
9891301ca4 | |||
77f2f8c5aa | |||
5f776e0757 | |||
20084b9429 | |||
b5bfd93cff | |||
ead3e6ebf0 | |||
6e482121ed | |||
f73a57d13b | |||
6d7cee5f48 | |||
e568177ea6 | |||
6d74144a3b | |||
7f94b454a7 | |||
e17ec20f79 |
16
README.md
16
README.md
@ -17,8 +17,21 @@ You'll setup a project which will need the following information:
|
|||||||
5. Path to the release file
|
5. Path to the release file
|
||||||
6. Script to set release name / version
|
6. Script to set release name / version
|
||||||
|
|
||||||
|
An optional set of environment strings can be set to define the environment in which the scripts run.
|
||||||
|
```
|
||||||
|
"environment": [
|
||||||
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/go/bin",
|
||||||
|
"GOPATH=@dir"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
Projects will be defined in a project.json file for now. I may add a web interface later.
|
Projects will be defined in a project.json file for now. I may add a web interface later.
|
||||||
|
|
||||||
|
@dir in any of the script strings or environment entries will be replaced with an absolute path to the current working directory of the specific version being worked on.
|
||||||
|
```
|
||||||
|
sh ./build.sh @dir
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Ironsmith will take the information for the defined project above and do the following
|
Ironsmith will take the information for the defined project above and do the following
|
||||||
|
|
||||||
@ -26,7 +39,8 @@ Ironsmith will take the information for the defined project above and do the fol
|
|||||||
2. Change to that directory
|
2. Change to that directory
|
||||||
2. Create a bolt DB file for the project to keep a log of all the builds
|
2. Create a bolt DB file for the project to keep a log of all the builds
|
||||||
3. Run an initial pull of the repository using the pull script
|
3. Run an initial pull of the repository using the pull script
|
||||||
4. If pull succeeds, Run the Build Scripts
|
4. Run version script
|
||||||
|
4. If pull is a new version, then Run the Build Scripts
|
||||||
5. If build succeeds, run the test scripts
|
5. If build succeeds, run the test scripts
|
||||||
6. If test succeeds, run the release scripts
|
6. If test succeeds, run the release scripts
|
||||||
7. Load the release file into project release folder with the release name
|
7. Load the release file into project release folder with the release name
|
||||||
|
147
bindata.go
147
bindata.go
File diff suppressed because one or more lines are too long
3
build.sh
Normal file
3
build.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
go-bindata web/... && go build -a -v -o ironsmith
|
12
cycle.go
12
cycle.go
@ -15,7 +15,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.townsourced.com/ironsmith/datastore"
|
"git.townsourced.com/townsourced/ironsmith/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -94,13 +94,13 @@ func (p *Project) fetch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//fetch project
|
//fetch project
|
||||||
fetchResult, err := runCmd(p.Fetch, tempDir)
|
fetchResult, err := runCmd(p.Fetch, tempDir, p.Environment)
|
||||||
if p.errHandled(err) {
|
if p.errHandled(err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetched succesfully, determine version
|
// fetched succesfully, determine version
|
||||||
version, err := runCmd(p.Version, tempDir)
|
version, err := runCmd(p.Version, tempDir, p.Environment)
|
||||||
|
|
||||||
if p.errHandled(err) {
|
if p.errHandled(err) {
|
||||||
return
|
return
|
||||||
@ -152,7 +152,7 @@ func (p *Project) build() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := runCmd(p.Build, p.workingDir())
|
output, err := runCmd(p.Build, p.workingDir(), p.Environment)
|
||||||
|
|
||||||
if p.errHandled(err) {
|
if p.errHandled(err) {
|
||||||
return
|
return
|
||||||
@ -173,7 +173,7 @@ func (p *Project) test() {
|
|||||||
if p.Test == "" {
|
if p.Test == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
output, err := runCmd(p.Test, p.workingDir())
|
output, err := runCmd(p.Test, p.workingDir(), p.Environment)
|
||||||
|
|
||||||
if p.errHandled(err) {
|
if p.errHandled(err) {
|
||||||
return
|
return
|
||||||
@ -195,7 +195,7 @@ func (p *Project) release() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := runCmd(p.Release, p.workingDir())
|
output, err := runCmd(p.Release, p.workingDir(), p.Environment)
|
||||||
|
|
||||||
if p.errHandled(err) {
|
if p.errHandled(err) {
|
||||||
return
|
return
|
||||||
|
2
error.go
2
error.go
@ -11,7 +11,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.townsourced.com/ironsmith/datastore"
|
"git.townsourced.com/townsourced/ironsmith/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
75
exec.go
75
exec.go
@ -6,12 +6,18 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runCmd(cmd, dir string) ([]byte, error) {
|
func runCmd(cmd, dir string, env []string) ([]byte, error) {
|
||||||
s := strings.Fields(cmd)
|
s := strings.Fields(strings.Replace(cmd, "@dir", dir, -1))
|
||||||
|
|
||||||
|
for i := range env {
|
||||||
|
env[i] = strings.Replace(env[i], "@dir", dir, -1)
|
||||||
|
}
|
||||||
|
|
||||||
var args []string
|
var args []string
|
||||||
|
|
||||||
@ -19,15 +25,74 @@ func runCmd(cmd, dir string) ([]byte, error) {
|
|||||||
args = s[1:]
|
args = s[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
ec := exec.Command(s[0], args...)
|
name := s[0]
|
||||||
|
ec := &exec.Cmd{
|
||||||
|
Path: name,
|
||||||
|
Args: append([]string{name}, args...),
|
||||||
|
Dir: dir,
|
||||||
|
Env: env,
|
||||||
|
}
|
||||||
|
|
||||||
ec.Dir = dir
|
if filepath.Base(name) == name {
|
||||||
|
lp, err := lookPath(name, env)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ec.Path = lp
|
||||||
|
}
|
||||||
|
|
||||||
vlog("Executing command: %s in dir %s\n", cmd, dir)
|
vlog("Executing command: %s in dir %s\n", cmd, dir)
|
||||||
|
|
||||||
result, err := ec.CombinedOutput()
|
result, err := ec.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s", result)
|
return nil, fmt.Errorf("%s\n%s", err, result)
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// similar to os/exec.LookPath, except it checks if the passed in
|
||||||
|
// custom environment includes a path definitions and uses that path instead
|
||||||
|
// note this probably only works on unix, that's all I care about for now
|
||||||
|
func lookPath(file string, env []string) (string, error) {
|
||||||
|
if strings.Contains(file, "/") {
|
||||||
|
err := findExecutable(file)
|
||||||
|
if err == nil {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
return "", &exec.Error{file, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range env {
|
||||||
|
if strings.HasPrefix(env[i], "PATH=") {
|
||||||
|
pathenv := env[i][5:]
|
||||||
|
if pathenv == "" {
|
||||||
|
return "", &exec.Error{file, exec.ErrNotFound}
|
||||||
|
}
|
||||||
|
for _, dir := range strings.Split(pathenv, ":") {
|
||||||
|
if dir == "" {
|
||||||
|
// Unix shell semantics: path element "" means "."
|
||||||
|
dir = "."
|
||||||
|
}
|
||||||
|
path := dir + "/" + file
|
||||||
|
if err := findExecutable(path); err == nil {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", &exec.Error{file, exec.ErrNotFound}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return exec.LookPath(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findExecutable(file string) error {
|
||||||
|
d, err := os.Stat(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.ErrPermission
|
||||||
|
}
|
||||||
|
2
main.go
2
main.go
@ -11,7 +11,7 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"git.townsourced.com/config"
|
"git.townsourced.com/townsourced/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
//settings
|
//settings
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.townsourced.com/ironsmith/datastore"
|
"git.townsourced.com/townsourced/ironsmith/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -48,6 +48,8 @@ The project lifecycle goes like this, each step calling the next if successful
|
|||||||
type Project struct {
|
type Project struct {
|
||||||
Name string `json:"name"` // name of the project
|
Name string `json:"name"` // name of the project
|
||||||
|
|
||||||
|
Environment []string `json:"environment"` // Environment for each of the scripts below, if empty will use the current processes environment
|
||||||
|
|
||||||
Fetch string `json:"fetch"` //Script to fetch the latest project code into the current directory
|
Fetch string `json:"fetch"` //Script to fetch the latest project code into the current directory
|
||||||
Build string `json:"build"` //Script to build the latest project code
|
Build string `json:"build"` //Script to build the latest project code
|
||||||
Test string `json:"test"` //Script to test the latest project code
|
Test string `json:"test"` //Script to test the latest project code
|
||||||
@ -273,6 +275,7 @@ func (p *Project) setData(new *Project) {
|
|||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
|
|
||||||
p.Name = new.Name
|
p.Name = new.Name
|
||||||
|
p.Environment = new.Environment
|
||||||
|
|
||||||
p.Fetch = new.Fetch
|
p.Fetch = new.Fetch
|
||||||
p.Build = new.Build
|
p.Build = new.Build
|
||||||
|
@ -163,10 +163,10 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Project</th>
|
<th>Project</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Last Release</th>
|
|
||||||
<th>Last Release File</th>
|
|
||||||
<th>Last Version</th>
|
<th>Last Version</th>
|
||||||
<th>Last Log</th>
|
<th>Last Log</th>
|
||||||
|
<th>Last Release</th>
|
||||||
|
<th>Last Release File</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -174,6 +174,10 @@
|
|||||||
<tr title="{{formatDate(.lastLog.when)}}">
|
<tr title="{{formatDate(.lastLog.when)}}">
|
||||||
<td><a href="/project/{{.id}}/">{{.name}}</a></td>
|
<td><a href="/project/{{.id}}/">{{.name}}</a></td>
|
||||||
<td>{{.status}}</td>
|
<td>{{.status}}</td>
|
||||||
|
<td>
|
||||||
|
<a href="/project/{{.id}}/{{.lastLog.version}}">{{.lastLog.version}}</a>
|
||||||
|
</td>
|
||||||
|
<td title="{{.lastLog.log}}">{{#if .lastLog.log}}{{.lastLog.log.substring(0,100)}}{{/if}}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/project/{{.id}}/{{.releaseVersion}}">{{.releaseVersion}}</a>
|
<a href="/project/{{.id}}/{{.releaseVersion}}">{{.releaseVersion}}</a>
|
||||||
</td>
|
</td>
|
||||||
@ -184,10 +188,6 @@
|
|||||||
No release file available
|
No release file available
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
<a href="/project/{{.id}}/{{.lastLog.version}}">{{.lastLog.version}}</a>
|
|
||||||
</td>
|
|
||||||
<td title="{{.lastLog.log}}">{{#if .lastLog.log}}{{.lastLog.log.substring(0,100)}}{{/if}}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{{/projects}}
|
{{/projects}}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.townsourced.com/ironsmith/datastore"
|
"git.townsourced.com/townsourced/ironsmith/datastore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// /path/<project-id>/<version>/<stage>
|
// /path/<project-id>/<version>/<stage>
|
||||||
|
Reference in New Issue
Block a user