这是indexloc提供的服务,不要输入任何密码
Skip to content

Port run to cobra #1298

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

Merged
merged 4 commits into from
Jun 1, 2022
Merged

Port run to cobra #1298

merged 4 commits into from
Jun 1, 2022

Conversation

gsoltis
Copy link
Contributor

@gsoltis gsoltis commented May 27, 2022

Removes some long-deprecated flags:

  • serial
  • includeDependencies
  • cacheFolder

@vercel
Copy link

vercel bot commented May 27, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Ignored Deployment
Name Status Preview Comments Updated
turbo-site ⬜️ Ignored (Inspect) Jun 1, 2022 at 4:50PM (UTC)

Base automatically changed from gsoltis/run_opts to main May 31, 2022 16:45
@gsoltis gsoltis force-pushed the gsoltis/run_cobra branch from 68ea84a to f42b7b3 Compare May 31, 2022 23:26
@gsoltis gsoltis marked this pull request as ready for review June 1, 2022 15:54
@kodiakhq kodiakhq bot merged commit e59d0a0 into main Jun 1, 2022
@gsoltis gsoltis deleted the gsoltis/run_cobra branch June 1, 2022 19:14
Copy link
Contributor

@nathanhammond nathanhammond left a comment

Choose a reason for hiding this comment

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

Some comments, some questions!

