Skip to content

Commit 0268845

Browse files
authored
feat: git remote helper for cloning codecommit repositories (#2)
1 parent 518c059 commit 0268845

8 files changed

Lines changed: 915 additions & 2 deletions

File tree

.goreleaser.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ brews:
7070
name: homebrew-tap
7171
folder: Formula
7272
homepage: "https://github.com/gembaadvantage/git-remote-codecommit"
73-
description: "Semantic versioning the easy way"
73+
description: "A git remote helper that removes the need for dedicated CodeCommit user credentials"
7474
license: MIT
7575
install: |
7676
bin.install "git-remote-codecommit"
@@ -82,5 +82,5 @@ scoop:
8282
owner: gembaadvantage
8383
name: scoops
8484
homepage: "https://github.com/gembaadvantage/git-remote-codecommit"
85-
description: "Semantic versioning the easy way"
85+
description: "A git remote helper that removes the need for dedicated CodeCommit user credentials"
8686
license: MIT

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,51 @@
11
# git-remote-codecommit
2+
3+
A [git-remote-helper](https://git-scm.com/docs/gitremote-helpers) that supports basic push and pull functionality when working with CodeCommit repositories using the AWS `codecommit` protocol. When installed the helper acts as a transparent proxy, converting the `codecommit` protocol into AWS V4 authenticated HTTPS requests. Removing the need for dedicated AWS HTTPS credentials.
4+
5+
## Install
6+
7+
Binary downloads can be found on the [Releases](https://github.com/gembaadvantage/git-remote-codecommit/releases) page. Unpack the `git-remote-codecommit` binary and add it to your PATH.
8+
9+
### Homebrew
10+
11+
To use [Homebrew](https://brew.sh/):
12+
13+
```sh
14+
brew tap gembaadvantage/tap
15+
brew install git-remote-codecommit
16+
```
17+
18+
### Scoop
19+
20+
To use [Scoop](https://scoop.sh/):
21+
22+
```sh
23+
scoop install git-remote-codecommit
24+
```
25+
26+
## Quick Start
27+
28+
Clone the repository using your standard git syntax, but provide the clone URL using the `codecommit` protocol format of:
29+
30+
```text
31+
codecommit::<REGION>://<PROFILE>@<REPOSITORY>
32+
```
33+
34+
```sh
35+
$ git clone codecommit::eu-west-1://repository
36+
37+
Cloning into 'repository'...
38+
remote: Counting objects: 167, done.
39+
Receiving objects: 100% (167/167), 96.07 KiB | 634.00 KiB/s, done.
40+
Resolving deltas: 100% (31/31), done.
41+
```
42+
43+
Both `git pull` and `git push` operations will behave as normal.
44+
45+
### AWS Named Profile
46+
47+
Depending on your chosen authentication mechansim, you may need to provide an AWS named profile to authenticate with CodeCommit. To do this, provide the named profile, suffixed with an `@`, before the repository name.
48+
49+
```sh
50+
git clone codecommit::eu-west-1://profile@repository
51+
```

cmd/grc/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,12 @@ SOFTWARE.
2222

2323
package main
2424

25+
import "os"
26+
2527
func main() {
28+
cmd := newRootCmd(os.Stdout, os.Args[1:])
29+
30+
if err := cmd.Execute(); err != nil {
31+
os.Exit(1)
32+
}
2633
}

cmd/grc/root.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
Copyright (c) 2021 Gemba Advantage
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in all
12+
copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
SOFTWARE.
21+
*/
22+
23+
package main
24+
25+
import (
26+
"context"
27+
"io"
28+
"os"
29+
"os/exec"
30+
"regexp"
31+
"strings"
32+
33+
"github.com/aws/aws-sdk-go-v2/config"
34+
"github.com/gembaadvantage/codecommit-sign/pkg/awsv4"
35+
"github.com/gembaadvantage/codecommit-sign/pkg/translate"
36+
"github.com/spf13/cobra"
37+
)
38+
39+
var (
40+
grcRgx = regexp.MustCompile(`^codecommit::(.+)://(.+)$`)
41+
)
42+
43+
type gitOptions struct {
44+
Cmd string
45+
RemoteURL string
46+
}
47+
48+
func newRootCmd(out io.Writer, args []string) *cobra.Command {
49+
opts := gitOptions{}
50+
51+
cmd := &cobra.Command{
52+
Use: "git-remote-codecommit",
53+
Short: "TODO",
54+
Args: cobra.ExactArgs(2),
55+
SilenceUsage: true,
56+
RunE: func(cmd *cobra.Command, args []string) error {
57+
opts.Cmd = args[0]
58+
59+
// Prefix protocol, as it will have been stipped off by the git client
60+
opts.RemoteURL = "codecommit::" + args[1]
61+
62+
return opts.Run(out)
63+
},
64+
}
65+
66+
cmd.AddCommand(newVersionCmd(out))
67+
return cmd
68+
}
69+
70+
func (o gitOptions) Run(out io.Writer) error {
71+
// Load named profile if one has been provided
72+
opts := []func(*config.LoadOptions) error{}
73+
if profile := identifyProfile(o.RemoteURL); profile != "" {
74+
opts = append(opts, config.WithSharedConfigProfile(profile))
75+
}
76+
77+
cfg, err := config.LoadDefaultConfig(context.TODO(), opts...)
78+
if err != nil {
79+
return err
80+
}
81+
82+
creds, err := cfg.Credentials.Retrieve(context.TODO())
83+
if err != nil {
84+
return err
85+
}
86+
87+
// Translate and then sign the RemoteURL
88+
grcURL, err := translate.FromGrc(o.RemoteURL)
89+
if err != nil {
90+
return err
91+
}
92+
93+
signer := awsv4.NewSigner(creds)
94+
url, err := signer.Sign(grcURL)
95+
if err != nil {
96+
return err
97+
}
98+
99+
cmd := exec.Command("git", "remote-http", o.Cmd, url)
100+
cmd.Stdin = os.Stdin
101+
cmd.Stderr = os.Stderr
102+
cmd.Stdout = os.Stdout
103+
return cmd.Run()
104+
}
105+
106+
func identifyProfile(url string) string {
107+
m := grcRgx.FindStringSubmatch(url)
108+
if len(m) < 3 {
109+
return ""
110+
}
111+
112+
// A GRC url will contain an optional profile with a trailing @
113+
profile := m[len(m)-1]
114+
if strings.Contains(profile, "@") {
115+
return strings.Split(profile, "@")[0]
116+
}
117+
118+
return ""
119+
}

cmd/grc/root_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright (c) 2021 Gemba Advantage
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in all
12+
copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
SOFTWARE.
21+
*/
22+
23+
package main
24+
25+
import "testing"
26+
27+
func TestIdentifyProfile(t *testing.T) {
28+
tests := []struct {
29+
name string
30+
url string
31+
expected string
32+
}{
33+
{
34+
name: "WithProfile",
35+
url: "codecommit::eu-west-1://profile@repository",
36+
expected: "profile",
37+
},
38+
{
39+
name: "NoProfile",
40+
url: "codecommit::eu-west-1://repository",
41+
expected: "",
42+
},
43+
{
44+
name: "InvalidURL",
45+
url: "codecommit::eu-west-1://",
46+
expected: "",
47+
},
48+
}
49+
for _, tt := range tests {
50+
t.Run(tt.name, func(t *testing.T) {
51+
profile := identifyProfile(tt.url)
52+
53+
if profile != tt.expected {
54+
t.Errorf("expected %s but received %s\n", tt.expected, profile)
55+
}
56+
})
57+
}
58+
}

cmd/grc/version.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
Copyright (c) 2021 Gemba Advantage
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in all
12+
copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
SOFTWARE.
21+
*/
22+
23+
package main
24+
25+
import (
26+
"fmt"
27+
"io"
28+
29+
"github.com/gembaadvantage/git-remote-codecommit/internal/version"
30+
"github.com/spf13/cobra"
31+
)
32+
33+
type versionOptions struct {
34+
short bool
35+
}
36+
37+
func newVersionCmd(out io.Writer) *cobra.Command {
38+
opts := versionOptions{}
39+
40+
cmd := &cobra.Command{
41+
Use: "version",
42+
Short: "Prints the build time version information",
43+
RunE: func(cmd *cobra.Command, args []string) error {
44+
return opts.run(out)
45+
},
46+
}
47+
48+
f := cmd.Flags()
49+
f.BoolVar(&opts.short, "short", false, "only print the semantic version number")
50+
51+
return cmd
52+
}
53+
54+
func (o versionOptions) run(out io.Writer) error {
55+
fmt.Fprintln(out, formatVersion(o.short))
56+
return nil
57+
}
58+
59+
func formatVersion(short bool) string {
60+
if short {
61+
return version.Short()
62+
}
63+
64+
return fmt.Sprintf("%#v", version.Long())
65+
}

go.mod

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
11
module github.com/gembaadvantage/git-remote-codecommit
22

33
go 1.17
4+
5+
require (
6+
github.com/aws/aws-sdk-go-v2/config v1.8.0
7+
github.com/gembaadvantage/codecommit-sign v1.1.0
8+
github.com/spf13/cobra v1.2.1
9+
)
10+
11+
require (
12+
github.com/aws/aws-sdk-go-v2 v1.9.0 // indirect
13+
github.com/aws/aws-sdk-go-v2/credentials v1.4.0 // indirect
14+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0 // indirect
15+
github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2 // indirect
16+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0 // indirect
17+
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect
18+
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect
19+
github.com/aws/smithy-go v1.8.0 // indirect
20+
github.com/inconshreveable/mousetrap v1.0.0 // indirect
21+
github.com/spf13/pflag v1.0.5 // indirect
22+
)

0 commit comments

Comments
 (0)