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

Lines changed: 13 additions & 9 deletions
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

Lines changed: 58 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy