-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b6be2d9
Showing
16 changed files
with
504 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# scm 💪 | ||
|
||
--- | ||
|
||
## It's time 🕒 to clone ⤵️ interesting 🧐 repo faster ⏩ and cleaner 🧹 | ||
|
||
![Usage example](demo.svg) | ||
|
||
Just type: | ||
|
||
```shell | ||
scm https://github.com/pkorobeinikov/scm | ||
``` | ||
|
||
It will clone `https://github.com/pkorobeinikov/scm` into `~/Workspace/github.com/pkorobeinikov/scm`. | ||
|
||
It's also possible to clone `hg`-repo. So command: | ||
|
||
```shell | ||
scm hg http://hg.nginx.org/nginx | ||
``` | ||
|
||
will clone `scm hg http://hg.nginx.org/nginx/` into `~/Workspace/hg.nginx.org/nginx`. | ||
|
||
## Configuration | ||
|
||
Put this into your `.rc`-file: | ||
|
||
```shell | ||
export SCM_WORKSPACE_DIR="~/Projects" # defaults to ~/Workspace | ||
export SCM_WORKSPACE_DIR_DEFAULT_PERM="0755" # defaults to 0755 | ||
``` | ||
|
||
## Building from source | ||
|
||
```shell | ||
go build -o ~/Bin/scm main.go | ||
``` | ||
|
||
## Running tests | ||
|
||
```shell | ||
go test -cover -v ./internal | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module scm | ||
|
||
go 1.15 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package internal | ||
|
||
import ( | ||
"errors" | ||
) | ||
|
||
func ParseArgs(args []string) (scmBin, scmUrl string, err error) { | ||
switch len(args) { | ||
default: | ||
err = TooLongArgumentListErr | ||
return | ||
case 1: | ||
err = NotEnoughArgumentsErr | ||
return | ||
case 2: | ||
scmBin = "git" | ||
scmUrl = args[1] | ||
return | ||
case 3: | ||
scmBin = args[1] | ||
scmUrl = args[2] | ||
return | ||
} | ||
} | ||
|
||
var ( | ||
NotEnoughArgumentsErr = errors.New(`need at least one argument with repo url`) | ||
TooLongArgumentListErr = errors.New(`too long argument list`) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package internal | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
func TestParseArgs(t *testing.T) { | ||
t.Run(`complex`, func(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
given []string | ||
expected struct { | ||
scmBin string | ||
scmUrl string | ||
err error | ||
} | ||
}{ | ||
{ | ||
name: "not enough arguments", | ||
given: []string{"scm"}, | ||
expected: struct { | ||
scmBin string | ||
scmUrl string | ||
err error | ||
}{ | ||
scmBin: "", | ||
scmUrl: "", | ||
err: NotEnoughArgumentsErr, | ||
}, | ||
}, | ||
{ | ||
name: "too long argument list", | ||
given: []string{"foo", "bar", "baz", "quix"}, | ||
expected: struct { | ||
scmBin string | ||
scmUrl string | ||
err error | ||
}{ | ||
scmBin: "", | ||
scmUrl: "", | ||
err: TooLongArgumentListErr, | ||
}, | ||
}, | ||
{ | ||
name: "git by default", | ||
given: []string{"scm", "https://github.com/user/repo"}, | ||
expected: struct { | ||
scmBin string | ||
scmUrl string | ||
err error | ||
}{ | ||
scmBin: "git", | ||
scmUrl: "https://github.com/user/repo", | ||
err: nil, | ||
}, | ||
}, | ||
{ | ||
name: "hg if needed", | ||
given: []string{"scm", "hg", "http://hg.robustwebserver.org/robustwebserver/"}, | ||
expected: struct { | ||
scmBin string | ||
scmUrl string | ||
err error | ||
}{ | ||
scmBin: "hg", | ||
scmUrl: "http://hg.robustwebserver.org/robustwebserver/", | ||
err: nil, | ||
}, | ||
}, | ||
} | ||
|
||
for _, testCase := range testCases { | ||
t.Run(fmt.Sprintf("%s", testCase.name), func(t *testing.T) { | ||
actualScmBin, actualScmUrl, actualErr := ParseArgs(testCase.given) | ||
|
||
if testCase.expected.scmBin != actualScmBin { | ||
t.Fail() | ||
} | ||
|
||
if testCase.expected.scmUrl != actualScmUrl { | ||
t.Fail() | ||
} | ||
|
||
if testCase.expected.err != actualErr { | ||
t.Fail() | ||
} | ||
}) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package internal | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
) | ||
|
||
type Cfg struct { | ||
ScmWorkspaceDirDefaultPerm os.FileMode | ||
ScmWorkingCopyPath string | ||
} | ||
|
||
func ReadCfg(scmUrl string) (Cfg, error) { | ||
scmWorkspaceDir := LookupEnvOrDefault("SCM_WORKSPACE_DIR", "~/Workspace") | ||
scmExpanedWorkspaceDir, err := ExpandHomeDir(scmWorkspaceDir) | ||
if err != nil { | ||
return Cfg{}, err | ||
} | ||
|
||
scmWorkspaceDirDefaultPermStr := LookupEnvOrDefault("SCM_WORKSPACE_DIR_DEFAULT_PERM", "0755") | ||
scmWorkspaceDirDefaultPerm, err := strconv.ParseInt(scmWorkspaceDirDefaultPermStr, 8, strconv.IntSize) | ||
scmWorkspaceDirDefaultPermFileMode := os.FileMode(scmWorkspaceDirDefaultPerm) | ||
|
||
scmPathFromUrl, err := ExtractLocalPathFromScmURL(scmUrl) | ||
if err != nil { | ||
return Cfg{}, err | ||
} | ||
scmWorkingCopyPath := filepath.Join(scmExpanedWorkspaceDir, scmPathFromUrl) | ||
|
||
return Cfg{ | ||
ScmWorkspaceDirDefaultPerm: scmWorkspaceDirDefaultPermFileMode, | ||
ScmWorkingCopyPath: scmWorkingCopyPath, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package internal | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
) | ||
|
||
func TestReadCfg(t *testing.T) { | ||
t.Run(`positive`, func(t *testing.T) { | ||
saveScmWorkspaceDir := os.Getenv(`SCM_WORKSPACE_DIR`) | ||
|
||
expected := Cfg{ | ||
ScmWorkspaceDirDefaultPerm: 0755, | ||
ScmWorkingCopyPath: `/tmp/Workspace/github.com/user/repo`, | ||
} | ||
|
||
_ = os.Setenv(`SCM_WORKSPACE_DIR`, `/tmp/Workspace`) | ||
actual, _ := ReadCfg(`https://github.com/user/repo`) | ||
|
||
if expected.ScmWorkspaceDirDefaultPerm != actual.ScmWorkspaceDirDefaultPerm { | ||
t.Errorf(`want "%s", got "%s"`, expected.ScmWorkspaceDirDefaultPerm, actual.ScmWorkspaceDirDefaultPerm) | ||
} | ||
|
||
if expected.ScmWorkingCopyPath != actual.ScmWorkingCopyPath { | ||
t.Errorf(`want "%s", got "%s"`, expected.ScmWorkingCopyPath, actual.ScmWorkingCopyPath) | ||
} | ||
|
||
_ = os.Setenv(`SCM_WORKSPACE_DIR`, saveScmWorkspaceDir) | ||
}) | ||
|
||
t.Run(`homedir not detected`, func(t *testing.T) { | ||
saveScmWorkspaceDir := os.Getenv(`SCM_WORKSPACE_DIR`) | ||
saveHome := os.Getenv(`HOME`) | ||
|
||
_ = os.Unsetenv(`HOME`) | ||
_ = os.Setenv(`SCM_WORKSPACE_DIR`, `~/Workspace`) | ||
_, err := ReadCfg(`https://github.com/user/repo`) | ||
if err == nil { | ||
t.Errorf(`homedir detected but shouldn't'`) | ||
} | ||
|
||
_ = os.Setenv(`HOME`, saveHome) | ||
_ = os.Setenv(`SCM_WORKSPACE_DIR`, saveScmWorkspaceDir) | ||
}) | ||
|
||
t.Run(`mailformed repo url given`, func(t *testing.T) { | ||
_, err := ReadCfg(`https://github % com/user/repo`) | ||
if err == nil { | ||
t.Errorf(`repo url parsed but shouldn't'`) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package internal | ||
|
||
import ( | ||
"os" | ||
"os/exec" | ||
) | ||
|
||
func PrepareLocalWorkingCopyPath(scmWorkingCopyPath string, scmWorkspaceDirDefaultPermFileMode os.FileMode) error { | ||
return os.MkdirAll(scmWorkingCopyPath, scmWorkspaceDirDefaultPermFileMode) | ||
} | ||
|
||
func Clone(scmBin, scmUrl, scmWorkingCopyPath string) error { | ||
scmCmd := exec.Command(scmBin, "clone", scmUrl, scmWorkingCopyPath) | ||
scmCmd.Stdout = os.Stdout | ||
scmCmd.Stderr = os.Stderr | ||
|
||
if err := scmCmd.Run(); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package internal | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
) | ||
|
||
func TestPrepareLocalWorkingCopyPath(t *testing.T) { | ||
t.Run(`positive`, func(t *testing.T) { | ||
err := PrepareLocalWorkingCopyPath(`/tmp/Workspace/foobar`, 0755) | ||
if err != nil { | ||
t.Error(`expected to create workspace directory without any errors`) | ||
} | ||
}) | ||
} | ||
|
||
func TestClone(t *testing.T) { | ||
t.Run(`positive`, func(t *testing.T) { | ||
dest := `/tmp/github/gitignore` | ||
|
||
err := Clone(`git`, `https://github.com/github/gitignore`, dest) | ||
if err != nil { | ||
t.Error(`expected to clone repo without any errors`) | ||
} | ||
|
||
_ = os.RemoveAll(dest) | ||
}) | ||
|
||
t.Run(`negative`, func(t *testing.T) { | ||
err := Clone(`unknown-scm`, `https://github.com/github/gitignore`, `/tmp/github/gitignore`) | ||
if err == nil { | ||
t.Error(`expected to fail with unknown command`) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package internal | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
func ExpandHomeDir(d string) (string, error) { | ||
if strings.HasPrefix(d, `~/`) { | ||
homeDir, err := os.UserHomeDir() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return filepath.Join(homeDir, d[2:]), nil | ||
} | ||
|
||
return d, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package internal | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
) | ||
|
||
func TestExpandHomeDir(t *testing.T) { | ||
t.Run(`positive`, func(t *testing.T) { | ||
actual, _ := ExpandHomeDir(`~/Workspace`) | ||
// fixme hardcoded value | ||
if `/Users/pkorobeinikov/Workspace` != actual { | ||
t.Fail() | ||
} | ||
}) | ||
|
||
t.Run(`no home dir in path`, func(t *testing.T) { | ||
actual, _ := ExpandHomeDir(`/mnt/Volumes/Workspace`) | ||
if `/mnt/Volumes/Workspace` != actual { | ||
t.Fail() | ||
} | ||
}) | ||
|
||
t.Run(`homedir not set`, func(t *testing.T) { | ||
saveHome := os.Getenv(`HOME`) | ||
|
||
_ = os.Unsetenv(`HOME`) | ||
_, err := ExpandHomeDir(`~/Workspace`) | ||
if err == nil { | ||
t.Fail() | ||
} | ||
|
||
_ = os.Setenv(`HOME`, saveHome) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package internal | ||
|
||
import "os" | ||
|
||
func LookupEnvOrDefault(envName, defaultValue string) string { | ||
if value, found := os.LookupEnv(envName); found { | ||
return value | ||
} else { | ||
return defaultValue | ||
} | ||
} |
Oops, something went wrong.