Skip to content

Subprocess stucks while read stdin #1293

Closed
@mtgto

Description

Description

I find odd situation in my rails project using lint-staged with rubocop.
Lint-staged stuck forever only when using rubocop with Server Mode.

bundle exec rubocop --start-server
RuboCop server starting on 127.0.0.1:50996.bundle exec rubocop --server-status
RuboCop server (90477) is running.git commit -m 'implement'
✔ Preparing lint-staged...Running tasks for staged files...
  ❯ .lintstagedrc — 4 files
    ❯ * — 4 files
      ⠦ bundle exec rubocop --version
◼ Applying modifications from tasks...
◼ Cleaning up temporary files...

I have research and found that non-TTY rubocop reads from stdin until EOF and will not exit forever in server mode.

rubocop which launched by lint-staged stucked in L24 in https://github.com/rubocop/rubocop/blob/v1.50.2/lib/rubocop/server/client_command/exec.rb#L21-L25

#1019 also says, subprocess spawned by lint-staged has non-tty and stdin is not closed.

node -e "import('execa').then(m => m.execa('ruby', ['-e', 'puts \$stdin.tty?']).then(r => console.log(r.stdout)))"
falsenode -e "import('execa').then(m => m.execa('ruby', ['-e', '\$stdin.read']).then(r => console.log(r)))"
(does not exit forever)

Execa has input option to close the stdin of subprocess.

# does not stuck because stdin of subprocess is closednode -e "import('execa').then(m => m.execa('bundle', ['exec', 'rubocop', '--version'], { input: '' })).then(s => console.log(s));"

# node.js versionnode -e "import('execa').then(m => m.execa('node', ['-e', 'process.stdin.read()'], { input: '' })).then(s => console.log(s));"

I would prefer that the default behavior is stdin subprocess is already closed, or create an option to set stdin of subprocess like input of execaOption.

Steps to reproduce

git clone my repo.
Or manually install rubocop.

bundle exec rubocop --start-server
RuboCop server starting on 127.0.0.1:50996.touch bar && git add bargit commit --allow-empty -m 'test'
✔ Preparing lint-staged...Running tasks for staged files...
  ❯ .lintstagedrc — 1 file
    ❯ * — 1 file
      ⠴ bundle exec rubocop --version
◼ Applying modifications from tasks...
◼ Cleaning up temporary files...

Debug Logs

