Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8833

`npm token create --json`をchild_processで実行しつつ、結果のトークンはコンソールに表示しない方法

$
0
0

npm token createをNodeJSのプログラム中から呼びたくて、

execだとプロセスが終わるまで帰ってこないので、対話的なプログラムには向かない

帰ってこない
constexec=child.exec("npm token create --json",(err,stdout,stderr)=>{console.info({err,stdout,stderr})})

非同期処理を書きたくないのでspawnSyncを使おうとした。

やりたいことは

  1. パスワードの入力を求めるメッセージはコンソールに表示したい
  2. ユーザーが入力するパスワードはコンソールに表示したくない
  3. 最終結果は(秘密のトークンを含むので)コンソールに表示したくない

である。
とりあえず

constchild=require('child_process')constoptions={encoding:"utf-8",stdio:"inherit"}constresult=child.spawnSync("npm",["token","create","--json"],options);console.info("result",result)

こうすると、1と2はクリアできるけど3がだめ。結果が普通に表示される。(そりゃそうだ)

じゃあこういうことでしょ?

constchild=require('child_process')const{Writable}=require('stream')classHookextendsWritable{_write(chunk,encoding,callback){console.log("chunk",chunk.toString())callback()}}consthook=newHook();hook.fd=1// これがないとspawnに渡せないconstoptions={encoding:"utf-8",stdio:["inherit",hook,"inherit"]}constresult=child.spawnSync("npm",["token","create","--json"],options)console.info("result",result)

と思ってやってみたが、挙動は変わらない。hook._writeが呼ばれていない。
ちなみにhook.fdの値をいくつか変えて試してみたが、コンソールに表示されなくなったりnpm ERR! code EPIPEが出たりする。

調べていると、spawnはカスタムストリームを受け取れない(バグ)という情報があった。
https://stackoverflow.com/questions/34967278/nodejs-child-process-spawn-custom-stdio
https://github.com/nodejs/node-v0.x-archive/issues/4030

これにならってこうすると、

constchild=require('child_process')constresult={};const{Writable}=require('stream')classHookextendsWritable{_write(chunk,encoding,callback){conststr=chunk.toString()try{result=JSON.parse(str)}catch{console.log(str)}callback()}}consthook=newHook();constoptions={stdio:["inherit","pipe","inherit"]}constprocess=child.spawn("npm",["token","create","--json"],options)process.stdout.pipe(hook)process.on("close",()=>{console.info("result",result)})

1と3はクリアできたが2がダメ。
1と3についても、渡ってきたデータがJSON.parseを通るかどうかで分岐させているのでどうもすっきりしない。

ここにあるような、process.stdout.writeを一時的に上書きするアプローチもだめだった。
上書きしたwriteが呼ばれてなさそうな挙動。

https://stackoverflow.com/questions/26675055/nodejs-parse-process-stdout-to-a-variable
https://gist.github.com/pguillory/729616

結局、stdinもstdoutもpipeしてしまって、状況をひとつひとつハンドリングするこのようなコードになってしまった。
1も2も3もクリアできはしたけど、ぜんぜん納得いってない。

constchild=require('child_process');// stdinとstdoutはユーザーコードで処理する。stderrは親プロセスに流すconstoptions={stdio:['pipe','pipe','inherit']}constproc=child.spawn("npm",["token","create","--json"],options);// デフォルトは`Buffer`(バイト列)なのでutf-8を指定proc.stdout.setEncoding('utf-8')// パスワード入力を受け付けるためにprompt-syncを使うconstprompt=require('prompt-sync')({sigint:true})letresult={};proc.stdout.on('data',(data)=>{try{// JSONでパースしてみるresult=JSON.parse(data)// できたら結果が出たということなので子プロセスを終了してよいproc.kill()}// パースできなかったらこちらへcatch(e){// パスワード入力を求められてたらif(data==="npm password: "){// prompt.hideで入力受け付けconstanswer=prompt.hide(data)// 入力された文字列をproc.stdinに渡す// 改行コードを送らないと向こうで処理を始めてくれないproc.stdin.write(answer+"\n")}}});proc.stdout.on('end',()=>{console.info("result",result)});
node main.js

npm password: 
result {
  token: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
  cidr_whitelist: [],
  readonly: false,
  created: '2020-08-14T06:03:21.551Z'
}

そういえば、stdoutをpipeしているのに、npm password:がコンソールに表示されるのはなんでだ・・・?

https://github.com/npm/cli/blob/latest/lib/token.js#L210
https://github.com/npm/cli/blob/latest/lib/utils/read-user-info.js#L41
https://github.com/npm/read/blob/master/lib/read.js#L19

こう読み進めていくと、結局process.stdoutを使っていそうなのだが、child_process内でのprocess.stdoutへの出力がpipeされるのではないのか・・・?

ちょっとよくわからないので放置しとく・・・。


Viewing all articles
Browse latest Browse all 8833

Trending Articles