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

fix: maintain working directory across script steps #711

Conversation

jessicatarra
Copy link
Contributor

This PR addresses the issue raised in #470, where the working directory is not maintained across multiple steps in a script.

For example, consider the following script:

fail:
  steps:
    - cd packages/
    - pwd
    

When running this script using melos run, the output will show the workspace root directory, not the packages/ directory as expected.

This PR introduces a change to ensure that the working directory is properly maintained across multiple steps in a script.

The key changes are:

  • Modify the script execution logic to preserve the working directory from one step to the next.
  • Add a safeguard to prevent users from mixing && operators with multi-step scripts, as this can lead to unexpected behavior.

By running the script defined in the issue with the fix implemented in this PR, the output is now:

➜  melos git:(bugfix/melos_run_script_steps_working_directory) ✗ melos success
Building package executable...
Built melos:melos.
melos run success
  └> cd packages/
     └> RUNNING


melos run success
  └> cd packages/
     └> SUCCESS

melos run success
  └> pwd> RUNNING

/Users/jessicatarra/Development/melos/packages

melos run success
  └> pwd> SUCCESS

Type of Change

  • feat -- New feature (non-breaking change which adds functionality)
  • 🛠️ fix -- Bug fix (non-breaking change which fixes an issue)
  • ! -- Breaking change (fix or feature that would cause existing functionality to change)
  • 🧹 refactor -- Code refactor
  • ci -- Build configuration change
  • 📝 docs -- Documentation
  • 🗑️ chore -- Chore

@jessicatarra jessicatarra changed the title Bugfix: maintain working directory across script steps fix: maintain working directory across script steps May 13, 2024
);

if (step.contains('cd ')) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think there must be a better way to keep the environment between steps than to look for cd commands 🤔
cd is also not the only way to change directory in Linux, for example pushd and popd are quite commonly used in scripts.

And I guess other things in the environment should be taken into consideration too, like exporting variables for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It makes sense, I'll think of another approach then. Thanks for the feedback!

Copy link
Member

@Salakar Salakar May 14, 2024

Choose a reason for hiding this comment

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

what about if a step and/or script could define it's working directory rather than us trying to detect it and magically preserve it perhaps, e.g.:

fail:
  # optional, for all steps:
  workingDirectory: packages/
  steps:
    # Step as a YamlMap definition:
    - run: pwd
      # optional, for an individual step + overrides top level if set
      workingDirectory: packages/
    # Step as a string in the YamlArray:
    - ls -la

Would also make it more cross-platform friendly and not worry about Windows weirdness too!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice! I'm all in, it makes a tons of sense. I will start working on it

Copy link
Collaborator

Choose a reason for hiding this comment

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

@Salakar I think that many of the people that have asked for this feature don't just need to be in one directory, but jump around during the multi-step script. Isn't it possible to keep the shell session alive and just keep on executing all steps in there, instead of creating a new shell for each step? 🤔

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 a proof of concept of the approach mentioned by @spydon, and it's doable. It actually follows the same logic of GitHub workflows, also mentioned in the comment replied by @timcreatedit:

I would understand if you keep it like this (also because it's more consistent with GitHub workflows)

The code that I used for the PoC is as follows:

class PersistentShell {
  PersistentShell({required bool isWindows}) {
    _isWindows = isWindows;
  }
  late Process _process;
  late bool _isWindows;

  Future<void> startShell() async {
    _process = await Process.start(
      _isWindows ? 'cmd.exe' : '/bin/sh',
      _isWindows ? ['/Q'] : [],
    );

    _process.stdout.transform(utf8.decoder).listen(print);
    _process.stderr.transform(utf8.decoder).listen((data) {
      print('Error: $data');
    });
  }

  void sendCommand(String command) {
    _process.stdin.writeln(_isWindows ? command : 'eval "$command"');
  }

  Future<void> stopShell() async {
    await _process.stdin.close();
    await _process.exitCode;
  }
}

void main() async {
  var shell = PersistentShell(isWindows: Platform.isWindows);
  await shell.startShell();
  shell.sendCommand('echo Hello World');
  await shell.stopShell();
}

I implemented the code and it works really well, as the hardcoded condition to check if 'cd ' is part of the step command but way better, because it's dynamic. Let me know if this is what you meant @spydon

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry for the late reply, this is what I meant indeed! :)

@spydon
Copy link
Collaborator

spydon commented Jun 5, 2024