expand to view
❯ git commit --allow-empty -m 'test'
  lint-staged:bin Options parsed from command-line: {
  allowEmpty: false,
  concurrent: true,
  configPath: undefined,
  cwd: undefined,
  debug: true,
  diff: undefined,
  diffFilter: undefined,
  maxArgLength: undefined,
  quiet: false,
  relative: false,
  shell: false,
  stash: true,
  verbose: false
} +0ms
  lint-staged:validateOptions Validating options... +0ms
  lint-staged:validateOptions Validated options! +0ms
  lint-staged Unset GIT_LITERAL_PATHSPECS (was `undefined`) +0ms
  lint-staged:runAll Running all linter scripts... +0ms
  lint-staged:runAll Using working directory `/Users/user/work/ruby/example-rubocop` +0ms
  lint-staged:resolveGitRepo Resolving git repo from `/Users/user/work/ruby/example-rubocop` +0ms
  lint-staged:resolveGitRepo Unset GIT_DIR (was `undefined`) +0ms
  lint-staged:resolveGitRepo Unset GIT_WORK_TREE (was `undefined`) +0ms
  lint-staged:execGit Running git command [ 'rev-parse', '--show-prefix' ] +0ms
  lint-staged:resolveGitRepo Resolved git directory to be `/Users/user/work/ruby/example-rubocop` +10ms
  lint-staged:resolveGitRepo Resolved git config directory to be `/Users/user/work/ruby/example-rubocop/.git` +0ms
  lint-staged:execGit Running git command [ 'log', '-1' ] +10ms
  lint-staged:execGit Running git command [ 'diff', '--name-only', '-z', '--diff-filter=ACMR', '--staged' ] +5ms
  lint-staged:runAll Loaded list of staged files in git:
  lint-staged:runAll [ '/Users/user/work/ruby/example-rubocop/foo' ] +22ms
  lint-staged:searchConfigs Searching for configuration files... +0ms
  lint-staged:execGit Running git command [ 'ls-files', '-z', '--full-name' ] +8ms
  lint-staged:execGit Running git command [ 'ls-files', '-z', '--full-name', '--others', '--exclude-standard' ] +1ms
  lint-staged:searchConfigs Found possible config files: [
  '/Users/user/work/ruby/example-rubocop/.lintstagedrc',
  '/Users/user/work/ruby/example-rubocop/package.json'
] +5ms
  lint-staged:loadConfig Loading configuration from `/Users/user/work/ruby/example-rubocop/.lintstagedrc`... +0ms
  lint-staged:loadConfig Loading configuration from `/Users/user/work/ruby/example-rubocop/package.json`... +0ms
  lint-staged:loadConfig Successfully loaded config from `/Users/user/work/ruby/example-rubocop/package.json`:
  lint-staged:loadConfig null +0ms
  lint-staged:loadConfig Successfully loaded config from `/Users/user/work/ruby/example-rubocop/.lintstagedrc`:
  lint-staged:loadConfig { '*': 'bundle exec rubocop --version' } +3ms
  lint-staged:validateConfig Validating config from `/Users/user/work/ruby/example-rubocop/.lintstagedrc`... +0ms
  lint-staged:validateConfig Validated config from `/Users/user/work/ruby/example-rubocop/.lintstagedrc`: +0ms
  lint-staged:validateConfig {
  lint-staged:validateConfig   '*': 'bundle exec rubocop --version'
  lint-staged:validateConfig } +1ms
  lint-staged:searchConfigs Found 1 config files +5ms
  lint-staged:groupFilesByConfig Grouping 1 files by 1 configurations +0ms
  lint-staged:chunkFiles Resolved an argument string length of 41 characters from 1 files +0ms
  lint-staged:chunkFiles Creating 1 chunks for maxArgLength of 131072 +0ms
  lint-staged:generateTasks Generating linter tasks +0ms
  lint-staged:generateTasks Generated task:
  lint-staged:generateTasks {
  lint-staged:generateTasks   pattern: '*',
  lint-staged:generateTasks   commands: 'bundle exec rubocop --version',
  lint-staged:generateTasks   fileList: [ '/Users/user/work/ruby/example-rubocop/foo' ]
  lint-staged:generateTasks } +1ms
  lint-staged:makeCmdTasks Creating listr tasks for commands 'bundle exec rubocop --version' +0ms
  lint-staged:resolveTaskFn cmd: bundle +0ms
  lint-staged:resolveTaskFn args: [ 'exec', 'rubocop', '--version' ] +0ms
  lint-staged:resolveTaskFn execaOptions: {
  cwd: '/Users/user/work/ruby/example-rubocop',
  preferLocal: true,
  reject: false,
  shell: false
} +0ms
  lint-staged:chunkFiles Resolved an argument string length of 41 characters from 1 files +2ms
  lint-staged:chunkFiles Creating 1 chunks for maxArgLength of 131072 +0ms
[STARTED] Preparing lint-staged...
  lint-staged:GitWorkflow Backing up original state... +0ms
  lint-staged:GitWorkflow Getting partially staged files... +0ms
  lint-staged:execGit Running git command [ 'status', '-z' ] +13ms
  lint-staged:GitWorkflow Found partially staged files: [] +5ms
  lint-staged:GitWorkflow Backing up merge state... +0ms
  lint-staged:file Reading file `/Users/user/work/ruby/example-rubocop/.git/MERGE_HEAD` +0ms
  lint-staged:file Reading file `/Users/user/work/ruby/example-rubocop/.git/MERGE_MODE` +0ms
  lint-staged:file Reading file `/Users/user/work/ruby/example-rubocop/.git/MERGE_MSG` +0ms
  lint-staged:file File `/Users/user/work/ruby/example-rubocop/.git/MERGE_HEAD` doesn't exist, ignoring... +0ms
  lint-staged:file File `/Users/user/work/ruby/example-rubocop/.git/MERGE_MODE` doesn't exist, ignoring... +0ms
  lint-staged:file File `/Users/user/work/ruby/example-rubocop/.git/MERGE_MSG` doesn't exist, ignoring... +0ms
  lint-staged:GitWorkflow Done backing up merge state! +0ms
  lint-staged:GitWorkflow Getting deleted files... +0ms
  lint-staged:execGit Running git command [ 'ls-files', '--deleted' ] +5ms
  lint-staged:GitWorkflow Found deleted files: [] +4ms
  lint-staged:execGit Running git command [ 'stash', 'create' ] +4ms
  lint-staged:execGit Running git command [
  'stash',
  'store',
  '--quiet',
  '--message',
  'lint-staged automatic backup',
  'c678441708c03f1361be5bf4be437162bd82ab98'
] +11ms
  lint-staged:GitWorkflow Done backing up original state! +17ms
[SUCCESS] Preparing lint-staged...
[STARTED] Running tasks for staged files...
[STARTED] .lintstagedrc — 1 file
[STARTED] * — 1 file
[STARTED] bundle exec rubocop --version

Environment

  • OS: macOS 13.3.1 (a)
  • Node.js: v18.16.0
  • lint-staged: v13.2.2
  • execa v7.1.1
  • rubocop v1.50.2

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions