mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
use statediff for output for setenv, unset, and cd
This commit is contained in:
+70
-14
@@ -499,9 +499,7 @@ func UnSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var cmdOutput bytes.Buffer
|
var cmdOutput bytes.Buffer
|
||||||
for varName, _ := range unsetVars {
|
displayStateUpdate(&cmdOutput, *ids.Remote.RemoteState, remoteInst.State)
|
||||||
cmdOutput.WriteString(fmt.Sprintf("unset %s\r\n", shellescape.Quote(varName)))
|
|
||||||
}
|
|
||||||
cmd, err := makeStaticCmd(ctx, "unset", ids, pk.GetRawStr(), cmdOutput.Bytes())
|
cmd, err := makeStaticCmd(ctx, "unset", ids, pk.GetRawStr(), cmdOutput.Bytes())
|
||||||
update, err := addLineForCmd(ctx, "/unset", false, ids, cmd)
|
update, err := addLineForCmd(ctx, "/unset", false, ids, cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -926,17 +924,20 @@ func SetEnvCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor
|
|||||||
}
|
}
|
||||||
state := *ids.Remote.RemoteState
|
state := *ids.Remote.RemoteState
|
||||||
state.Env0 = shexec.MakeEnv0(envMap)
|
state.Env0 = shexec.MakeEnv0(envMap)
|
||||||
remote, err := sstore.UpdateRemoteState(ctx, ids.SessionId, ids.WindowId, ids.Remote.RemotePtr, state)
|
remoteInst, err := sstore.UpdateRemoteState(ctx, ids.SessionId, ids.WindowId, ids.Remote.RemotePtr, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
update := sstore.ModelUpdate{
|
var cmdOutput bytes.Buffer
|
||||||
Sessions: sstore.MakeSessionsUpdateForRemote(ids.SessionId, remote),
|
displayStateUpdate(&cmdOutput, *ids.Remote.RemoteState, remoteInst.State)
|
||||||
Info: &sstore.InfoMsgType{
|
cmd, err := makeStaticCmd(ctx, "setenv", ids, pk.GetRawStr(), cmdOutput.Bytes())
|
||||||
InfoMsg: fmt.Sprintf("[%s] set vars: %s", ids.Remote.DisplayName, formatStrs(mapToStrs(setVars), "and", false)),
|
update, err := addLineForCmd(ctx, "/setenv", false, ids, cmd)
|
||||||
TimeoutMs: 2000,
|
if err != nil {
|
||||||
},
|
// TODO tricky error since the command was a success, but we can't show the output
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
update.Interactive = pk.Interactive
|
||||||
|
update.Sessions = sstore.MakeSessionsUpdateForRemote(ids.SessionId, remoteInst)
|
||||||
return update, nil
|
return update, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1020,8 +1021,9 @@ func CdCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cmdOutput := fmt.Sprintf("cwd = %s", shellescape.Quote(newDir))
|
var cmdOutput bytes.Buffer
|
||||||
cmd, err := makeStaticCmd(ctx, "cd", ids, pk.GetRawStr(), []byte(cmdOutput))
|
displayStateUpdate(&cmdOutput, *ids.Remote.RemoteState, remoteInst.State)
|
||||||
|
cmd, err := makeStaticCmd(ctx, "cd", ids, pk.GetRawStr(), cmdOutput.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO tricky error since the command was a success, but we can't show the output
|
// TODO tricky error since the command was a success, but we can't show the output
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1542,7 +1544,7 @@ func HistoryCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ssto
|
|||||||
return nil, fmt.Errorf("invalid maxitems value '%s' (must be a number): %v", pk.Kwargs["maxitems"], err)
|
return nil, fmt.Errorf("invalid maxitems value '%s' (must be a number): %v", pk.Kwargs["maxitems"], err)
|
||||||
}
|
}
|
||||||
if maxItems < 0 {
|
if maxItems < 0 {
|
||||||
return nil, fmt.Errorf("invalid maxitems value '%s' (cannot be negative)", maxItems)
|
return nil, fmt.Errorf("invalid maxitems value '%d' (cannot be negative)", maxItems)
|
||||||
}
|
}
|
||||||
if maxItems == 0 {
|
if maxItems == 0 {
|
||||||
maxItems = DefaultMaxHistoryItems
|
maxItems = DefaultMaxHistoryItems
|
||||||
@@ -1678,7 +1680,7 @@ func LineShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sst
|
|||||||
ts := time.UnixMilli(line.Ts)
|
ts := time.UnixMilli(line.Ts)
|
||||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "ts", ts.Format("2006-01-02 15:04:05")))
|
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "ts", ts.Format("2006-01-02 15:04:05")))
|
||||||
if line.Ephemeral {
|
if line.Ephemeral {
|
||||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "ephemeral", true))
|
buf.WriteString(fmt.Sprintf(" %-15s %v\n", "ephemeral", true))
|
||||||
}
|
}
|
||||||
if cmd != nil {
|
if cmd != nil {
|
||||||
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "cmdid", cmd.CmdId))
|
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "cmdid", cmd.CmdId))
|
||||||
@@ -1757,3 +1759,57 @@ func formatTextTable(totalCols int, data [][]string, colMeta []ColMeta) []string
|
|||||||
}
|
}
|
||||||
return rtn
|
return rtn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func displayStateUpdate(buf *bytes.Buffer, oldState packet.ShellState, newState packet.ShellState) {
|
||||||
|
if newState.Cwd != oldState.Cwd {
|
||||||
|
buf.WriteString(fmt.Sprintf("cwd %s\r\n", newState.Cwd))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(newState.Env0, oldState.Env0) {
|
||||||
|
newEnvMap := shexec.ParseEnv0(newState.Env0)
|
||||||
|
oldEnvMap := shexec.ParseEnv0(oldState.Env0)
|
||||||
|
for key, newVal := range newEnvMap {
|
||||||
|
oldVal, found := oldEnvMap[key]
|
||||||
|
if !found || oldVal != newVal {
|
||||||
|
buf.WriteString(fmt.Sprintf("%s=%s\r\n", shellescape.Quote(key), shellescape.Quote(newVal)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key, _ := range oldEnvMap {
|
||||||
|
_, found := newEnvMap[key]
|
||||||
|
if !found {
|
||||||
|
buf.WriteString(fmt.Sprintf("unset %s\r\n", shellescape.Quote(key)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newState.Aliases != oldState.Aliases {
|
||||||
|
newAliasMap, _ := ParseAliases(newState.Aliases)
|
||||||
|
oldAliasMap, _ := ParseAliases(oldState.Aliases)
|
||||||
|
for aliasName, newAliasVal := range newAliasMap {
|
||||||
|
oldAliasVal, found := oldAliasMap[aliasName]
|
||||||
|
if !found || newAliasVal != oldAliasVal {
|
||||||
|
buf.WriteString(fmt.Sprintf("alias %s\n", shellescape.Quote(aliasName)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for aliasName, _ := range oldAliasMap {
|
||||||
|
_, found := newAliasMap[aliasName]
|
||||||
|
if !found {
|
||||||
|
buf.WriteString(fmt.Sprintf("unalias %s\r\n", shellescape.Quote(aliasName)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newState.Funcs != oldState.Funcs {
|
||||||
|
newFuncMap, _ := ParseFuncs(newState.Funcs)
|
||||||
|
oldFuncMap, _ := ParseFuncs(oldState.Funcs)
|
||||||
|
for funcName, newFuncVal := range newFuncMap {
|
||||||
|
oldFuncVal, found := oldFuncMap[funcName]
|
||||||
|
if !found || newFuncVal != oldFuncVal {
|
||||||
|
buf.WriteString(fmt.Sprintf("function %s\n", shellescape.Quote(funcName)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for funcName, _ := range oldFuncMap {
|
||||||
|
_, found := newFuncMap[funcName]
|
||||||
|
if !found {
|
||||||
|
buf.WriteString(fmt.Sprintf("unset -f %s\r\n", shellescape.Quote(funcName)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -217,3 +217,99 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType)
|
|||||||
}
|
}
|
||||||
return rtnPk, nil
|
return rtnPk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseAliasStmt(stmt *syntax.Stmt) (string, string, error) {
|
||||||
|
cmd := stmt.Cmd
|
||||||
|
callExpr, ok := cmd.(*syntax.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
return "", "", fmt.Errorf("wrong cmd type for alias")
|
||||||
|
}
|
||||||
|
if len(callExpr.Args) != 2 {
|
||||||
|
return "", "", fmt.Errorf("wrong number of words in alias expr wordslen=%d", len(callExpr.Args))
|
||||||
|
}
|
||||||
|
firstWord := callExpr.Args[0]
|
||||||
|
if firstWord.Lit() != "alias" {
|
||||||
|
return "", "", fmt.Errorf("invalid alias cmd word (not 'alias')")
|
||||||
|
}
|
||||||
|
secondWord := callExpr.Args[1]
|
||||||
|
val, err := quotedLitToStr(secondWord)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
eqIdx := strings.Index(val, "=")
|
||||||
|
if eqIdx == -1 {
|
||||||
|
return "", "", fmt.Errorf("no '=' in alias definition")
|
||||||
|
}
|
||||||
|
return val[0:eqIdx], val[eqIdx+1:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func quotedLitToStr(word *syntax.Word) (string, error) {
|
||||||
|
cfg := &expand.Config{
|
||||||
|
Env: &parseEnviron{Env: make(map[string]string)},
|
||||||
|
GlobStar: false,
|
||||||
|
NullGlob: false,
|
||||||
|
NoUnset: false,
|
||||||
|
CmdSubst: func(w io.Writer, word *syntax.CmdSubst) error { return doCmdSubst("", w, word) },
|
||||||
|
ProcSubst: doProcSubst,
|
||||||
|
ReadDir: nil,
|
||||||
|
}
|
||||||
|
return expand.Literal(cfg, word)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseAliases(aliases string) (map[string]string, error) {
|
||||||
|
r := strings.NewReader(aliases)
|
||||||
|
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
||||||
|
file, err := parser.Parse(r, "aliases")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rtn := make(map[string]string)
|
||||||
|
for _, stmt := range file.Stmts {
|
||||||
|
aliasName, aliasVal, err := parseAliasStmt(stmt)
|
||||||
|
if err != nil {
|
||||||
|
// fmt.Printf("stmt-err: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if aliasName != "" {
|
||||||
|
rtn[aliasName] = aliasVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rtn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFuncStmt(stmt *syntax.Stmt, source string) (string, string, error) {
|
||||||
|
cmd := stmt.Cmd
|
||||||
|
funcDecl, ok := cmd.(*syntax.FuncDecl)
|
||||||
|
if !ok {
|
||||||
|
return "", "", fmt.Errorf("cmd not FuncDecl")
|
||||||
|
}
|
||||||
|
name := funcDecl.Name.Value
|
||||||
|
// fmt.Printf("func: [%s]\n", name)
|
||||||
|
funcBody := funcDecl.Body
|
||||||
|
// fmt.Printf(" %d:%d\n", funcBody.Cmd.Pos().Offset(), funcBody.Cmd.End().Offset())
|
||||||
|
bodyStr := source[funcBody.Cmd.Pos().Offset():funcBody.Cmd.End().Offset()]
|
||||||
|
// fmt.Printf("<<<\n%s\n>>>\n", bodyStr)
|
||||||
|
// fmt.Printf("\n")
|
||||||
|
return name, bodyStr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseFuncs(funcs string) (map[string]string, error) {
|
||||||
|
r := strings.NewReader(funcs)
|
||||||
|
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
||||||
|
file, err := parser.Parse(r, "funcs")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rtn := make(map[string]string)
|
||||||
|
for _, stmt := range file.Stmts {
|
||||||
|
funcName, funcVal, err := parseFuncStmt(stmt, funcs)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("stmt-err: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if funcName != "" {
|
||||||
|
rtn[funcName] = funcVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rtn, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user