Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 9392b25

Browse files
authored
Handle single file sync (#152)
1 parent b3a91b7 commit 9392b25

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

internal/cmd/sync.go

+13-9
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func rsyncVersion() string {
4747
func makeRunSync(init *bool) func(cmd *cobra.Command, args []string) error {
4848
return func(cmd *cobra.Command, args []string) error {
4949
var (
50+
ctx = cmd.Context()
5051
local = args[0]
5152
remote = args[1]
5253
)
@@ -56,17 +57,9 @@ func makeRunSync(init *bool) func(cmd *cobra.Command, args []string) error {
5657
return err
5758
}
5859

59-
info, err := os.Stat(local)
60-
if err != nil {
61-
return err
62-
}
63-
if !info.IsDir() {
64-
return xerrors.Errorf("%s must be a directory", local)
65-
}
66-
6760
remoteTokens := strings.SplitN(remote, ":", 2)
6861
if len(remoteTokens) != 2 {
69-
return xerrors.New("remote misformatted")
62+
return xerrors.New("remote malformatted")
7063
}
7164
var (
7265
envName = remoteTokens[0]
@@ -78,6 +71,17 @@ func makeRunSync(init *bool) func(cmd *cobra.Command, args []string) error {
7871
return err
7972
}
8073

74+
info, err := os.Stat(local)
75+
if err != nil {
76+
return err
77+
}
78+
if info.Mode().IsRegular() {
79+
return sync.SingleFile(ctx, local, remoteDir, env, client)
80+
}
81+
if !info.IsDir() {
82+
return xerrors.Errorf("local path must lead to a regular file or directory: %w", err)
83+
}
84+
8185
absLocal, err := filepath.Abs(local)
8286
if err != nil {
8387
return xerrors.Errorf("make abs path out of %s, %s: %w", local, absLocal, err)

internal/sync/singlefile.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package sync
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"io/ioutil"
8+
"os"
9+
"path/filepath"
10+
"strings"
11+
12+
"cdr.dev/coder-cli/coder-sdk"
13+
"cdr.dev/wsep"
14+
"golang.org/x/xerrors"
15+
"nhooyr.io/websocket"
16+
)
17+
18+
// SingleFile copies the given file into the remote dir or remote path of the given coder.Environment.
19+
func SingleFile(ctx context.Context, local, remoteDir string, env *coder.Environment, client *coder.Client) error {
20+
conn, err := client.DialWsep(ctx, env)
21+
if err != nil {
22+
return xerrors.Errorf("dial remote execer: %w", err)
23+
}
24+
defer func() { _ = conn.Close(websocket.StatusNormalClosure, "normal closure") }()
25+
26+
if strings.HasSuffix(remoteDir, string(filepath.Separator)) {
27+
remoteDir += filepath.Base(local)
28+
}
29+
30+
execer := wsep.RemoteExecer(conn)
31+
cmd := fmt.Sprintf(`[ -d %s ] && cat > %s/%s || cat > %s`, remoteDir, remoteDir, filepath.Base(local), remoteDir)
32+
process, err := execer.Start(ctx, wsep.Command{
33+
Command: "sh",
34+
Args: []string{"-c", cmd},
35+
Stdin: true,
36+
})
37+
if err != nil {
38+
return xerrors.Errorf("start sync command: %w", err)
39+
}
40+
41+
sourceFile, err := os.Open(local)
42+
if err != nil {
43+
return xerrors.Errorf("open source file: %w", err)
44+
}
45+
46+
go func() { _, _ = io.Copy(ioutil.Discard, process.Stdout()) }()
47+
go func() { _, _ = io.Copy(ioutil.Discard, process.Stderr()) }()
48+
go func() {
49+
stdin := process.Stdin()
50+
defer stdin.Close()
51+
_, _ = io.Copy(stdin, sourceFile)
52+
}()
53+
54+
if err := process.Wait(); err != nil {
55+
return xerrors.Errorf("copy process: %w", err)
56+
}
57+
return nil
58+
}

0 commit comments

Comments
 (0)