Skip to content

Commit

Permalink
tools: install trzsz with --install-path
Browse files Browse the repository at this point in the history
  • Loading branch information
lonnywong committed Dec 15, 2023
1 parent 3b2d6c9 commit 25937a2
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 29 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ _`~/` 代表 HOME 目录。在 Windows 中,请将下文的 `~/` 替换成 `C:\

- 运行 `tssh --new-host` 可以在 TUI 界面轻松添加 SSH 配置,并且完成后可以立即登录。

- 运行 `tssh --install-trzsz` 可以自动安装 [trzsz](https://github.com/trzsz/trzsz-go) 到服务器的 `~/.local/bin/` 目录。若获取 `trzsz` 的最新版本号失败,可以通过 `--trzsz-version x.x.x` 参数自行指定。若下载 `trzsz` 的安装包失败,可以自行下载并通过 `--trzsz-bin-path /path/to/trzsz.tar.gz` 参数指定。
- 运行 `tssh --install-trzsz` 可以自动安装 [trzsz](https://github.com/trzsz/trzsz-go) 到服务器上。默认安装到 `~/.local/bin/` 目录,可以通过 `--install-path /path/to/install` 指定安装目录。若安装目录含有 `~/`,则必须加上单引号,如`--install-path '~/path'`。若获取 `trzsz` 的最新版本号失败,可以通过 `--trzsz-version x.x.x` 参数自行指定。若下载 `trzsz` 的安装包失败,可以自行下载并通过 `--trzsz-bin-path /path/to/trzsz.tar.gz` 参数指定。

## 快捷键

Expand Down Expand Up @@ -425,6 +425,16 @@ _`~/` 代表 HOME 目录。在 Windows 中,请将下文的 `~/` 替换成 `C:\

- 可以在出错配置项中加上前缀 `#!!`,标准 `ssh` 会将它当作注释,而 `tssh` 则会认为它是有效配置之一。

- 关于动态修改终端标题,其实不需要 `tssh` 就能实现,只要在服务器的 shell 配置文件中(如`~/.bashrc`)配置:

```sh
# 设置固定的服务器标题
PROMPT_COMMAND='echo -ne "\033]0;固定的服务器标题\007"'

# 根据环境变量动态变化的标题
PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'
```

## 录屏演示

