Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ jobs:
run: |
mvn -q -Pdeveloper -pl developer -Ddeploydb
mvn -q -Pdeveloper -pl developer -Ddeploydb-simulator
python3 -m pip install --user --upgrade tools/marvin/dist/Marvin-*.tar.gz
Comment thread
vishesh92 marked this conversation as resolved.
python3 -m pip install --user --upgrade tools/marvin/dist/[mM]arvin-*.tar.gz

- name: Start CloudStack mgmt (Jetty) in background
id: start_ms
Expand Down
3 changes: 2 additions & 1 deletion .rat-excludes
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ apache-rat-0.17
go.sum
rat-report.txt
/snap/snapcraft.yaml
vendor
vendor
**/*.patch
Comment thread
vishesh92 marked this conversation as resolved.
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ run: all
debug:
$(GO) build -mod=vendor -gcflags='-N -l' -o cmk && dlv --listen=:2345 --headless=true --api-version=2 exec ./cmk

.PHONY: vendor-patch
vendor-patch: ## Refresh vendor/ and re-apply the local patches in patches/
$(info $(M) Refreshing vendor/ and re-applying local patches…)
$Q $(GO) mod vendor
$Q for p in patches/*.patch; do git apply "$$p" || exit 1; done
$Q echo "$(M) Done! See patches/README.md"

.PHONY: vendor-check
vendor-check: ## Verify vendor/ still carries the local patches in patches/
$(info $(M) Checking local patches are present in vendor/…)
$Q for p in patches/*.patch; do \
git apply --reverse --check "$$p" || { \
echo "ERROR: $$p is not applied to vendor/; run 'make vendor-patch'"; exit 1; }; \
done
$Q echo "$(M) All local patches present"

dist-mkdir: all
rm -fr dist
mkdir -p dist
Expand Down
2 changes: 1 addition & 1 deletion cli/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"strings"

"github.com/apache/cloudstack-cloudmonkey/config"
"github.com/chzyer/readline"
"github.com/ergochat/readline"
)

// CLI config instance
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ go 1.20

require (
github.com/briandowns/spinner v1.22.0
github.com/chzyer/readline v1.5.1
github.com/ergochat/readline v0.1.3
github.com/gofrs/flock v0.8.1
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/mitchellh/go-homedir v1.1.0
Expand All @@ -37,7 +37,8 @@ require (
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/stretchr/testify v1.8.2 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.5.0 // indirect
golang.org/x/text v0.9.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
15 changes: 6 additions & 9 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
github.com/briandowns/spinner v1.22.0 h1:fJ/7tyeM2q9ebM57kGfjnUSrgPJBsULk+/s61UpMGrw=
github.com/briandowns/spinner v1.22.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
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=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ergochat/readline v0.1.3 h1:/DytGTmwdUJcLAe3k3VJgowh5vNnsdifYT6uVaf4pSo=
github.com/ergochat/readline v0.1.3/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
Expand Down Expand Up @@ -44,12 +40,13 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
91 changes: 91 additions & 0 deletions patches/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# Local patches to vendored dependencies

Some files under `vendor/` carry local changes that are **not** present in the
upstream release. Running `go mod vendor` reverts them silently — the build
still succeeds, but the resulting binary loses the fixes. Every local change
must therefore be recorded here.

After any `go mod vendor`, re-apply the patches:

make vendor-patch

which is equivalent to:

go mod vendor
git apply patches/*.patch

## `ergochat-readline-v0.1.3.patch`

Applies to `github.com/ergochat/readline` v0.1.3. Three changes:

### 1. `complete.go` — insert the value, display the detail

CloudMonkey builds completion candidates as `<value> (<detail>)` so the user
can see what a UUID refers to (see `cli/completer.go`). Upstream's
`AutoCompleter.Do` returns a single slice that is used both for display *and*
for insertion, so without this patch the ` (<detail>)` suffix is written into
the command line.

`writeRunes` strips everything from the ` … (` boundary before inserting, and
`truncateBufferAfterLastEqual` drops the partial text after the last `=` when
the candidate matched on its detail rather than on its value.

Originally added for #133 and #196 against `chzyer/readline`; forward-ported
when the library was switched to `ergochat/readline`.

### 2. `complete.go` — redraw the prompt in `CompleteRefresh`

The `\033[J` erase issued while rendering the candidate grid can clear the
prompt line, leaving the prompt blank until the next keystroke. The patch
redraws the prompt and buffer contents after the erase.

This is an upstream rendering bug, not a CloudMonkey-specific need.

### 3. `operation.go` — bound the cursor position query

`Runes()` calls `getAndSetOffset(nil)` before printing every prompt, which
sends a DSR cursor position request (`ESC[6n`) and blocks on the reply with a
nil deadline — i.e. forever. Under a pty whose emulator never answers
(`expect`, `pexpect`, Ansible with a pty, some CI runners) `cmk` prints its
banner and then hangs with no prompt.

The patch passes a deadline of `dsrTimeout` (250ms, the constant upstream
already uses in `waitForDSR`). On expiry `GetCursorPosition` returns an error,
`getAndSetOffset` leaves the offset unchanged and the prompt is printed anyway.
A late CPR reply is still recognised and discarded by `consumeANSIEscape`, so
it is never mistaken for user input.

The timeout would otherwise be paid before *every* prompt, so the result is
latched: on `deadlineExceeded` the `dsrUnsupported` flag is set and no further
queries are made for the life of the session. A terminal that answers — even
one that takes 150ms to do so — never sets the flag and keeps full offset
tracking on every prompt. The tradeoff is that a terminal which misses one
query and then recovers stays latched off for the session; the consequence is
cosmetic (the prompt may overwrite pre-existing text on the line) and real
emulators answer CPR synchronously, so a one-off miss is not expected.

## Upstream status

None of these are fixed in `ergochat/readline` v0.1.3, which has been the
latest release since September 2024. Changes 2 and 3 are general bugs and
should be reported upstream so this patch can shrink; change 1 needs an
upstream API that separates a candidate's display text from its insert text.
175 changes: 175 additions & 0 deletions patches/ergochat-readline-v0.1.3.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
--- a/vendor/github.com/ergochat/readline/complete.go
+++ b/vendor/github.com/ergochat/readline/complete.go
@@ -4,6 +4,7 @@
"bufio"
"bytes"
"fmt"
+ "strings"
"sync/atomic"

"github.com/ergochat/readline/internal/platform"
@@ -46,9 +47,38 @@
}
}

+func (o *opCompleter) truncateBufferAfterLastEqual(completion []rune) {
+ bufRunes := o.op.buf.Runes()
+ for i := len(bufRunes) - 1; i >= 0; i-- {
+ if bufRunes[i] == '=' {
+ prefix := bufRunes[i+1:] // part after '=' in buffer
+ if len(prefix) > 0 && len(completion) >= len(prefix) && string(completion[:len(prefix)]) == string(prefix) {
+ o.op.buf.Set(bufRunes[:i+1]) // Keep content till '='
+ }
+ break
+ }
+ }
+}
+
+func (o *opCompleter) writeRunes(candidate []rune) {
+ selected := candidate
+ spaceFound := false
+ for idx, r := range candidate {
+ if r == ' ' {
+ spaceFound = true
+ }
+ if spaceFound && r == '(' {
+ o.truncateBufferAfterLastEqual(candidate[idx+1:])
+ selected = candidate[:idx]
+ break
+ }
+ }
+ o.op.buf.WriteRunes(selected)
+}
+
func (o *opCompleter) doSelect() {
if len(o.candidate) == 1 {
- o.op.buf.WriteRunes(o.candidate[0])
+ o.writeRunes(o.candidate[0])
o.ExitCompleteMode(false)
return
}
@@ -232,7 +262,7 @@
if !o.IsInCompleteMode() {
if len(newLines) == 1 {
// not yet in complete mode but only 1 candidate so complete it
- buf.WriteRunes(newLines[0])
+ o.writeRunes(newLines[0])
o.ExitCompleteMode(false)
return true
}
@@ -264,7 +294,7 @@
switch r {
case CharEnter, CharCtrlJ:
next = false
- o.op.buf.WriteRunes(o.candidate[o.candidateChoice])
+ o.writeRunes(o.candidate[o.candidateChoice])
o.ExitCompleteMode(false)
case CharLineStart:
o.lineStart()
@@ -459,6 +489,33 @@

// wrote out choices over "lines", move back to cursor (positioned at index)
fmt.Fprintf(buf, "\033[%dA", lines)
+
+ // Redraw the prompt and buffer since \033[J may have cleared them
+ // Calculate which line the cursor is on (0-indexed, where 0 is the prompt line)
+ cursorLine := o.op.buf.IdxLine(tWidth)
+
+ // Move to the beginning of the prompt line (line 0)
+ if cursorLine > 0 {
+ fmt.Fprintf(buf, "\033[%dA", cursorLine)
+ }
+ // Move to column 1 to redraw from the start
+ buf.WriteString("\033[1G")
+ // Redraw prompt and buffer content
+ cfg := o.op.GetConfig()
+ buf.WriteString(cfg.Prompt)
+ buf.WriteString("\x1b[0K") // Clear line from cursor right
+ rs := o.op.buf.Runes()
+ for _, e := range cfg.Painter(rs, o.op.buf.Pos()) {
+ if e == '\t' {
+ buf.WriteString(strings.Repeat(" ", runes.TabWidth))
+ } else {
+ buf.WriteRune(e)
+ }
+ }
+ // Now position cursor correctly - move back down to the cursor line
+ if cursorLine > 0 {
+ fmt.Fprintf(buf, "\033[%dB", cursorLine)
+ }
buf.Write(o.op.buf.getBackspaceSequence())
buf.Flush()
}
--- a/vendor/github.com/ergochat/readline/operation.go
+++ b/vendor/github.com/ergochat/readline/operation.go
@@ -5,6 +5,7 @@
"io"
"sync"
"sync/atomic"
+ "time"

"github.com/ergochat/readline/internal/platform"
"github.com/ergochat/readline/internal/runes"
@@ -23,6 +24,12 @@

isPrompting bool // true when prompt written and waiting for input

+ // dsrUnsupported is set once a cursor position query has timed out. The
+ // terminal is then assumed not to implement DSR and no further queries
+ // are made, so the timeout is paid at most once per session rather than
+ // before every prompt. Read and written under m, via getAndSetOffset.
+ dsrUnsupported bool
+
history *opHistory
search *opSearch
completer *opCompleter
@@ -419,7 +426,10 @@
// Query cursor position before printing the prompt as there
// may be existing text on the same line that ideally we don't
// want to overwrite and cause prompt to jump left.
- o.getAndSetOffset(nil)
+ // Bound the query: a pty that never answers the DSR request (expect,
+ // pexpect, Ansible with a pty, some CI runners) would otherwise block
+ // here forever and no prompt would ever be printed.
+ o.getAndSetOffset(newDSRDeadline())
o.buf.Print() // print prompt & buffer contents
// Prompt written safely, unlock until read completes and then
// lock again to unset.
@@ -440,7 +450,7 @@
}

func (o *operation) getAndSetOffset(deadline chan struct{}) {
- if !o.GetConfig().isInteractive {
+ if !o.GetConfig().isInteractive || o.dsrUnsupported {
return
}

@@ -451,11 +461,27 @@
// TODO ???
o.t.Write([]byte(" \b"))

- if offset, err := o.t.GetCursorPosition(deadline); err == nil {
+ offset, err := o.t.GetCursorPosition(deadline)
+ switch {
+ case err == nil:
o.buf.SetOffset(offset)
+ case errors.Is(err, deadlineExceeded):
+ // The terminal did not answer in time; assume it never will and
+ // stop querying, so the timeout is not paid before every prompt.
+ o.dsrUnsupported = true
}
}

+// newDSRDeadline returns a channel that closes once dsrTimeout has elapsed,
+// bounding how long a cursor position query will wait for the terminal to
+// answer. On expiry GetCursorPosition returns an error, the offset is left
+// unchanged and the prompt is printed anyway.
+func newDSRDeadline() chan struct{} {
+ deadline := make(chan struct{})
+ time.AfterFunc(dsrTimeout, func() { close(deadline) })
+ return deadline
+}
+
func (o *operation) GenPasswordConfig() *Config {
baseConfig := o.GetConfig()
return &Config{
5 changes: 3 additions & 2 deletions snap/snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ parts:
cd "$CRAFT_PART_SRC"
SHORT_SHA="$(cat .remote-sha 2>/dev/null || echo unknown)"
BUILD_DATE="$(date +%FT%T%z)"
go mod vendor || true
go build -trimpath \
# Do not run "go mod vendor" here: vendor/ is committed and carries local
# patches that regenerating it would silently revert. See patches/README.md.
go build -trimpath -mod=vendor \
Comment thread
vishesh92 marked this conversation as resolved.
-ldflags "-s -w -X main.GitSHA=${SHORT_SHA} -X main.BuildDate=${BUILD_DATE}" \
-o cmk cmk.go
install -Dm0755 cmk "$CRAFT_PART_INSTALL/bin/cmk"
Expand Down
8 changes: 0 additions & 8 deletions vendor/github.com/chzyer/readline/.travis.yml

This file was deleted.

Loading
Loading