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

Add run command #546

Merged
merged 4 commits into from
Mar 22, 2022
Merged

Conversation

luislavena
Copy link
Contributor

@luislavena luislavena commented Mar 5, 2022

Introduces shards run, similar to shards build, that builds and runs the specified target, providing a unified interface to developers.

With this, a developer can directly use shards run <target> in a single step without having first to compile (build) and then execute it with ./bin/target. No need for a script or wrapper.

This is inspired by crystal build and crystal run user experience (with the difference that crystal run creates a temporary binary).

This is very helpful for automatic rebuild scenarios, where a process is monitoring your code and can rebuild and relaunch your application.

When invoking shards run, it will first build the target (with the supplied options) and then invoke it, passing the extra options:

$ shards run myapp --stats -- --args

Is the same as:

$ shards build myapp --stats

$ ./bin/myapp --args

If the target exits with error, the exit code is passed back.

This builds on top of the great work done by @denolfe in #298 and aims to resolve #157 initial set of requests.

Sending this out for review and possible address the other points left out in the original PR.

Thank you.
❤️ ❤️ ❤️

luislavena and others added 3 commits March 5, 2022 19:13
Introduces `shards run`, similar to `shards build`, that builds and runs
the specified target, providing a unified interface to developers.

With this, a developer can directly use `shards run <target>` in a
single step without having first to compile (build) and then execute it
with `./bin/target`. No need for a script or wrapper.

This is inspired by `crystal build` and `crystal run` user experience
(with the difference that `crystal run` creates a temporary binary).

This is very helpful for automatic rebuild scenarios, where a process is
monitoring your code and can rebuild and relaunch your application.

When invoking `shards run`, it will first build the target (with the
supplied options) and then invoke it, passing the extra options:

  $ shards run myapp --stats -- --args

Is the same as:

  $ shards build myapp --stats

  $ ./bin/myapp --args

If the target exits with error, the exit code is passed back.

Resolves crystal-lang#157
Relates crystal-lang#298

Co-authored-by: Elliot DeNolf <65888+denolfe@users.noreply.github.com>
Update helper to correctly build `app.exe` when testing on Windows.
Make sure integration tests for `run` are evaluating the output of the
command and not the direct call to the built executable.
Comment on lines +17 to +25
if targets.empty?
if spec.targets.size > 1
raise Error.new("Error please specify the target with 'shards run target'")
else
name = spec.targets.first.name
end
else
name = targets.first
end
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'm too rusty to came with a non-convoluted version of these nested conditions 😬

Better versions are welcome! 😊

Copy link
Member

Choose a reason for hiding this comment

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

The implementation looks fine.

But I'm not sure the resulting behaviour is what we should have. At least the discussion in #157 seemed pretty indecisive on that. It might not be that important what we pick, but this alternative is at least not my favourite 🤷

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but this alternative is at least not my favourite

Can you rephrase or explain this? Not sure I follow. The scenarios I made were:

  1. When no target was indicated and spec contains only one, assume is that target, runs.
  2. When no target was indicated and spec contains more than one, errors out

Looking at the comments in #157 is confusing me:

Yes. No default, unless there is only 1 target.
#157 (comment)

I'm not sure whether the default behaviour is a good idea. shards build defaults to building all. shards run defaults to running one, but only if there is one defined seems off.
#157 (comment)

Always running the first target if no name specified sounds good. That's consistent behaviour.
#157 (comment)

I lean more and more on always specifying the target to run: consistent, no configuration, no documentation, no repeated boring message (that boils down to: you should always specify the target), no surprises (you know what will happen).
#157 (comment)

(skipping the shards isn't a build tool dilema)

I'm ok with making the target mandatory, that will definitely simply the above logic, but I thought of shards run similar to cargo run and the default target selection

Either way, again, we can change it, that is why I sent this PR 😀

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, we just need to come to a decision in #157. There are contradicting views on that.
Having a PR ready certainly helps to push this forward ;)

Address comments from original PR asking to validate ARGV passing to
child process (it was working before, but this spec confirms it).
name = targets.first
end

if target = spec.targets.find { |t| t.name == name }
Copy link
Contributor

@Sija Sija Mar 5, 2022

Choose a reason for hiding this comment

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

Suggested change
if target = spec.targets.find { |t| t.name == name }
if target = spec.targets.find(&.name.== name)

Copy link
Member

Choose a reason for hiding this comment

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

IMO the original reads better.

Commands::Build.run(path, [target.name], options)

Log.info { "Executing: #{target.name} #{run_options.join(' ')}" }
status = Process.run(File.join(Shards.bin_path, target.name), args: run_options, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
status = Process.run(File.join(Shards.bin_path, target.name), args: run_options, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
status = Process.run(File.join(Shards.bin_path, target.name), args: run_options, output: :inherit, error: :inherit)

Copy link
Contributor Author

@luislavena luislavena Mar 6, 2022

Choose a reason for hiding this comment

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

Thought about this, however decided to follow the same pattern/style used in build command:

https://github.com/crystal-lang/shards/blob/master/src/commands/build.cr#L46-L48

Could change if really needed.

Copy link
Member

Choose a reason for hiding this comment

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

Either way is good

@luislavena
Copy link
Contributor Author

One note, if approved, I would like to squash all the changes into a single commit.

Thank you.

@straight-shoota straight-shoota mentioned this pull request Mar 7, 2022
Copy link
Member

@beta-ziliani beta-ziliani left a comment

Choose a reason for hiding this comment

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

To me this PR does what I expect. If we want to change the semantics of an unspecified target with multiple targets configured, we can see how to proceed later.

Many thanks for your attention to detail @luislavena <3

name = targets.first
end

if target = spec.targets.find { |t| t.name == name }
Copy link
Member

Choose a reason for hiding this comment

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

IMO the original reads better.

Commands::Build.run(path, [target.name], options)

Log.info { "Executing: #{target.name} #{run_options.join(' ')}" }
status = Process.run(File.join(Shards.bin_path, target.name), args: run_options, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit)
Copy link
Member

Choose a reason for hiding this comment

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

Either way is good

@straight-shoota straight-shoota added this to the v0.17.0 milestone Mar 21, 2022
@straight-shoota straight-shoota merged commit a5d1c69 into crystal-lang:master Mar 22, 2022
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.

Run command
4 participants