Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# build gate: every push/PR must format-check, vet, and compile on a current Go.
# replaces the defunct wercker pipeline (wercker.yml).
name: ci

on:
push:
branches: [ master ]
pull_request:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
# 'stable' always pulls the latest patched Go release, which is the
# whole point of this modernization: the binary embeds a current,
# non-vulnerable Go toolchain.
go-version: 'stable'
- name: gofmt, vet, build
run: make test
34 changes: 34 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# release: on a version tag (e.g. v1.0.1), build the linux/amd64 tarball that
# cloud66/central installs from S3 and attach it to a GitHub Release.
name: release

on:
push:
tags: [ 'v*' ]

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write # required to create the GitHub Release
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 'stable'
# produces builds/gotty_linux_amd64_<version>.tar.gz (executable only)
- name: build linux/amd64 tarball
run: make dist
- name: attach tarball to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: builds/gotty_linux_amd64_*.tar.gz
# optional: publish to the bucket central's installer downloads from.
# only runs when AWS credentials are configured as repo secrets.
- name: upload to downloads.cloud66.com (optional)
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-1
if: env.AWS_ACCESS_KEY_ID != ''
run: aws s3 cp builds/gotty_linux_amd64_*.tar.gz s3://downloads.cloud66.com/gotty/
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

57 changes: 0 additions & 57 deletions Godeps/Godeps.json

This file was deleted.

5 changes: 0 additions & 5 deletions Godeps/Readme

This file was deleted.

86 changes: 37 additions & 49 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,54 +1,42 @@
OUTPUT_DIR = ./builds
# gotty build — modern Go modules flow (replaces the old godep / go-bindata setup).
# web assets are pre-generated and committed in app/resource.go (Code generated, DO NOT EDIT),
# so they are not rebuilt here: the libapps submodule and go-bindata are no longer required.

gotty: app/resource.go main.go app/*.go
godep go build
# single source of truth for the version is the Version var in app/app.go
VERSION := $(shell grep -oE 'Version = "[0-9][^"]*"' app/app.go | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
# cloud66/central's installer fetches the tarball with dots replaced by underscores
VERSION_US := $(subst .,_,$(VERSION))

resource: app/resource.go
OUTPUT_DIR := ./builds
# hand-written Go files only — exclude vendored deps and the generated bindata file
GOFILES := $(shell find . -name '*.go' -not -path './vendor/*' -not -name 'resource.go')

app/resource.go: bindata/static/js/hterm.js bindata/static/js/gotty.js bindata/static/index.html bindata/static/favicon.png
go-bindata -prefix bindata -pkg app -ignore=\\.gitkeep -o app/resource.go bindata/...
gofmt -w app/resource.go

bindata:
mkdir bindata

bindata/static: bindata
mkdir bindata/static

bindata/static/index.html: bindata/static resources/index.html
cp resources/index.html bindata/static/index.html

bindata/static/favicon.png: bindata/static resources/favicon.png
cp resources/favicon.png bindata/static/favicon.png

bindata/static/js: bindata/static
mkdir -p bindata/static/js

bindata/static/js/hterm.js: bindata/static/js libapps/hterm/js/*.js
cd libapps && \
LIBDOT_SEARCH_PATH=`pwd` ./libdot/bin/concat.sh -i ./hterm/concat/hterm_all.concat -o ../bindata/static/js/hterm.js

bindata/static/js/gotty.js: bindata/static/js resources/gotty.js
cp resources/gotty.js bindata/static/js/gotty.js

tools:
go get github.com/tools/godep
go get github.com/mitchellh/gox
go get github.com/tcnksm/ghr
go get github.com/jteeuwen/go-bindata/...
# default target: build a local binary for the host platform
gotty: $(GOFILES) go.mod
go build -o gotty .

# CI gate: formatting, vet, and a full build must all pass
test:
if [ `go fmt $(go list ./... | grep -v /vendor/) | wc -l` -gt 0 ]; then echo "go fmt error"; exit 1; fi

cross_compile:
GOARM=5 gox -os="darwin linux freebsd netbsd openbsd" -arch="386 amd64 arm" -osarch="!darwin/arm" -output "${OUTPUT_DIR}/pkg/{{.OS}}_{{.Arch}}/{{.Dir}}"

targz:
mkdir -p ${OUTPUT_DIR}/dist
cd ${OUTPUT_DIR}/pkg/; for osarch in *; do (cd $$osarch; tar zcvf ../../dist/gotty_$$osarch.tar.gz ./*); done;

shasums:
cd ${OUTPUT_DIR}/dist; sha256sum * > ./SHA256SUMS

release:
ghr --delete --prerelease -u yudai -r gotty pre-release ${OUTPUT_DIR}/dist
@unformatted=$$(gofmt -l $(GOFILES)); \
if [ -n "$$unformatted" ]; then echo "gofmt needed on:"; echo "$$unformatted"; exit 1; fi
go vet ./...
go build ./...

# rewrite any unformatted hand-written files in place
fmt:
gofmt -w $(GOFILES)

# build the linux/amd64 release artifact that central installs from S3.
# CGO is disabled so the binary is static and runs across all supported Ubuntu releases.
# output: builds/gotty_linux_amd64_<version>.tar.gz containing only the gotty executable.
dist:
mkdir -p $(OUTPUT_DIR)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $(OUTPUT_DIR)/gotty .
tar -czf $(OUTPUT_DIR)/gotty_linux_amd64_$(VERSION_US).tar.gz -C $(OUTPUT_DIR) gotty
@echo "built $(OUTPUT_DIR)/gotty_linux_amd64_$(VERSION_US).tar.gz (version $(VERSION))"

clean:
rm -f gotty
rm -rf $(OUTPUT_DIR)

.PHONY: test fmt dist clean
25 changes: 11 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ $ brew install yudai/gotty/gotty

## `go get` Installation (Development)

If you have a Go language environment, you can install GoTTY with the `go get` command. However, this command builds a binary file from the latest master branch, which can include unstable or breaking changes.
If you have a Go language environment (Go 1.23+), you can install GoTTY with the `go install` command. However, this builds a binary from the latest master branch, which can include unstable or breaking changes.

```sh
$ go get github.com/yudai/gotty
$ go install github.com/cloud66/gotty@latest
```

# Usage
Expand Down Expand Up @@ -148,23 +148,20 @@ $ gotty -w docker run -it --rm busybox

## Development

You can build a binary using the following commands. Windows is not supported now.
You can build a binary using the following commands. Requires Go 1.23 or newer. Windows is not supported now.

```sh
# Install tools
go get github.com/jteeuwen/go-bindata/...
go get github.com/tools/godep

# Checkout hterm
git submodule sync && git submodule update --init --recursive

# Restore libraries in Godeps
godep restore

# Build
# Build (uses the committed go.mod and vendor/ — no extra tools needed)
make

# ...or build directly
go build .
```

The web assets (hterm.js, gotty.js, index.html, favicon) are pre-generated and
committed in `app/resource.go`. They are no longer rebuilt at build time, so
`go-bindata` and the hterm (`libapps`) submodule are not required to build.

## Architecture

GoTTY uses [hterm](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-hterm) to run a JavaScript based terminal on web browsers. GoTTY itself provides a websocket server that simply relays output from the TTY to clients and receives input from clients and forwards it to the TTY. This hterm + websocket idea is inspired by [Wetty](https://github.com/krishnasrinivas/wetty).
Expand Down
31 changes: 15 additions & 16 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"io/ioutil"
"log"
"math/big"
"net"
Expand All @@ -23,9 +22,9 @@ import (
"time"

"github.com/braintree/manners"
"github.com/creack/pty"
"github.com/elazarl/go-bindata-assetfs"
"github.com/gorilla/websocket"
"github.com/kr/pty"
"github.com/yudai/hcl"
"github.com/yudai/umutex"
)
Expand Down Expand Up @@ -80,7 +79,7 @@ type Options struct {
Height int `hcl:"height"`
}

var Version = "1.0.0"
var Version = "1.0.1"

var DefaultOptions = Options{
Address: "",
Expand Down Expand Up @@ -140,7 +139,7 @@ func ApplyConfigFile(options *Options, filePath string) error {

fileString := []byte{}
log.Printf("Loading config file at: %s", filePath)
fileString, err := ioutil.ReadFile(filePath)
fileString, err := os.ReadFile(filePath)
if err != nil {
return err
}
Expand All @@ -161,11 +160,11 @@ func CheckConfig(options *Options) error {

func (app *App) Run() error {
if app.options.PermitWrite {
log.Printf("Permitting clients to write input to the PTY.")
log.Print("Permitting clients to write input to the PTY.")
}

if app.options.Once {
log.Printf("Once option is provided, accepting only one client")
log.Print("Once option is provided, accepting only one client")
}

path := ""
Expand All @@ -185,7 +184,7 @@ func (app *App) Run() error {
var siteMux = http.NewServeMux()

if app.options.IndexFile != "" {
log.Printf("Using index file at " + app.options.IndexFile)
log.Print("Using index file at " + app.options.IndexFile)
siteMux.Handle(path+"/", customIndexHandler)
} else {
siteMux.Handle(path+"/", http.StripPrefix(path+"/", staticHandler))
Expand All @@ -197,7 +196,7 @@ func (app *App) Run() error {
siteHandler := http.Handler(siteMux)

if app.options.EnableBasicAuth {
log.Printf("Using Basic Authentication")
log.Print("Using Basic Authentication")
siteHandler = wrapBasicAuth(siteHandler, app.options.Credential)
}

Expand Down Expand Up @@ -255,8 +254,8 @@ func (app *App) Run() error {
if app.options.EnableTLS {
crtFile := ExpandHomeDir(app.options.TLSCrtFile)
keyFile := ExpandHomeDir(app.options.TLSKeyFile)
log.Printf("TLS crt file: " + crtFile)
log.Printf("TLS key file: " + keyFile)
log.Print("TLS crt file: " + crtFile)
log.Print("TLS key file: " + keyFile)

err = app.server.ListenAndServeTLS(crtFile, keyFile)
} else {
Expand All @@ -266,7 +265,7 @@ func (app *App) Run() error {
return err
}

log.Printf("Exiting...")
log.Print("Exiting...")

return nil
}
Expand All @@ -279,8 +278,8 @@ func (app *App) makeServer(addr string, handler *http.Handler) (*http.Server, er

if app.options.EnableTLSClientAuth {
caFile := ExpandHomeDir(app.options.TLSCACrtFile)
log.Printf("CA file: " + caFile)
caCert, err := ioutil.ReadFile(caFile)
log.Print("CA file: " + caFile)
caCert, err := os.ReadFile(caFile)
if err != nil {
return nil, errors.New("Could not open CA crt file " + caFile)
}
Expand Down Expand Up @@ -373,10 +372,10 @@ func (app *App) handleWS(w http.ResponseWriter, r *http.Request) {

if app.options.Once {
if app.onceMutex.TryLock() { // no unlock required, it will die soon
log.Printf("Last client accepted, closing the listener.")
log.Print("Last client accepted, closing the listener.")
app.server.Close()
} else {
log.Printf("Server is already closing.")
log.Print("Server is already closing.")
conn.Close()
return
}
Expand Down Expand Up @@ -422,7 +421,7 @@ func (app *App) Exit() (firstCall bool) {
if app.server != nil {
firstCall = app.server.Close()
if firstCall {
log.Printf("Received Exit command, waiting for all clients to close sessions...")
log.Print("Received Exit command, waiting for all clients to close sessions...")
}
return firstCall
}
Expand Down
Loading
Loading