Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export git package and git client #75

Closed
wants to merge 2 commits into from
Closed

Export git package and git client #75

wants to merge 2 commits into from

Conversation

samcoe
Copy link
Contributor

@samcoe samcoe commented Sep 20, 2022

This PR exports the git package that had been internal for some time, no behavior changes here. It also adds in a new git client type that will be used for interacting with a local git repository. It is heavily inspired by this work done by @mislav. Right now the only exported methods on the git client are Command which is the base method, similar to Do for http.Client, that all other methods will use to execute commands and Remotes which lists remotes for the local repo. The reason I added Remotes is because it is needed within go-gh. I think for the initial version these are the only two that will be supported but more will be added in the future as necessary.

TODO:

  • Fill in missing tests
  • Add documentation

@samcoe samcoe self-assigned this Sep 20, 2022
@samcoe samcoe force-pushed the git-pkg branch 2 times, most recently from 1458ed4 to 5baaea0 Compare September 20, 2022 16:47
}

type ClientOptions struct {
Command func(ctx context.Context, args ...string) (*exec.Cmd, error)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love exposing something that is necessary for testing purposes only but couldn't think of a better way to go about this.

Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great start; thank you for spiking this out!

While this evolves, wouldn't it be better to first live in the cli/cli repo so that it can be immediately used by the code there? I had felt that the CLI codebase had more immediate use of this functionality than extensions do.

Comment on lines +34 to +36
Stderr io.Writer // will use stderr if none specified
Stdin io.Reader // will use stdin if none specified
Stdout io.Writer // will use stdout if none specified
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stdin, Stderr, and Stdout feel to me like they should be specified per each git operation, rather than as a property of the client.

Consider that a client could be used to call multiple operations:

client := git.NewClient()
client.Commit(message)
client.Push(remote, branch)

If the same stdin was passed to both git invocations that read from it, it would have failed for the 2nd invocation because the 1st one would have closed it after reading from it. Furthermore, we might not want to connect the same stdout/stderr to every git operation.

Copy link
Contributor Author

@samcoe samcoe Sep 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct in that the second invocation will be broken but that is easy enough to fix with cmd.StdinPipe.

Having each method take in stdin, stdout, stderr, seems like a lot of arguments. It is very explicit though. I do imagine people would actually run into the same stdin being closed issue on subsequent method calls in this case also, since not everyone would realize it gets closed by cmd.Run().

I am envisioning that most methods will actually overwrite stdin, stdout, and stderr (that is what is happening in Remotes()) but the most basic and widely used case would be to just execute a command with all three attached to the exec.Cmd.

}

type ClientOptions struct {
Command func(ctx context.Context, name string, args ...string) *exec.Cmd
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to expose this publicly. Tests that need to stub out Client.command can just initialize the struct directly instead of using the constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct, but what about tests outside of this package? I was thinking about tests being written in gh would probably need to utilize it.

Copy link
Contributor

@mislav mislav Sep 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't think at all about tests from outside of the package. But it feels wrong to me that the consumer of go-gh library (even if it's us) would need a mechanism to test go-gh internals. I feel that other codebases using this package should stub out git.Client invocations not by stubbing underlying git calls, but by avoiding calling real git.Client methods altogether.

WorkTree string // will be passed to commands as --work-tree
}

func NewClient(opts *ClientOptions) Client {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If passing nil options is a valid way to initialize a zero-value Client, then couldn't we make the zero-value Client{} struct be useful as a client, and have users do that when there are no options?

And then, if there are some options, users can use NewClient() where the options struct would now be a required argument. Just a thought!

}
}

func (c *Client) Command(ctx context.Context, args ...string) (*exec.Cmd, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My advice would be to not make this public, otherwise the end caller has to be concerned with API details of git on the command-line, including the names of flags, etc. Ideally, we would have completely hidden git's own API behind our own Go API that just exposes the pieces of functionality that we need for our own purposes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without having this exported then the only method the client has is Remotes. I feel like this method allows for ultimate flexibility when interacting with git and then we don't need to anticipate or implement every one of our users needs, we can just tell them to utilize Command and everything will be taken care of them. Going down the route of implementing every git command with every option they allow seems like a lot of work, and there are other git packages out there that do that which extension authors can choose to use instead if they like that interface.

@samcoe
Copy link
Contributor Author

samcoe commented Sep 22, 2022

@mislav Thanks for the feedback! You have raised some good points. It is possible that this work should be started in cli/cli, I mostly started it here due to cli/cli#5544 where we had targeted it for extraction. We can take it off that list though if you don't think it is valuable to have in go-gh though.

@samcoe
Copy link
Contributor Author

samcoe commented Sep 27, 2022

Offline, we have decided now is not the right time to introduce this feature into go-gh but rather start with an implementation in cli/cli and see how that goes and when we feel more confident in this direction we will extract it.

@samcoe samcoe closed this Sep 27, 2022
@samcoe samcoe deleted the git-pkg branch September 27, 2022 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants