2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Node.jsでSVNからcheckoutするためにsvn-spawnを使う

Posted at

コード

checkout.js
const Client = require('svn-spawn');
const client = new Client({
    // ディレクトリがなければ作成される
    cwd: __dirname + '/../target',
    // 実行時に手動入力できるのでコードに載せなくても良い。
    //username: 'username', // optional if authentication not required or is already saved
    //password: 'password', // optional if authentication not required or is already saved
    noAuthCache: true, // optional, if true, username does not become the logged in user on the machine
});

// targetディレクトリは作成されないので、cwdの方に新規ディレクトリを指定しないと混ざるので注意
client.checkout('http://repo/~/target');

実行すると、

> node .\checkout.js
Authentication realm: <http://repo:80> Kerberos Login
Password for 'khsk': ********
A    hogehoge.txt
~~

Checked out revision xxxx.

とパスワードを聞いてくるので入力してやる。
ユーザー名はたぶん普段遣いのSVNの記録から読み込んでいると思われる。パスワードを間違えればUsernameからの入力になるので、違うユーザーでチェックアウトしたいときも焦らない。

実行環境

  • Windows7
  • node v9.3.0

ライブラリ

Readmeにはcheckoutがなくて、client.cmdからか…と凹んだが、念の為コードを検索すると

node-svn-spawn/svn.js at 59c4295e33403023f4e8a0e1fe34447a1cef9b5b · ddliu/node-svn-spawn

ちゃんと生えていた。

(peteward44/node-svn-ultimate: The ultimate SVN wrapper for node. Contains all the basic methods checkout, update, info, etc, and includes svnmucc supportを先に試したんですが、失敗したんですよね)

Promise化したい

await大好きマンなので、コールバックはちょっと…

コールバック関数をPromise化して使う - Qiita
いいのがあった。

promise1.js
const Client = require('svn-spawn');
const {promisify} = require('util');

const client = new Client({
    // ディレクトリがなければ作成される
    cwd: __dirname + '/../target',
    // 実行時に手動入力できるのでコードに載せなくても良い。
    //username: 'username', // optional if authentication not required or is already saved
    //password: 'password', // optional if authentication not required or is already saved
    noAuthCache: true, // optional, if true, username does not become the logged in user on the machine
});

// targetディレクトリは作成されないので、cwdの方に新規ディレクトリを指定しないと混ざるので注意
(async () => {
    await promisify(client.checkout)('http://repo/~/target');
})();
promise1実行結果
(node:7396) UnhandledPromiseRejectionWarning:TypeError: Cannot read property 'getOption' of undefin
ed    at Client.checkout (~\node_modules\svn-spawn\lib\svn.js:4
0:20)

…

4行目ではないけれど
node-svn-spawn/svn.js at 59c4295e33403023f4e8a0e1fe34447a1cef9b5b · ddliu/node-svn-spawn
このthis.getOptionが問題な気がする。(util.inherits初めてみた。継承らしい。)
thisの値が変わっちゃったのかな?

thisclientに戻してやればいいのかなーと.bind(client)の位置をあれこれいじり、

promise2.js
const Client = require('svn-spawn');
const {promisify} = require('util');

const client = new Client({
    // ディレクトリがなければ作成される
    cwd: __dirname + '/../target',
    // 実行時に手動入力できるのでコードに載せなくても良い。
    //username: 'username', // optional if authentication not required or is already saved
    //password: 'password', // optional if authentication not required or is already saved
    noAuthCache: true, // optional, if true, username does not become the logged in user on the machine
});

// targetディレクトリは作成されないので、cwdの方に新規ディレクトリを指定しないと混ざるので注意
(async () => {
    await promisify(client.checkout).bind(client)('http://repo/~/target');
})();

とした。
が、どうやらコールバックのときから、チェックアウトには成功するものの失敗扱いらしく、Rejectされるのでtry/catchしてやらないといけない。1

ついでにakamecoさん風ならawait/catch
というか今回は

  • 同期処理(await)が目的
  • 成功がエラーなので握りつぶす(邪悪)

なので、await/catchの方が簡潔に握りつぶせると気づいた。

async関数においてtry/catchではなくawait/catchパターンを活用する - Qiita
(ちよ父すき)

つまり

(async () => {
    try {
        await promisify(client.checkout).bind(client)('http://repo/~/target');
    } catch () {}
})();

// よりも

(async () => {
    await promisify(client.checkout).bind(client)('http://repo/~/target').catch(() => {});
})();

ネストも行数も減るし見やすい。
(ちゃんとしたtry/catchの場合は上下どちらにももう一段try/catchが必要かな。上のtry/catchは握りつぶすためのtry/catchなので)

これでひとまず、チェックアウト後の処理をコールバックの中に書かずに済むようになった。

やっぱりexportも

同じフォルダにSVNから別のファイルを参照用に置きたくなった。
となると同一ディレクトリに別リポジトリはおけないので、エクスポートすることに。
exportは残念ながら実装されていないみたい
Issues · ddliu/node-svn-spawn
なので、結局コマンドで実行することに。

await promisify(client.cmd).bind(client)(['export','http://repo/~/targetfile']).catch(() => {});

と続けて書いてエクスポートした。
clientを使いまわしているので、ダウンロード先は同じcwdになる。

  1. 普段から真面目にtry/catchしてる人なら出ない発言

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?