New deployment / build infrastructure
Esse commit está contido em:
+1
-1
@@ -1,2 +1,2 @@
|
||||
/bin
|
||||
/http/fs/fs.go
|
||||
/dist
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
version = $(shell git describe --tags --dirty)
|
||||
|
||||
dev:
|
||||
./scripts/build.bash -version $(version) dist/dev
|
||||
./dist/dev/deploy
|
||||
|
||||
dist:
|
||||
./scripts/dist.bash $(version)
|
||||
|
||||
.PHONY: dev release
|
||||
@@ -0,0 +1,146 @@
|
||||
// Command install allows people to install and run GoDrone binaries on their
|
||||
// ardrone.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/kardianos/osext"
|
||||
"fmt"
|
||||
ftp "github.com/jlaffaye/goftp"
|
||||
ptelnet "github.com/ziutek/telnet"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const cmdName = "install"
|
||||
|
||||
func Printf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stdout, format+"\n", args...)
|
||||
}
|
||||
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
dir, err := osext.ExecutableFolder()
|
||||
if err != nil {
|
||||
Fatalf("Could not determine ExecutableFolder: %s", err)
|
||||
}
|
||||
|
||||
task := DeployTask{
|
||||
Host: "192.168.1.1",
|
||||
FtpPort: "21",
|
||||
FtpDir: "/data/video",
|
||||
TelnetPort: "23",
|
||||
TelnetPrompt: "# ",
|
||||
Src: dir,
|
||||
Dst: "godrone",
|
||||
NetTimeout: 5 * time.Second,
|
||||
}
|
||||
if err := task.Run(); err != nil {
|
||||
Fatalf("Failed to install godrone: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
type DeployTask struct {
|
||||
// Host is the IP address of the drone.
|
||||
Host string
|
||||
// FtpPort is the ftp port.
|
||||
FtpPort string
|
||||
// FtpDir is the absolute path of the FTP directory on the drone file system.
|
||||
FtpDir string
|
||||
// TelnetPort is the telnet port.
|
||||
TelnetPort string
|
||||
// TelnetPrompt is the prompt string used by the drone's shell.
|
||||
TelnetPrompt string
|
||||
// Src is the absolute path to the godrone folder on the host.
|
||||
Src string
|
||||
// Dst is path relative to the FtpDir to deploy godrone on the drone.
|
||||
Dst string
|
||||
// NetTimeout is the timeout for all networking operations.
|
||||
NetTimeout time.Duration
|
||||
}
|
||||
|
||||
func (t DeployTask) Run() error {
|
||||
dir, err := os.Open(t.Src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
ftpAddr := net.JoinHostPort(t.Host, t.FtpPort)
|
||||
Printf("Connecting to %s", ftpAddr)
|
||||
// @TODO figure out how to set a connection timeout for ftp, might require
|
||||
// sending a patch to goftp.
|
||||
ftpConn, err := ftp.Connect(ftpAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ftpConn.Quit()
|
||||
|
||||
Printf("Connected")
|
||||
|
||||
dstDir := t.Dst + ".next"
|
||||
ftpConn.MakeDir(dstDir)
|
||||
|
||||
entries, err := dir.Readdir(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
baseName := filepath.Base(name)
|
||||
if strings.HasPrefix(baseName, cmdName) {
|
||||
// don't upload the installer itself
|
||||
continue
|
||||
}
|
||||
|
||||
srcName := filepath.Join(t.Src, name)
|
||||
dstName := path.Join(dstDir, name)
|
||||
|
||||
file, err := os.Open(srcName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Printf("Uploading %s", name)
|
||||
if err := ftpConn.Stor(dstName, file); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
telnetAddr := net.JoinHostPort(t.Host, t.TelnetPort)
|
||||
Printf("Connecting to %s", telnetAddr)
|
||||
telnetConn, err := net.DialTimeout("tcp", telnetAddr, t.NetTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer telnetConn.Close()
|
||||
Printf("Connected")
|
||||
|
||||
telnet, err := ptelnet.NewConn(telnetConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Printf("Running start.sh on drone")
|
||||
if _, err := telnet.ReadUntil(t.TelnetPrompt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprintf(telnet, "cd %s/%s && sh start.sh\n", t.FtpDir, dstDir); err != nil {
|
||||
return err
|
||||
}
|
||||
go io.Copy(telnet, os.Stdin)
|
||||
if _, err := io.Copy(os.Stdout, telnet); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -16,20 +16,6 @@ type Config struct {
|
||||
HttpAddr string
|
||||
}
|
||||
|
||||
var (
|
||||
defaultRollPitchPID = []float64{0.04, 0, 0.002}
|
||||
)
|
||||
|
||||
// DefaultConfig provides sensible defaults in absence of a config file.
|
||||
var DefaultConfig = Config{
|
||||
NavboardTTY: "/dev/ttyO1",
|
||||
MotorboardTTY: "/dev/ttyO0",
|
||||
RollPID: defaultRollPitchPID,
|
||||
PitchPID: defaultRollPitchPID,
|
||||
YawPID: []float64{0.04, 0, 0}, // disabled, needs magnotometer to work well
|
||||
HttpAddr: ":80",
|
||||
}
|
||||
|
||||
// LoadConfig loads the configuration from a toml file. Other file formats may
|
||||
// be supported in the future as well.
|
||||
func LoadConfig(file string, config *Config) error {
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
NavboardTTY = "/dev/ttyO1"
|
||||
MotorboardTTY = "/dev/ttyO0"
|
||||
# PID controller settings
|
||||
# feel free to play with these to adjust control behavior
|
||||
RollPID = [ 0.04, 0.0, 0.002 ]
|
||||
PitchPID = [ 0.04, 0.0, 0.002 ]
|
||||
#disabled, needs magnotometer to work well
|
||||
YawPID = [ 0.04, 0.0, 0.0 ]
|
||||
AltitudePID = [ 0.3, 0.03, 0.03 ]
|
||||
HttpAddr = ":80"
|
||||
|
||||
# http server settings
|
||||
HttpAddr = ":80"
|
||||
|
||||
# do not change, otherwise nothing will work
|
||||
NavboardTTY = "/dev/ttyO1"
|
||||
MotorboardTTY = "/dev/ttyO0"
|
||||
|
||||
+14
-7
@@ -17,23 +17,29 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
c = flag.String("c", "", "Absolute or relative path to config file.")
|
||||
|
||||
// Convenience values to set the colors of all leds
|
||||
green = motorboard.Leds(motorboard.LedGreen)
|
||||
orange = motorboard.Leds(motorboard.LedOrange)
|
||||
red = motorboard.Leds(motorboard.LedRed)
|
||||
)
|
||||
|
||||
// Version is inserted during the build process.
|
||||
// see http://stackoverflow.com/questions/11354518/golang-application-auto-build-versioning/11355611#11355611
|
||||
var Version = "N/A"
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
config := DefaultConfig
|
||||
if *c != "" {
|
||||
if err := LoadConfig(*c, &config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var config Config
|
||||
configPath := flag.Arg(0)
|
||||
if configPath == "" {
|
||||
configPath = "config.toml"
|
||||
}
|
||||
|
||||
if err := LoadConfig(configPath, &config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
g, err := NewGoDrone(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -58,6 +64,7 @@ func NewGoDrone(c Config) (g GoDrone, err error) {
|
||||
g.http = http.NewHandler(http.Config{
|
||||
Control: g.control,
|
||||
Log: g.log,
|
||||
Version: Version,
|
||||
})
|
||||
g.httpAddr = c.HttpAddr
|
||||
g.navCh = make(chan navboard.Data)
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// Command version is a utility that prints the current godrone version and
|
||||
// exits.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/felixge/godrone"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(godrone.Version)
|
||||
}
|
||||
@@ -6,12 +6,15 @@
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/css/main.css" />
|
||||
|
||||
<!-- 3rd party -->
|
||||
<script src="/js/vendor/jquery.js"></script>
|
||||
<script src="/js/vendor/react-with-addons.js"></script>
|
||||
<script src="/js/vendor/JSXTransformer.js"></script>
|
||||
<!-- GoDrone -->
|
||||
<script src="/js/client.js"></script>
|
||||
<script src="/js/gamepad.js"></script>
|
||||
<script src="/js/table.js" type="text/jsx"></script>
|
||||
<script src="/js/config.js"></script><!-- dynamically generated -->
|
||||
<script src="/js/main.js" type="text/jsx"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
|
||||
React.renderComponent(
|
||||
<GoDrone
|
||||
version="0.1"
|
||||
version={Config.version}
|
||||
wsUrl="ws://192.168.1.1/ws"
|
||||
gamepadAxisMin={0.01}
|
||||
maxAngle={6}
|
||||
|
||||
@@ -6,6 +6,8 @@ package http
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/felixge/godrone/attitude"
|
||||
"github.com/felixge/godrone/control"
|
||||
"github.com/felixge/godrone/drivers/navboard"
|
||||
@@ -19,6 +21,7 @@ import (
|
||||
type Config struct {
|
||||
Control *control.Control
|
||||
Log log.Interface
|
||||
Version string
|
||||
}
|
||||
|
||||
// Handler provides a http.Handler.
|
||||
@@ -93,6 +96,21 @@ func (h *Handler) handleWebsocket(conn *websocket.Conn) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config := map[string]interface{}{
|
||||
"version": h.config.Version,
|
||||
}
|
||||
data, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
err = h.config.Log.Error("Could marshal JSON config. err=%s", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/javascript; charset=UTF-8")
|
||||
fmt.Fprintf(w, "window.Config = %s;", data)
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler interface. It acts as a router
|
||||
// dispatching websocket / asset requests.
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -100,6 +118,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.websocketHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
if r.Method == "GET" && r.URL.Path == "/js/config.js" {
|
||||
h.handleConfig(w, r)
|
||||
return
|
||||
}
|
||||
h.fileHandler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
|
||||
Arquivo executável
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
main() {
|
||||
local scripts_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
local root_dir="$( cd "${scripts_dir}"/.. && pwd )"
|
||||
local base_pkg='github.com/felixge/godrone'
|
||||
local arch="$(go env GOARCH)"
|
||||
local os="$(go env GOOS)"
|
||||
local ldflags=''
|
||||
local deploy_name='deploy'
|
||||
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
'-arch')
|
||||
arch="$2"
|
||||
shift
|
||||
;;
|
||||
'-os')
|
||||
os="$2"
|
||||
shift
|
||||
;;
|
||||
'-version')
|
||||
ldflags="-X main.Version $2"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
local out_dir="$1"
|
||||
|
||||
if [[ "${os}" = "windows" ]]; then
|
||||
deploy_name="${deploy_name}.exe"
|
||||
fi
|
||||
|
||||
rm -rf "${out_dir}"
|
||||
mkdir -p "${out_dir}"
|
||||
|
||||
makefs "${root_dir}/http/fs"
|
||||
env \
|
||||
GOOS="${os}" \
|
||||
GOARCH="${arch}" \
|
||||
go build -o "${out_dir}/${deploy_name}" "${base_pkg}/cmd/deploy"
|
||||
env \
|
||||
GOOS=linux \
|
||||
GOARCH=arm \
|
||||
GOARM=7 \
|
||||
CGO_ENABLED=0 \
|
||||
go build \
|
||||
-o "${out_dir}/godrone" \
|
||||
-ldflags "${ldflags}" \
|
||||
"${base_pkg}/cmd/godrone"
|
||||
cp "${scripts_dir}/start.sh" "${out_dir}"
|
||||
cp "${root_dir}/LICENSE.txt" "${out_dir}"
|
||||
cp "${root_dir}/cmd/godrone/config.toml" "${out_dir}"
|
||||
}
|
||||
|
||||
main $@
|
||||
@@ -1,110 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
usage() {
|
||||
local exitcode="$1"
|
||||
if [[ "$exitcode" = 1 ]]; then
|
||||
exec 1>&2
|
||||
fi
|
||||
echo "Usage: $0 [-h] [--tracegc] [-i <ip>] [-e <envargs>] [<cmd>]"
|
||||
exit "$exitcode"
|
||||
}
|
||||
|
||||
log() {
|
||||
echo "--> $@"
|
||||
}
|
||||
|
||||
build_http_fs() {
|
||||
log "Building http fs ..."
|
||||
makefs "$1"
|
||||
}
|
||||
|
||||
fetch_deps() {
|
||||
log 'Fetching dependencies ...'
|
||||
go get "$1"
|
||||
}
|
||||
|
||||
build() {
|
||||
local pkg="$1"
|
||||
local path="$2"
|
||||
log "Creating ${pkg} arm binary in ${path} ..."
|
||||
mkdir -p "$(dirname "${path}")"
|
||||
env \
|
||||
GOOS=linux \
|
||||
GOARCH=arm \
|
||||
CGO_ENABLED=0 \
|
||||
go build -o "${path}" "${pkg}"
|
||||
}
|
||||
|
||||
upload() {
|
||||
local ip="$1"
|
||||
shift
|
||||
|
||||
local curlcmd="curl"
|
||||
for file in $@; do
|
||||
curlcmd=" ${curlcmd} -T '${file}' 'ftp://@${ip}/$(basename "${file}")'"
|
||||
done
|
||||
|
||||
log "Uploading to ${ip}..."
|
||||
bash -c "${curlcmd}"
|
||||
}
|
||||
|
||||
main() {
|
||||
local ip="192.168.1.1"
|
||||
local envargs=''
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
'-h')
|
||||
usage 0
|
||||
shift
|
||||
;;
|
||||
'-i')
|
||||
ip="$2"
|
||||
shift
|
||||
;;
|
||||
'--tracegc')
|
||||
envargs="${envargs} GOGCTRACE=1"
|
||||
;;
|
||||
'-e')
|
||||
envargs="${envargs} $2"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo -e "unknown option: $1\n" 1>&2
|
||||
usage 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
readonly ip
|
||||
readonly envargs
|
||||
|
||||
if [[ "$#" -gt 1 ]]; then
|
||||
echo -e "unexpected argument: $2\n" 1>&2
|
||||
usage 1
|
||||
fi
|
||||
|
||||
local readonly cmd="${1:-godrone}"
|
||||
local readonly scripts_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
local readonly dir="$( cd "${scripts_dir}"/.. && pwd )"
|
||||
local readonly bin_path="${dir}/bin/${cmd}"
|
||||
local readonly pkg_path="github.com/felixge/godrone/cmd/${cmd}"
|
||||
local readonly startup_script='start.sh'
|
||||
|
||||
build_http_fs "${dir}/http/fs"
|
||||
fetch_deps "${pkg_path}"
|
||||
build "${pkg_path}" "${bin_path}"
|
||||
upload "${ip}" "${scripts_dir}/${startup_script}" "${bin_path}"
|
||||
|
||||
log "Starting ${cmd} ..."
|
||||
"${scripts_dir}/start.expect" \
|
||||
"${ip}" \
|
||||
"${startup_script}" \
|
||||
"${cmd}" \
|
||||
"${envargs}"
|
||||
}
|
||||
|
||||
main $@
|
||||
Arquivo executável
+55
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
main() {
|
||||
local version="$1"
|
||||
local scripts_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
local root_dir="$( cd "${scripts_dir}"/.. && pwd )"
|
||||
local targets=(
|
||||
'darwin 386 zip'
|
||||
'darwin amd64 zip'
|
||||
'windows 386 zip'
|
||||
'windows amd64 zip'
|
||||
'linux 386 tar'
|
||||
'linux amd64 tar'
|
||||
)
|
||||
|
||||
for target in "${targets[@]}"; do
|
||||
build $target
|
||||
done
|
||||
}
|
||||
|
||||
build() {
|
||||
local os="$1"
|
||||
local arch="$2"
|
||||
local archive="$3"
|
||||
local name="godrone-${version}-${os}-${arch}"
|
||||
local out_dir="${root_dir}/dist/${name}"
|
||||
|
||||
echo "Building ${name}"
|
||||
"${scripts_dir}/build.bash" \
|
||||
-os "${os}" \
|
||||
-arch "${arch}" \
|
||||
-version ${version} \
|
||||
"${out_dir}"
|
||||
cd "${out_dir}/.."
|
||||
case "${archive}" in
|
||||
'zip')
|
||||
local archive_name="${out_dir}.zip"
|
||||
rm -f "${archive_name}"
|
||||
zip -q -r "${archive_name}" "${name}"
|
||||
;;
|
||||
'tar')
|
||||
local archive_name="${out_dir}.tar.gz"
|
||||
rm -f "${archive_name}"
|
||||
tar -czf "${archive_name}" "${name}"
|
||||
;;
|
||||
*)
|
||||
echo "unknown archive format: ${archive}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
rm -rf "${out_dir}"
|
||||
}
|
||||
|
||||
main $@
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env expect -f
|
||||
set ip [lindex $argv 0]
|
||||
set script [lindex $argv 1]
|
||||
set cmd [lindex $argv 2]
|
||||
set envargs [lindex $argv 3]
|
||||
set prompt "# *$"
|
||||
|
||||
spawn telnet $ip
|
||||
expect -re $prompt
|
||||
send "sh /data/video/$script \"$cmd\" \"$envargs\"; exit\n"
|
||||
interact
|
||||
+11
-13
@@ -1,23 +1,21 @@
|
||||
#!/bin/sh
|
||||
# This script kills any previously running control program (e.g. the original
|
||||
# parrot firmware, godrone, or godrone diagnostic program) and then starts
|
||||
# up the desired program.
|
||||
set -eu
|
||||
|
||||
readonly cmd="$1"
|
||||
readonly envargs="$2"
|
||||
src_dir="$(pwd)"
|
||||
target_dir="${src_dir/.next/}"
|
||||
|
||||
# avoid parrot firmware from restarting restarts
|
||||
rm -rf "${target_dir}"
|
||||
mv "${src_dir}" "${target_dir}"
|
||||
cd "${target_dir}"
|
||||
|
||||
# avoid parrot firmware getting restarted after killing it
|
||||
touch /tmp/.norespawn
|
||||
# kill parrot firmware and the cmd we're deploying if it's running
|
||||
killall -9 program.elf "${cmd}" 2> /dev/null || true
|
||||
|
||||
cd /data/video
|
||||
|
||||
chmod +x "${cmd}"
|
||||
# kill parrot firmware and godrone
|
||||
killall -9 program.elf godrone 2> /dev/null || true
|
||||
|
||||
# Taken from /bin/program.elf.respawner.sh. Not sure why/if it is needed, seems
|
||||
# to turn the bottom LED red.
|
||||
gpio 181 -d ho 1
|
||||
|
||||
exec env $envargs "./${cmd}"
|
||||
chmod +x godrone
|
||||
./godrone
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
package godrone
|
||||
|
||||
const Version = "0.1"
|
||||
Referência em uma Nova Issue
Bloquear um usuário