Any progress on this @jessicatarra? 😊

@jessicatarra jessicatarra force-pushed the bugfix/melos_run_script_steps_working_directory branch from 4a8f856 to e7fb6dc Compare June 23, 2024 18:41
@jessicatarra
Copy link
Contributor Author

Hey everyone,
@spydon Sorry for the late reply. Work's been pretty busy lately, but I finally got some time this weekend to dig back into this fix. I managed to make things work, but now I'm stuck on figuring out how to keep the logs consistent with our current style.
The issue is, this persistent shell (as it is implemented) makes it tricky to track the results of individual commands. To help us brainstorm a solution, I'm sharing my implementation so far. Let's see what we can come up with together.

Example script

melos:test:
  steps:
    - cd packages/melos
    - pwd
    - dart test .





Output

melos run melos:test
➡️ step: cd packages/melos
➡️ step: pwd
/Users/jessicatarra/Development/melos/packages/melos
➡️ step: dart test .

… (long logs from dart test step)


ERROR: Shell process completed with errors.
Shell process completed some steps successfully.



@jessicatarra jessicatarra force-pushed the bugfix/melos_run_script_steps_working_directory branch from e7fb6dc to d08fd0f Compare June 23, 2024 18:57
Copy link
Collaborator

@spydon spydon left a comment

Choose a reason for hiding this comment

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

@spydon Sorry for the late reply. Work's been pretty busy lately, but I finally got some time this weekend to dig back into this fix.

No worries! Thanks for looking into it again. :)

I managed to make things work, but now I'm stuck on figuring out how to keep the logs consistent with our current style.

I left a few suggestions that I think might help, let me know what you think.

packages/melos/lib/src/common/utils.dart Outdated Show resolved Hide resolved
packages/melos/lib/src/common/utils.dart Outdated Show resolved Hide resolved
packages/melos/lib/src/common/utils.dart Outdated Show resolved Hide resolved
packages/melos/lib/src/common/utils.dart Outdated Show resolved Hide resolved
packages/melos/lib/src/common/utils.dart Outdated Show resolved Hide resolved
@jessicatarra jessicatarra force-pushed the bugfix/melos_run_script_steps_working_directory branch 3 times, most recently from 7e177e2 to 4840a78 Compare June 26, 2024 13:22
@jessicatarra jessicatarra force-pushed the bugfix/melos_run_script_steps_working_directory branch 9 times, most recently from a4bfcbb to 5d1bdb4 Compare August 18, 2024 21:38
- Update command `_executeAndLogCommand` to use `PersistentShell` for executing scripts efficiently.
- Add a new file `persistent_shell.dart` for defining the `PersistentShell` class.
- Import `PersistentShell` in `runner.dart` for using it in the commands.
- Introduce the `PersistentShell` class with methods for starting, sending commands, and stopping the shell.
- Refactor the log handling in `MelosLogger` to complete based on specific markers.
- Modify string manipulation in `utils.dart`, adding a method to prepend a step prefix emoji.
- Update tests for `melos run` commands in `run_test.dart` to reflect the changes in the command execution flow.
@jessicatarra jessicatarra force-pushed the bugfix/melos_run_script_steps_working_directory branch from 5d1bdb4 to 494cad3 Compare August 18, 2024 21:46
Copy link
Collaborator

@spydon spydon left a comment

Choose a reason for hiding this comment

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

Starts to look very good!
No idea about the test failing on windows though...

packages/melos/lib/src/commands/run.dart Show resolved Hide resolved
@spydon
Copy link
Collaborator

spydon commented Aug 19, 2024

I see that there are some tests failing for windows on main too, is it maybe the same ones @jessicatarra, and we just have bad tests that aren't affected by this PR?

@jessicatarra
Copy link
Contributor Author

That's a good point @spydon . I'll check that out. Maybe it would be a good idea to separate the work and open a new PR to fix the failing Windows tests.

@spydon
Copy link
Collaborator

spydon commented Aug 19, 2024

That's a good point @spydon . I'll check that out. Maybe it would be a good idea to separate the work and open a new PR to fix the failing Windows tests.

Yeah, if there aren't tests failing due to this PR that sounds like a good idea

Copy link
Collaborator

@spydon spydon left a comment

Choose a reason for hiding this comment

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

Lgtm! @jessicatarra this is ready for merge now right?

@spydon spydon merged commit a3784c1 into invertase:main Sep 13, 2024
10 checks passed
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