![tssh登录演示](https://trzsz.github.io/images/tssh.gif)
Expand Down
3 changes: 2 additions & 1 deletion tssh/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ type sshArgs struct {
Zmodem bool `arg:"--zmodem" help:"enable zmodem lrzsz ( rz / sz ) feature"`
NewHost bool `arg:"--new-host" help:"[tools] add new host to configuration"`
EncSecret bool `arg:"--enc-secret" help:"[tools] encode secret for configuration"`
InstallTrzsz bool `arg:"--install-trzsz" help:"[tools] install trzsz to remote ~/.local/bin/"`
InstallTrzsz bool `arg:"--install-trzsz" help:"[tools] install trzsz to the remote server"`
InstallPath string `arg:"--install-path" placeholder:"path" help:"[tools] install path, default: '~/.local/bin/'"`
TrzszVersion string `arg:"--trzsz-version" placeholder:"x.x.x" help:"[tools] install the specified version of trzsz"`
TrzszBinPath string `arg:"--trzsz-bin-path" placeholder:"path" help:"[tools] trzsz binary installation package path"`
originalDest string
Expand Down
1 change: 1 addition & 0 deletions tssh/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func TestSshArgs(t *testing.T) {
assertArgsEqual("--new-host", sshArgs{NewHost: true})
assertArgsEqual("--enc-secret", sshArgs{EncSecret: true})
assertArgsEqual("--install-trzsz", sshArgs{InstallTrzsz: true})
assertArgsEqual("--install-trzsz --install-path /bin", sshArgs{InstallTrzsz: true, InstallPath: "/bin"})
assertArgsEqual("--install-trzsz --trzsz-version 1.1.6", sshArgs{InstallTrzsz: true, TrzszVersion: "1.1.6"})
assertArgsEqual("--install-trzsz --trzsz-bin-path a.tgz", sshArgs{InstallTrzsz: true, TrzszBinPath: "a.tgz"})

Expand Down
91 changes: 64 additions & 27 deletions tssh/tools_install_trzsz.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ package tssh

import (
"archive/tar"
"bufio"
"bytes"
"compress/gzip"
"encoding/json"
Expand All @@ -42,6 +43,12 @@ import (

const kMaxBufferSize = 32 * 1024

const (
kScpOK = 0
kScpWarn = 1
kScpError = 2
)

type trzszRelease struct {
TagName string `json:"tag_name"`
}
Expand Down Expand Up @@ -79,19 +86,20 @@ func checkTrzszVersion(client *ssh.Client, cmd, name, version string) bool {
return strings.TrimSpace(string(output)) == fmt.Sprintf("%s (trzsz) go %s", name, version)
}

func checkInstalledVersion(client *ssh.Client, name, version string) bool {
return checkTrzszVersion(client, fmt.Sprintf("~/.local/bin/%s -v", name), name, version)
func checkInstalledVersion(client *ssh.Client, path, name, version string) bool {
// local may be Windows, remote may be Linux, so filepath.Join is not suitable here.
return checkTrzszVersion(client, fmt.Sprintf("%s/%s -v", path, name), name, version)
}

func checkTrzszExecutable(client *ssh.Client, name, version string) bool {
return checkTrzszVersion(client, fmt.Sprintf("$SHELL -l -c '%s -v'", name), name, version)
}

func checkTrzszPathEnv(client *ssh.Client, version string) {
func checkTrzszPathEnv(client *ssh.Client, version, path string) {
trzExecutable := checkTrzszExecutable(client, "trz", version)
tszExecutable := checkTrzszExecutable(client, "tsz", version)
if !trzExecutable || !tszExecutable {
toolsInfo("InstallTrzsz", "you may need to add ~/.local/bin/ to the PATH environment variable")
toolsInfo("InstallTrzsz", "you may need to add %s to the PATH environment variable", path)
}
}

Expand Down Expand Up @@ -143,6 +151,23 @@ func getRemoteServerArch(client *ssh.Client) (string, error) {
}
}

func mkdirInstallPath(client *ssh.Client, path string) error {
session, err := client.NewSession()
if err != nil {
return err
}
defer session.Close()
output, err := session.CombinedOutput(fmt.Sprintf("mkdir -p -m 755 %s", path))
if err != nil {
errMsg := string(bytes.TrimSpace(output))
if errMsg != "" {
return fmt.Errorf("%s", errMsg)
}
return err
}
return nil
}

func extractTrzszBinary(gzr io.Reader, version, svrOS, arch string) ([]byte, []byte, error) {
pkgName := fmt.Sprintf("trzsz_%s_%s_%s", version, svrOS, arch)
var trz, tsz bytes.Buffer
Expand Down Expand Up @@ -249,7 +274,7 @@ func readTrzszBinary(path, version, svrOS, arch string) ([]byte, []byte, error)
return extractTrzszBinary(gzr, version, svrOS, arch)
}

func uploadTrzszBinary(client *ssh.Client, trz, tsz []byte) error {
func uploadTrzszBinary(client *ssh.Client, path string, trz, tsz []byte) error {
session, err := client.NewSession()
if err != nil {
return err
Expand All @@ -266,13 +291,24 @@ func uploadTrzszBinary(client *ssh.Client, trz, tsz []byte) error {
}
progress := newToolsProgress("InstallTrzsz", "upload percentage", len(trz)+len(tsz))

var errMsg []string
checkTransferResponse := func() bool {
buf := make([]byte, 1)
n, err := reader.Read(buf)
if err != nil || n != 1 {
return false
}
return buf[0] == 0
switch buf[0] {
case kScpOK:
return true
case kScpWarn, kScpError:
msg, _ := bufio.NewReader(reader).ReadString('\n')
errMsg = append(errMsg, fmt.Sprintf("scp response [%d]: %s", buf[0], strings.TrimSpace(msg)))
return false
default:
errMsg = append(errMsg, fmt.Sprintf("unknown scp response [%d]", buf[0]))
return false
}
}
writeTransferCommand := func(cmd string) bool {
if n, err := writer.Write([]byte(cmd)); err != nil || n != len(cmd) {
Expand Down Expand Up @@ -304,12 +340,6 @@ func uploadTrzszBinary(client *ssh.Client, trz, tsz []byte) error {
if !checkTransferResponse() {
return
}
if !writeTransferCommand("D0755 0 .local\n") {
return
}
if !writeTransferCommand("D0755 0 bin\n") {
return
}
if !writeTransferCommand(fmt.Sprintf("C0755 %d trz\n", len(trz))) {
return
}
Expand All @@ -322,19 +352,16 @@ func uploadTrzszBinary(client *ssh.Client, trz, tsz []byte) error {
if !writeBinaryContent(tsz) {
return
}
if !writeTransferCommand("E\n") {
return
}
if !writeTransferCommand("E\n") {
return
}
}()

output, err := session.CombinedOutput("scp -tqr ~/")
output, err := session.CombinedOutput(fmt.Sprintf("scp -tqr %s", path))
if err != nil {
msg := strings.TrimSpace(string(output))
if msg != "" {
return fmt.Errorf("%s: %s", err.Error(), msg)
errMsg = append(errMsg, msg)
}
if len(errMsg) > 0 {
return fmt.Errorf("%s", strings.Join(errMsg, ", "))
}
return err
}
Expand All @@ -352,11 +379,16 @@ func execInstallTrzsz(args *sshArgs, client *ssh.Client) {
}
}

trzInstalled := checkInstalledVersion(client, "trz", version)
tszInstalled := checkInstalledVersion(client, "tsz", version)
installPath := args.InstallPath
if installPath == "" {
installPath = "~/.local/bin/"
}

trzInstalled := checkInstalledVersion(client, installPath, "trz", version)
tszInstalled := checkInstalledVersion(client, installPath, "tsz", version)
if trzInstalled && tszInstalled {
toolsSucc("InstallTrzsz", "trzsz %s has been installed in ~/.local/bin/", version)
checkTrzszPathEnv(client, version)
toolsSucc("InstallTrzsz", "trzsz %s has been installed in %s", version, installPath)
checkTrzszPathEnv(client, version, installPath)
return
}

Expand All @@ -372,6 +404,11 @@ func execInstallTrzsz(args *sshArgs, client *ssh.Client) {
return
}

if err := mkdirInstallPath(client, installPath); err != nil {
toolsWarn("InstallTrzsz", "mkdir [%s] failed: %v", installPath, err)
return
}

var trz, tsz []byte
if args.TrzszBinPath != "" {
trz, tsz, err = readTrzszBinary(args.TrzszBinPath, version, svrOS, arch)
Expand All @@ -388,11 +425,11 @@ func execInstallTrzsz(args *sshArgs, client *ssh.Client) {
}
}

if err := uploadTrzszBinary(client, trz, tsz); err != nil {
if err := uploadTrzszBinary(client, installPath, trz, tsz); err != nil {
toolsWarn("InstallTrzsz", "upload trzsz binary files failed: %v", err)
return
}

toolsSucc("InstallTrzsz", "trzsz %s installation to ~/.local/bin/ completed successfully", version)
checkTrzszPathEnv(client, version)
toolsSucc("InstallTrzsz", "trzsz %s installation to %s completed successfully", version, installPath)
checkTrzszPathEnv(client, version, installPath)
}

0 comments on commit 25937a2

Please sign in to comment.