@c-time/frelio-cli 1.3.12 → 1.4.0
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/dist/commands/add-staging.d.ts +2 -3
- package/dist/commands/add-staging.js +38 -184
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +171 -368
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +11 -67
- package/dist/core/bundle.d.ts +14 -0
- package/dist/core/bundle.js +122 -0
- package/dist/core/cloudflare.d.ts +26 -0
- package/dist/core/cloudflare.js +60 -0
- package/dist/core/config.d.ts +26 -0
- package/dist/core/config.js +120 -0
- package/dist/core/content-structure.d.ts +19 -0
- package/dist/core/content-structure.js +116 -0
- package/dist/core/file-generators.d.ts +28 -0
- package/dist/core/file-generators.js +93 -0
- package/dist/core/git-operations.d.ts +15 -0
- package/dist/core/git-operations.js +78 -0
- package/dist/core/github.d.ts +16 -0
- package/dist/core/github.js +43 -0
- package/dist/core/index.d.ts +23 -0
- package/dist/core/index.js +30 -0
- package/dist/core/prerequisites.d.ts +22 -0
- package/dist/core/prerequisites.js +107 -0
- package/dist/core/status.d.ts +18 -0
- package/dist/core/status.js +122 -0
- package/dist/core/terraform.d.ts +7 -0
- package/dist/core/terraform.js +47 -0
- package/dist/core/types.d.ts +48 -0
- package/dist/core/types.js +21 -0
- package/dist/core/workflows.d.ts +11 -0
- package/dist/core/workflows.js +180 -0
- package/dist/index.js +2 -4
- package/dist/lib/templates.d.ts +9 -1
- package/dist/lib/templates.js +351 -27
- package/package.json +1 -1
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* frelio add-staging - ステージング環境(プレビュー用ブランチ)の追加
|
|
3
|
+
*
|
|
4
|
+
* core/* の関数を呼ぶ薄いオーケストレーター。
|
|
3
5
|
*/
|
|
4
6
|
type AddStagingOptions = {
|
|
5
7
|
name?: string;
|
|
6
|
-
pagesProject?: string;
|
|
7
|
-
domain?: string;
|
|
8
|
-
skipCloudflare?: boolean;
|
|
9
8
|
};
|
|
10
9
|
export declare function addStagingCommand(options: AddStagingOptions): Promise<void>;
|
|
11
10
|
export {};
|
|
@@ -1,136 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* frelio add-staging - ステージング環境(プレビュー用ブランチ)の追加
|
|
3
|
+
*
|
|
4
|
+
* core/* の関数を呼ぶ薄いオーケストレーター。
|
|
3
5
|
*/
|
|
4
6
|
import prompts from 'prompts';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { log, logStep, logSuccess, logError } from '../lib/shell.js';
|
|
8
|
+
import { validateStagingName } from '../lib/validators.js';
|
|
9
|
+
import { checkGit } from '../core/prerequisites.js';
|
|
10
|
+
import { isGitRepo, createBranch, checkWorkflowBranchCoverage } from '../core/git-operations.js';
|
|
11
|
+
import { readConfig } from '../core/config.js';
|
|
8
12
|
export async function addStagingCommand(options) {
|
|
9
13
|
log('');
|
|
10
14
|
log('🌿 ステージング環境の追加');
|
|
11
15
|
log('');
|
|
12
16
|
// 前提チェック
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
const gitCheck = checkGit();
|
|
18
|
+
if (!gitCheck.success) {
|
|
19
|
+
logError(gitCheck.error);
|
|
15
20
|
process.exit(1);
|
|
16
21
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
exec('git rev-parse --is-inside-work-tree', { silent: true });
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
+
const projectDir = process.cwd();
|
|
23
|
+
if (!isGitRepo(projectDir)) {
|
|
22
24
|
logError('Git リポジトリ内で実行してください。');
|
|
23
25
|
process.exit(1);
|
|
24
26
|
}
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
try {
|
|
32
|
-
exec('wrangler whoami', { silent: true });
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
logError('wrangler にログインしていません。`wrangler login` を実行してください。');
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
27
|
+
// Pages プロジェクト名を config.json から取得
|
|
28
|
+
let pagesProjectName = '';
|
|
29
|
+
const configResult = readConfig(projectDir);
|
|
30
|
+
if (configResult.success && configResult.data.config) {
|
|
31
|
+
pagesProjectName = configResult.data.config.pagesProjectName || '';
|
|
38
32
|
}
|
|
39
|
-
//
|
|
40
|
-
let basePagesProject = '';
|
|
41
|
-
let baseDomain = '';
|
|
42
|
-
try {
|
|
43
|
-
const wranglerToml = exec('cat wrangler.toml', { silent: true });
|
|
44
|
-
const nameMatch = wranglerToml.match(/^name\s*=\s*"([^"]+)"/m);
|
|
45
|
-
if (nameMatch) {
|
|
46
|
-
basePagesProject = nameMatch[1];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
// wrangler.toml がなくても続行
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
const configJson = exec('cat admin/config.json', { silent: true });
|
|
54
|
-
const config = JSON.parse(configJson);
|
|
55
|
-
if (config.productionUrl) {
|
|
56
|
-
const url = new URL(config.productionUrl);
|
|
57
|
-
baseDomain = url.hostname;
|
|
58
|
-
}
|
|
59
|
-
else if (config.previewUrl) {
|
|
60
|
-
// previewUrl から親ドメインを推定(staging-xxx.example.com → example.com)
|
|
61
|
-
const url = new URL(config.previewUrl);
|
|
62
|
-
const parts = url.hostname.split('.');
|
|
63
|
-
if (parts.length > 2) {
|
|
64
|
-
baseDomain = parts.slice(-2).join('.');
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// config.json がなくても続行
|
|
70
|
-
}
|
|
71
|
-
// 非対話 / 対話モード判定
|
|
33
|
+
// ステージング名の取得
|
|
72
34
|
const isInteractive = process.stdin.isTTY === true;
|
|
73
35
|
let stagingName;
|
|
74
|
-
|
|
75
|
-
let stagingDomain;
|
|
76
|
-
if (options.name && options.pagesProject) {
|
|
77
|
-
// 全必須オプション指定 → プロンプトスキップ
|
|
78
|
-
const nameCheck = validateStagingName(options.name);
|
|
79
|
-
if (nameCheck !== true) {
|
|
80
|
-
logError(`--name: ${nameCheck}`);
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
stagingName = options.name;
|
|
84
|
-
pagesProject = options.pagesProject;
|
|
85
|
-
stagingDomain = options.domain ?? generateDefaultDomain(stagingName, basePagesProject, baseDomain);
|
|
86
|
-
}
|
|
87
|
-
else if (options.name && !options.pagesProject) {
|
|
88
|
-
// --name のみ指定 → pagesProject はデフォルト算出 or プロンプト
|
|
36
|
+
if (options.name) {
|
|
89
37
|
const nameCheck = validateStagingName(options.name);
|
|
90
38
|
if (nameCheck !== true) {
|
|
91
39
|
logError(`--name: ${nameCheck}`);
|
|
92
40
|
process.exit(1);
|
|
93
41
|
}
|
|
94
42
|
stagingName = options.name;
|
|
95
|
-
const defaultPagesProject = basePagesProject ? `${basePagesProject}-staging-${stagingName}` : '';
|
|
96
|
-
if (!isInteractive) {
|
|
97
|
-
// 非 TTY → デフォルト値で続行
|
|
98
|
-
pagesProject = defaultPagesProject;
|
|
99
|
-
if (!pagesProject) {
|
|
100
|
-
logError('--pages-project が必要です(wrangler.toml からプロジェクト名を推定できません)');
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
|
103
|
-
stagingDomain = options.domain ?? generateDefaultDomain(stagingName, basePagesProject, baseDomain);
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
// TTY → 残りをプロンプト
|
|
107
|
-
const response = await prompts([
|
|
108
|
-
{
|
|
109
|
-
type: 'text',
|
|
110
|
-
name: 'pagesProject',
|
|
111
|
-
message: 'ステージング用 Pages プロジェクト名:',
|
|
112
|
-
initial: defaultPagesProject,
|
|
113
|
-
validate: (v) => validateRequired(v, 'プロジェクト名'),
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
type: 'text',
|
|
117
|
-
name: 'domain',
|
|
118
|
-
message: 'カスタムドメイン(推測困難なハッシュ付き推奨):',
|
|
119
|
-
initial: generateDefaultDomain(stagingName, basePagesProject, baseDomain),
|
|
120
|
-
},
|
|
121
|
-
], { onCancel: () => process.exit(0) });
|
|
122
|
-
pagesProject = response.pagesProject;
|
|
123
|
-
stagingDomain = response.domain;
|
|
124
|
-
}
|
|
125
43
|
}
|
|
126
44
|
else {
|
|
127
|
-
// --name 未指定
|
|
128
45
|
if (!isInteractive) {
|
|
129
46
|
logError('非対話モードでは --name が必須です。');
|
|
130
|
-
logError('使用例: frelio add-staging --name preview1
|
|
47
|
+
logError('使用例: frelio add-staging --name preview1');
|
|
131
48
|
process.exit(1);
|
|
132
49
|
}
|
|
133
|
-
// 従来通りの対話式プロンプト
|
|
134
50
|
const response = await prompts([
|
|
135
51
|
{
|
|
136
52
|
type: 'text',
|
|
@@ -138,86 +54,36 @@ export async function addStagingCommand(options) {
|
|
|
138
54
|
message: 'ステージング名(staging-{name} のブランチが作成されます):',
|
|
139
55
|
validate: (v) => validateStagingName(v),
|
|
140
56
|
},
|
|
141
|
-
{
|
|
142
|
-
type: 'text',
|
|
143
|
-
name: 'pagesProject',
|
|
144
|
-
message: 'ステージング用 Pages プロジェクト名:',
|
|
145
|
-
initial: (_prev, values) => {
|
|
146
|
-
const name = values.name || '';
|
|
147
|
-
return basePagesProject ? `${basePagesProject}-staging-${name}` : '';
|
|
148
|
-
},
|
|
149
|
-
validate: (v) => validateRequired(v, 'プロジェクト名'),
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
type: 'text',
|
|
153
|
-
name: 'domain',
|
|
154
|
-
message: 'カスタムドメイン(推測困難なハッシュ付き推奨):',
|
|
155
|
-
initial: (_prev, values) => {
|
|
156
|
-
const name = values.name || '';
|
|
157
|
-
return generateDefaultDomain(name, basePagesProject, baseDomain);
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
57
|
], { onCancel: () => process.exit(0) });
|
|
161
58
|
if (!response.name) {
|
|
162
59
|
log('キャンセルしました。');
|
|
163
60
|
process.exit(0);
|
|
164
61
|
}
|
|
165
62
|
stagingName = response.name;
|
|
166
|
-
pagesProject = response.pagesProject;
|
|
167
|
-
stagingDomain = response.domain;
|
|
168
63
|
}
|
|
169
64
|
const branchName = `staging-${stagingName}`;
|
|
170
65
|
log('');
|
|
171
|
-
const totalSteps =
|
|
66
|
+
const totalSteps = 2;
|
|
172
67
|
let step = 0;
|
|
173
68
|
// Step 1: Git ブランチ作成
|
|
174
69
|
step++;
|
|
175
70
|
logStep(step, totalSteps, `ブランチ "${branchName}" を作成...`);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
exec(`git rev-parse --verify origin/${branchName}`, { silent: true });
|
|
182
|
-
logSuccess(`ブランチ "${branchName}" はリモートに既に存在します`);
|
|
183
|
-
}
|
|
184
|
-
catch {
|
|
185
|
-
// ローカルブランチ作成 & プッシュ
|
|
186
|
-
exec(`git branch ${branchName} origin/develop`, { silent: true });
|
|
187
|
-
exec(`git push -u origin ${branchName}`, { silent: true });
|
|
188
|
-
logSuccess(`ブランチ "${branchName}" を作成・プッシュしました`);
|
|
189
|
-
}
|
|
71
|
+
const branchResult = createBranch(projectDir, branchName, 'origin/develop');
|
|
72
|
+
if (branchResult.success) {
|
|
73
|
+
logSuccess(branchResult.data.alreadyExisted
|
|
74
|
+
? `ブランチ "${branchName}" はリモートに既に存在します`
|
|
75
|
+
: `ブランチ "${branchName}" を作成・プッシュしました`);
|
|
190
76
|
}
|
|
191
|
-
|
|
192
|
-
logError(
|
|
77
|
+
else {
|
|
78
|
+
logError(branchResult.error);
|
|
193
79
|
process.exit(1);
|
|
194
80
|
}
|
|
195
|
-
// Step 2:
|
|
196
|
-
if (!options.skipCloudflare) {
|
|
197
|
-
step++;
|
|
198
|
-
logStep(step, totalSteps, `Pages プロジェクト "${pagesProject}" を作成...`);
|
|
199
|
-
try {
|
|
200
|
-
exec(`wrangler pages project create ${pagesProject} --production-branch ${branchName}`, {
|
|
201
|
-
silent: true,
|
|
202
|
-
});
|
|
203
|
-
logSuccess(`Pages プロジェクト "${pagesProject}" を作成しました`);
|
|
204
|
-
}
|
|
205
|
-
catch (error) {
|
|
206
|
-
const msg = error.message;
|
|
207
|
-
if (msg.includes('already exists')) {
|
|
208
|
-
logSuccess(`Pages プロジェクト "${pagesProject}" は既に存在します`);
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
logError(`Pages プロジェクト作成失敗: ${msg}`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
// Step 3: build-staging.yml のブランチリスト確認
|
|
81
|
+
// Step 2: build-staging.yml のブランチリスト確認
|
|
216
82
|
step++;
|
|
217
83
|
logStep(step, totalSteps, 'GitHub Actions ワークフローを確認...');
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (
|
|
84
|
+
const workflowCheck = checkWorkflowBranchCoverage(projectDir, branchName);
|
|
85
|
+
if (workflowCheck.success) {
|
|
86
|
+
if (workflowCheck.data.covered) {
|
|
221
87
|
logSuccess('ワークフローは既に対応済みです(ワイルドカードまたは明示列挙)');
|
|
222
88
|
}
|
|
223
89
|
else {
|
|
@@ -227,10 +93,10 @@ export async function addStagingCommand(options) {
|
|
|
227
93
|
log(' - staging');
|
|
228
94
|
log(` - ${branchName} # ← 追加`);
|
|
229
95
|
log('');
|
|
230
|
-
log('
|
|
96
|
+
log(' または、CMS 管理画面の基本設定 → ファイル所在一覧からワークフローを再生成してください。');
|
|
231
97
|
}
|
|
232
98
|
}
|
|
233
|
-
|
|
99
|
+
else {
|
|
234
100
|
log(' ⚠ .github/workflows/build-staging.yml が見つかりません。手動で確認してください。');
|
|
235
101
|
}
|
|
236
102
|
// 完了
|
|
@@ -238,25 +104,13 @@ export async function addStagingCommand(options) {
|
|
|
238
104
|
log('✅ ステージング環境の追加が完了しました!');
|
|
239
105
|
log('');
|
|
240
106
|
log(` ブランチ: ${branchName}`);
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
log(` URL: https://${domain}`);
|
|
107
|
+
if (pagesProjectName) {
|
|
108
|
+
log(` プレビュー URL: https://${branchName}.${pagesProjectName}.pages.dev`);
|
|
244
109
|
}
|
|
245
110
|
log('');
|
|
246
111
|
log(' 残りの手動作業:');
|
|
247
|
-
log(' - Cloudflare Pages でリポジトリを接続(GitHub integration)');
|
|
248
|
-
if (stagingDomain) {
|
|
249
|
-
log(` - Pages プロジェクトにカスタムドメイン "${stagingDomain}" を設定`);
|
|
250
|
-
}
|
|
251
|
-
log(' - Cloudflare Access でアクセス制限を設定(推奨)');
|
|
252
112
|
log(' - CMS 管理画面の /staging ページでステージングブランチを登録');
|
|
113
|
+
log(' - カスタムドメインが必要な場合は Cloudflare Pages のカスタムドメイン設定');
|
|
114
|
+
log(' - Cloudflare Access でアクセス制限を設定(推奨)');
|
|
253
115
|
log('');
|
|
254
116
|
}
|
|
255
|
-
function generateDefaultDomain(name, basePagesProject, baseDomain) {
|
|
256
|
-
const hash = generateHash();
|
|
257
|
-
if (baseDomain) {
|
|
258
|
-
return `${name}-${hash}.${baseDomain}`;
|
|
259
|
-
}
|
|
260
|
-
const project = basePagesProject || 'site';
|
|
261
|
-
return `${project}-${name}-${hash}.pages.dev`;
|
|
262
|
-
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* frelio init - 新規プロジェクトの対話式セットアップ
|
|
3
|
+
*
|
|
4
|
+
* core/* の関数を順番に呼ぶ薄いオーケストレーター。
|
|
5
|
+
* UI 層(プロンプト、ログ出力、process.exit)はここに閉じる。
|
|
3
6
|
*/
|
|
4
7
|
type InitOptions = {
|
|
5
8
|
skipGithub?: boolean;
|
|
6
9
|
skipCloudflare?: boolean;
|
|
10
|
+
terraform?: boolean;
|
|
7
11
|
contentRepo?: string;
|
|
8
12
|
siteTitle?: string;
|
|
9
13
|
productionUrl?: string;
|