Skip to content

Commit

Permalink
chore: support for using the proper origin remote
Browse files Browse the repository at this point in the history
  • Loading branch information
mdelapenya committed Aug 26, 2024
1 parent 30f892b commit 96f5c81
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 58 deletions.
8 changes: 2 additions & 6 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,11 @@ First, check that the following packages are installed in your local machine:

Once checked, it's really important that you verify that the [version.go](./internal/version.go) file is up-to-date, containing the right version you want to create. That file will be used by the automation to perform the release.

Once the version file is correct in the repository, please check that the git remote for the `origin` is pointing to `github.com/testcontainers/testcontainers-go`. You can check it by running:

```shell
git remote -v
```
This automated process needs that the `origin` remote is pointing to `git@github.com:testcontainers/testcontainers-go.git`. If you already have an `origin` remote, the script backs it up and sets the `origin` remote to the right URL. If you don't have an `origin` remote, the script will set it up for you.

## Prepare the release

Once the remote is properly set, please follow these steps:
Please follow these steps:

- Prepare the release with the following command:
```shell
Expand Down
71 changes: 46 additions & 25 deletions cmd/devtools/internal/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ import (
"os/exec"
"path/filepath"
"strings"
"time"

"github.com/testcontainers/testcontainers-go/devtools/internal/context"
)

// tcOrigin is the expected origin remote URL for the testcontainers-go repository.
// It is used for pushing the release state (commits and tags) to the upstream repository.
const tcOrigin string = "git@github.com:testcontainers/testcontainers-go.git"

type GitClient struct {
ctx context.Context
defaultBranch string
dryRun bool
origin string
}

func New(ctx context.Context, branch string, dryRun bool) *GitClient {
Expand All @@ -26,22 +30,18 @@ func New(ctx context.Context, branch string, dryRun bool) *GitClient {
ctx: ctx,
defaultBranch: branch,
dryRun: dryRun,
origin: "git@github.com:testcontainers/testcontainers-go.git",
}
}

// InitRepository initializes a git repository in the root directory of the context.
// Handy for testing.
func (g *GitClient) InitRepository() error {
// set a fake origin for testing purposes
g.origin = "git@testing-github.com:testcontainers/testcontainers-go.git"

func (g *GitClient) InitRepository(remote string) error {
if err := g.Exec("init"); err != nil {
return err
}

// URL is not real, just for testing purposes, but the name must be origin
if err := g.Exec("remote", "add", "origin", g.origin); err != nil {
if err := g.Exec("remote", "add", "origin", remote); err != nil {
return err
}

Expand Down Expand Up @@ -136,7 +136,7 @@ func (g *GitClient) PushTags() error {
return g.Exec("push", "origin", "--tags")
}

func (g *GitClient) remotes() (map[string]string, error) {
func (g *GitClient) Remotes() (map[string]string, error) {
args := []string{
"remote", "-v",
}
Expand Down Expand Up @@ -171,22 +171,15 @@ func (g *GitClient) Tag(tag string) error {
return g.Exec("tag", tag)
}

// HasOriginRemote checks if the repository has an origin remote set to the expected value.
// CheckOriginRemote checks if the repository has an origin remote set to the expected value.
// The expected value is set in the origin field of the GitClient,
// and defaults to the testcontainers-go upstream repository.
func (g *GitClient) HasOriginRemote() error {
remotes, err := g.remotes()
if err != nil {
return err
}
func (g *GitClient) CheckOriginRemote() (func() error, error) {
noopCleanup := func() error { return nil }

if g.dryRun {
// no errors in dry run
return nil
}

if len(remotes) == 0 {
return fmt.Errorf("no remotes found")
remotes, err := g.Remotes()
if err != nil {
return noopCleanup, err
}

// verify the origin remote exists
Expand All @@ -196,12 +189,40 @@ func (g *GitClient) HasOriginRemote() error {
} else if orf, ok := remotes["origin-(fetch)"]; ok {
origin = orf
} else {
return fmt.Errorf("origin remote not found")
// create the origin remote
if err := g.Exec("remote", "add", "origin", tcOrigin); err != nil {
return noopCleanup, fmt.Errorf("origin remote not added: %w", err)
}

// no need to cleanup the origin remote
return noopCleanup, nil
}

if origin != g.origin {
return fmt.Errorf("origin remote, %s, is not the expected one: %s", origin, g.origin)
// create a random remote to backup the existing origin remote
randomRemote := "backup-" + origin + fmt.Sprintf("-%d", time.Now().Unix())
if err := g.Exec("remote", "add", randomRemote, origin); err != nil {
return noopCleanup, fmt.Errorf("error adding remote %s: %w", randomRemote, err)
}

return nil
cleanUpRemote := func() error {
// return back the original original value for the origin remote
err := g.Exec("remote", "set-url", "origin", origin)
if err != nil {
return fmt.Errorf("error setting origin remote back to %s: %w", origin, err)
}

// finally remove the backup remote
return g.Exec("remote", "remove", randomRemote)
}

if g.dryRun {
cleanUpRemote = noopCleanup
}

// set the origin remote to the expected value
if err := g.Exec("remote", "set-url", "origin", tcOrigin); err != nil {
return cleanUpRemote, fmt.Errorf("error setting origin remote to %s: %w", tcOrigin, err)
}

return cleanUpRemote, nil
}
57 changes: 33 additions & 24 deletions cmd/devtools/internal/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,18 @@ import (
"github.com/testcontainers/testcontainers-go/devtools/internal/context"
)

func TestHasOriginRemote(t *testing.T) {
func TestCheckOriginRemote(t *testing.T) {
tests := []struct {
name string
dryRun bool
wantErr bool
name string
dryRun bool
}{
{
name: "Test Has Origin with Dry Run",
dryRun: true,
wantErr: false,
name: "Test Has Origin with Dry Run",
dryRun: true,
},
{
name: "Test Has Origin without Dry Run",
dryRun: false,
wantErr: false,
},
{
name: "Test Has no Origin without Dry Run",
dryRun: false,
wantErr: true,
name: "Test Has Origin without Dry Run",
dryRun: false,
},
}

Expand All @@ -37,20 +29,37 @@ func TestHasOriginRemote(t *testing.T) {
ctx := context.New(tt.TempDir())

gitClient := New(ctx, "main", tc.dryRun)
if err := gitClient.InitRepository(); err != nil {
if err := gitClient.InitRepository("foo"); err != nil {
tt.Fatalf("Error initializing git repository: %v", err)
}

if tc.wantErr {
// updating the origin for the error case
gitClient.origin = "foo"
cleanUp, err := gitClient.CheckOriginRemote()
if err != nil {
tt.Fatalf("Error checking origin remote: %v", err)
}
tt.Cleanup(func() {
if err := cleanUp(); err != nil {
tt.Fatalf("Error cleaning up: %v", err)
}
})

if !tc.dryRun {
remotes, err := gitClient.Remotes()
if err != nil {
tt.Fatalf("Error getting remotes: %v", err)
}

if len(remotes) != 4 {
tt.Errorf("Expected 4 remotes, got %d", len(remotes))
}

err := gitClient.HasOriginRemote()
if (err != nil) != tc.wantErr {
tt.Errorf("HasOriginRemote() error = %v, wantErr %v", err, tc.wantErr)
} else if err == nil && tc.wantErr {
tt.Errorf("HasOriginRemote() error = %v, wantErr %v", err, tc.wantErr)
// verify that the origin remote contains the Github repository URL
if r, ok := remotes["origin-(fetch)"]; !ok || r != tcOrigin {
tt.Fatalf("Expected origin-fetch remote to be %s, got %s", tcOrigin, r)
}
if r, ok := remotes["origin-(push)"]; !ok || r != tcOrigin {
tt.Fatalf("Expected origin-fetch remote to be %s, got %s", tcOrigin, r)
}
}
})
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/devtools/internal/release/pre.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ func preRun(ctx context.Context, gitClient *git.GitClient, branch string, dryRun

vVersion := "v" + version

err = gitClient.HasOriginRemote()
cleanUpRemote, err := gitClient.CheckOriginRemote()
if err != nil {
return err
}
defer cleanUpRemote()

err = gitClient.Exec("checkout", branch)
if err != nil {
Expand Down
27 changes: 26 additions & 1 deletion cmd/devtools/internal/release/pre_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,11 @@ func TestPre(t *testing.T) {
// initialise project files
initialiseProject(tt, ctx, rootCtx, initVersion, nextVersion)

expectedRemote := "foo"

// init the git repository for testing
gitClient := git.New(ctx, releaser.branch, tc.args.dryRun)
if err := gitClient.InitRepository(); err != nil {
if err := gitClient.InitRepository(expectedRemote); err != nil {
tt.Fatalf("Error initializing git repository: %v", err)
}

Expand All @@ -85,6 +87,10 @@ func TestPre(t *testing.T) {
assertModules(tt, ctx, true, expectedVersion)
assertModules(tt, ctx, false, expectedVersion)
assertMarkdownFiles(tt, ctx, expectedMarkDown)

if !tc.args.dryRun {
assertGitState(tt, gitClient, expectedRemote)
}
})
}
}
Expand All @@ -111,6 +117,25 @@ func assertBumpFiles(t *testing.T, ctx context.Context, version string) {
}
}

func assertGitState(t *testing.T, gitClient *git.GitClient, expectedRemote string) {
remotes, err := gitClient.Remotes()
if err != nil {
t.Fatalf("Error getting remotes: %v", err)
}

if len(remotes) != 2 {
t.Errorf("Expected 2 remotes, got %d", len(remotes))
}

// verify that the origin remote contains the expected Github repository URL
if r, ok := remotes["origin-(fetch)"]; !ok || r != expectedRemote {
t.Fatalf("Expected origin-fetch remote to be %s, got %s", expectedRemote, r)
}
if r, ok := remotes["origin-(push)"]; !ok || r != expectedRemote {
t.Fatalf("Expected origin-fetch remote to be %s, got %s", expectedRemote, r)
}
}

func assertMarkdownFiles(t *testing.T, ctx context.Context, expected string) {
for _, f := range testMarkdownFiles {
read, err := os.ReadFile(filepath.Join(ctx.DocsDir(), f))
Expand Down
6 changes: 6 additions & 0 deletions cmd/devtools/internal/release/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ func run(ctx devcontext.Context, gitClient *git.GitClient, bumpType string, dryR
bumpType = "minor"
}

cleanUpRemote, err := gitClient.CheckOriginRemote()
if err != nil {
return err
}
defer cleanUpRemote()

version, err := extractCurrentVersion(ctx)
if err != nil {
return fmt.Errorf("failed to extract the current version: %w", err)
Expand Down
6 changes: 5 additions & 1 deletion cmd/devtools/internal/release/release_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,11 @@ func TestRun(t *testing.T) {
// initialise project files
initialiseProject(tt, ctx, rootCtx, initVersion, nextDevelopmentVersion)

expectedRemote := "foo"

// init the git repository for testing
gitClient := git.New(ctx, releaser.branch, false)
if err := gitClient.InitRepository(); err != nil {
if err := gitClient.InitRepository(expectedRemote); err != nil {
tt.Fatalf("Error initializing git repository: %v", err)
}

Expand Down Expand Up @@ -244,6 +246,8 @@ func TestRun(t *testing.T) {
if version != tc.args.expectedVersion {
tt.Errorf("Expected next development version not found: %s", version)
}

assertGitState(tt, gitClient, expectedRemote)
})
}
}
Expand Down

0 comments on commit 96f5c81

Please sign in to comment.