backlog-exporter 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -8
- package/dist/commands/all/index.d.ts +1 -0
- package/dist/commands/all/index.js +35 -9
- package/dist/commands/issue/index.d.ts +2 -0
- package/dist/commands/issue/index.js +32 -6
- package/dist/commands/update/index.js +19 -2
- package/dist/commands/wiki/index.js +11 -3
- package/dist/utils/backlog-api.d.ts +18 -12
- package/dist/utils/backlog-api.js +164 -57
- package/dist/utils/sleep.d.ts +28 -0
- package/dist/utils/sleep.js +42 -0
- package/oclif.manifest.json +82 -51
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ $ npm install -g backlog-exporter
|
|
|
59
59
|
$ backlog-exporter COMMAND
|
|
60
60
|
running command...
|
|
61
61
|
$ backlog-exporter (--version)
|
|
62
|
-
backlog-exporter/0.
|
|
62
|
+
backlog-exporter/0.2.1 linux-x64 node-v23.9.0
|
|
63
63
|
$ backlog-exporter --help [COMMAND]
|
|
64
64
|
USAGE
|
|
65
65
|
$ backlog-exporter COMMAND
|
|
@@ -196,9 +196,11 @@ Backlogから課題とWikiを取得してMarkdownファイルとして保存す
|
|
|
196
196
|
|
|
197
197
|
```
|
|
198
198
|
USAGE
|
|
199
|
-
$ backlog-exporter all --domain <value> --projectIdOrKey <value> [--apiKey <value>] [-
|
|
199
|
+
$ backlog-exporter all --domain <value> --projectIdOrKey <value> [--apiKey <value>] [-m <value>] [-o
|
|
200
|
+
<value>]
|
|
200
201
|
|
|
201
202
|
FLAGS
|
|
203
|
+
-m, --maxCount=<value> [default: 5000] 一度に取得する課題の最大数(デフォルト: 5000)
|
|
202
204
|
-o, --output=<value> 出力ディレクトリパス
|
|
203
205
|
--apiKey=<value> Backlog API key (環境変数 BACKLOG_API_KEY からも自動読み取り可能)
|
|
204
206
|
--domain=<value> (required) Backlog domain (e.g. example.backlog.jp)
|
|
@@ -213,9 +215,12 @@ EXAMPLES
|
|
|
213
215
|
|
|
214
216
|
$ backlog-exporter all --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./my-project
|
|
215
217
|
指定したディレクトリに課題とWikiを保存する
|
|
218
|
+
|
|
219
|
+
$ backlog-exporter all --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --maxCount 1000
|
|
220
|
+
最大1000件の課題を取得する(デフォルトは100件)
|
|
216
221
|
```
|
|
217
222
|
|
|
218
|
-
_See code: [src/commands/all/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.
|
|
223
|
+
_See code: [src/commands/all/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.2.1/src/commands/all/index.ts)_
|
|
219
224
|
|
|
220
225
|
## `backlog-exporter help [COMMAND]`
|
|
221
226
|
|
|
@@ -243,13 +248,16 @@ Backlogから課題を取得してMarkdownファイルとして保存する
|
|
|
243
248
|
|
|
244
249
|
```
|
|
245
250
|
USAGE
|
|
246
|
-
$ backlog-exporter issue --domain <value> --projectIdOrKey <value> [--apiKey <value>] [-
|
|
251
|
+
$ backlog-exporter issue --domain <value> --projectIdOrKey <value> [--apiKey <value>] [-m <value>] [-o
|
|
252
|
+
<value>] [--statusId <value>]
|
|
247
253
|
|
|
248
254
|
FLAGS
|
|
255
|
+
-m, --maxCount=<value> [default: 5000] 一度に取得する課題の最大数(デフォルト: 5000)
|
|
249
256
|
-o, --output=<value> 出力ディレクトリパス
|
|
250
257
|
--apiKey=<value> Backlog API key (環境変数 BACKLOG_API_KEY からも自動読み取り可能)
|
|
251
258
|
--domain=<value> (required) Backlog domain (e.g. example.backlog.jp)
|
|
252
259
|
--projectIdOrKey=<value> (required) Backlog project ID or key
|
|
260
|
+
--statusId=<value> ステータスID(カンマ区切りで複数指定可能)
|
|
253
261
|
|
|
254
262
|
DESCRIPTION
|
|
255
263
|
Backlogから課題を取得してMarkdownファイルとして保存する
|
|
@@ -258,11 +266,17 @@ EXAMPLES
|
|
|
258
266
|
$ backlog-exporter issue --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY
|
|
259
267
|
課題をMarkdownファイルとして保存する
|
|
260
268
|
|
|
261
|
-
$ backlog-exporter issue --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./my-
|
|
269
|
+
$ backlog-exporter issue --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./my-issues
|
|
262
270
|
指定したディレクトリに課題を保存する
|
|
271
|
+
|
|
272
|
+
$ backlog-exporter issue --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --statusId 1,2,3
|
|
273
|
+
指定したステータスIDの課題のみを取得する
|
|
274
|
+
|
|
275
|
+
$ backlog-exporter issue --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --maxCount 10000
|
|
276
|
+
最大10000件の課題を取得する(デフォルトは5000件)
|
|
263
277
|
```
|
|
264
278
|
|
|
265
|
-
_See code: [src/commands/issue/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.
|
|
279
|
+
_See code: [src/commands/issue/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.2.1/src/commands/issue/index.ts)_
|
|
266
280
|
|
|
267
281
|
## `backlog-exporter plugins`
|
|
268
282
|
|
|
@@ -591,7 +605,7 @@ EXAMPLES
|
|
|
591
605
|
指定したディレクトリの設定を使用して更新する
|
|
592
606
|
```
|
|
593
607
|
|
|
594
|
-
_See code: [src/commands/update/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.
|
|
608
|
+
_See code: [src/commands/update/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.2.1/src/commands/update/index.ts)_
|
|
595
609
|
|
|
596
610
|
## `backlog-exporter wiki`
|
|
597
611
|
|
|
@@ -618,7 +632,7 @@ EXAMPLES
|
|
|
618
632
|
指定したディレクトリにWikiを保存する
|
|
619
633
|
```
|
|
620
634
|
|
|
621
|
-
_See code: [src/commands/wiki/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.
|
|
635
|
+
_See code: [src/commands/wiki/index.ts](https://github.com/ShuntaToda/backlog-exporter/blob/v0.2.1/src/commands/wiki/index.ts)_
|
|
622
636
|
<!-- commandsstop -->
|
|
623
637
|
|
|
624
638
|
# 出力形式
|
|
@@ -5,6 +5,7 @@ export default class All extends Command {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
apiKey: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
domain: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
maxCount: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
projectIdOrKey: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
};
|
|
@@ -15,6 +15,9 @@ export default class All extends Command {
|
|
|
15
15
|
`,
|
|
16
16
|
`<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./my-project
|
|
17
17
|
指定したディレクトリに課題とWikiを保存する
|
|
18
|
+
`,
|
|
19
|
+
`<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --maxCount 1000
|
|
20
|
+
最大1000件の課題を取得する(デフォルトは100件)
|
|
18
21
|
`,
|
|
19
22
|
];
|
|
20
23
|
static flags = {
|
|
@@ -26,6 +29,12 @@ export default class All extends Command {
|
|
|
26
29
|
description: 'Backlog domain (e.g. example.backlog.jp)',
|
|
27
30
|
required: true,
|
|
28
31
|
}),
|
|
32
|
+
maxCount: Flags.integer({
|
|
33
|
+
char: 'm',
|
|
34
|
+
default: 5000,
|
|
35
|
+
description: '一度に取得する課題の最大数(デフォルト: 5000)',
|
|
36
|
+
required: false,
|
|
37
|
+
}),
|
|
29
38
|
output: Flags.string({
|
|
30
39
|
char: 'o',
|
|
31
40
|
description: '出力ディレクトリパス',
|
|
@@ -39,7 +48,7 @@ export default class All extends Command {
|
|
|
39
48
|
async run() {
|
|
40
49
|
const { flags } = await this.parse(All);
|
|
41
50
|
try {
|
|
42
|
-
const { domain, projectIdOrKey } = flags;
|
|
51
|
+
const { domain, maxCount, projectIdOrKey } = flags;
|
|
43
52
|
const apiKey = flags.apiKey || getApiKey(this);
|
|
44
53
|
const outputRoot = flags.output || './backlog-data';
|
|
45
54
|
// 出力ディレクトリの作成
|
|
@@ -53,31 +62,48 @@ export default class All extends Command {
|
|
|
53
62
|
// Wikiの出力ディレクトリ
|
|
54
63
|
const wikiOutput = path.join(outputRoot, 'wiki');
|
|
55
64
|
await createOutputDirectory(wikiOutput);
|
|
56
|
-
// 課題の取得と保存
|
|
57
|
-
this.log('課題の取得を開始します...');
|
|
58
|
-
await downloadIssues(this, domain, projectId, apiKey, issueOutput, 100);
|
|
59
65
|
// 課題フォルダに設定ファイルを保存
|
|
60
66
|
await updateSettings(issueOutput, {
|
|
61
67
|
apiKey,
|
|
62
68
|
domain,
|
|
63
69
|
folderType: FolderType.ISSUE,
|
|
64
|
-
lastUpdated: new Date().toISOString(),
|
|
65
70
|
outputDir: issueOutput,
|
|
66
71
|
projectIdOrKey,
|
|
67
72
|
});
|
|
68
|
-
this.log('課題の取得が完了しました');
|
|
69
|
-
// Wikiの取得と保存
|
|
70
|
-
this.log('Wikiの取得を開始します...');
|
|
71
|
-
await downloadWikis(this, domain, projectIdOrKey, apiKey, wikiOutput);
|
|
72
73
|
// Wikiフォルダに設定ファイルを保存
|
|
73
74
|
await updateSettings(wikiOutput, {
|
|
74
75
|
apiKey,
|
|
75
76
|
domain,
|
|
76
77
|
folderType: FolderType.WIKI,
|
|
78
|
+
outputDir: wikiOutput,
|
|
79
|
+
projectIdOrKey,
|
|
80
|
+
});
|
|
81
|
+
// 課題の取得と保存
|
|
82
|
+
this.log('課題の取得を開始します...');
|
|
83
|
+
await downloadIssues(this, {
|
|
84
|
+
apiKey,
|
|
85
|
+
count: maxCount,
|
|
86
|
+
domain,
|
|
87
|
+
outputDir: issueOutput,
|
|
88
|
+
projectId,
|
|
89
|
+
});
|
|
90
|
+
// 課題フォルダの最終更新日時を更新
|
|
91
|
+
await updateSettings(issueOutput, {
|
|
77
92
|
lastUpdated: new Date().toISOString(),
|
|
93
|
+
});
|
|
94
|
+
this.log('課題の取得が完了しました');
|
|
95
|
+
// Wikiの取得と保存
|
|
96
|
+
this.log('Wikiの取得を開始します...');
|
|
97
|
+
await downloadWikis(this, {
|
|
98
|
+
apiKey,
|
|
99
|
+
domain,
|
|
78
100
|
outputDir: wikiOutput,
|
|
79
101
|
projectIdOrKey,
|
|
80
102
|
});
|
|
103
|
+
// Wikiフォルダの最終更新日時を更新
|
|
104
|
+
await updateSettings(wikiOutput, {
|
|
105
|
+
lastUpdated: new Date().toISOString(),
|
|
106
|
+
});
|
|
81
107
|
this.log('Wikiの取得が完了しました');
|
|
82
108
|
this.log('すべてのデータの取得が完了しました!');
|
|
83
109
|
}
|
|
@@ -5,8 +5,10 @@ export default class Issue extends Command {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
apiKey: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
domain: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
maxCount: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
projectIdOrKey: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
statusId: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
12
|
};
|
|
11
13
|
run(): Promise<void>;
|
|
12
14
|
}
|
|
@@ -12,8 +12,14 @@ export default class Issue extends Command {
|
|
|
12
12
|
`<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY
|
|
13
13
|
課題をMarkdownファイルとして保存する
|
|
14
14
|
`,
|
|
15
|
-
`<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./my-
|
|
15
|
+
`<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./my-issues
|
|
16
16
|
指定したディレクトリに課題を保存する
|
|
17
|
+
`,
|
|
18
|
+
`<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --statusId 1,2,3
|
|
19
|
+
指定したステータスIDの課題のみを取得する
|
|
20
|
+
`,
|
|
21
|
+
`<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --maxCount 10000
|
|
22
|
+
最大10000件の課題を取得する(デフォルトは5000件)
|
|
17
23
|
`,
|
|
18
24
|
];
|
|
19
25
|
static flags = {
|
|
@@ -25,6 +31,12 @@ export default class Issue extends Command {
|
|
|
25
31
|
description: 'Backlog domain (e.g. example.backlog.jp)',
|
|
26
32
|
required: true,
|
|
27
33
|
}),
|
|
34
|
+
maxCount: Flags.integer({
|
|
35
|
+
char: 'm',
|
|
36
|
+
default: 5000,
|
|
37
|
+
description: '一度に取得する課題の最大数(デフォルト: 5000)',
|
|
38
|
+
required: false,
|
|
39
|
+
}),
|
|
28
40
|
output: Flags.string({
|
|
29
41
|
char: 'o',
|
|
30
42
|
description: '出力ディレクトリパス',
|
|
@@ -34,29 +46,43 @@ export default class Issue extends Command {
|
|
|
34
46
|
description: 'Backlog project ID or key',
|
|
35
47
|
required: true,
|
|
36
48
|
}),
|
|
49
|
+
statusId: Flags.string({
|
|
50
|
+
description: 'ステータスID(カンマ区切りで複数指定可能)',
|
|
51
|
+
required: false,
|
|
52
|
+
}),
|
|
37
53
|
};
|
|
38
54
|
async run() {
|
|
39
55
|
const { flags } = await this.parse(Issue);
|
|
40
56
|
try {
|
|
41
|
-
const { domain, projectIdOrKey } = flags;
|
|
57
|
+
const { domain, maxCount, projectIdOrKey, statusId } = flags;
|
|
42
58
|
const apiKey = flags.apiKey || getApiKey(this);
|
|
43
|
-
const outputDir = flags.output || './issues';
|
|
59
|
+
const outputDir = flags.output || './backlog-issues';
|
|
44
60
|
// 出力ディレクトリの作成
|
|
45
61
|
await createOutputDirectory(outputDir);
|
|
46
62
|
// プロジェクトキーからプロジェクトIDを取得
|
|
47
63
|
const projectId = await validateAndGetProjectId(domain, projectIdOrKey, apiKey);
|
|
48
64
|
this.log(`プロジェクトID: ${projectId} を使用します`);
|
|
49
|
-
// 課題の取得と保存
|
|
50
|
-
await downloadIssues(this, domain, projectId, apiKey, outputDir, 100);
|
|
51
65
|
// 設定ファイルを保存
|
|
52
66
|
await updateSettings(outputDir, {
|
|
53
67
|
apiKey,
|
|
54
68
|
domain,
|
|
55
69
|
folderType: FolderType.ISSUE,
|
|
56
|
-
lastUpdated: new Date().toISOString(),
|
|
57
70
|
outputDir,
|
|
58
71
|
projectIdOrKey,
|
|
59
72
|
});
|
|
73
|
+
// 課題の取得と保存
|
|
74
|
+
await downloadIssues(this, {
|
|
75
|
+
apiKey,
|
|
76
|
+
count: maxCount,
|
|
77
|
+
domain,
|
|
78
|
+
outputDir,
|
|
79
|
+
projectId,
|
|
80
|
+
statusId,
|
|
81
|
+
});
|
|
82
|
+
// 最終更新日時を更新
|
|
83
|
+
await updateSettings(outputDir, {
|
|
84
|
+
lastUpdated: new Date().toISOString(),
|
|
85
|
+
});
|
|
60
86
|
this.log('課題の取得が完了しました!');
|
|
61
87
|
}
|
|
62
88
|
catch (error) {
|
|
@@ -207,7 +207,16 @@ export default class Update extends Command {
|
|
|
207
207
|
// 課題の更新
|
|
208
208
|
async updateIssues(options) {
|
|
209
209
|
this.log('課題の更新を開始します...');
|
|
210
|
-
|
|
210
|
+
// 設定ファイルから前回の更新日時を取得
|
|
211
|
+
const { lastUpdated } = await loadSettings(options.targetDir);
|
|
212
|
+
await downloadIssues(this, {
|
|
213
|
+
apiKey: options.apiKey,
|
|
214
|
+
count: 100,
|
|
215
|
+
domain: options.domain,
|
|
216
|
+
lastUpdated,
|
|
217
|
+
outputDir: options.targetDir,
|
|
218
|
+
projectId: options.projectId,
|
|
219
|
+
});
|
|
211
220
|
// 設定ファイルを更新
|
|
212
221
|
await updateSettings(options.targetDir, {
|
|
213
222
|
apiKey: options.apiKey,
|
|
@@ -222,7 +231,15 @@ export default class Update extends Command {
|
|
|
222
231
|
// Wikiの更新
|
|
223
232
|
async updateWikis(options) {
|
|
224
233
|
this.log('Wikiの更新を開始します...');
|
|
225
|
-
|
|
234
|
+
// 設定ファイルから前回の更新日時を取得
|
|
235
|
+
const { lastUpdated } = await loadSettings(options.targetDir);
|
|
236
|
+
await downloadWikis(this, {
|
|
237
|
+
apiKey: options.apiKey,
|
|
238
|
+
domain: options.domain,
|
|
239
|
+
lastUpdated,
|
|
240
|
+
outputDir: options.targetDir,
|
|
241
|
+
projectIdOrKey: options.projectIdOrKey,
|
|
242
|
+
});
|
|
226
243
|
// 設定ファイルを更新
|
|
227
244
|
await updateSettings(options.targetDir, {
|
|
228
245
|
apiKey: options.apiKey,
|
|
@@ -42,17 +42,25 @@ Wikiをダウンロードする
|
|
|
42
42
|
const outputDir = flags.output || './wiki';
|
|
43
43
|
// 出力ディレクトリの作成
|
|
44
44
|
await createOutputDirectory(outputDir);
|
|
45
|
-
// Wikiの取得と保存
|
|
46
|
-
await downloadWikis(this, domain, projectIdOrKey, apiKey, outputDir);
|
|
47
45
|
// 設定ファイルを保存
|
|
48
46
|
await updateSettings(outputDir, {
|
|
49
47
|
apiKey,
|
|
50
48
|
domain,
|
|
51
49
|
folderType: FolderType.WIKI,
|
|
52
|
-
lastUpdated: new Date().toISOString(),
|
|
53
50
|
outputDir,
|
|
54
51
|
projectIdOrKey,
|
|
55
52
|
});
|
|
53
|
+
// Wikiの取得と保存
|
|
54
|
+
await downloadWikis(this, {
|
|
55
|
+
apiKey,
|
|
56
|
+
domain,
|
|
57
|
+
outputDir,
|
|
58
|
+
projectIdOrKey,
|
|
59
|
+
});
|
|
60
|
+
// 最終更新日時を更新
|
|
61
|
+
await updateSettings(outputDir, {
|
|
62
|
+
lastUpdated: new Date().toISOString(),
|
|
63
|
+
});
|
|
56
64
|
this.log('Wikiの取得が完了しました!');
|
|
57
65
|
}
|
|
58
66
|
catch (error) {
|
|
@@ -2,20 +2,26 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
/**
|
|
3
3
|
* Backlogから課題をダウンロードする
|
|
4
4
|
* @param command コマンドインスタンス
|
|
5
|
-
* @param
|
|
6
|
-
* @param projectId プロジェクトID
|
|
7
|
-
* @param apiKey APIキー
|
|
8
|
-
* @param outputDir 出力ディレクトリ
|
|
9
|
-
* @param count 一度に取得する課題数
|
|
10
|
-
* @param statusId ステータスID(オプション)
|
|
5
|
+
* @param options 課題ダウンロードのオプション
|
|
11
6
|
*/
|
|
12
|
-
export declare function downloadIssues(command: Command,
|
|
7
|
+
export declare function downloadIssues(command: Command, options: {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
count?: number;
|
|
10
|
+
domain: string;
|
|
11
|
+
lastUpdated?: string;
|
|
12
|
+
outputDir: string;
|
|
13
|
+
projectId: number;
|
|
14
|
+
statusId?: string;
|
|
15
|
+
}): Promise<void>;
|
|
13
16
|
/**
|
|
14
17
|
* BacklogからWikiをダウンロードする
|
|
15
18
|
* @param command コマンドインスタンス
|
|
16
|
-
* @param
|
|
17
|
-
* @param projectIdOrKey プロジェクトIDまたはキー
|
|
18
|
-
* @param apiKey APIキー
|
|
19
|
-
* @param outputDir 出力ディレクトリ
|
|
19
|
+
* @param options Wikiダウンロードのオプション
|
|
20
20
|
*/
|
|
21
|
-
export declare function downloadWikis(command: Command,
|
|
21
|
+
export declare function downloadWikis(command: Command, options: {
|
|
22
|
+
apiKey: string;
|
|
23
|
+
domain: string;
|
|
24
|
+
lastUpdated?: string;
|
|
25
|
+
outputDir: string;
|
|
26
|
+
projectIdOrKey: string;
|
|
27
|
+
}): Promise<void>;
|
|
@@ -1,56 +1,101 @@
|
|
|
1
1
|
import ky from 'ky';
|
|
2
2
|
import * as fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import process from 'node:process';
|
|
4
5
|
import { sanitizeFileName, sanitizeWikiFileName } from './common.js';
|
|
6
|
+
import { RateLimiter } from './sleep.js';
|
|
5
7
|
/**
|
|
6
8
|
* Backlogから課題をダウンロードする
|
|
7
9
|
* @param command コマンドインスタンス
|
|
8
|
-
* @param
|
|
9
|
-
* @param projectId プロジェクトID
|
|
10
|
-
* @param apiKey APIキー
|
|
11
|
-
* @param outputDir 出力ディレクトリ
|
|
12
|
-
* @param count 一度に取得する課題数
|
|
13
|
-
* @param statusId ステータスID(オプション)
|
|
10
|
+
* @param options 課題ダウンロードのオプション
|
|
14
11
|
*/
|
|
15
|
-
export async function downloadIssues(command,
|
|
16
|
-
const baseUrl = `https://${domain}/api/v2`;
|
|
12
|
+
export async function downloadIssues(command, options) {
|
|
13
|
+
const baseUrl = `https://${options.domain}/api/v2`;
|
|
17
14
|
command.log('課題の取得を開始します...');
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
// 全ての課題を格納する配列
|
|
16
|
+
let allIssues = [];
|
|
17
|
+
// countのデフォルト値を5000に設定
|
|
18
|
+
const count = options.count ?? 5000;
|
|
19
|
+
const maxCount = Math.min(count, 100); // APIの制限は100件
|
|
20
|
+
// APIリクエスト数をカウントするためのRateLimiterを作成
|
|
21
|
+
const rateLimiter = new RateLimiter(command);
|
|
22
|
+
// 課題を取得する関数
|
|
23
|
+
const fetchIssues = async (offset) => {
|
|
24
|
+
// APIリクエスト数をインクリメント
|
|
25
|
+
await rateLimiter.increment();
|
|
26
|
+
// APIパラメータの構築
|
|
27
|
+
const params = new URLSearchParams({
|
|
28
|
+
apiKey: options.apiKey,
|
|
29
|
+
count: maxCount.toString(),
|
|
30
|
+
offset: offset.toString(),
|
|
31
|
+
'projectId[]': options.projectId.toString(),
|
|
32
|
+
});
|
|
33
|
+
// ステータスIDが指定されている場合は追加
|
|
34
|
+
if (options.statusId) {
|
|
35
|
+
params.append('statusId[]', options.statusId);
|
|
36
|
+
}
|
|
37
|
+
// 進捗状況を一行で更新
|
|
38
|
+
process.stdout.write(`\r課題を取得中... (${allIssues.length}件取得済み)`);
|
|
39
|
+
return ky.get(`${baseUrl}/issues?${params.toString()}`).json();
|
|
40
|
+
};
|
|
41
|
+
// 再帰的に全ての課題を取得
|
|
42
|
+
const fetchAllIssues = async (offset) => {
|
|
43
|
+
try {
|
|
44
|
+
const issues = await fetchIssues(offset);
|
|
45
|
+
// 取得した課題を追加
|
|
46
|
+
allIssues = [...allIssues, ...issues];
|
|
47
|
+
// 次のページがあるかどうかを確認
|
|
48
|
+
if (issues.length === maxCount) {
|
|
49
|
+
// 次のページを取得
|
|
50
|
+
await fetchAllIssues(offset + maxCount);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
command.error(`課題の取得に失敗しました: ${error instanceof Error ? error.message : String(error)}`);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
// 課題取得開始
|
|
58
|
+
await fetchAllIssues(0);
|
|
59
|
+
command.log(`\n合計 ${allIssues.length}件の課題が見つかりました。`);
|
|
60
|
+
// 前回の更新日時より新しい課題のみをフィルタリング
|
|
61
|
+
let filteredIssues = allIssues;
|
|
62
|
+
if (options.lastUpdated) {
|
|
63
|
+
const lastUpdatedDate = new Date(options.lastUpdated);
|
|
64
|
+
filteredIssues = allIssues.filter((issue) => {
|
|
65
|
+
const issueUpdatedDate = new Date(issue.updated);
|
|
66
|
+
return issueUpdatedDate > lastUpdatedDate;
|
|
67
|
+
});
|
|
68
|
+
command.log(`前回の更新日時(${options.lastUpdated})以降に更新された${filteredIssues.length}件の課題を処理します。`);
|
|
69
|
+
}
|
|
70
|
+
if (filteredIssues.length === 0) {
|
|
71
|
+
command.log('更新が必要な課題はありません。');
|
|
72
|
+
return;
|
|
27
73
|
}
|
|
28
|
-
const issues = await ky.get(`${baseUrl}/issues?${params.toString()}`).json();
|
|
29
|
-
command.log(`${issues.length}件の課題が見つかりました。`);
|
|
30
74
|
// 各課題の詳細情報を取得して保存
|
|
31
75
|
command.log('課題を保存しています...');
|
|
32
|
-
//
|
|
33
|
-
const
|
|
76
|
+
// 並列処理ではなく順次処理に変更
|
|
77
|
+
for (const issue of filteredIssues) {
|
|
34
78
|
try {
|
|
35
|
-
//
|
|
36
|
-
const
|
|
37
|
-
|
|
79
|
+
// 進捗状況を一行で更新
|
|
80
|
+
const currentIndex = filteredIssues.indexOf(issue) + 1;
|
|
81
|
+
process.stdout.write(`\r課題を保存中... (${currentIndex}/${filteredIssues.length}件)`);
|
|
38
82
|
// BacklogのIssueへのリンクを作成
|
|
39
|
-
const backlogIssueUrl = `https://${domain}/view/${issue.issueKey}`;
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
apiKey,
|
|
44
|
-
|
|
45
|
-
|
|
83
|
+
const backlogIssueUrl = `https://${options.domain}/view/${issue.issueKey}`;
|
|
84
|
+
// コメントを取得する関数を呼び出し
|
|
85
|
+
// eslint-disable-next-line no-await-in-loop
|
|
86
|
+
const { comments: allComments } = await fetchAllCommentsForIssue({
|
|
87
|
+
apiKey: options.apiKey,
|
|
88
|
+
baseUrl,
|
|
89
|
+
command,
|
|
90
|
+
issueKey: issue.issueKey,
|
|
91
|
+
rateLimiter,
|
|
46
92
|
});
|
|
47
|
-
const comments = await ky.get(`${baseUrl}/issues/${issue.issueKey}/comments?${commentsParams.toString()}`).json();
|
|
48
93
|
// コメントセクションを作成
|
|
49
94
|
let commentsSection = '';
|
|
50
|
-
if (
|
|
95
|
+
if (allComments.length > 0) {
|
|
51
96
|
commentsSection = '\n\n## コメント\n';
|
|
52
97
|
let commentIndex = 1;
|
|
53
|
-
for (const comment of
|
|
98
|
+
for (const comment of allComments) {
|
|
54
99
|
const commentDate = new Date(comment.created).toLocaleString('ja-JP');
|
|
55
100
|
commentsSection += `\n### コメント ${commentIndex}\n- **投稿者**: ${comment.createdUser.name}\n- **日時**: ${commentDate}\n\n${comment.content || '(内容なし)'}\n\n---\n`;
|
|
56
101
|
commentIndex++;
|
|
@@ -58,6 +103,16 @@ export async function downloadIssues(command, domain, projectId, apiKey, outputD
|
|
|
58
103
|
// 最後の区切り線を削除
|
|
59
104
|
commentsSection = commentsSection.slice(0, -5);
|
|
60
105
|
}
|
|
106
|
+
// 課題の作成年を取得
|
|
107
|
+
const createdYear = new Date(issue.created).getFullYear();
|
|
108
|
+
// 年ごとのフォルダパスを作成
|
|
109
|
+
const yearDirPath = path.join(options.outputDir, createdYear.toString());
|
|
110
|
+
// 年ごとのフォルダを作成
|
|
111
|
+
// eslint-disable-next-line no-await-in-loop
|
|
112
|
+
await fs.mkdir(yearDirPath, { recursive: true });
|
|
113
|
+
// 年ごとのフォルダ内にMarkdownファイルを保存
|
|
114
|
+
const issueFileName = `${sanitizeFileName(issue.summary)}.md`;
|
|
115
|
+
const issueFilePath = path.join(yearDirPath, issueFileName);
|
|
61
116
|
// Markdownファイルに書き込む
|
|
62
117
|
const assigneeName = issue.assignee ? issue.assignee.name : '未割り当て';
|
|
63
118
|
const markdownContent = `# ${issue.summary}
|
|
@@ -73,42 +128,91 @@ export async function downloadIssues(command, domain, projectId, apiKey, outputD
|
|
|
73
128
|
|
|
74
129
|
## 詳細
|
|
75
130
|
${issue.description || '詳細情報なし'}${commentsSection}`;
|
|
131
|
+
// eslint-disable-next-line no-await-in-loop
|
|
76
132
|
await fs.writeFile(issueFilePath, markdownContent);
|
|
77
|
-
command.log(`課題 "${issue.issueKey}: ${issue.summary}" を ${issueFilePath} に保存しました`);
|
|
78
133
|
}
|
|
79
134
|
catch (error) {
|
|
80
135
|
command.warn(`課題 ${issue.issueKey} の保存に失敗しました: ${error instanceof Error ? error.message : String(error)}`);
|
|
81
136
|
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
137
|
+
}
|
|
138
|
+
command.log('\n課題のダウンロードが完了しました!');
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 課題のコメントを全て取得する
|
|
142
|
+
*/
|
|
143
|
+
async function fetchAllCommentsForIssue({ apiKey, baseUrl, command, issueKey, rateLimiter, }) {
|
|
144
|
+
const allComments = [];
|
|
145
|
+
const fetchComments = async (minId) => {
|
|
146
|
+
// APIリクエスト数をインクリメント
|
|
147
|
+
await rateLimiter.increment();
|
|
148
|
+
let url = `${baseUrl}/issues/${issueKey}/comments?apiKey=${apiKey}&count=100`;
|
|
149
|
+
if (minId) {
|
|
150
|
+
url += `&minId=${minId}`;
|
|
151
|
+
}
|
|
152
|
+
const comments = await ky.get(url).json();
|
|
153
|
+
allComments.push(...comments);
|
|
154
|
+
if (comments.length === 100) {
|
|
155
|
+
// 取得したコメントの最後のIDを次のリクエストのminIdとして使用
|
|
156
|
+
const lastCommentId = comments.at(-1).id;
|
|
157
|
+
await fetchComments(lastCommentId + 1);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
try {
|
|
161
|
+
await fetchComments();
|
|
162
|
+
return { comments: allComments };
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
command.warn(`課題 ${issueKey} のコメント取得に失敗しました: ${error instanceof Error ? error.message : String(error)}`);
|
|
166
|
+
return { comments: allComments };
|
|
167
|
+
}
|
|
85
168
|
}
|
|
86
169
|
/**
|
|
87
170
|
* BacklogからWikiをダウンロードする
|
|
88
171
|
* @param command コマンドインスタンス
|
|
89
|
-
* @param
|
|
90
|
-
* @param projectIdOrKey プロジェクトIDまたはキー
|
|
91
|
-
* @param apiKey APIキー
|
|
92
|
-
* @param outputDir 出力ディレクトリ
|
|
172
|
+
* @param options Wikiダウンロードのオプション
|
|
93
173
|
*/
|
|
94
|
-
export async function downloadWikis(command,
|
|
95
|
-
const baseUrl = `https://${domain}/api/v2`;
|
|
174
|
+
export async function downloadWikis(command, options) {
|
|
175
|
+
const baseUrl = `https://${options.domain}/api/v2`;
|
|
96
176
|
command.log('Wikiの取得を開始します...');
|
|
177
|
+
// APIリクエスト数をカウントするためのRateLimiterを作成
|
|
178
|
+
const rateLimiter = new RateLimiter(command);
|
|
97
179
|
// Wiki一覧の取得
|
|
98
180
|
command.log('Wiki一覧を取得しています...');
|
|
181
|
+
// APIリクエスト数をインクリメント
|
|
182
|
+
await rateLimiter.increment();
|
|
99
183
|
const wikis = await ky
|
|
100
|
-
.get(`${baseUrl}/wikis?apiKey=${apiKey}&projectIdOrKey=${projectIdOrKey}`)
|
|
184
|
+
.get(`${baseUrl}/wikis?apiKey=${options.apiKey}&projectIdOrKey=${options.projectIdOrKey}`)
|
|
101
185
|
.json();
|
|
102
186
|
command.log(`${wikis.length}件のWikiが見つかりました。`);
|
|
187
|
+
// 前回の更新日時より新しいWikiのみをフィルタリング
|
|
188
|
+
let filteredWikis = wikis;
|
|
189
|
+
if (options.lastUpdated) {
|
|
190
|
+
const lastUpdatedDate = new Date(options.lastUpdated);
|
|
191
|
+
filteredWikis = wikis.filter((wiki) => {
|
|
192
|
+
const wikiUpdatedDate = new Date(wiki.updated);
|
|
193
|
+
return wikiUpdatedDate > lastUpdatedDate;
|
|
194
|
+
});
|
|
195
|
+
command.log(`前回の更新日時(${options.lastUpdated})以降に更新された${filteredWikis.length}件のWikiを処理します。`);
|
|
196
|
+
}
|
|
197
|
+
if (filteredWikis.length === 0) {
|
|
198
|
+
command.log('更新が必要なWikiはありません。');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
103
201
|
// 各Wikiの詳細情報を取得
|
|
104
202
|
command.log('Wiki詳細を取得しています...');
|
|
105
|
-
//
|
|
106
|
-
const
|
|
203
|
+
// 並列処理ではなく順次処理に変更
|
|
204
|
+
for (const wiki of filteredWikis) {
|
|
107
205
|
const wikiId = wiki.id;
|
|
108
|
-
command.log(`Wiki: ${wiki.name} (ID: ${wikiId}) を取得しています`);
|
|
109
206
|
try {
|
|
207
|
+
// APIリクエスト数をインクリメント
|
|
208
|
+
// eslint-disable-next-line no-await-in-loop
|
|
209
|
+
await rateLimiter.increment();
|
|
210
|
+
// 進捗状況を一行で更新
|
|
211
|
+
const currentIndex = filteredWikis.indexOf(wiki) + 1;
|
|
212
|
+
process.stdout.write(`\rWikiを取得中... (${currentIndex}/${filteredWikis.length}件)`);
|
|
213
|
+
// eslint-disable-next-line no-await-in-loop
|
|
110
214
|
const wikiDetail = await ky
|
|
111
|
-
.get(`${baseUrl}/wikis/${wikiId}?projectIdOrKey=${projectIdOrKey}&apiKey=${apiKey}`)
|
|
215
|
+
.get(`${baseUrl}/wikis/${wikiId}?projectIdOrKey=${options.projectIdOrKey}&apiKey=${options.apiKey}`)
|
|
112
216
|
.json();
|
|
113
217
|
// Wikiの名前をファイルパスとして使用
|
|
114
218
|
const wikiName = wiki.name;
|
|
@@ -119,23 +223,26 @@ export async function downloadWikis(command, domain, projectIdOrKey, apiKey, out
|
|
|
119
223
|
// ディレクトリ構造を作成(必要な場合)
|
|
120
224
|
const dirPath = path.dirname(wikiFileName);
|
|
121
225
|
if (dirPath !== '.') {
|
|
122
|
-
|
|
226
|
+
// eslint-disable-next-line no-await-in-loop
|
|
227
|
+
await fs.mkdir(path.join(options.outputDir, dirPath), { recursive: true });
|
|
123
228
|
}
|
|
124
229
|
// Markdownファイルを保存
|
|
125
|
-
const wikiFilePath = path.join(outputDir, wikiFileName);
|
|
230
|
+
const wikiFilePath = path.join(options.outputDir, wikiFileName);
|
|
126
231
|
// JSONからcontentフィールドを取得
|
|
127
232
|
const content = wikiDetail.content || '';
|
|
128
233
|
// BacklogのWikiへのリンクを作成
|
|
129
|
-
const backlogWikiUrl = `https://${domain}/alias/wiki/${wikiId}`;
|
|
234
|
+
const backlogWikiUrl = `https://${options.domain}/alias/wiki/${wikiId}`;
|
|
130
235
|
// Markdownファイルに書き込む(タイトルとBacklogリンクを追加)
|
|
131
236
|
const markdownContent = `# ${wiki.name}\n\n[Backlog Wiki Link](${backlogWikiUrl})\n\n${content}`;
|
|
237
|
+
// eslint-disable-next-line no-await-in-loop
|
|
132
238
|
await fs.writeFile(wikiFilePath, markdownContent);
|
|
133
|
-
|
|
239
|
+
// 進捗状況を一行で更新
|
|
240
|
+
const wikiIndex = filteredWikis.indexOf(wiki) + 1;
|
|
241
|
+
process.stdout.write(`\rWikiを保存中... (${wikiIndex}/${filteredWikis.length}件)`);
|
|
134
242
|
}
|
|
135
243
|
catch (error) {
|
|
136
|
-
command.warn(`Wiki
|
|
244
|
+
command.warn(`Wiki ${wiki.name} の取得に失敗しました: ${error instanceof Error ? error.message : String(error)}`);
|
|
137
245
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
command.log('Wikiのダウンロードが完了しました!');
|
|
246
|
+
}
|
|
247
|
+
command.log('\nWikiのダウンロードが完了しました!');
|
|
141
248
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* 指定したミリ秒だけ待機する
|
|
4
|
+
* @param ms 待機時間(ミリ秒)
|
|
5
|
+
*/
|
|
6
|
+
export declare const sleep: (ms: number) => Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* APIリクエストのレート制限対策のためのユーティリティ
|
|
9
|
+
* 100リクエストごとに15秒間待機する
|
|
10
|
+
*/
|
|
11
|
+
export declare class RateLimiter {
|
|
12
|
+
private readonly command;
|
|
13
|
+
private requestCount;
|
|
14
|
+
constructor(command: Command);
|
|
15
|
+
/**
|
|
16
|
+
* 現在のリクエスト数を取得する
|
|
17
|
+
*/
|
|
18
|
+
getCount(): number;
|
|
19
|
+
/**
|
|
20
|
+
* APIリクエストをカウントし、必要に応じて待機する
|
|
21
|
+
* @returns 現在のリクエスト数
|
|
22
|
+
*/
|
|
23
|
+
increment(): Promise<number>;
|
|
24
|
+
/**
|
|
25
|
+
* リクエスト数を設定する
|
|
26
|
+
*/
|
|
27
|
+
setCount(count: number): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 指定したミリ秒だけ待機する
|
|
3
|
+
* @param ms 待機時間(ミリ秒)
|
|
4
|
+
*/
|
|
5
|
+
export const sleep = (ms) => new Promise((resolve) => {
|
|
6
|
+
setTimeout(resolve, ms);
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* APIリクエストのレート制限対策のためのユーティリティ
|
|
10
|
+
* 100リクエストごとに15秒間待機する
|
|
11
|
+
*/
|
|
12
|
+
export class RateLimiter {
|
|
13
|
+
command;
|
|
14
|
+
requestCount = 0;
|
|
15
|
+
constructor(command) {
|
|
16
|
+
this.command = command;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 現在のリクエスト数を取得する
|
|
20
|
+
*/
|
|
21
|
+
getCount() {
|
|
22
|
+
return this.requestCount;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* APIリクエストをカウントし、必要に応じて待機する
|
|
26
|
+
* @returns 現在のリクエスト数
|
|
27
|
+
*/
|
|
28
|
+
async increment() {
|
|
29
|
+
this.requestCount++;
|
|
30
|
+
if (this.requestCount > 1 && this.requestCount % 100 === 0) {
|
|
31
|
+
this.command.log('レート制限を回避するため15秒間待機します...');
|
|
32
|
+
await sleep(15_000);
|
|
33
|
+
}
|
|
34
|
+
return this.requestCount;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* リクエスト数を設定する
|
|
38
|
+
*/
|
|
39
|
+
setCount(count) {
|
|
40
|
+
this.requestCount = count;
|
|
41
|
+
}
|
|
42
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
"description": "Backlogから課題とWikiを取得してMarkdownファイルとして保存する",
|
|
7
7
|
"examples": [
|
|
8
8
|
"<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY\n課題とWikiをMarkdownファイルとして保存する\n",
|
|
9
|
-
"<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./my-project\n指定したディレクトリに課題とWikiを保存する\n"
|
|
9
|
+
"<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./my-project\n指定したディレクトリに課題とWikiを保存する\n",
|
|
10
|
+
"<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --maxCount 1000\n最大1000件の課題を取得する(デフォルトは100件)\n"
|
|
10
11
|
],
|
|
11
12
|
"flags": {
|
|
12
13
|
"apiKey": {
|
|
@@ -25,6 +26,16 @@
|
|
|
25
26
|
"multiple": false,
|
|
26
27
|
"type": "option"
|
|
27
28
|
},
|
|
29
|
+
"maxCount": {
|
|
30
|
+
"char": "m",
|
|
31
|
+
"description": "一度に取得する課題の最大数(デフォルト: 5000)",
|
|
32
|
+
"name": "maxCount",
|
|
33
|
+
"required": false,
|
|
34
|
+
"default": 5000,
|
|
35
|
+
"hasDynamicHelp": false,
|
|
36
|
+
"multiple": false,
|
|
37
|
+
"type": "option"
|
|
38
|
+
},
|
|
28
39
|
"output": {
|
|
29
40
|
"char": "o",
|
|
30
41
|
"description": "出力ディレクトリパス",
|
|
@@ -59,21 +70,15 @@
|
|
|
59
70
|
"index.js"
|
|
60
71
|
]
|
|
61
72
|
},
|
|
62
|
-
"
|
|
73
|
+
"issue": {
|
|
63
74
|
"aliases": [],
|
|
64
|
-
"args": {
|
|
65
|
-
|
|
66
|
-
"description": "更新対象のディレクトリ(設定ファイルが保存されている場所)",
|
|
67
|
-
"name": "directory",
|
|
68
|
-
"required": false
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
"description": "Backlogから最新データを取得して更新する",
|
|
75
|
+
"args": {},
|
|
76
|
+
"description": "Backlogから課題を取得してMarkdownファイルとして保存する",
|
|
72
77
|
"examples": [
|
|
73
|
-
"<%= config.bin %> <%= command.id
|
|
74
|
-
"<%= config.bin %> <%= command.id %> --
|
|
75
|
-
"<%= config.bin %> <%= command.id %> --
|
|
76
|
-
"<%= config.bin %> <%= command.id %>
|
|
78
|
+
"<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY\n課題をMarkdownファイルとして保存する\n",
|
|
79
|
+
"<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --output ./my-issues\n指定したディレクトリに課題を保存する\n",
|
|
80
|
+
"<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --statusId 1,2,3\n指定したステータスIDの課題のみを取得する\n",
|
|
81
|
+
"<%= config.bin %> <%= command.id %> --domain example.backlog.jp --projectIdOrKey PROJECT_KEY --apiKey YOUR_API_KEY --maxCount 10000\n最大10000件の課題を取得する(デフォルトは5000件)\n"
|
|
77
82
|
],
|
|
78
83
|
"flags": {
|
|
79
84
|
"apiKey": {
|
|
@@ -87,45 +92,50 @@
|
|
|
87
92
|
"domain": {
|
|
88
93
|
"description": "Backlog domain (e.g. example.backlog.jp)",
|
|
89
94
|
"name": "domain",
|
|
90
|
-
"required":
|
|
95
|
+
"required": true,
|
|
91
96
|
"hasDynamicHelp": false,
|
|
92
97
|
"multiple": false,
|
|
93
98
|
"type": "option"
|
|
94
99
|
},
|
|
95
|
-
"
|
|
96
|
-
"char": "
|
|
97
|
-
"description": "
|
|
98
|
-
"name": "
|
|
100
|
+
"maxCount": {
|
|
101
|
+
"char": "m",
|
|
102
|
+
"description": "一度に取得する課題の最大数(デフォルト: 5000)",
|
|
103
|
+
"name": "maxCount",
|
|
99
104
|
"required": false,
|
|
100
|
-
"
|
|
101
|
-
"
|
|
105
|
+
"default": 5000,
|
|
106
|
+
"hasDynamicHelp": false,
|
|
107
|
+
"multiple": false,
|
|
108
|
+
"type": "option"
|
|
102
109
|
},
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
110
|
+
"output": {
|
|
111
|
+
"char": "o",
|
|
112
|
+
"description": "出力ディレクトリパス",
|
|
113
|
+
"name": "output",
|
|
106
114
|
"required": false,
|
|
107
|
-
"
|
|
108
|
-
"
|
|
115
|
+
"hasDynamicHelp": false,
|
|
116
|
+
"multiple": false,
|
|
117
|
+
"type": "option"
|
|
109
118
|
},
|
|
110
119
|
"projectIdOrKey": {
|
|
111
120
|
"description": "Backlog project ID or key",
|
|
112
121
|
"name": "projectIdOrKey",
|
|
113
|
-
"required":
|
|
122
|
+
"required": true,
|
|
114
123
|
"hasDynamicHelp": false,
|
|
115
124
|
"multiple": false,
|
|
116
125
|
"type": "option"
|
|
117
126
|
},
|
|
118
|
-
"
|
|
119
|
-
"description": "
|
|
120
|
-
"name": "
|
|
127
|
+
"statusId": {
|
|
128
|
+
"description": "ステータスID(カンマ区切りで複数指定可能)",
|
|
129
|
+
"name": "statusId",
|
|
121
130
|
"required": false,
|
|
122
|
-
"
|
|
123
|
-
"
|
|
131
|
+
"hasDynamicHelp": false,
|
|
132
|
+
"multiple": false,
|
|
133
|
+
"type": "option"
|
|
124
134
|
}
|
|
125
135
|
},
|
|
126
136
|
"hasDynamicHelp": false,
|
|
127
137
|
"hiddenAliases": [],
|
|
128
|
-
"id": "
|
|
138
|
+
"id": "issue",
|
|
129
139
|
"pluginAlias": "backlog-exporter",
|
|
130
140
|
"pluginName": "backlog-exporter",
|
|
131
141
|
"pluginType": "core",
|
|
@@ -135,17 +145,25 @@
|
|
|
135
145
|
"relativePath": [
|
|
136
146
|
"dist",
|
|
137
147
|
"commands",
|
|
138
|
-
"
|
|
148
|
+
"issue",
|
|
139
149
|
"index.js"
|
|
140
150
|
]
|
|
141
151
|
},
|
|
142
|
-
"
|
|
152
|
+
"update": {
|
|
143
153
|
"aliases": [],
|
|
144
|
-
"args": {
|
|
145
|
-
|
|
154
|
+
"args": {
|
|
155
|
+
"directory": {
|
|
156
|
+
"description": "更新対象のディレクトリ(設定ファイルが保存されている場所)",
|
|
157
|
+
"name": "directory",
|
|
158
|
+
"required": false
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"description": "Backlogから最新データを取得して更新する",
|
|
146
162
|
"examples": [
|
|
147
|
-
"<%= config.bin %> <%= command.id
|
|
148
|
-
"<%= config.bin %> <%= command.id %> --
|
|
163
|
+
"<%= config.bin %> <%= command.id %>\nカレントディレクトリの設定を使用して更新する\n",
|
|
164
|
+
"<%= config.bin %> <%= command.id %> --force\n確認プロンプトをスキップする\n",
|
|
165
|
+
"<%= config.bin %> <%= command.id %> --apiKey YOUR_API_KEY --domain example.backlog.jp --projectIdOrKey PROJECT_KEY\n指定したパラメータで更新する(設定ファイルが存在する場合は上書きされます)\n",
|
|
166
|
+
"<%= config.bin %> <%= command.id %> ./my-project\n指定したディレクトリの設定を使用して更新する\n"
|
|
149
167
|
],
|
|
150
168
|
"flags": {
|
|
151
169
|
"apiKey": {
|
|
@@ -159,32 +177,45 @@
|
|
|
159
177
|
"domain": {
|
|
160
178
|
"description": "Backlog domain (e.g. example.backlog.jp)",
|
|
161
179
|
"name": "domain",
|
|
162
|
-
"required":
|
|
180
|
+
"required": false,
|
|
163
181
|
"hasDynamicHelp": false,
|
|
164
182
|
"multiple": false,
|
|
165
183
|
"type": "option"
|
|
166
184
|
},
|
|
167
|
-
"
|
|
168
|
-
"char": "
|
|
169
|
-
"description": "
|
|
170
|
-
"name": "
|
|
185
|
+
"force": {
|
|
186
|
+
"char": "f",
|
|
187
|
+
"description": "確認プロンプトをスキップする",
|
|
188
|
+
"name": "force",
|
|
171
189
|
"required": false,
|
|
172
|
-
"
|
|
173
|
-
"
|
|
174
|
-
|
|
190
|
+
"allowNo": false,
|
|
191
|
+
"type": "boolean"
|
|
192
|
+
},
|
|
193
|
+
"issuesOnly": {
|
|
194
|
+
"description": "課題のみを更新する",
|
|
195
|
+
"name": "issuesOnly",
|
|
196
|
+
"required": false,
|
|
197
|
+
"allowNo": false,
|
|
198
|
+
"type": "boolean"
|
|
175
199
|
},
|
|
176
200
|
"projectIdOrKey": {
|
|
177
201
|
"description": "Backlog project ID or key",
|
|
178
202
|
"name": "projectIdOrKey",
|
|
179
|
-
"required":
|
|
203
|
+
"required": false,
|
|
180
204
|
"hasDynamicHelp": false,
|
|
181
205
|
"multiple": false,
|
|
182
206
|
"type": "option"
|
|
207
|
+
},
|
|
208
|
+
"wikisOnly": {
|
|
209
|
+
"description": "Wikiのみを更新する",
|
|
210
|
+
"name": "wikisOnly",
|
|
211
|
+
"required": false,
|
|
212
|
+
"allowNo": false,
|
|
213
|
+
"type": "boolean"
|
|
183
214
|
}
|
|
184
215
|
},
|
|
185
216
|
"hasDynamicHelp": false,
|
|
186
217
|
"hiddenAliases": [],
|
|
187
|
-
"id": "
|
|
218
|
+
"id": "update",
|
|
188
219
|
"pluginAlias": "backlog-exporter",
|
|
189
220
|
"pluginName": "backlog-exporter",
|
|
190
221
|
"pluginType": "core",
|
|
@@ -194,7 +225,7 @@
|
|
|
194
225
|
"relativePath": [
|
|
195
226
|
"dist",
|
|
196
227
|
"commands",
|
|
197
|
-
"
|
|
228
|
+
"update",
|
|
198
229
|
"index.js"
|
|
199
230
|
]
|
|
200
231
|
},
|
|
@@ -258,5 +289,5 @@
|
|
|
258
289
|
]
|
|
259
290
|
}
|
|
260
291
|
},
|
|
261
|
-
"version": "0.
|
|
292
|
+
"version": "0.2.1"
|
|
262
293
|
}
|