func AddFlags(opts *Opts, flags *pflag.FlagSet, repoRoot fs.AbsolutePath) {
// skipping remote caching not currently a flag
flags.BoolVar(&opts.SkipFilesystem, "remote-only", false, _remoteOnlyHelp)
fs.AbsolutePathVar(flags, &opts.Dir, "cache-dir", repoRoot, "Specify local filesystem cache directory.", "./node_modules/.cache/turbo")
Copy link
Contributor

Choose a reason for hiding this comment

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

The input path spec is unclear, it could be any of (Absolute|Relative)*(System|Unix). We should probably start communicating that users should use Unix separators, even on Windows.

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 might be missing something, why do windows users need to use unix separators?

Copy link
Contributor

Choose a reason for hiding this comment

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

If we're running things in CI we should generally encourage consistent input otherwise the user can receive surprising output. I think that if we actually push path types through the entire system then we'd be okay, but right now I worry that there are edge cases is the user specifies Windows paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm. We have a few different kinds of paths. Cache directory is pretty clearly machine-local, and thus seems like it should be strictly platform-dependent. Especially since I don't think it can or should be configured via shared config file.

Other paths are shared, for instance, global dependency paths. In my mind those are two different types (the global deps one is really a glob, even if you use a file path), but I'm sure we don't do a great job calling that out or helping with that distinction.

Actually, perhaps those are the only two in the user input? Absolute-or-relative platform-dependent path for things like cache directory, and unix-like glob for any paths shared across machines? Might be some good candidates for at least some internal types, if not external documentation.

In the CI case, I don't think it would make sense to share the same input value for cache directory across a linux machine and windows machine. Unless there's a windows convention I'm not aware of, I wouldn't expect a unix path to work to point to a local directory.


func (pv *pathValue) String() string {
if *pv.current == "" {
return ResolveUnknownPath(pv.base, pv.defValue).ToString()
Copy link
Contributor

Choose a reason for hiding this comment

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

In this case, if pv.defValue is an absolute path but doesn't use os.PathSeparator, it still short-circuits out of calling Join which would result in a consistent system path by virtue of calling Clean.

In other words, on Windows: C:\path\to\thing and C:/path/to/thing can both come out of this. (Meets the strict definition of AbsolutePath but I've been treating it as a system path thus far.)

Relative paths should work just fine.

(Sorry, I've spent far too much time staring at paths recently and have all of this top-of-mind.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IIUC, the issue is that if a user supplies C:/unix/like/path, we will pass it through as an AbsolutePath? I'm a little tempted to say "don't do that". Passing /unix/like/path on windows ends up getting treated as a relative path and the slashes appropriately handled. It is likely a user error, but not really one we can correct because we don't know the drive letter.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd mostly just pitch running it through Clean afterward so that it's consistent.

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 did some research, and it appears that / is forbidden in windows filenames, so running Clean should be safe, as we are not removing their ability to reference a directory called "unix/like/path" contained within C:.

However, and I don't know if it matters, but without running it through Clean, some combination of the Go and windows filesystem APIs end up treating / as a separator. Calling os.MkdirAll("C:/some", ...) and ioutil.WriteFile("C:/some/path", ...) on windows successfully results in a file at C:\some\path.

I think I would've expected it to fail, given that / is both not the separator and not allowed in filenames.

_ = flags.Bool("stream", true, "Unused")
if err := flags.MarkDeprecated("stream", "[WARNING] The --stream flag is unnecesary and has been deprecated. It will be removed in future versions of turbo."); err != nil {
// fail fast if we've misconfigured our flags
panic(err)
Copy link
Contributor

Choose a reason for hiding this comment

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

If I'm reading this correctly, this can only occur if we removed stream but forgot to remove it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah. Unfortunately due to how the flags api works, there are some methods that return errors when they can't find the specified flag, as opposed to being able to set attributes on the flag declaration itself. I put those as panics since they should all be executed synchronously from main, so it should fail quickly and consistently if it's broken.


func (l *logsModeValue) Set(value string) error {
switch value {
case "full":
Copy link
Contributor

Choose a reason for hiding this comment

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

These come in as a string but can we make our internal references to a constant instead of string literals?

}

func (l *logsModeValue) String() string {
if l.opts.CacheHitLogsMode == FullLogs && l.opts.CacheMissLogsMode == FullLogs {
Copy link
Contributor

Choose a reason for hiding this comment

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

MissMode↓ HitsMode→ Full No Hash
Full full new-only
No none
Hash hash-only

if err != nil {
exitErr := &process.ChildExit{}
if errors.As(err, &exitErr) {
return exitErr.ExitCode
Copy link
Contributor

Choose a reason for hiding this comment

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

This is an interesting change; I don't know what I think about it.

  • Did we fail, or did the thing we called fail?
  • Should we replace the child error with a consistent error code at our level, or propagate the child error?

I'm not sure I have strong opinions here, but I do know that if we do this coding to Turbo's exit code API becomes irregular.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The intention is to exit with the highest exit code that we've observed. If we generate our own error, we return 1. I don't think it's worth doing more than checking for exit code == 0 when programming against this value currently.

If we wanted to improve this situation, we could attempt to smuggle more information through this single value, but I think we would need to see a compelling use case for an automated tool reacting differently to turbo failing vs something that turbo called failing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, fwiw, unless I've missed something, this is not new behavior, it was previously implemented here

Copy link
Contributor

Choose a reason for hiding this comment

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

I may also misunderstand in trying to statically reason about it, I read it as a change from "always 1" to "whatever we have gotten back."

I agree that this isn't an API we've currently got planned, so I'm comfortable with 0 vs. non-zero.

}

return exitCode
return nil
Copy link
Contributor

Choose a reason for hiding this comment

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

I like the change of hoisting exit code.

opts.cacheOpts.SkipFilesystem = true
func (d *dryRunValue) String() string {
if d.opts.dryRunJSON {
return "json"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make these constants?

// fail fast if we've messed up our flag configuration
panic(err)
}
aliases["dry"] = "dry-run"
Copy link
Contributor

Choose a reason for hiding this comment

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

While reading through I went back to find this, good catch. 👍

if !config.IsLoggedIn() {
opts.cacheOpts.SkipRemote = true
}
func (d *dryRunValue) Type() string {
Copy link
Contributor

Choose a reason for hiding this comment

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

I have no idea what this is doing. Can you help me understand? (It doesn't appear in old code either.) Is it specifying the alias for some help text somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The short answer is that this is required to implement the pflag.Value interface.

The longer answer is that it is used for a variety of things internal to how flags are parsed and displayed in help/usage text. I'll add some comments, or maybe pull this out into its own utility.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added some comments. I think we need at least one more instance of this kind of flag before extracting it, and I'm not yet sure if it's feasbile or worthwhile w/ Go's type system. I expect to at least attempt this implementing #1286

kodiakhq bot pushed a commit that referenced this pull request Jun 6, 2022
Addressing some of @nathanhammond's review comments from #1298 

I added a test for `AbsolutePathVar` so that we can have a way to discuss the intended behavior.
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.

3 participants