Compare commits

...

7 Commits

Author SHA1 Message Date
Jesse Duffield
b146c05578 WIP 2023-05-27 13:11:25 +10:00
Jesse Duffield
6c7f893f7a WIP 2023-05-27 12:08:27 +10:00
Jesse Duffield
8b15f22379 WIP 2023-05-26 19:05:22 +10:00
Jesse Duffield
9c7db3a37b Utilise file instead of stderr for integration tests
Windows for some reason is not properly making use of stderr so instead
we're going to write our result to a file explicitly
2023-05-26 19:05:22 +10:00
Jesse Duffield
c961a5833b WIP 2023-05-26 19:05:22 +10:00
Jesse Duffield
6509875009 WIP 2023-05-26 19:05:22 +10:00
Jesse Duffield
3bf1be65b3 Run integration tests on windows 2023-05-26 19:04:29 +10:00
70 changed files with 1700 additions and 419 deletions

View File

@@ -43,12 +43,23 @@ jobs:
restore-keys: |
${{runner.os}}-go-
- name: Test code
# we're passing -short so that we skip the integration tests, which will be run in parallel below
# we're passing -short so that we skip the integration tests, which will be run below
run: |
go test ./... -short
integration-tests:
runs-on: ubuntu-latest
name: "Integration Tests"
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest
include:
- os: ubuntu-latest
cache_path: ~/.cache/go-build
- os: windows-latest
cache_path: ~\AppData\Local\go-build
runs-on: ${{matrix.os}}
name: "Integration Tests - ${{matrix.os}}"
env:
GOFLAGS: -mod=vendor
steps:
@@ -59,18 +70,20 @@ jobs:
with:
go-version: 1.18.x
- name: Cache build
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
${{matrix.cache_path}}
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
restore-keys: |
${{runner.os}}-go-
- name: Test code
# LONG_WAIT_BEFORE_FAIL means that for a given test assertion, we'll wait longer before failing
env:
# LONG_WAIT_BEFORE_FAIL means that for a given test assertion, we'll wait longer before failing
LONG_WAIT_BEFORE_FAIL: true
run: |
LONG_WAIT_BEFORE_FAIL=true go test pkg/integration/clients/*.go
go test ./pkg/integration/clients
build:
runs-on: ubuntu-latest
env:

6
.vscode/tasks.json vendored
View File

@@ -24,7 +24,7 @@
{
"label": "Run current file integration test",
"type": "shell",
"command": "go generate pkg/integration/tests/tests.go && go run cmd/integration_test/main.go cli ${relativeFile}",
"command": "go run cmd/integration_test/main.go cli ${relativeFile}",
"problemMatcher": [],
"group": {
"kind": "test",
@@ -37,7 +37,7 @@
{
"label": "Run current file integration test (slow)",
"type": "shell",
"command": "go generate pkg/integration/tests/tests.go && go run cmd/integration_test/main.go cli --slow ${relativeFile}",
"command": "go run cmd/integration_test/main.go cli --slow ${relativeFile}",
"problemMatcher": [],
"group": {
"kind": "test",
@@ -49,7 +49,7 @@
{
"label": "Run current file integration test (sandbox)",
"type": "shell",
"command": "go generate pkg/integration/tests/tests.go && go run cmd/integration_test/main.go cli --sandbox ${relativeFile}",
"command": "go run cmd/integration_test/main.go cli --sandbox ${relativeFile}",
"problemMatcher": [],
"group": {
"kind": "test",

View File

@@ -6,6 +6,8 @@ import (
"os"
"github.com/jesseduffield/lazygit/pkg/integration/clients"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/samber/lo"
)
var usage = `
@@ -49,6 +51,11 @@ func main() {
}
}
// allowing full test paths to be passed for convenience
testNames = lo.Map(testNames, func(name string, _ int) string {
return components.TestNameFromFilePath(name)
})
clients.RunCLI(testNames, slow, sandbox)
case "tui":
clients.RunTUI()

2
go.mod
View File

@@ -73,3 +73,5 @@ require (
golang.org/x/text v0.8.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
replace github.com/creack/pty => github.com/photostorm/pty v1.1.19-0.20230324012736-6794a5ba0ba0

8
go.sum
View File

@@ -6,8 +6,6 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY=
github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aybabtme/humanlog v0.4.1 h1:D8d9um55rrthJsP8IGSHBcti9lTb/XknmDAX6Zy8tek=
@@ -17,9 +15,6 @@ github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -131,6 +126,8 @@ github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/photostorm/pty v1.1.19-0.20230324012736-6794a5ba0ba0 h1:ahg2J7KgBYMH1KJNvde12jjJZGXwGq0AdRPztBgX2Rc=
github.com/photostorm/pty v1.1.19-0.20230324012736-6794a5ba0ba0/go.mod h1:KO+FcPtyLAiRC0hJwreJVvfwc7vnNz77UxBTIGHdPVk=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -207,6 +204,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220721230656-c6bc011c0c49/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=

View File

@@ -4,48 +4,20 @@
package oscommands
import (
"bytes"
"io"
"os/exec"
"github.com/sasha-s/go-deadlock"
"github.com/creack/pty"
)
type Buffer struct {
b bytes.Buffer
m deadlock.Mutex
}
func (b *Buffer) Read(p []byte) (n int, err error) {
b.m.Lock()
defer b.m.Unlock()
return b.b.Read(p)
}
func (b *Buffer) Write(p []byte) (n int, err error) {
b.m.Lock()
defer b.m.Unlock()
return b.b.Write(p)
}
// TODO: Remove this hack and replace it with a proper way to run commands live on windows. We still have an issue where if a password is requested, the request for a password is written straight to stdout because we can't control the stdout of a subprocess of a subprocess. Keep an eye on https://github.com/creack/pty/pull/109
func (self *cmdObjRunner) getCmdHandler(cmd *exec.Cmd) (*cmdHandler, error) {
stdoutReader, stdoutWriter := io.Pipe()
cmd.Stdout = stdoutWriter
buf := &Buffer{}
cmd.Stdin = buf
if err := cmd.Start(); err != nil {
ptmx, err := pty.Start(cmd)
if err != nil {
return nil, err
}
// because we don't yet have windows support for a pty, we instead just
// pass our standard stream handlers and because there's no pty to close
// we pass a no-op function for that.
return &cmdHandler{
stdoutPipe: stdoutReader,
stdinPipe: buf,
close: func() error { return nil },
stdoutPipe: ptmx,
stdinPipe: ptmx,
close: ptmx.Close,
}, nil
}

View File

@@ -8,6 +8,7 @@ import (
"strings"
"sync"
"github.com/creack/pty"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazycore/pkg/boxlayout"
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
@@ -75,7 +76,7 @@ type Gui struct {
// holds a mapping of view names to ptmx's. This is for rendering command outputs
// from within a pty. The point of keeping track of them is so that if we re-size
// the window, we can tell the pty it needs to resize accordingly.
viewPtmxMap map[string]*os.File
viewPtmxMap map[string]pty.Pty
stopChan chan struct{}
// when lazygit is opened outside a git directory we want to open to the most
@@ -436,7 +437,7 @@ func NewGui(
Updater: updater,
statusManager: status.NewStatusManager(),
viewBufferManagerMap: map[string]*tasks.ViewBufferManager{},
viewPtmxMap: map[string]*os.File{},
viewPtmxMap: map[string]pty.Pty{},
showRecentRepos: showRecentRepos,
RepoPathStack: &utils.StringStack{},
RepoStateMap: map[Repo]*GuiRepoState{},
@@ -616,6 +617,7 @@ func (gui *Gui) Run(startArgs appTypes.StartArgs) error {
gui.c.Log.Info("starting main loop")
defer gui.handlePanicInTest(startArgs.IntegrationTest)
gui.handleTestMode(startArgs.IntegrationTest)
return gui.g.MainLoop()

View File

@@ -70,6 +70,7 @@ func (self *GuiDriver) Fail(message string) {
self.gui.g.Close()
// need to give the gui time to close
time.Sleep(time.Millisecond * 100)
self.Log("test failed")
panic(fullMessage)
}

View File

@@ -5,7 +5,6 @@ package gui
import (
"io"
"os"
"os/exec"
"strings"
@@ -57,7 +56,7 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error
manager := gui.getManager(view)
var ptmx *os.File
var ptmx pty.Pty
start := func() (*exec.Cmd, io.Reader) {
var err error
ptmx, err = pty.StartWithSize(cmd, gui.desiredPtySize())

View File

@@ -1,12 +1,15 @@
package gui
import (
"fmt"
"log"
"os"
"runtime"
"time"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/result"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -16,29 +19,60 @@ type IntegrationTest interface {
}
func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) {
if os.Getenv(components.SANDBOX_ENV_VAR) == "true" {
if os.Getenv(components.SANDBOX_ENV_VAR) == "true" || test == nil {
return
}
if test != nil {
go func() {
time.Sleep(time.Millisecond * 100)
go func() {
defer gui.handlePanicInTest(test)
test.Run(&GuiDriver{gui: gui})
time.Sleep(time.Millisecond * 100)
gui.g.Update(func(*gocui.Gui) error {
return gocui.ErrQuit
})
guiDriver := &GuiDriver{gui: gui}
test.Run(guiDriver)
time.Sleep(time.Second * 1)
// if we're here then the test must have passed: it panics upon failure
gui.Log.Warnf("test %s logging success", test.Name())
if err := result.LogSuccess(); err != nil {
gui.Log.Warnf("test %s failed to log success!", test.Name())
panic(err)
}
log.Fatal("gocui should have already exited")
}()
go utils.Safe(func() {
time.Sleep(time.Second * 40)
log.Fatal("40 seconds is up, lazygit recording took too long to complete")
gui.g.Update(func(*gocui.Gui) error {
return gocui.ErrQuit
})
time.Sleep(time.Second * 1)
log.Fatal("gocui should have already exited")
}()
go utils.Safe(func() {
defer gui.handlePanicInTest(test)
time.Sleep(time.Second * 40)
panic("40 seconds is up, lazygit recording took too long to complete")
})
}
func (gui *Gui) handlePanicInTest(test integrationTypes.IntegrationTest) {
if test == nil {
return
}
if r := recover(); r != nil {
buf := make([]byte, 4096*4) // arbitrarily large buffer size
stackSize := runtime.Stack(buf, false)
stackTrace := string(buf[:stackSize])
gui.Log.Warnf("test %s panicked!", test.Name())
if err := result.LogFailure(fmt.Sprintf("%v\n%s", r, stackTrace)); err != nil {
panic(err)
}
// Re-panic
panic(r)
}
}

View File

@@ -4,11 +4,9 @@ import (
"log"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/tests"
"github.com/samber/lo"
@@ -30,6 +28,11 @@ func RunCLI(testNames []string, slow bool, sandbox bool) {
keyPressDelay = SLOW_KEY_PRESS_DELAY
}
// replace backslashes with forward slashes for windows compatibility
testNames = lo.Map(testNames, func(name string, _ int) string {
return strings.ReplaceAll(name, "\\", "/")
})
err := components.RunTests(
getTestsToRun(testNames),
log.Printf,
@@ -58,14 +61,6 @@ func getTestsToRun(testNames []string) []*components.IntegrationTest {
return allIntegrationTests
}
testNames = slices.Map(testNames, func(name string) string {
// allowing full test paths to be passed for convenience
return strings.TrimSuffix(
regexp.MustCompile(`.*pkg/integration/tests/`).ReplaceAllString(name, ""),
".go",
)
})
if lo.SomeBy(testNames, func(name string) bool {
return strings.HasSuffix(name, "/shared")
}) {

View File

@@ -1,6 +1,3 @@
//go:build !windows
// +build !windows
package clients
// This file allows you to use `go test` to run integration tests.
@@ -9,14 +6,15 @@ package clients
import (
"bytes"
"errors"
"io"
"io/ioutil"
"fmt"
"os"
"os/exec"
"testing"
"time"
"github.com/creack/pty"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/result"
"github.com/jesseduffield/lazygit/pkg/integration/tests"
"github.com/stretchr/testify/assert"
)
@@ -30,8 +28,10 @@ func TestIntegration(t *testing.T) {
parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0)
testNumber := 0
tests := tests.GetTests()
err := components.RunTests(
tests.GetTests(),
tests,
t.Logf,
runCmdHeadless,
func(test *components.IntegrationTest, f func() error) {
@@ -40,6 +40,10 @@ func TestIntegration(t *testing.T) {
return
}
// if test.Name() != "commit/commit" {
// return
// }
t.Run(test.Name(), func(t *testing.T) {
t.Parallel()
err := f()
@@ -63,6 +67,9 @@ func runCmdHeadless(cmd *exec.Cmd) error {
"TERM=xterm",
)
resultPath := result.GetResultPath()
result.SetResultPathEnvVar(cmd, resultPath)
// not writing stderr to the pty because we want to capture a panic if
// there is one. But some commands will not be in tty mode if stderr is
// not a terminal. We'll need to keep an eye out for that.
@@ -72,17 +79,23 @@ func runCmdHeadless(cmd *exec.Cmd) error {
// these rows and columns are ignored because internally we use tcell's
// simulation screen. However we still need the pty for the sake of
// running other commands in a pty.
f, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 300, Cols: 300})
_, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 300, Cols: 300})
if err != nil {
return err
}
_, _ = io.Copy(ioutil.Discard, f)
_ = cmd.Wait()
if cmd.Wait() != nil {
// return an error with the stderr output
return errors.New(stderr.String())
time.Sleep(time.Second)
result, err := result.ReadResult(resultPath)
if err != nil {
return fmt.Errorf("Error reading integration test result: %w", err)
}
if !result.Success {
return errors.New(result.Message)
}
return f.Close()
return nil
}

View File

@@ -18,7 +18,7 @@ func retryWaitTimes() []int {
// give it more leeway compared to when we're running things locally.
return []int{0, 1, 1, 1, 1, 1, 5, 10, 20, 40, 100, 200, 500, 1000, 2000, 4000}
} else {
return []int{0, 1, 1, 1, 1, 1, 5, 10, 20, 40, 100, 200}
return []int{0, 1, 1, 1, 1, 1, 5, 10, 20, 40, 100, 200, 500, 1000, 2000, 4000}
}
}

View File

@@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"github.com/jesseduffield/lazycore/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
@@ -122,7 +123,7 @@ func prepareTestDir(
err := os.Mkdir(paths.ActualRepo(), 0o777)
if err != nil {
return err
panic(err)
}
return createFixture(test, paths, rootDir)
@@ -198,7 +199,12 @@ func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string, sandb
}
func tempLazygitPath() string {
return filepath.Join("/tmp", "lazygit", "test_lazygit")
filename := "test_lazygit"
if runtime.GOOS == "windows" {
filename += ".exe"
}
return filepath.Join(os.TempDir(), "lazygit", filename)
}
func findOrCreateDir(path string) {

View File

@@ -2,6 +2,8 @@ package components
import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
@@ -170,10 +172,14 @@ func testNameFromCurrentFilePath() string {
return TestNameFromFilePath(path)
}
// takes either a path or a test name and returns a test name
// so commit/commit -> commit/commit
// and /my/path/to/lazygit/pkg/integration/tests/commit/commit -> commit/commit
func TestNameFromFilePath(path string) string {
name := strings.Split(path, "integration/tests/")[1]
return name[:len(name)-len(".go")]
return strings.TrimSuffix(
regexp.MustCompile(`.*pkg/integration/tests/`).ReplaceAllString(filepath.ToSlash(path), ""),
".go",
)
}
// this is the delay in milliseconds between keypresses

View File

@@ -0,0 +1,103 @@
package result
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
)
// On windows the pty package is failing to obtain stderr text when a test fails,
// so this is our workaround: when a test fails, it writes the result to a file,
// and then the test runner reads the file and checks the result
const PathEnvVar = "LAZYGIT_INTEGRATION_TEST_RESULT_PATH"
type IntegrationTestResult struct {
Success bool
Message string
}
func LogFailure(message string) error {
return writeResult(failure(message))
}
func LogSuccess() error {
return writeResult(success())
}
func failure(message string) IntegrationTestResult {
return IntegrationTestResult{Success: false, Message: message}
}
func success() IntegrationTestResult {
return IntegrationTestResult{Success: true}
}
func writeResult(result IntegrationTestResult) error {
resultPath := os.Getenv(PathEnvVar)
if resultPath == "" {
// path env var not set so we'll assume we don't need to write the result to a file
return nil
}
file, err := os.Create(resultPath)
if err != nil {
return fmt.Errorf("Error creating file: %w", err)
}
defer file.Close()
encoder := json.NewEncoder(file)
err = encoder.Encode(result)
if err != nil {
return fmt.Errorf("Error encoding JSON: %w", err)
}
return nil
}
// Reads the result file stored by the lazygit test, and deletes the file
func ReadResult(resultPath string) (IntegrationTestResult, error) {
file, err := os.Open(resultPath)
if err != nil {
return IntegrationTestResult{}, fmt.Errorf("Error reading file: %w", err)
}
decoder := json.NewDecoder(file)
var result IntegrationTestResult
err = decoder.Decode(&result)
if err != nil {
file.Close()
return IntegrationTestResult{}, fmt.Errorf("Error decoding JSON: %w", err)
}
file.Close()
// _ = os.Remove(resultPath)
return result, nil
}
func SetResultPathEnvVar(cmd *exec.Cmd, resultPath string) {
cmd.Env = append(
cmd.Env,
fmt.Sprintf("%s=%s", PathEnvVar, resultPath),
)
}
func GetResultPath() string {
return filepath.Join(os.TempDir(), fmt.Sprintf("lazygit_result_%s.json", generateRandomString(10)))
}
func generateRandomString(length int) string {
buffer := make([]byte, length)
_, err := rand.Read(buffer)
if err != nil {
panic(fmt.Sprintf("Could not generate random string: %s", err))
}
randomString := base64.URLEncoding.EncodeToString(buffer)
return randomString[:length]
}

View File

@@ -27,7 +27,7 @@ var Commit = NewIntegrationTest(NewIntegrationTestArgs{
PressPrimaryAction(). // stage file
Lines(
Contains("A myfile").IsSelected(),
Contains("?? myfile2"),
Contains("?? myfile23"),
).
SelectNextItem().
PressPrimaryAction(). // stage other file
@@ -55,7 +55,7 @@ var Commit = NewIntegrationTest(NewIntegrationTestArgs{
IsFocused().
Lines(
Contains("A myfile"),
Contains("A myfile2"),
Contains("A myfile3"),
)
},
})

View File

@@ -27,7 +27,7 @@ func GetTests() []*components.IntegrationTest {
missingTestNames := []string{}
if err := filepath.Walk(filepath.Join(utils.GetLazyRootDirectory(), "pkg/integration/tests"), func(path string, info os.FileInfo, err error) error {
if err := filepath.Walk(filepath.Join(utils.GetLazyRootDirectory(), "pkg", "integration", "tests"), func(path string, info os.FileInfo, err error) error {
if !info.IsDir() && strings.HasSuffix(path, ".go") {
// ignoring non-test files
if filepath.Base(path) == "tests.go" || filepath.Base(path) == "test_list.go" || filepath.Base(path) == "test_list_generator.go" {

View File

@@ -16,6 +16,9 @@ var DoublePopup = NewIntegrationTest(NewIntegrationTestArgs{
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Branches().
Focus().
Lines(
Contains("master"),
).
// arbitrarily bringing up a popup
PressPrimaryAction()

View File

@@ -13,6 +13,7 @@ import (
type IntegrationTest interface {
Run(GuiDriver)
SetupConfig(config *config.AppConfig)
Name() string
}
// this is the interface through which our integration tests interact with the lazygit gui

View File

@@ -21,7 +21,7 @@ import (
var Global *logrus.Entry
func init() {
logPath := os.Getenv("LAZYGIT_LOG_PATH")
logPath := `C:\Users\New\AppData\Roaming\jesseduffield\lazygit\development.log`
if logPath != "" {
Global = NewDevelopmentLogger(logPath)
}
@@ -35,6 +35,7 @@ func NewProductionLogger() *logrus.Entry {
}
func NewDevelopmentLogger(logPath string) *logrus.Entry {
logPath = `C:\Users\New\AppData\Roaming\jesseduffield\lazygit\development.log`
logger := logrus.New()
logger.SetLevel(getLogLevel())

View File

@@ -8,20 +8,15 @@
# this will hang if you're using git from the command line, so only enable this
# when you are testing the credentials view in lazygit
exec < /dev/tty
read -p "Username for 'github': " username
read -p "Password for 'github': " password
echo -n "Username for 'github': "
read username
echo
echo -n "Password for 'github': "
# this will print the password to the log view but real git won't do that.
# We could use read -s but that's not POSIX compliant.
read password
if [ "$username" = "username" -a "$password" = "password" ]; then
if [[ "$username" == "username" && "$password" == "password" ]]; then
echo "success"
exit 0
fi
>&2 echo "incorrect username/password"
echo "incorrect username/password" >&2
exit 1

17
vendor/github.com/creack/pty/Dockerfile.golang generated vendored Normal file
View File

@@ -0,0 +1,17 @@
ARG GOVERSION=1.14
FROM golang:${GOVERSION}
# Set base env.
ARG GOOS=linux
ARG GOARCH=amd64
ENV GOOS=${GOOS} GOARCH=${GOARCH} CGO_ENABLED=0 GOFLAGS='-v -ldflags=-s -ldflags=-w'
# Pre compile the stdlib for 386/arm (32bits).
RUN go build -a std
# Add the code to the image.
WORKDIR pty
ADD . .
# Build the lib.
RUN go build

View File

@@ -1,3 +1,4 @@
# NOTE: Using 1.13 as a base to build the RISCV compiler, the resulting version is based on go1.6.
FROM golang:1.13
# Clone and complie a riscv compatible version of the go compiler.
@@ -8,7 +9,15 @@ ENV PATH=/riscv-go/misc/riscv:/riscv-go/bin:$PATH
RUN cd /riscv-go/src && GOROOT_BOOTSTRAP=$(go env GOROOT) ./make.bash
ENV GOROOT=/riscv-go
# Make sure we compile.
# Set the base env.
ENV GOOS=linux GOARCH=riscv CGO_ENABLED=0 GOFLAGS='-v -ldflags=-s -ldflags=-w'
# Pre compile the stdlib.
RUN go build -a std
# Add the code to the image.
WORKDIR pty
ADD . .
RUN GOOS=linux GOARCH=riscv go build
# Build the lib.
RUN go build

View File

@@ -1,12 +1,18 @@
# pty
Pty is a Go package for using unix pseudo-terminals.
Pty is a Go package for using unix pseudo-terminals and windows ConPty.
## Install
go get github.com/creack/pty
```sh
go get github.com/creack/pty
```
## Example
## Examples
Note that those examples are for demonstration purpose only, to showcase how to use the library. They are not meant to be used in any kind of production environment.
__NOTE:__ This package requires `ConPty` support on windows platform, please make sure your windows system meet [these requirements](https://docs.microsoft.com/en-us/windows/console/createpseudoconsole#requirements)
### Command
@@ -14,10 +20,11 @@ Pty is a Go package for using unix pseudo-terminals.
package main
import (
"github.com/creack/pty"
"io"
"os"
"os/exec"
"github.com/creack/pty"
)
func main() {
@@ -51,7 +58,7 @@ import (
"syscall"
"github.com/creack/pty"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/term"
)
func test() error {
@@ -77,15 +84,17 @@ func test() error {
}
}()
ch <- syscall.SIGWINCH // Initial resize.
defer func() { signal.Stop(ch); close(ch) }() // Cleanup signals when done.
// Set stdin in raw mode.
oldState, err := terminal.MakeRaw(int(os.Stdin.Fd()))
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
panic(err)
}
defer func() { _ = terminal.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
defer func() { _ = term.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
// Copy stdin to the pty and the pty to stdout.
// NOTE: The goroutine will keep reading until the next keystroke before returning.
go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
_, _ = io.Copy(os.Stdout, ptmx)

18
vendor/github.com/creack/pty/asm_solaris_amd64.s generated vendored Normal file
View File

@@ -0,0 +1,18 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc
//+build gc
#include "textflag.h"
//
// System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go
//
TEXT ·sysvicall6(SB),NOSPLIT,$0-88
JMP syscall·sysvicall6(SB)
TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88
JMP syscall·rawSysvicall6(SB)

356
vendor/github.com/creack/pty/cmd_windows.go generated vendored Normal file
View File

@@ -0,0 +1,356 @@
//go:build windows
// +build windows
package pty
import (
"bytes"
"errors"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"unicode/utf16"
"unsafe"
"golang.org/x/sys/windows"
)
// copied from os/exec.Cmd for platform compatibility
// we need to use startupInfoEx for pty support, but os/exec.Cmd only have
// support for startupInfo on windows, so we have to rewrite some internal
// logic for windows while keep its behavior compatible with other platforms.
// windowExecCmd represents an external command being prepared or run.
//
// A cmd cannot be reused after calling its Run, Output or CombinedOutput
// methods.
type windowExecCmd struct {
cmd *exec.Cmd
waitCalled bool
conPty *WindowsPty
attrList *windows.ProcThreadAttributeListContainer
}
var errProcessNotStarted = errors.New("exec: process has not started yet")
func (c *windowExecCmd) close() error {
c.attrList.Delete()
_ = c.conPty.Close()
return nil
}
func (c *windowExecCmd) Run() error {
err := c.Start()
if err != nil {
return err
}
return c.Wait()
}
func (c *windowExecCmd) Wait() error {
if c.cmd.Process == nil {
return errProcessNotStarted
}
var err error
if c.waitCalled {
return errors.New("exec: wait was already called")
}
c.waitCalled = true
c.cmd.ProcessState, err = c.cmd.Process.Wait()
if err != nil {
return err
}
err = c.close()
if err != nil {
return err
}
if !c.cmd.ProcessState.Success() {
return &exec.ExitError{ProcessState: c.cmd.ProcessState}
}
return nil
}
func (c *windowExecCmd) StdinPipe() (io.WriteCloser, error) {
return nil, ErrUnsupported
}
func (c *windowExecCmd) StdoutPipe() (io.ReadCloser, error) {
return nil, ErrUnsupported
}
func (c *windowExecCmd) StderrPipe() (io.ReadCloser, error) {
return nil, ErrUnsupported
}
func (c *windowExecCmd) Output() ([]byte, error) {
if c.cmd.Stdout != nil {
return nil, errors.New("exec: Stdout already set")
}
var stdout bytes.Buffer
c.cmd.Stdout = &stdout
err := c.Run()
return stdout.Bytes(), err
}
func (c *windowExecCmd) CombinedOutput() ([]byte, error) {
if c.cmd.Stdout != nil {
return nil, errors.New("exec: Stdout already set")
}
if c.cmd.Stderr != nil {
return nil, errors.New("exec: Stderr already set")
}
var b bytes.Buffer
c.cmd.Stdout = &b
c.cmd.Stderr = &b
err := c.Run()
return b.Bytes(), err
}
func (c *windowExecCmd) argv() []string {
if len(c.cmd.Args) > 0 {
return c.cmd.Args
}
return []string{c.cmd.Path}
}
//
// Helpers for working with Windows. These are exact copies of the same utilities found in the go stdlib.
//
func lookExtensions(path, dir string) (string, error) {
if filepath.Base(path) == path {
path = filepath.Join(".", path)
}
if dir == "" {
return exec.LookPath(path)
}
if filepath.VolumeName(path) != "" {
return exec.LookPath(path)
}
if len(path) > 1 && os.IsPathSeparator(path[0]) {
return exec.LookPath(path)
}
dirandpath := filepath.Join(dir, path)
// We assume that LookPath will only add file extension.
lp, err := exec.LookPath(dirandpath)
if err != nil {
return "", err
}
ext := strings.TrimPrefix(lp, dirandpath)
return path + ext, nil
}
func dedupEnvCase(caseInsensitive bool, env []string) []string {
// Construct the output in reverse order, to preserve the
// last occurrence of each key.
out := make([]string, 0, len(env))
saw := make(map[string]bool, len(env))
for n := len(env); n > 0; n-- {
kv := env[n-1]
i := strings.Index(kv, "=")
if i == 0 {
// We observe in practice keys with a single leading "=" on Windows.
// TODO(#49886): Should we consume only the first leading "=" as part
// of the key, or parse through arbitrarily many of them until a non-"="?
i = strings.Index(kv[1:], "=") + 1
}
if i < 0 {
if kv != "" {
// The entry is not of the form "key=value" (as it is required to be).
// Leave it as-is for now.
// TODO(#52436): should we strip or reject these bogus entries?
out = append(out, kv)
}
continue
}
k := kv[:i]
if caseInsensitive {
k = strings.ToLower(k)
}
if saw[k] {
continue
}
saw[k] = true
out = append(out, kv)
}
// Now reverse the slice to restore the original order.
for i := 0; i < len(out)/2; i++ {
j := len(out) - i - 1
out[i], out[j] = out[j], out[i]
}
return out
}
func addCriticalEnv(env []string) []string {
for _, kv := range env {
eq := strings.Index(kv, "=")
if eq < 0 {
continue
}
k := kv[:eq]
if strings.EqualFold(k, "SYSTEMROOT") {
// We already have it.
return env
}
}
return append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT"))
}
func execEnvDefault(sys *syscall.SysProcAttr) (env []string, err error) {
if sys == nil || sys.Token == 0 {
return syscall.Environ(), nil
}
var block *uint16
err = windows.CreateEnvironmentBlock(&block, windows.Token(sys.Token), false)
if err != nil {
return nil, err
}
defer windows.DestroyEnvironmentBlock(block)
blockp := uintptr(unsafe.Pointer(block))
for {
// find NUL terminator
end := unsafe.Pointer(blockp)
for *(*uint16)(end) != 0 {
end = unsafe.Pointer(uintptr(end) + 2)
}
n := (uintptr(end) - uintptr(unsafe.Pointer(blockp))) / 2
if n == 0 {
// environment block ends with empty string
break
}
entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:n:n]
env = append(env, string(utf16.Decode(entry)))
blockp += 2 * (uintptr(len(entry)) + 1)
}
return
}
func createEnvBlock(envv []string) *uint16 {
if len(envv) == 0 {
return &utf16.Encode([]rune("\x00\x00"))[0]
}
length := 0
for _, s := range envv {
length += len(s) + 1
}
length += 1
b := make([]byte, length)
i := 0
for _, s := range envv {
l := len(s)
copy(b[i:i+l], []byte(s))
copy(b[i+l:i+l+1], []byte{0})
i = i + l + 1
}
copy(b[i:i+1], []byte{0})
return &utf16.Encode([]rune(string(b)))[0]
}
func makeCmdLine(args []string) string {
var s string
for _, v := range args {
if s != "" {
s += " "
}
s += windows.EscapeArg(v)
}
return s
}
func isSlash(c uint8) bool {
return c == '\\' || c == '/'
}
func normalizeDir(dir string) (name string, err error) {
ndir, err := syscall.FullPath(dir)
if err != nil {
return "", err
}
if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
// dir cannot have \\server\share\path form
return "", syscall.EINVAL
}
return ndir, nil
}
func volToUpper(ch int) int {
if 'a' <= ch && ch <= 'z' {
ch += 'A' - 'a'
}
return ch
}
func joinExeDirAndFName(dir, p string) (name string, err error) {
if len(p) == 0 {
return "", syscall.EINVAL
}
if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
// \\server\share\path form
return p, nil
}
if len(p) > 1 && p[1] == ':' {
// has drive letter
if len(p) == 2 {
return "", syscall.EINVAL
}
if isSlash(p[2]) {
return p, nil
} else {
d, err := normalizeDir(dir)
if err != nil {
return "", err
}
if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
return syscall.FullPath(d + "\\" + p[2:])
} else {
return syscall.FullPath(p)
}
}
} else {
// no drive letter
d, err := normalizeDir(dir)
if err != nil {
return "", err
}
if isSlash(p[0]) {
return windows.FullPath(d[:2] + p)
} else {
return windows.FullPath(d + "\\" + p)
}
}
}

36
vendor/github.com/creack/pty/doc.go generated vendored
View File

@@ -3,14 +3,44 @@ package pty
import (
"errors"
"os"
"io"
)
// ErrUnsupported is returned if a function is not
// available on the current platform.
var ErrUnsupported = errors.New("unsupported")
// Opens a pty and its corresponding tty.
func Open() (pty, tty *os.File, err error) {
// Open a pty and its corresponding tty.
func Open() (Pty, Tty, error) {
return open()
}
type FdHolder interface {
Fd() uintptr
}
// Pty for terminal control in current process
// for unix systems, the real type is *os.File
// for windows, the real type is a *WindowsPty for ConPTY handle
type Pty interface {
// FdHolder Fd intended to resize Tty of child process in current process
FdHolder
Name() string
// WriteString is only used to identify Pty and Tty
WriteString(s string) (n int, err error)
io.ReadWriteCloser
}
// Tty for data i/o in child process
// for unix systems, the real type is *os.File
// for windows, the real type is a *WindowsTty, which is a combination of two pipe file
type Tty interface {
// FdHolder Fd only intended for manual InheritSize from Pty
FdHolder
Name() string
io.ReadWriteCloser
}

View File

@@ -1,9 +1,15 @@
// +build !windows,!solaris
//go:build !windows && !solaris && !aix
// +build !windows,!solaris,!aix
package pty
import "syscall"
const (
TIOCGWINSZ = syscall.TIOCGWINSZ
TIOCSWINSZ = syscall.TIOCSWINSZ
)
func ioctl(fd, cmd, ptr uintptr) error {
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
if e != 0 {

View File

@@ -1,3 +1,4 @@
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
// +build darwin dragonfly freebsd netbsd openbsd
package pty

View File

@@ -1,30 +1,48 @@
//go:build solaris
// +build solaris
package pty
import (
"golang.org/x/sys/unix"
"syscall"
"unsafe"
)
//go:cgo_import_dynamic libc_ioctl ioctl "libc.so"
//go:linkname procioctl libc_ioctl
var procioctl uintptr
const (
// see /usr/include/sys/stropts.h
I_PUSH = uintptr((int32('S')<<8 | 002))
I_STR = uintptr((int32('S')<<8 | 010))
I_FIND = uintptr((int32('S')<<8 | 013))
I_PUSH = uintptr((int32('S')<<8 | 002))
I_STR = uintptr((int32('S')<<8 | 010))
I_FIND = uintptr((int32('S')<<8 | 013))
// see /usr/include/sys/ptms.h
ISPTM = (int32('P') << 8) | 1
UNLKPT = (int32('P') << 8) | 2
PTSSTTY = (int32('P') << 8) | 3
ZONEPT = (int32('P') << 8) | 4
OWNERPT = (int32('P') << 8) | 5
// see /usr/include/sys/termios.h
TIOCSWINSZ = (uint32('T') << 8) | 103
TIOCGWINSZ = (uint32('T') << 8) | 104
)
type strioctl struct {
ic_cmd int32
ic_timout int32
ic_len int32
ic_dp unsafe.Pointer
icCmd int32
icTimeout int32
icLen int32
icDP unsafe.Pointer
}
// Defined in asm_solaris_amd64.s.
func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
func ioctl(fd, cmd, ptr uintptr) error {
return unix.IoctlSetInt(int(fd), uint(cmd), int(ptr))
if _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procioctl)), 3, fd, cmd, ptr, 0, 0, 0); errno != 0 {
return errno
}
return nil
}

13
vendor/github.com/creack/pty/ioctl_unsupported.go generated vendored Normal file
View File

@@ -0,0 +1,13 @@
//go:build aix
// +build aix
package pty
const (
TIOCGWINSZ = 0
TIOCSWINSZ = 0
)
func ioctl(fd, cmd, ptr uintptr) error {
return ErrUnsupported
}

View File

@@ -13,7 +13,7 @@ GODEFS="go tool cgo -godefs"
$GODEFS types.go |gofmt > ztypes_$GOARCH.go
case $GOOS in
freebsd|dragonfly|openbsd)
freebsd|dragonfly|netbsd|openbsd)
$GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
;;
esac

View File

@@ -1,3 +1,6 @@
//go:build darwin
// +build darwin
package pty
import (
@@ -33,7 +36,7 @@ func open() (pty, tty *os.File, err error) {
return nil, nil, err
}
t, err := os.OpenFile(sname, os.O_RDWR, 0)
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
if err != nil {
return nil, nil, err
}

View File

@@ -1,3 +1,6 @@
//go:build dragonfly
// +build dragonfly
package pty
import (

View File

@@ -1,3 +1,6 @@
//go:build freebsd
// +build freebsd
package pty
import (

View File

@@ -1,3 +1,6 @@
//go:build linux
// +build linux
package pty
import (
@@ -28,7 +31,7 @@ func open() (pty, tty *os.File, err error) {
return nil, nil, err
}
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0) //nolint:gosec // Expected Open from a variable.
if err != nil {
return nil, nil, err
}
@@ -37,7 +40,7 @@ func open() (pty, tty *os.File, err error) {
func ptsname(f *os.File) (string, error) {
var n _C_uint
err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) //nolint:gosec // Expected unsafe pointer for Syscall call.
if err != nil {
return "", err
}
@@ -47,5 +50,5 @@ func ptsname(f *os.File) (string, error) {
func unlockpt(f *os.File) error {
var u _C_int
// use TIOCSPTLCK with a pointer to zero to clear the lock
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) //nolint:gosec // Expected unsafe pointer for Syscall call.
}

69
vendor/github.com/creack/pty/pty_netbsd.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
//go:build netbsd
// +build netbsd
package pty
import (
"errors"
"os"
"syscall"
"unsafe"
)
func open() (pty, tty *os.File, err error) {
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
if err != nil {
return nil, nil, err
}
// In case of error after this point, make sure we close the ptmx fd.
defer func() {
if err != nil {
_ = p.Close() // Best effort.
}
}()
sname, err := ptsname(p)
if err != nil {
return nil, nil, err
}
if err := grantpt(p); err != nil {
return nil, nil, err
}
// In NetBSD unlockpt() does nothing, so it isn't called here.
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
if err != nil {
return nil, nil, err
}
return p, t, nil
}
func ptsname(f *os.File) (string, error) {
/*
* from ptsname(3): The ptsname() function is equivalent to:
* struct ptmget pm;
* ioctl(fd, TIOCPTSNAME, &pm) == -1 ? NULL : pm.sn;
*/
var ptm ptmget
if err := ioctl(f.Fd(), uintptr(ioctl_TIOCPTSNAME), uintptr(unsafe.Pointer(&ptm))); err != nil {
return "", err
}
name := make([]byte, len(ptm.Sn))
for i, c := range ptm.Sn {
name[i] = byte(c)
if c == 0 {
return string(name[:i]), nil
}
}
return "", errors.New("TIOCPTSNAME string not NUL-terminated")
}
func grantpt(f *os.File) error {
/*
* from grantpt(3): Calling grantpt() is equivalent to:
* ioctl(fd, TIOCGRANTPT, 0);
*/
return ioctl(f.Fd(), uintptr(ioctl_TIOCGRANTPT), 0)
}

