From 4b37df12e3289bf8fd6cf7dc88fd9c12330a682f Mon Sep 17 00:00:00 2001 From: shylinux Date: Sat, 15 Apr 2023 00:26:30 +0800 Subject: [PATCH] add git --- base/ssh/script.go | 20 ++-- base/web/dream.go | 1 + core/code/go.go | 17 ++-- core/code/js.go | 7 +- core/code/xterm.go | 8 +- core/team/plan.go | 6 ++ core/wiki/draw.go | 2 +- go.mod | 1 - go.sum | 2 - misc/git/git.go | 6 ++ misc/git/repos.go | 30 ++---- misc/git/status.go | 161 +++++------------------------- misc/git/total.go | 2 +- misc/xterm/pty_darwin.go | 65 ++++++++++++ misc/xterm/pty_linux.go | 47 +++++++++ misc/xterm/winsize_unix.go | 27 +++++ misc/xterm/winsize_unsupported.go | 12 +++ misc/xterm/xterm.go | 22 +++- 18 files changed, 248 insertions(+), 188 deletions(-) create mode 100644 misc/xterm/pty_darwin.go create mode 100644 misc/xterm/pty_linux.go create mode 100644 misc/xterm/winsize_unix.go create mode 100644 misc/xterm/winsize_unsupported.go diff --git a/base/ssh/script.go b/base/ssh/script.go index f87dce65..25bfa763 100644 --- a/base/ssh/script.go +++ b/base/ssh/script.go @@ -146,11 +146,19 @@ func (f *Frame) Begin(m *ice.Message, arg ...string) { func (f *Frame) Start(m *ice.Message, arg ...string) { m.Optionv(FRAME, f) switch f.source = kit.Select(STDIO, arg, 0); f.source { - case STDIO: + case WEBIO: + wio := m.Optionv(WEBIO).(io.ReadWriter) + r, w, _ := os.Pipe() + go func() { io.Copy(w, wio) }() + f.pipe, f.stdin, f.stdout = w, r, wio kit.If(f.target == nil, func() { f.target = m.Target() }) + m.Optionv(ice.MSG_OPTS, ice.MSG_USERNAME, ice.MSG_USERROLE) + f.scan(m, STDIO, "") + case STDIO: r, w, _ := os.Pipe() go func() { io.Copy(w, os.Stdin) }() f.pipe, f.stdin, f.stdout = w, r, os.Stdout + kit.If(f.target == nil, func() { f.target = m.Target() }) m.Optionv(ice.MSG_OPTS, ice.MSG_USERNAME, ice.MSG_USERROLE) f.scan(m, STDIO, "") default: @@ -162,15 +170,14 @@ func (f *Frame) Start(m *ice.Message, arg ...string) { } } } - m.Option(ice.MSG_SCRIPT, f.source) - f.target = m.Source() - if msg := m.Cmd(nfs.CAT, f.source); msg.IsErr() { + if msg := m.Cmd(nfs.CAT, m.Option(ice.MSG_SCRIPT, f.source)); msg.IsErr() { return } else { buf := bytes.NewBuffer(make([]byte, 0, ice.MOD_BUFS)) f.stdin, f.stdout = bytes.NewBufferString(msg.Result()), buf defer func() { m.Echo(buf.String()) }() } + f.target = m.Source() f.scan(m, "", "") } } @@ -183,6 +190,7 @@ func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server const ( FRAME = "frame" SHELL = "shell" + WEBIO = "webio" STDIO = "stdio" PS1 = "PS1" PS2 = "PS2" @@ -215,8 +223,8 @@ func init() { } }}, PROMPT: {Name: "prompt arg run", Help: "命令提示", Actions: ctx.ConfAction( - PS1, ice.List{"\033[33;44m", mdb.COUNT, "@", tcp.HOSTNAME, "[", mdb.TIME, "]", "\033[5m", TARGET, "\033[0m", "\033[44m", ">", "\033[0m ", "\033[?25h", "\033[32m"}, - PS2, ice.List{mdb.COUNT, " ", TARGET, "> "}, + PS1, ice.List{"\033[33;44m", mdb.COUNT, ice.AT, tcp.HOSTNAME, "[", mdb.TIME, "]", "\033[5m", TARGET, "\033[0m", "\033[44m", ">", "\033[0m ", "\033[?25h", "\033[32m"}, + PS2, ice.List{mdb.COUNT, ice.SP, TARGET, "> "}, ), Hand: func(m *ice.Message, arg ...string) { if f, ok := m.Target().Server().(*Frame); ok { f.prompt(m, arg...) diff --git a/base/web/dream.go b/base/web/dream.go index 898ac43d..29745436 100644 --- a/base/web/dream.go +++ b/base/web/dream.go @@ -140,6 +140,7 @@ func init() { }, ctx.CmdAction(), DreamAction()), Hand: func(m *ice.Message, arg ...string) { if len(arg) == 0 { _dream_list(m) + ctx.DisplayTableCard(m) } else if arg[0] == ctx.ACTION { gdb.Event(m, DREAM_ACTION, arg) } else { diff --git a/core/code/go.go b/core/code/go.go index 000e208a..ffc3d0ad 100644 --- a/core/code/go.go +++ b/core/code/go.go @@ -9,7 +9,6 @@ import ( "shylinux.com/x/icebergs/base/ctx" "shylinux.com/x/icebergs/base/mdb" "shylinux.com/x/icebergs/base/nfs" - "shylinux.com/x/icebergs/base/ssh" "shylinux.com/x/icebergs/base/yac" kit "shylinux.com/x/toolkits" ) @@ -101,6 +100,11 @@ func init() { Index.MergeCommands(ice.Commands{ GO: {Name: "go path auto", Help: "后端编程", Actions: ice.MergeActions(ice.Actions{ mdb.RENDER: {Hand: func(m *ice.Message, arg ...string) { + if arg[1] == "main.go" { + // ProcessXterm(m, ssh.WEBIO, "", arg[1]) + ProcessXterm(m, "ice.bin source stdio", "", arg[1]) + return + } ctx.ProcessCommand(m, yac.STACK, kit.Simple(path.Join(arg[2], arg[1]))) return cmds, text := "ice.bin source stdio", ctx.GetFileCmd(path.Join(arg[2], arg[1])) @@ -113,18 +117,15 @@ func init() { ProcessXterm(m, cmds, text, arg[1]) }}, mdb.ENGINE: {Hand: func(m *ice.Message, arg ...string) { + if cmd := ctx.GetFileCmd(path.Join(arg[2], arg[1])); cmd != "" { + ctx.ProcessCommand(m, cmd, kit.Simple()) + return + } if msg := m.Cmd(yac.STACK, path.Join(arg[2], arg[1])); msg.Option("__index") != "" { ctx.ProcessCommand(m, msg.Option("__index"), kit.Simple()) } else { ctx.ProcessCommand(m, yac.STACK, kit.Simple(path.Join(arg[2], arg[1]))) } - return - if cmd := ctx.GetFileCmd(path.Join(arg[2], arg[1])); cmd != "" { - ctx.ProcessCommand(m, cmd, kit.Simple()) - } else { - cmds := []string{GO, ice.RUN, path.Join(arg[2], arg[1])} - m.Cmdy(cli.SYSTEM, cmds).StatusTime(ssh.SHELL, strings.Join(cmds, ice.SP)) - } }}, TEMPLATE: {Hand: func(m *ice.Message, arg ...string) { m.Echo(nfs.Template(m, "demo.go"), path.Base(path.Dir(path.Join(arg[2], arg[1])))) diff --git a/core/code/js.go b/core/code/js.go index e2898080..d061ae1a 100644 --- a/core/code/js.go +++ b/core/code/js.go @@ -17,16 +17,17 @@ func init() { Index.MergeCommands(ice.Commands{ JS: {Name: "js path auto", Help: "前端", Actions: ice.MergeActions(ice.Actions{ mdb.RENDER: {Hand: func(m *ice.Message, arg ...string) { + if arg[1] == "main.js" { + m.EchoIFrame(nfs.PS) + return + } ProcessXterm(m, "node", kit.Format(`require("./usr/volcanos/proto.js"), require("./usr/volcanos/publish/client/nodejs/proto.js"), Volcanos.meta._main("%s")`, path.Join(nfs.PS, arg[2], arg[1]))) }}, mdb.ENGINE: {Hand: func(m *ice.Message, arg ...string) { if arg[2] == ice.USR_VOLCANOS { - m.Debug("what %v %v", arg[1], ice.PLUGIN_LOCAL) if strings.HasPrefix(arg[1], "plugin/local/") { ctx.ProcessCommand(m, kit.Select(ice.CAN_PLUGIN, "web."+strings.Replace(strings.TrimSuffix(strings.TrimPrefix(arg[1], "plugin/local/"), nfs.PT+JS), nfs.PS, nfs.PT, -1)), kit.Simple()) - m.Debug("what %v", m.FormatMeta()) } - m.Debug("what %v", m.FormatMeta()) } else { ctx.DisplayBase(m, require(arg[2], arg[1])) ctx.ProcessCommand(m, kit.Select(ice.CAN_PLUGIN, ctx.GetFileCmd(kit.ExtChange(path.Join(arg[2], arg[1]), GO))), kit.Simple()) diff --git a/core/code/xterm.go b/core/code/xterm.go index 911db9a9..daeee924 100644 --- a/core/code/xterm.go +++ b/core/code/xterm.go @@ -17,7 +17,7 @@ import ( kit "shylinux.com/x/toolkits" ) -func _xterm_get(m *ice.Message, h string) xterm.XTerm { +func _xterm_get(m *ice.Message, h string) *xterm.XTerm { h = kit.Select(m.Option(mdb.HASH), h) m.Assert(h != "") mdb.HashModify(m, mdb.TIME, m.Time(), web.VIEW, m.Option(ice.MSG_DAEMON)) @@ -25,7 +25,7 @@ func _xterm_get(m *ice.Message, h string) xterm.XTerm { text := strings.Split(value[mdb.TEXT], ice.NL) ls := kit.Split(strings.Split(kit.Select(nfs.SH, value[mdb.TYPE]), " # ")[0]) kit.If(value[nfs.PATH] != "" && !strings.HasSuffix(value[nfs.PATH], nfs.PS), func() { value[nfs.PATH] = path.Dir(value[nfs.PATH]) }) - term, e := xterm.Command(m, value[nfs.PATH], cli.SystemFind(m, ls[0]), ls[1:]...) + term, e := xterm.Command(m, value[nfs.PATH], kit.Select(ls[0], cli.SystemFind(m, ls[0])), ls[1:]...) if m.Warn(e) { return nil } @@ -52,7 +52,7 @@ func _xterm_get(m *ice.Message, h string) xterm.XTerm { } }) return term - }).(xterm.XTerm) + }).(*xterm.XTerm) } func _xterm_echo(m *ice.Message, h string, str string) { m.Options(ice.MSG_DAEMON, mdb.HashSelectField(m, h, web.VIEW)) @@ -123,7 +123,7 @@ func init() { m.PushAction(web.OUTPUT, mdb.REMOVE).Action(mdb.CREATE, mdb.PRUNES) } else { if m.Length() == 0 { - arg[0] = m.Cmdx("", mdb.CREATE, arg) + arg[0] = m.Cmdx("", mdb.CREATE, mdb.TYPE, arg) mdb.HashSelect(m, arg[0]) } m.Push(mdb.HASH, arg[0]) diff --git a/core/team/plan.go b/core/team/plan.go index ea36ba00..fc879229 100644 --- a/core/team/plan.go +++ b/core/team/plan.go @@ -6,6 +6,7 @@ import ( ice "shylinux.com/x/icebergs" "shylinux.com/x/icebergs/base/aaa" "shylinux.com/x/icebergs/base/ctx" + "shylinux.com/x/icebergs/base/log" "shylinux.com/x/icebergs/base/mdb" "shylinux.com/x/icebergs/base/web" kit "shylinux.com/x/toolkits" @@ -61,6 +62,11 @@ func init() { mdb.INSERT: {Name: "insert zone* type=once,step,week name* text begin_time@date close_time@date", Hand: func(m *ice.Message, arg ...string) { m.Cmdy(TASK, mdb.INSERT, arg) }}, + mdb.SEARCH: {Hand: func(m *ice.Message, arg ...string) { + if arg[0] == mdb.FOREACH && arg[1] == "" { + m.PushSearch(mdb.TYPE, web.LINK, mdb.NAME, m.CommandKey(), mdb.TEXT, m.MergePodCmd("", "", log.DEBUG, ice.TRUE)) + } + }}, ice.RUN: {Hand: func(m *ice.Message, arg ...string) { if m.RenameOption(TASK_POD, ice.POD); ctx.PodCmd(m, m.PrefixKey(), ice.RUN, arg) { return diff --git a/core/wiki/draw.go b/core/wiki/draw.go index 44ab66bd..3f0759aa 100644 --- a/core/wiki/draw.go +++ b/core/wiki/draw.go @@ -16,7 +16,7 @@ const DRAW = "draw" func init() { Index.MergeCommands(ice.Commands{ - DRAW: {Name: "draw path=src/main.svg@key pid refresh save edit actions", Help: "思维导图", Actions: ice.MergeActions(ice.Actions{ + DRAW: {Name: "draw path=src/main.svg@key pid refresh save actions", Help: "思维导图", Actions: ice.MergeActions(ice.Actions{ ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { m.Cmd(mdb.RENDER, mdb.CREATE, mdb.TYPE, nfs.SVG, mdb.NAME, m.PrefixKey()) }}, diff --git a/go.mod b/go.mod index 129bc7db..5e0d0422 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module shylinux.com/x/icebergs go 1.11 require ( - shylinux.com/x/creackpty v0.0.2 shylinux.com/x/go-qrcode v0.0.2 shylinux.com/x/gogit v0.0.7 shylinux.com/x/ice v1.3.0 diff --git a/go.sum b/go.sum index a3f81700..96014a51 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -shylinux.com/x/creackpty v0.0.2 h1:1ekWD5zeKZiQZCYvKSkHnm819wryZxzoq3kmvheC7bA= -shylinux.com/x/creackpty v0.0.2/go.mod h1:SOsAaW5FdicXUprrxgENk2JP1f8LdHBXZjqOFot488o= shylinux.com/x/go-qrcode v0.0.2 h1:/c0PLj+1RT+kUPfnZVXwgbgH5m1SxBUjM2MIKXbDk+E= shylinux.com/x/go-qrcode v0.0.2/go.mod h1:TlzGBENHXy19xC3AsC6h4Vs5fx2ZuDA4TZ0U3C2OeK4= shylinux.com/x/gogit v0.0.7 h1:2ep5QpXWLs0UBCywJuUHda/aagskYvFmn0nj3vpEdY4= diff --git a/misc/git/git.go b/misc/git/git.go index 0c222cfb..726a8c6c 100644 --- a/misc/git/git.go +++ b/misc/git/git.go @@ -19,6 +19,12 @@ func _git_url(m *ice.Message, repos string) string { func _git_dir(arg ...string) string { return path.Join(path.Join(arg...), ".git") } func _git_cmd(m *ice.Message, arg ...string) *ice.Message { return m.Cmd(cli.SYSTEM, GIT, arg) } func _git_cmds(m *ice.Message, arg ...string) string { return _git_cmd(m, arg...).Results() } +func _git_tags(m *ice.Message) string { return _git_cmds(m, "describe", "--tags") } +func _git_diff(m *ice.Message) string { return _git_cmds(m, DIFF, "--shortstat") } +func _git_status(m *ice.Message) string { return _git_cmds(m, STATUS, "-sb") } +func _git_remote(m *ice.Message) string { + return _git_cmds(m, nfs.REMOTE, "get-url", nfs.ORIGIN) +} const GIT = "git" diff --git a/misc/git/repos.go b/misc/git/repos.go index 0673bc4b..3578d53e 100644 --- a/misc/git/repos.go +++ b/misc/git/repos.go @@ -19,8 +19,7 @@ func _repos_path(name string) string { return kit.Select(path.Join(ice.USR, name)+ice.PS, nfs.PWD, name == path.Base(kit.Pwd())) } func _repos_cmd(m *ice.Message, name string, arg ...string) *ice.Message { - m.Option(cli.CMD_DIR, _repos_path(name)) - return m.Copy(_git_cmd(m, arg...)) + return m.Copy(_git_cmd(m.Options(cli.CMD_DIR, _repos_path(name)), arg...)) } func _repos_init(m *ice.Message, dir string) string { os.MkdirAll(path.Join(dir, REFS_HEADS), ice.MOD_DIR) @@ -30,15 +29,11 @@ func _repos_init(m *ice.Message, dir string) string { func _repos_insert(m *ice.Message, name string, path string) bool { if repos, e := gogit.OpenRepository(_git_dir(path)); e == nil { origin := kit.Select("", kit.Split(repos.GetOrigin()), -1) - if origin == "" { - origin = _configs_read(m, _git_dir(path, "config"))["remote.origin.url"] - } + kit.If(origin == "", func() { origin = _configs_read(m, _git_dir(path, "config"))["remote.origin.url"] }) if ci, e := repos.GetCommit(); e == nil { - mdb.HashCreate(m, REPOS, name, nfs.PATH, path, mdb.TIME, ci.Author.When.Format(ice.MOD_TIME), COMMIT, strings.TrimSpace(ci.Message), - BRANCH, repos.GetBranch(), ORIGIN, origin) + mdb.HashCreate(m, REPOS, name, nfs.PATH, path, mdb.TIME, ci.Author.When.Format(ice.MOD_TIME), COMMIT, strings.TrimSpace(ci.Message), BRANCH, repos.GetBranch(), ORIGIN, origin) } else { - mdb.HashCreate(m, REPOS, name, nfs.PATH, path, mdb.TIME, m.Time(), - BRANCH, repos.GetBranch(), ORIGIN, origin) + mdb.HashCreate(m, REPOS, name, nfs.PATH, path, mdb.TIME, m.Time(), BRANCH, repos.GetBranch(), ORIGIN, origin) } return true } @@ -215,9 +210,9 @@ func init() { if p = path.Join(ice.USR_REQUIRE, path.Join(arg...)); !nfs.Exists(m, p) { ls := strings.SplitN(path.Join(arg[:3]...), ice.AT, 2) if v := kit.Select(ice.Info.Gomod[ls[0]], ls, 1); v == "" { - m.Cmd(cli.SYSTEM, "git", "clone", "https://"+ls[0], path.Join(ice.USR_REQUIRE, path.Join(arg[:3]...))) + _git_cmd(m, "clone", "https://"+ls[0], path.Join(ice.USR_REQUIRE, path.Join(arg[:3]...))) } else { - m.Cmd(cli.SYSTEM, "git", "clone", "-b", v, "https://"+ls[0], path.Join(ice.USR_REQUIRE, path.Join(arg[:3]...))) + _git_cmd(m, "clone", "-b", v, "https://"+ls[0], path.Join(ice.USR_REQUIRE, path.Join(arg[:3]...))) } } } @@ -256,7 +251,7 @@ func init() { }}, INIT: {Hand: func(m *ice.Message, arg ...string) { if dir := _repos_init(m, _git_dir(m.Option(cli.CMD_DIR))); m.Option(ORIGIN, kit.Select("", kit.Split(m.Option(ORIGIN)), -1)) != "" { - m.Cmd(nfs.SAVE, path.Join(dir, CONFIG), kit.Format(_repos_config, m.Option(ORIGIN))) + m.Cmd(nfs.SAVE, path.Join(dir, CONFIG), kit.Format(nfs.TemplateText(m, CONFIG), m.Option(ORIGIN))) _git_cmd(m, PULL, ORIGIN, m.OptionDefault(BRANCH, MASTER)) } }}, @@ -268,7 +263,7 @@ func init() { _repos_dir(m, dir, m.Option(BRANCH), m.Option(COMMIT), kit.Select("", arg, 1), nil) } else { _repos_cat(m, dir, m.Option(BRANCH), m.Option(COMMIT), arg[2]) - ctx.DisplayLocal(m, "code/inner.js") + // ctx.DisplayLocal(m, "code/inner.js") } return } @@ -292,12 +287,3 @@ func init() { }) } func ReposList(m *ice.Message) *ice.Message { return m.Cmd(REPOS, ice.OptionFields("repos,path")) } - -var _repos_config = ` -[remote "origin"] - url = %s - fetch = +refs/heads/*:refs/remotes/origin/* -[branch "master"] - remote = origin - merge = refs/heads/master -` diff --git a/misc/git/status.go b/misc/git/status.go index c8d26565..da9bf90b 100644 --- a/misc/git/status.go +++ b/misc/git/status.go @@ -15,7 +15,6 @@ import ( "shylinux.com/x/icebergs/base/nfs" "shylinux.com/x/icebergs/base/tcp" "shylinux.com/x/icebergs/base/web" - "shylinux.com/x/icebergs/core/code" kit "shylinux.com/x/toolkits" ) @@ -34,52 +33,6 @@ func _status_tag(m *ice.Message, tags string) string { return "v0.0.1" } } -func _status_tags(m *ice.Message) { - vs := ice.Maps{} - m.Cmd(STATUS, func(value ice.Maps) { - if value[mdb.TYPE] == "##" { - if value[REPOS] == ice.RELEASE { - value[REPOS] = ice.ICE - } - vs[value[REPOS]] = strings.Split(value[TAGS], "-")[0] - } - }) - web.GoToast(m, TAGS, func(toast func(string, int, int)) { - count, total := 0, len(vs) - defer toast(ice.SUCCESS, count, count) - for k := range vs { - if count++; k == ice.ICE { - k = ice.RELEASE - } - change := false - toast(k, count, total) - m.Option(nfs.DIR_ROOT, _repos_path(k)) - mod := m.Cmdx(nfs.CAT, ice.GO_MOD, func(text string, line int) string { - ls := kit.Split(strings.TrimPrefix(text, ice.REQUIRE)) - if len(ls) < 2 || !strings.Contains(ls[0], ice.PS) || !strings.Contains(ls[1], ice.PT) { - return text - } - if v, ok := vs[kit.Select("", strings.Split(ls[0], ice.PS), -1)]; ok && ls[1] != v { - m.Logs(mdb.MODIFY, REPOS, ls[0], nfs.FROM, ls[1], nfs.TO, v) - text, change = strings.Replace(text, ls[1], v, -1), true - } - return text - }) - if mod == "" || !change { - continue - } - m.Cmd(nfs.SAVE, ice.GO_MOD, mod) - switch m.Option(cli.CMD_DIR, _repos_path(k)); k { - case ice.RELEASE, ice.ICEBERGS, ice.TOOLKITS: - m.Cmd(cli.SYSTEM, code.GO, cli.BUILD) - case ice.CONTEXTS: - defer m.Cmd(cli.SYSTEM, cli.MAKE) - default: - m.Cmd(cli.SYSTEM, cli.MAKE) - } - } - }) -} func _status_each(m *ice.Message, title string, cmds ...string) { web.GoToast(m, kit.Select(strings.Join(cmds, ice.SP), title), func(toast func(string, int, int)) { list, count, total := []string{}, 0, m.Cmd(REPOS).Length() @@ -87,8 +40,8 @@ func _status_each(m *ice.Message, title string, cmds ...string) { toast(value[REPOS], count, total) if msg := m.Cmd(cmds, kit.Dict(cli.CMD_DIR, value[nfs.PATH])); !cli.IsSuccess(msg) { web.Toast(m, msg.Append(cli.CMD_ERR)+msg.Append(cli.CMD_OUT), "error: "+value[REPOS], "", "3s") - m.Sleep3s() list = append(list, value[REPOS]) + m.Sleep3s() } count++ }) @@ -100,7 +53,7 @@ func _status_each(m *ice.Message, title string, cmds ...string) { }) } func _status_stat(m *ice.Message, files, adds, dels int) (int, int, int) { - kit.SplitKV(ice.SP, ice.FS, _git_cmds(m, DIFF, "--shortstat"), func(text string, ls []string) { + kit.SplitKV(ice.SP, ice.FS, _git_diff(m), func(text string, ls []string) { n := kit.Int(ls[0]) switch { case strings.Contains(text, "file"): @@ -114,7 +67,7 @@ func _status_stat(m *ice.Message, files, adds, dels int) (int, int, int) { return files, adds, dels } func _status_list(m *ice.Message) (files, adds, dels int, last time.Time) { - onlychange := m.Option(ice.MSG_MODE) == mdb.ZONE || m.Option("view") == "change" + onlychange := m.Option(ice.MSG_MODE) == mdb.ZONE defer m.Option(cli.CMD_DIR, "") ReposList(m).Table(func(value ice.Maps) { m.Option(cli.CMD_DIR, value[nfs.PATH]) @@ -124,8 +77,8 @@ func _status_list(m *ice.Message) (files, adds, dels int, last time.Time) { last = ci.Author.When } } - tags := _git_cmds(m, "describe", "--tags") - kit.SplitKV(ice.SP, ice.NL, _git_cmds(m, STATUS, "-sb"), func(text string, ls []string) { + tags := _git_tags(m) + kit.SplitKV(ice.SP, ice.NL, _git_status(m), func(text string, ls []string) { switch kit.Ext(ls[1]) { case "swp", "swo", ice.BIN, ice.VAR: return @@ -134,8 +87,7 @@ func _status_list(m *ice.Message) (files, adds, dels int, last time.Time) { return } if m.Push(REPOS, value[REPOS]).Push(mdb.TYPE, ls[0]).Push(nfs.FILE, ls[1]); onlychange { - m.Push(nfs.PATH, value[nfs.PATH]).Push(mdb.VIEW, kit.Format("%s %s", - ls[0]+strings.Repeat(" ", len(ls[0])-9), kit.Select("", ice.USR+ice.PS+value[REPOS]+ice.PS, value[REPOS] != ice.CONTEXTS)+ls[1])) + m.Push(nfs.PATH, value[nfs.PATH]).Push(mdb.VIEW, kit.Format("%s %s", ls[0]+strings.Repeat(ice.SP, len(ls[0])-9), kit.Select("", nfs.USR+value[REPOS]+nfs.PS, value[REPOS] != ice.CONTEXTS)+ls[1])) } switch ls[0] { case "##": @@ -168,9 +120,8 @@ const ( ADD = "add" OPT = "opt" - PRO = "pro" + FIX = "fix" TAG = "tag" - PIE = "pie" COMMENT = "comment" VERSION = "version" @@ -199,7 +150,7 @@ func init() { m.Push(arg[0], kit.Join(kit.Slice(ls, -1), ice.PS)) m.Push(arg[0], kit.Join(kit.Slice(ls, -2), ice.PS)) m.Push(arg[0], m.Option(nfs.FILE)) - case VERSION, TAGS: + case VERSION: m.Push(VERSION, _status_tag(m, m.Option(TAGS))) case aaa.EMAIL: m.Push(arg[0], _configs_get(m, USER_EMAIL), ice.Info.Make.Email) @@ -220,95 +171,36 @@ func init() { m.Sleep3s() }}, PUSH: {Help: "上传", Hand: func(m *ice.Message, arg ...string) { - if m.Option(REPOS) == "" { - _status_each(m, "", cli.SYSTEM, GIT, PUSH) - _status_each(m, "", cli.SYSTEM, GIT, PUSH, "--tags") - m.Sleep3s() - return - } - m.Option(cli.CMD_DIR, _repos_path(m.Option(REPOS))) - if strings.TrimSpace(_git_cmds(m, "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}")) == "" { - _git_cmd(m, PUSH, "--set-upstream", ORIGIN, MASTER) - } else { - _git_cmd(m, PUSH) - } - _git_cmd(m, PUSH, "--tags") + _status_each(m, "", cli.SYSTEM, GIT, PUSH) + _status_each(m, "", cli.SYSTEM, GIT, PUSH, "--tags") + m.Sleep3s() }}, - ADD: {Help: "添加", Hand: func(m *ice.Message, arg ...string) { - _repos_cmd(m, m.Option(REPOS), ADD, m.Option(nfs.FILE)) - }}, OPT: {Help: "优化"}, PRO: {Help: "升级"}, - COMMIT: {Name: "commit action=opt,add,pro comment=some", Help: "提交", Hand: func(m *ice.Message, arg ...string) { + ADD: {Help: "添加", Hand: func(m *ice.Message, arg ...string) { _repos_cmd(m, m.Option(REPOS), ADD, m.Option(nfs.FILE)) }}, OPT: {Help: "优化"}, FIX: {Help: "修复"}, + COMMIT: {Name: "commit action=add,opt,fix comment=some", Help: "提交", Hand: func(m *ice.Message, arg ...string) { _repos_cmd(m, m.Option(REPOS), COMMIT, "-am", m.Option(ctx.ACTION)+ice.SP+m.Option(COMMENT)) m.ProcessBack() }}, - PIE: {Help: "饼图", Hand: func(m *ice.Message, arg ...string) { m.Cmdy(TOTAL, PIE) }}, TAG: {Name: "tag version", Help: "标签", Hand: func(m *ice.Message, arg ...string) { - if m.Option(VERSION) == "" { - m.Option(VERSION, _status_tag(m, m.Option(TAGS))) - } + kit.If(m.Option(VERSION) == "", func() { m.Option(VERSION, _status_tag(m, m.Option(TAGS))) }) _repos_cmd(m, m.Option(REPOS), TAG, m.Option(VERSION)) _repos_cmd(m, m.Option(REPOS), PUSH, "--tags") ctx.ProcessRefresh(m) }}, - TAGS: {Help: "标签", Hand: func(m *ice.Message, arg ...string) { _status_tags(m) }}, STASH: {Help: "缓存", Hand: func(m *ice.Message, arg ...string) { - if len(arg) == 0 && m.Option(REPOS) == "" { - _status_each(m, STASH, cli.SYSTEM, GIT, STASH) - } else { - _repos_cmd(m, kit.Select(m.Option(REPOS), arg, 0), STASH) - } - }}, - BRANCH: {Help: "分支", Hand: func(m *ice.Message, arg ...string) { - for _, line := range kit.Split(_repos_cmd(m.Spawn(), arg[0], BRANCH).Result(), ice.NL, ice.NL) { - if strings.HasPrefix(line, "*") { - m.Push(BRANCH, strings.TrimPrefix(line, "* ")).PushButton("") - } else { - m.Push(BRANCH, strings.TrimSpace(line)).PushButton("branch_switch") - } - } - }}, - "insteadof": {Name: "insteadof from* to", Help: "代理", Hand: func(m *ice.Message, arg ...string) { - m.Cmd(CONFIGS, func(value ice.Maps) { - if value[mdb.VALUE] == m.Option(nfs.FROM) { - _configs_set(m, "--unset", value[mdb.NAME]) - } - }) - if m.Option(nfs.TO) != "" { - _git_cmd(m, "config", "--global", "url."+m.Option(nfs.TO)+".insteadof", m.Option(nfs.FROM)) - } - }}, - "oauth": {Help: "授权", Hand: func(m *ice.Message, arg ...string) { - m.ProcessOpen(kit.MergeURL2(kit.Select(ice.Info.Make.Remote, m.Cmdx(cli.SYSTEM, "git", "remote", "get-url", "origin")), "/chat/cmd/web.code.git.token", - aaa.USERNAME, m.Option(ice.MSG_USERNAME), tcp.HOST, web.UserHost(m))) - }}, - "token": {Name: "token token", Help: "令牌", Hand: func(m *ice.Message, arg ...string) { - list := []string{m.Option(TOKEN)} - m.Cmd(nfs.CAT, kit.HomePath(".git-credentials"), func(line string) { list = append(list, line) }) - m.Cmd(nfs.SAVE, kit.HomePath(".git-credentials"), strings.Join(list, ice.NL)+ice.NL) - ctx.ProcessHold(m) - }}, - "branch_switch": {Help: "切换", Hand: func(m *ice.Message, arg ...string) { - _repos_cmd(m, m.Option(REPOS), "checkout", m.Option(BRANCH)) + _repos_cmd(m, kit.Select(m.Option(REPOS), arg, 0), STASH) }}, nfs.TRASH: {Hand: func(m *ice.Message, arg ...string) { m.Assert(m.Option(REPOS) != "" && m.Option(nfs.FILE) != "") nfs.Trash(m, path.Join(_repos_path(m.Option(REPOS)), m.Option(nfs.FILE))) }}, - code.COMPILE: {Help: "编译", Hand: func(m *ice.Message, arg ...string) { - defer web.ToastProcess(m)() - m.Cmdy(code.VIMER, code.COMPILE) + "insteadof": {Name: "insteadof from* to", Help: "代理", Hand: func(m *ice.Message, arg ...string) { + m.Cmd(CONFIGS, func(value ice.Maps) { + kit.If(value[mdb.VALUE] == m.Option(nfs.FROM), func() { _configs_set(m, "--unset", value[mdb.NAME]) }) + }) + kit.If(m.Option(nfs.TO), func() { _git_cmd(m, CONFIG, "--global", "url."+m.Option(nfs.TO)+".insteadof", m.Option(nfs.FROM)) }) }}, - code.PUBLISH: {Help: "发布", Hand: func(m *ice.Message, arg ...string) { - m.Cmdy(code.PUBLISH, ice.CONTEXTS, ice.MISC, ice.CORE) - }}, - code.BINPACK: {Help: "发布模式", Hand: func(m *ice.Message, arg ...string) { - m.Cmd(nfs.LINK, ice.GO_SUM, path.Join(ice.SRC_RELEASE, ice.GO_SUM)) - m.Cmd(nfs.LINK, ice.GO_MOD, path.Join(ice.SRC_RELEASE, ice.GO_MOD)) - m.Cmdy(nfs.CAT, ice.GO_MOD) - m.Cmdy(code.VIMER, code.BINPACK) - }}, - code.DEVPACK: {Help: "开发模式", Hand: func(m *ice.Message, arg ...string) { - m.Cmdy(code.VIMER, code.DEVPACK) + "oauth": {Help: "授权", Hand: func(m *ice.Message, arg ...string) { + m.ProcessOpen(kit.MergeURL2(kit.Select(ice.Info.Make.Remote, _git_remote(m)), "/chat/cmd/web.code.git.token", aaa.USERNAME, m.Option(ice.MSG_USERNAME), tcp.HOST, web.UserHost(m))) }}, web.DREAM_TABLES: {Hand: func(m *ice.Message, arg ...string) { if m.Option(mdb.TYPE) != web.WORKER { @@ -326,19 +218,18 @@ func init() { } m.Push(mdb.TEXT, strings.Join(text, ", ")) }}, - }, gdb.EventAction(web.DREAM_TABLES), ctx.CmdAction(), aaa.RoleAction()), Hand: func(m *ice.Message, arg ...string) { + }, gdb.EventAction(web.DREAM_TABLES), aaa.RoleAction()), Hand: func(m *ice.Message, arg ...string) { if _configs_get(m, USER_EMAIL) == "" { m.Echo("please config user.email").Action(CONFIGS) } else if len(arg) == 0 { files, adds, dels, last := _status_list(m) - m.StatusTimeCount("files", files, "adds", adds, "dels", dels, "last", last.Format(ice.MOD_TIME), "origin", _git_cmds(m, "remote", "get-url", "origin")) - m.Action(PULL, PUSH, "insteadof", "oauth") - m.Sort("repos,type,file") + m.StatusTimeCount("files", files, "adds", adds, "dels", dels, "last", last.Format(ice.MOD_TIME), nfs.ORIGIN, _git_remote(m)) + m.Action(PULL, PUSH, "insteadof", "oauth").Sort("repos,type,file") } else { _repos_cmd(m, arg[0], DIFF) files, adds, dels := _status_stat(m, 0, 0, 0) m.StatusTime("files", files, "adds", adds, "dels", dels) - m.Action(COMMIT, TAGS, STASH, BRANCH) + m.Action(COMMIT, STASH) } }}, }) diff --git a/misc/git/total.go b/misc/git/total.go index e6f38b10..6c23650c 100644 --- a/misc/git/total.go +++ b/misc/git/total.go @@ -29,7 +29,7 @@ func init() { ) Index.MergeCommands(ice.Commands{ TOTAL: {Name: "total repos auto pie", Help: "统计量", Actions: ice.MergeActions(ice.Actions{ - PIE: {Help: "饼图", Hand: func(m *ice.Message, arg ...string) { + "pie": {Help: "饼图", Hand: func(m *ice.Message, arg ...string) { defer ctx.DisplayStory(m, "pie.js") m.Cmd("", func(value ice.Maps) { if value[REPOS] != mdb.TOTAL { diff --git a/misc/xterm/pty_darwin.go b/misc/xterm/pty_darwin.go new file mode 100644 index 00000000..84e64d8e --- /dev/null +++ b/misc/xterm/pty_darwin.go @@ -0,0 +1,65 @@ +//go:build darwin +// +build darwin + +package xterm + +import ( + "errors" + "os" + "syscall" + "unsafe" + + kit "shylinux.com/x/toolkits" +) + +func open() (pty, tty *os.File, err error) { + pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + p := os.NewFile(uintptr(pFD), "/dev/ptmx") + defer func() { kit.If(err != nil, func() { _ = p.Close() }) }() + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + if err := grantpt(p); err != nil { + return nil, nil, err + } + if err := unlockpt(p); err != nil { + return nil, nil, err + } + 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) { + n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME)) + err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0]))) + if err != nil { + return "", err + } + for i, c := range n { + if c == 0 { + return string(n[:i]), nil + } + } + return "", errors.New("TIOCPTYGNAME string not NUL-terminated") +} +func grantpt(f *os.File) error { return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0) } +func unlockpt(f *os.File) error { return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0) } + +const ( + _IOC_VOID uintptr = 0x20000000 + _IOC_OUT uintptr = 0x40000000 + _IOC_IN uintptr = 0x80000000 + _IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN + _IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN + + _IOC_PARAM_SHIFT = 13 + _IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1 +) + +func _IOC_PARM_LEN(ioctl uintptr) uintptr { return (ioctl >> 16) & _IOC_PARAM_MASK } diff --git a/misc/xterm/pty_linux.go b/misc/xterm/pty_linux.go new file mode 100644 index 00000000..cc8915b0 --- /dev/null +++ b/misc/xterm/pty_linux.go @@ -0,0 +1,47 @@ +//go:build linux +// +build linux + +package xterm + +import ( + "os" + "strconv" + "syscall" + "unsafe" + + kit "shylinux.com/x/toolkits" +) + +func open() (*os.File, *os.File, error) { + p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + defer func() { kit.If(err != nil, func() { _ = p.Close() }) }() + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + if err := unlockpt(p); err != nil { + return nil, nil, err + } + 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) { + var n uint32 + err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) + if err != nil { + return "", err + } + return "/dev/pts/" + strconv.Itoa(int(n)), nil +} + +func unlockpt(f *os.File) error { + var u int32 + return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) +} diff --git a/misc/xterm/winsize_unix.go b/misc/xterm/winsize_unix.go new file mode 100644 index 00000000..9ad69794 --- /dev/null +++ b/misc/xterm/winsize_unix.go @@ -0,0 +1,27 @@ +//go:build !windows +// +build !windows + +package xterm + +import ( + "os" + "syscall" + "unsafe" +) + +func Setsize(t *os.File, ws *Winsize) error { + return ioctl(t.Fd(), syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(ws))) +} + +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 { + return e + } + return nil +} diff --git a/misc/xterm/winsize_unsupported.go b/misc/xterm/winsize_unsupported.go new file mode 100644 index 00000000..73395840 --- /dev/null +++ b/misc/xterm/winsize_unsupported.go @@ -0,0 +1,12 @@ +//go:build windows +// +build windows + +package pty + +import ( + "errors" + "os" +) + +func Setsize(*os.File, *Winsize) error { return errors.New("unsupported") } +func open() (pty, tty *os.File, err error) { return nil, nil, errors.New("unsuported") } diff --git a/misc/xterm/xterm.go b/misc/xterm/xterm.go index dbcd82c0..9f884edd 100644 --- a/misc/xterm/xterm.go +++ b/misc/xterm/xterm.go @@ -3,20 +3,27 @@ package xterm import ( "os" "os/exec" + "syscall" - pty "shylinux.com/x/creackpty" ice "shylinux.com/x/icebergs" "shylinux.com/x/icebergs/base/nfs" kit "shylinux.com/x/toolkits" ) +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 +} + type XTerm struct { *exec.Cmd *os.File } func (s XTerm) Setsize(rows, cols string) error { - return pty.Setsize(s.File, &pty.Winsize{Rows: uint16(kit.Int(rows)), Cols: uint16(kit.Int(cols))}) + return Setsize(s.File, &Winsize{Rows: uint16(kit.Int(rows)), Cols: uint16(kit.Int(cols))}) } func (s XTerm) Writeln(data string, arg ...ice.Any) { s.Write(kit.Format(data, arg...) + ice.NL) @@ -27,11 +34,16 @@ func (s XTerm) Write(data string) (int, error) { func (s XTerm) Close() error { return s.Cmd.Process.Kill() } -func Command(m *ice.Message, dir string, cli string, arg ...string) (XTerm, error) { +func Command(m *ice.Message, dir string, cli string, arg ...string) (*XTerm, error) { cmd := exec.Command(cli, arg...) cmd.Dir = nfs.MkdirAll(m, kit.Path(dir)) cmd.Env = append(cmd.Env, os.Environ()...) cmd.Env = append(cmd.Env, "TERM=xterm") - tty, err := pty.Start(cmd) - return XTerm{cmd, tty}, err + cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true, Setctty: true} + pty, tty, err := open() + if err != nil { + return nil, err + } + cmd.Stdin, cmd.Stdout, cmd.Stderr = tty, tty, tty + return &XTerm{cmd, pty}, cmd.Start() }