View File

@@ -1,3 +1,6 @@
//go:build openbsd
// +build openbsd
package pty
import (
@@ -6,6 +9,17 @@ import (
"unsafe"
)
func cInt8ToString(in []int8) string {
var s []byte
for _, v := range in {
if v == 0 {
break
}
s = append(s, byte(v))
}
return string(s)
}
func open() (pty, tty *os.File, err error) {
/*
* from ptm(4):
@@ -26,8 +40,8 @@ func open() (pty, tty *os.File, err error) {
return nil, nil, err
}
pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm")
tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm")
pty = os.NewFile(uintptr(ptm.Cfd), cInt8ToString(ptm.Cn[:]))
tty = os.NewFile(uintptr(ptm.Sfd), cInt8ToString(ptm.Sn[:]))
return pty, tty, nil
}

View File

@@ -1,3 +1,6 @@
//go:build solaris
// +build solaris
package pty
/* based on:
@@ -6,122 +9,134 @@ http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/pt.c
import (
"errors"
"golang.org/x/sys/unix"
"os"
"strconv"
"syscall"
"unsafe"
)
const NODEV = ^uint64(0)
func open() (pty, tty *os.File, err error) {
masterfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|unix.O_NOCTTY, 0)
//masterfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC|unix.O_NOCTTY, 0)
ptmxfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY, 0)
if err != nil {
return nil, nil, err
}
p := os.NewFile(uintptr(masterfd), "/dev/ptmx")
p := os.NewFile(uintptr(ptmxfd), "/dev/ptmx")
// In case of error after this point, make sure we close the ptmx fd.
defer func() {
if err != nil {
_ = p.Close() // Best effort.
}
}()
sname, err := ptsname(p)
if err != nil {
return nil, nil, err
}
err = grantpt(p)
if err != nil {
if err := grantpt(p); err != nil {
return nil, nil, err
}
err = unlockpt(p)
if err != nil {
if err := unlockpt(p); err != nil {
return nil, nil, err
}
slavefd, err := syscall.Open(sname, os.O_RDWR|unix.O_NOCTTY, 0)
ptsfd, err := syscall.Open(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
if err != nil {
return nil, nil, err
}
t := os.NewFile(uintptr(slavefd), sname)
t := os.NewFile(uintptr(ptsfd), sname)
// In case of error after this point, make sure we close the pts fd.
defer func() {
if err != nil {
_ = t.Close() // Best effort.
}
}()
// pushing terminal driver STREAMS modules as per pts(7)
for _, mod := range([]string{"ptem", "ldterm", "ttcompat"}) {
err = streams_push(t, mod)
if err != nil {
for _, mod := range []string{"ptem", "ldterm", "ttcompat"} {
if err := streamsPush(t, mod); err != nil {
return nil, nil, err
}
}
return p, t, nil
}
func minor(x uint64) uint64 {
return x & 0377
}
func ptsdev(fd uintptr) uint64 {
istr := strioctl{ISPTM, 0, 0, nil}
err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr)))
if err != nil {
return NODEV
}
var status unix.Stat_t
err = unix.Fstat(int(fd), &status)
if err != nil {
return NODEV
}
return uint64(minor(status.Rdev))
}
func ptsname(f *os.File) (string, error) {
dev := ptsdev(f.Fd())
if dev == NODEV {
return "", errors.New("not a master pty")
dev, err := ptsdev(f.Fd())
if err != nil {
return "", err
}
fn := "/dev/pts/" + strconv.FormatInt(int64(dev), 10)
// access(2) creates the slave device (if the pty exists)
// F_OK == 0 (unistd.h)
err := unix.Access(fn, 0)
if err != nil {
if err := syscall.Access(fn, 0); err != nil {
return "", err
}
return fn, nil
}
type pt_own struct {
pto_ruid int32
pto_rgid int32
func unlockpt(f *os.File) error {
istr := strioctl{
icCmd: UNLKPT,
icTimeout: 0,
icLen: 0,
icDP: nil,
}
return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
}
func minor(x uint64) uint64 { return x & 0377 }
func ptsdev(fd uintptr) (uint64, error) {
istr := strioctl{
icCmd: ISPTM,
icTimeout: 0,
icLen: 0,
icDP: nil,
}
if err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
return 0, err
}
var status syscall.Stat_t
if err := syscall.Fstat(int(fd), &status); err != nil {
return 0, err
}
return uint64(minor(status.Rdev)), nil
}
type ptOwn struct {
rUID int32
rGID int32
}
func grantpt(f *os.File) error {
if ptsdev(f.Fd()) == NODEV {
return errors.New("not a master pty")
if _, err := ptsdev(f.Fd()); err != nil {
return err
}
var pto pt_own
pto.pto_ruid = int32(os.Getuid())
// XXX should first attempt to get gid of DEFAULT_TTY_GROUP="tty"
pto.pto_rgid = int32(os.Getgid())
var istr strioctl
istr.ic_cmd = OWNERPT
istr.ic_timout = 0
istr.ic_len = int32(unsafe.Sizeof(istr))
istr.ic_dp = unsafe.Pointer(&pto)
err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
if err != nil {
pto := ptOwn{
rUID: int32(os.Getuid()),
// XXX should first attempt to get gid of DEFAULT_TTY_GROUP="tty"
rGID: int32(os.Getgid()),
}
istr := strioctl{
icCmd: OWNERPT,
icTimeout: 0,
icLen: int32(unsafe.Sizeof(strioctl{})),
icDP: unsafe.Pointer(&pto),
}
if err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
return errors.New("access denied")
}
return nil
}
func unlockpt(f *os.File) error {
istr := strioctl{UNLKPT, 0, 0, nil}
return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
}
// push STREAMS modules if not already done so
func streams_push(f *os.File, mod string) error {
var err error
// streamsPush pushes STREAMS modules if not already done so.
func streamsPush(f *os.File, mod string) error {
buf := []byte(mod)
// XXX I_FIND is not returning an error when the module
// is already pushed even though truss reports a return
// value of 1. A bug in the Go Solaris syscall interface?
@@ -129,11 +144,9 @@ func streams_push(f *os.File, mod string) error {
// https://www.illumos.org/issues/9042
// but since we are not using libc or XPG4.2, we should not be
// double-pushing modules
err = ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0])))
if err != nil {
if err := ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0]))); err != nil {
return nil
}
err = ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
return err
return ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
}

View File

@@ -1,4 +1,5 @@
// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd,!solaris
//go:build !linux && !darwin && !freebsd && !dragonfly && !netbsd && !openbsd && !solaris && !windows
// +build !linux,!darwin,!freebsd,!dragonfly,!netbsd,!openbsd,!solaris,!windows
package pty

187
vendor/github.com/creack/pty/pty_windows.go generated vendored Normal file
View File

@@ -0,0 +1,187 @@
//go:build windows
// +build windows
package pty
import (
"fmt"
"os"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
const (
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x20016
)
type WindowsPty struct {
handle windows.Handle
r, w *os.File
}
type WindowsTty struct {
handle windows.Handle
r, w *os.File
}
var (
// NOTE(security): as noted by the comment of syscall.NewLazyDLL and syscall.LoadDLL
// user need to call internal/syscall/windows/sysdll.Add("kernel32.dll") to make sure
// the kernel32.dll is loaded from windows system path
//
// ref: https://pkg.go.dev/syscall@go1.13?GOOS=windows#LoadDLL
kernel32DLL = windows.NewLazyDLL("kernel32.dll")
// https://docs.microsoft.com/en-us/windows/console/createpseudoconsole
createPseudoConsole = kernel32DLL.NewProc("CreatePseudoConsole")
closePseudoConsole = kernel32DLL.NewProc("ClosePseudoConsole")
resizePseudoConsole = kernel32DLL.NewProc("ResizePseudoConsole")
getConsoleScreenBufferInfo = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
)
func open() (_ Pty, _ Tty, err error) {
pr, consoleW, err := os.Pipe()
if err != nil {
return nil, nil, err
}
consoleR, pw, err := os.Pipe()
if err != nil {
_ = consoleW.Close()
_ = pr.Close()
return nil, nil, err
}
var consoleHandle windows.Handle
err = procCreatePseudoConsole(windows.Handle(consoleR.Fd()), windows.Handle(consoleW.Fd()),
0, &consoleHandle)
if err != nil {
_ = consoleW.Close()
_ = pr.Close()
_ = pw.Close()
_ = consoleR.Close()
return nil, nil, err
}
// These pipes can be closed here without any worry
err = consoleW.Close()
if err != nil {
return nil, nil, fmt.Errorf("failed to close pseudo console handle: %w", err)
}
err = consoleR.Close()
if err != nil {
return nil, nil, fmt.Errorf("failed to close pseudo console handle: %w", err)
}
return &WindowsPty{
handle: consoleHandle,
r: pr,
w: pw,
}, &WindowsTty{
handle: consoleHandle,
r: consoleR,
w: consoleW,
}, nil
}
func (p *WindowsPty) Name() string {
return p.r.Name()
}
func (p *WindowsPty) Fd() uintptr {
return uintptr(p.handle)
}
func (p *WindowsPty) Read(data []byte) (int, error) {
return p.r.Read(data)
}
func (p *WindowsPty) Write(data []byte) (int, error) {
return p.w.Write(data)
}
func (p *WindowsPty) WriteString(s string) (int, error) {
return p.w.WriteString(s)
}
func (p *WindowsPty) UpdateProcThreadAttribute(attrList *windows.ProcThreadAttributeListContainer) error {
var err error
if err = attrList.Update(
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
unsafe.Pointer(p.handle),
unsafe.Sizeof(p.handle),
); err != nil {
return fmt.Errorf("failed to update proc thread attributes for pseudo console: %w", err)
}
return nil
}
func (p *WindowsPty) Close() error {
_ = p.r.Close()
_ = p.w.Close()
err := closePseudoConsole.Find()
if err != nil {
return err
}
_, _, err = closePseudoConsole.Call(uintptr(p.handle))
return err
}
func (t *WindowsTty) Name() string {
return t.r.Name()
}
func (t *WindowsTty) Fd() uintptr {
return uintptr(t.handle)
}
func (t *WindowsTty) Read(p []byte) (int, error) {
return t.r.Read(p)
}
func (t *WindowsTty) Write(p []byte) (int, error) {
return t.w.Write(p)
}
func (t *WindowsTty) Close() error {
_ = t.r.Close()
return t.w.Close()
}
func procCreatePseudoConsole(hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, consoleHandle *windows.Handle) error {
var r0 uintptr
var err error
err = createPseudoConsole.Find()
if err != nil {
return err
}
r0, _, err = createPseudoConsole.Call(
(windowsCoord{X: 80, Y: 30}).Pack(), // size: default 80x30 window
uintptr(hInput), // console input
uintptr(hOutput), // console output
uintptr(dwFlags), // console flags, currently only PSEUDOCONSOLE_INHERIT_CURSOR supported
uintptr(unsafe.Pointer(consoleHandle)), // console handler value return
)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
// S_OK: 0
return syscall.Errno(r0)
}
return nil
}

64
vendor/github.com/creack/pty/run.go generated vendored
View File

@@ -1,11 +1,7 @@
// +build !windows
package pty
import (
"os"
"os/exec"
"syscall"
)
// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
@@ -13,62 +9,6 @@ import (
// corresponding pty.
//
// Starts the process in a new session and sets the controlling terminal.
func Start(c *exec.Cmd) (pty *os.File, err error) {
return StartWithSize(c, nil)
}
// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
// and c.Stderr, calls c.Start, and returns the File of the tty's
// corresponding pty.
//
// This will resize the pty to the specified size before starting the command.
// Starts the process in a new session and sets the controlling terminal.
func StartWithSize(c *exec.Cmd, sz *Winsize) (pty *os.File, err error) {
if c.SysProcAttr == nil {
c.SysProcAttr = &syscall.SysProcAttr{}
}
c.SysProcAttr.Setsid = true
c.SysProcAttr.Setctty = true
return StartWithAttrs(c, sz, c.SysProcAttr)
}
// StartWithAttrs assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
// and c.Stderr, calls c.Start, and returns the File of the tty's
// corresponding pty.
//
// This will resize the pty to the specified size before starting the command if a size is provided.
// The `attrs` parameter overrides the one set in c.SysProcAttr.
//
// This should generally not be needed. Used in some edge cases where it is needed to create a pty
// without a controlling terminal.
func StartWithAttrs(c *exec.Cmd, sz *Winsize, attrs *syscall.SysProcAttr) (pty *os.File, err error) {
pty, tty, err := Open()
if err != nil {
return nil, err
}
defer tty.Close()
if sz != nil {
if err := Setsize(pty, sz); err != nil {
pty.Close()
return nil, err
}
}
if c.Stdout == nil {
c.Stdout = tty
}
if c.Stderr == nil {
c.Stderr = tty
}
if c.Stdin == nil {
c.Stdin = tty
}
c.SysProcAttr = attrs
if err := c.Start(); err != nil {
_ = pty.Close()
return nil, err
}
return pty, err
func Start(cmd *exec.Cmd) (Pty, error) {
return StartWithSize(cmd, nil)
}

69
vendor/github.com/creack/pty/run_unix.go generated vendored Normal file
View File

@@ -0,0 +1,69 @@
//go:build !windows
// +build !windows
package pty
import (
"os/exec"
"syscall"
)
// StartWithSize assigns a pseudo-terminal Tty to c.Stdin, c.Stdout,
// and c.Stderr, calls c.Start, and returns the File of the tty's
// corresponding Pty.
//
// This will resize the Pty to the specified size before starting the command.
// Starts the process in a new session and sets the controlling terminal.
func StartWithSize(c *exec.Cmd, sz *Winsize) (Pty, error) {
if c.SysProcAttr == nil {
c.SysProcAttr = &syscall.SysProcAttr{}
}
c.SysProcAttr.Setsid = true
c.SysProcAttr.Setctty = true
return StartWithAttrs(c, sz, c.SysProcAttr)
}
// StartWithAttrs assigns a pseudo-terminal Tty to c.Stdin, c.Stdout,
// and c.Stderr, calls c.Start, and returns the File of the tty's
// corresponding Pty.
//
// This will resize the Pty to the specified size before starting the command if a size is provided.
// The `attrs` parameter overrides the one set in c.SysProcAttr.
//
// This should generally not be needed. Used in some edge cases where it is needed to create a pty
// without a controlling terminal.
func StartWithAttrs(c *exec.Cmd, sz *Winsize, attrs *syscall.SysProcAttr) (Pty, error) {
pty, tty, err := open()
if err != nil {
return nil, err
}
defer func() {
// always close tty fds since it's being used in another process
// but pty is kept to resize tty
_ = tty.Close()
}()
if sz != nil {
if err := Setsize(pty, sz); err != nil {
_ = pty.Close()
return nil, err
}
}
if c.Stdout == nil {
c.Stdout = tty
}
if c.Stderr == nil {
c.Stderr = tty
}
if c.Stdin == nil {
c.Stdin = tty
}
c.SysProcAttr = attrs
if err := c.Start(); err != nil {
_ = pty.Close()
return nil, err
}
return pty, err
}

230
vendor/github.com/creack/pty/run_windows.go generated vendored Normal file
View File

@@ -0,0 +1,230 @@
//go:build windows
// +build windows
package pty
import (
"errors"
"fmt"
"os"
"os/exec"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
// StartWithSize assigns a pseudo-terminal Tty to c.Stdin, c.Stdout,
// and c.Stderr, calls c.Start, and returns the File of the tty's
// corresponding Pty.
//
// This will resize the Pty to the specified size before starting the command.
// Starts the process in a new session and sets the controlling terminal.
func StartWithSize(c *exec.Cmd, sz *Winsize) (Pty, error) {
return StartWithAttrs(c, sz, c.SysProcAttr)
}
// StartWithAttrs assigns a pseudo-terminal Tty to c.Stdin, c.Stdout,
// and c.Stderr, calls c.Start, and returns the File of the tty's
// corresponding Pty.
//
// This will resize the Pty to the specified size before starting the command if a size is provided.
// The `attrs` parameter overrides the one set in c.SysProcAttr.
//
// This should generally not be needed. Used in some edge cases where it is needed to create a pty
// without a controlling terminal.
func StartWithAttrs(c *exec.Cmd, sz *Winsize, attrs *syscall.SysProcAttr) (Pty, error) {
pty, _, err := open()
if err != nil {
return nil, err
}
defer func() {
// unlike unix command exec, do not close tty unless error happened
if err != nil {
_ = pty.Close()
}
}()
if sz != nil {
if err = Setsize(pty, sz); err != nil {
return nil, err
}
}
// unlike unix command exec, do not set stdin/stdout/stderr
c.SysProcAttr = attrs
// do not use os/exec.Start since we need to append console handler to startup info
w := windowExecCmd{
cmd: c,
waitCalled: false,
conPty: pty.(*WindowsPty),
}
err = w.Start()
if err != nil {
return nil, err
}
return pty, err
}
// Start the specified command but does not wait for it to complete.
//
// If Start returns successfully, the c.Process field will be set.
//
// The Wait method will return the exit code and release associated resources
// once the command exits.
func (c *windowExecCmd) Start() error {
if c.cmd.Process != nil {
return errors.New("exec: already started")
}
var argv0 = c.cmd.Path
var argv0p *uint16
var argvp *uint16
var dirp *uint16
var err error
sys := c.cmd.SysProcAttr
if sys == nil {
sys = &syscall.SysProcAttr{}
}
if c.cmd.Env == nil {
c.cmd.Env, err = execEnvDefault(sys)
if err != nil {
return err
}
}
var lp string
lp, err = lookExtensions(c.cmd.Path, c.cmd.Dir)
if err != nil {
return err
}
c.cmd.Path = lp
if len(c.cmd.Dir) != 0 {
// Windows CreateProcess looks for argv0 relative to the current
// directory, and, only once the new process is started, it does
// Chdir(attr.Dir). We are adjusting for that difference here by
// making argv0 absolute.
argv0, err = joinExeDirAndFName(c.cmd.Dir, c.cmd.Path)
if err != nil {
return err
}
}
argv0p, err = syscall.UTF16PtrFromString(argv0)
if err != nil {
return err
}
var cmdline string
// Windows CreateProcess takes the command line as a single string:
// use attr.CmdLine if set, else build the command line by escaping
// and joining each argument with spaces
if sys.CmdLine != "" {
cmdline = sys.CmdLine
} else {
cmdline = makeCmdLine(c.argv())
}
if len(cmdline) != 0 {
argvp, err = windows.UTF16PtrFromString(cmdline)
if err != nil {
return err
}
}
if len(c.cmd.Dir) != 0 {
dirp, err = windows.UTF16PtrFromString(c.cmd.Dir)
if err != nil {
return err
}
}
// Acquire the fork lock so that no other threads
// create new fds that are not yet close-on-exec
// before we fork.
syscall.ForkLock.Lock()
defer syscall.ForkLock.Unlock()
siEx := new(windows.StartupInfoEx)
siEx.Flags = windows.STARTF_USESTDHANDLES
if sys.HideWindow {
siEx.Flags |= syscall.STARTF_USESHOWWINDOW
siEx.ShowWindow = syscall.SW_HIDE
}
pi := new(windows.ProcessInformation)
// Need EXTENDED_STARTUPINFO_PRESENT as we're making use of the attribute list field.
flags := sys.CreationFlags | uint32(windows.CREATE_UNICODE_ENVIRONMENT) | windows.EXTENDED_STARTUPINFO_PRESENT
c.attrList, err = windows.NewProcThreadAttributeList(1)
if err != nil {
return fmt.Errorf("failed to initialize process thread attribute list: %w", err)
}
if c.conPty != nil {
if err = c.conPty.UpdateProcThreadAttribute(c.attrList); err != nil {
return err
}
}
siEx.ProcThreadAttributeList = c.attrList.List()
siEx.Cb = uint32(unsafe.Sizeof(*siEx))
if sys.Token != 0 {
err = windows.CreateProcessAsUser(
windows.Token(sys.Token),
argv0p,
argvp,
nil,
nil,
false,
flags,
createEnvBlock(addCriticalEnv(dedupEnvCase(true, c.cmd.Env))),
dirp,
&siEx.StartupInfo,
pi,
)
} else {
err = windows.CreateProcess(
argv0p,
argvp,
nil,
nil,
false,
flags,
createEnvBlock(addCriticalEnv(dedupEnvCase(true, c.cmd.Env))),
dirp,
&siEx.StartupInfo,
pi,
)
}
if err != nil {
return err
}
defer func() {
_ = windows.CloseHandle(pi.Thread)
}()
c.cmd.Process, err = os.FindProcess(int(pi.ProcessId))
if err != nil {
return err
}
return nil
}

View File

@@ -4,47 +4,59 @@
# Does not actually test the logic, just the compilation so we make sure we don't break code depending on the lib.
echo2() {
echo $@ >&2
echo $@ >&2
}
trap end 0
end() {
[ "$?" = 0 ] && echo2 "Pass." || (echo2 "Fail."; exit 1)
[ "$?" = 0 ] && echo2 "Pass." || (echo2 "Fail."; exit 1)
}
cross() {
os=$1
shift
echo2 "Build for $os."
for arch in $@; do
echo2 " - $os/$arch"
GOOS=$os GOARCH=$arch go build
done
echo2
os=$1
shift
echo2 "Build for $os."
for arch in $@; do
echo2 " - $os/$arch"
GOOS=$os GOARCH=$arch go build
done
echo2
}
set -e
cross linux amd64 386 arm arm64 ppc64 ppc64le s390x mips mipsle mips64 mips64le
cross darwin amd64 386 arm arm64
cross freebsd amd64 386 arm
cross netbsd amd64 386 arm
cross darwin amd64 arm64
cross freebsd amd64 386 arm arm64
cross netbsd amd64 386 arm arm64
cross openbsd amd64 386 arm arm64
cross dragonfly amd64
cross solaris amd64
# Not expected to work but should still compile.
cross windows amd64 386 arm
cross windows amd64 386 arm
# TODO: Fix compilation error on openbsd/arm.
# TODO: Merge the solaris PR.
# Some os/arch require a different compiler. Run in docker.
if ! hash docker; then
# If docker is not present, stop here.
return
# If docker is not present, stop here.
return
fi
echo2 "Build for linux."
echo2 " - linux/riscv"
docker build -t test -f Dockerfile.riscv .
docker build -t creack-pty-test -f Dockerfile.riscv .
# Golang dropped support for darwin 32bits since go1.15. Make sure the lib still compile with go1.14 on those archs.
echo2 "Build for darwin (32bits)."
echo2 " - darwin/386"
docker build -t creack-pty-test -f Dockerfile.golang --build-arg=GOVERSION=1.14 --build-arg=GOOS=darwin --build-arg=GOARCH=386 .
echo2 " - darwin/arm"
docker build -t creack-pty-test -f Dockerfile.golang --build-arg=GOVERSION=1.14 --build-arg=GOOS=darwin --build-arg=GOARCH=arm .
# Run a single test for an old go version. Would be best with go1.0, but not available on Dockerhub.
# Using 1.6 as it is the base version for the RISCV compiler.
# Would also be better to run all the tests, not just one, need to refactor this file to allow for specifc archs per version.
echo2 "Build for linux - go1.6."
echo2 " - linux/amd64"
docker build -t creack-pty-test -f Dockerfile.golang --build-arg=GOVERSION=1.6 --build-arg=GOOS=linux --build-arg=GOARCH=amd64 .

64
vendor/github.com/creack/pty/util.go generated vendored
View File

@@ -1,64 +0,0 @@
// +build !windows,!solaris
package pty
import (
"os"
"syscall"
"unsafe"
)
// InheritSize applies the terminal size of pty to tty. This should be run
// in a signal handler for syscall.SIGWINCH to automatically resize the tty when
// the pty receives a window size change notification.
func InheritSize(pty, tty *os.File) error {
size, err := GetsizeFull(pty)
if err != nil {
return err
}
err = Setsize(tty, size)
if err != nil {
return err
}
return nil
}
// Setsize resizes t to s.
func Setsize(t *os.File, ws *Winsize) error {
return windowRectCall(ws, t.Fd(), syscall.TIOCSWINSZ)
}
// GetsizeFull returns the full terminal size description.
func GetsizeFull(t *os.File) (size *Winsize, err error) {
var ws Winsize
err = windowRectCall(&ws, t.Fd(), syscall.TIOCGWINSZ)
return &ws, err
}
// Getsize returns the number of rows (lines) and cols (positions
// in each line) in terminal t.
func Getsize(t *os.File) (rows, cols int, err error) {
ws, err := GetsizeFull(t)
return int(ws.Rows), int(ws.Cols), err
}
// Winsize describes the terminal size.
type Winsize struct {
Rows uint16 // ws_row: Number of rows (in cells)
Cols uint16 // ws_col: Number of columns (in cells)
X uint16 // ws_xpixel: Width in pixels
Y uint16 // ws_ypixel: Height in pixels
}
func windowRectCall(ws *Winsize, fd, a2 uintptr) error {
_, _, errno := syscall.Syscall(
syscall.SYS_IOCTL,
fd,
a2,
uintptr(unsafe.Pointer(ws)),
)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
}

View File

@@ -1,51 +0,0 @@
//
package pty
import (
"os"
"golang.org/x/sys/unix"
)
const (
TIOCGWINSZ = 21608 // 'T' << 8 | 104
TIOCSWINSZ = 21607 // 'T' << 8 | 103
)
// Winsize describes the terminal size.
type Winsize struct {
Rows uint16 // ws_row: Number of rows (in cells)
Cols uint16 // ws_col: Number of columns (in cells)
X uint16 // ws_xpixel: Width in pixels
Y uint16 // ws_ypixel: Height in pixels
}
// GetsizeFull returns the full terminal size description.
func GetsizeFull(t *os.File) (size *Winsize, err error) {
var wsz *unix.Winsize
wsz, err = unix.IoctlGetWinsize(int(t.Fd()), TIOCGWINSZ)
if err != nil {
return nil, err
} else {
return &Winsize{wsz.Row, wsz.Col, wsz.Xpixel, wsz.Ypixel}, nil
}
}
// Get Windows Size
func Getsize(t *os.File) (rows, cols int, err error) {
var wsz *unix.Winsize
wsz, err = unix.IoctlGetWinsize(int(t.Fd()), TIOCGWINSZ)
if err != nil {
return 80, 25, err
} else {
return int(wsz.Row), int(wsz.Col), nil
}
}
// Setsize resizes t to s.
func Setsize(t *os.File, ws *Winsize) error {
wsz := unix.Winsize{ws.Rows, ws.Cols, ws.X, ws.Y}
return unix.IoctlSetWinsize(int(t.Fd()), TIOCSWINSZ, &wsz)
}

31
vendor/github.com/creack/pty/winsize.go generated vendored Normal file
View File

@@ -0,0 +1,31 @@
package pty
// Winsize describes the terminal size.
type Winsize struct {
Rows uint16 // ws_row: Number of rows (in cells)
Cols uint16 // ws_col: Number of columns (in cells)
X uint16 // ws_xpixel: Width in pixels
Y uint16 // ws_ypixel: Height in pixels
}
// InheritSize applies the terminal size of pty to tty. This should be run
// in a signal handler for syscall.SIGWINCH to automatically resize the tty when
// the pty receives a window size change notification.
func InheritSize(pty Pty, tty Tty) error {
size, err := GetsizeFull(pty)
if err != nil {
return err
}
if err := Setsize(tty, size); err != nil {
return err
}
return nil
}
// Getsize returns the number of rows (lines) and cols (positions
// in each line) in terminal t.
func Getsize(t FdHolder) (rows, cols int, err error) {
ws, err := GetsizeFull(t)
return int(ws.Rows), int(ws.Cols), err
}

26
vendor/github.com/creack/pty/winsize_unix.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
//go:build !windows
// +build !windows
package pty
import (
"syscall"
"unsafe"
)
// Setsize resizes t to s.
func Setsize(t FdHolder, ws *Winsize) error {
//nolint:gosec // Expected unsafe pointer for Syscall call.
return ioctl(t.Fd(), syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(ws)))
}
// GetsizeFull returns the full terminal size description.
func GetsizeFull(t FdHolder) (size *Winsize, err error) {
var ws Winsize
//nolint:gosec // Expected unsafe pointer for Syscall call.
if err := ioctl(t.Fd(), syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))); err != nil {
return nil, err
}
return &ws, nil
}

89
vendor/github.com/creack/pty/winsize_windows.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
//go:build windows
// +build windows
package pty
import (
"syscall"
"unsafe"
)
// types from golang.org/x/sys/windows
// copy of https://pkg.go.dev/golang.org/x/sys/windows#Coord
type windowsCoord struct {
X int16
Y int16
}
// copy of https://pkg.go.dev/golang.org/x/sys/windows#SmallRect
type windowsSmallRect struct {
Left int16
Top int16
Right int16
Bottom int16
}
// copy of https://pkg.go.dev/golang.org/x/sys/windows#ConsoleScreenBufferInfo
type windowsConsoleScreenBufferInfo struct {
Size windowsCoord
CursorPosition windowsCoord
Attributes uint16
Window windowsSmallRect
MaximumWindowSize windowsCoord
}
func (c windowsCoord) Pack() uintptr {
return uintptr((int32(c.Y) << 16) | int32(c.X))
}
// Setsize resizes t to ws.
func Setsize(t FdHolder, ws *Winsize) error {
var r0 uintptr
var err error
err = resizePseudoConsole.Find()
if err != nil {
return err
}
r0, _, err = resizePseudoConsole.Call(
t.Fd(),
(windowsCoord{X: int16(ws.Cols), Y: int16(ws.Rows)}).Pack(),
)
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
// S_OK: 0
return syscall.Errno(r0)
}
return nil
}
// GetsizeFull returns the full terminal size description.
func GetsizeFull(t FdHolder) (size *Winsize, err error) {
err = getConsoleScreenBufferInfo.Find()
if err != nil {
return nil, err
}
var info windowsConsoleScreenBufferInfo
var r0 uintptr
r0, _, err = getConsoleScreenBufferInfo.Call(t.Fd(), uintptr(unsafe.Pointer(&info)))
if int32(r0) < 0 {
if r0&0x1fff0000 == 0x00070000 {
r0 &= 0xffff
}
// S_OK: 0
return nil, syscall.Errno(r0)
}
return &Winsize{
Rows: uint16(info.Window.Bottom - info.Window.Top + 1),
Cols: uint16(info.Window.Right - info.Window.Left + 1),
}, nil
}

View File

@@ -1,3 +1,6 @@
//go:build 386
// +build 386
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go

View File

@@ -1,3 +1,6 @@
//go:build amd64
// +build amd64
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go

View File

@@ -1,3 +1,6 @@
//go:build arm
// +build arm
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go

View File

@@ -1,8 +1,9 @@
//go:build arm64
// +build arm64
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
// +build arm64
package pty
type (

View File

@@ -1,3 +1,6 @@
//go:build amd64 && dragonfly
// +build amd64,dragonfly
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_dragonfly.go

View File

@@ -1,3 +1,6 @@
//go:build 386 && freebsd
// +build 386,freebsd
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go

View File

@@ -1,3 +1,6 @@
//go:build amd64 && freebsd
// +build amd64,freebsd
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go

View File

@@ -1,3 +1,6 @@
//go:build arm && freebsd
// +build arm,freebsd
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go

View File

@@ -1,3 +1,6 @@
//go:build arm64 && freebsd
// +build arm64,freebsd
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs types_freebsd.go

14
vendor/github.com/creack/pty/ztypes_freebsd_ppc64.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types_freebsd.go
package pty
const (
_C_SPECNAMELEN = 0x3f
)
type fiodgnameArg struct {
Len int32
Pad_cgo_0 [4]byte
Buf *byte
}

12
vendor/github.com/creack/pty/ztypes_loong64.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
//go:build loong64
// +build loong64
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View File

@@ -1,9 +1,10 @@
//go:build (mips || mipsle || mips64 || mips64le) && linux
// +build mips mipsle mips64 mips64le
// +build linux
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
// +build linux
// +build mips mipsle mips64 mips64le
package pty
type (

View File

@@ -0,0 +1,17 @@
//go:build (386 || amd64 || arm || arm64) && netbsd
// +build 386 amd64 arm arm64
// +build netbsd
package pty
type ptmget struct {
Cfd int32
Sfd int32
Cn [1024]int8
Sn [1024]int8
}
var (
ioctl_TIOCPTSNAME = 0x48087448
ioctl_TIOCGRANTPT = 0x20007447
)

View File

@@ -1,13 +1,14 @@
//go:build (386 || amd64 || arm || arm64 || mips64) && openbsd
// +build 386 amd64 arm arm64 mips64
// +build openbsd
// +build 386 amd64 arm arm64
package pty
type ptmget struct {
Cfd int32
Sfd int32
Cn [16]int8
Sn [16]int8
Cfd int32
Sfd int32
Cn [16]int8
Sn [16]int8
}
var ioctl_PTMGET = 0x40287401

View File

@@ -1,3 +1,4 @@
//go:build ppc64
// +build ppc64
// Created by cgo -godefs - DO NOT EDIT

View File

@@ -1,3 +1,4 @@
//go:build ppc64le
// +build ppc64le
// Created by cgo -godefs - DO NOT EDIT

View File

@@ -1,8 +1,9 @@
//go:build riscv || riscv64
// +build riscv riscv64
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs types.go
// +build riscv riscv64
package pty
type (

View File

@@ -1,3 +1,4 @@
//go:build s390x
// +build s390x
// Created by cgo -godefs - DO NOT EDIT

3
vendor/modules.txt vendored
View File

@@ -13,7 +13,7 @@ github.com/cli/safeexec
# github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
## explicit
github.com/cloudfoundry/jibber_jabber
# github.com/creack/pty v1.1.11
# github.com/creack/pty v1.1.11 => github.com/photostorm/pty v1.1.19-0.20230324012736-6794a5ba0ba0
## explicit; go 1.13
github.com/creack/pty
# github.com/davecgh/go-spew v1.1.1
@@ -320,3 +320,4 @@ gopkg.in/warnings.v0
# gopkg.in/yaml.v3 v3.0.1
## explicit
gopkg.in/yaml.v3
# github.com/creack/pty => github.com/photostorm/pty v1.1.19-0.20230324012736-6794a5ba0ba0