@c-time/frelio-cli 1.4.9 → 1.4.11
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 +179 -3
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.js +71 -0
- package/dist/commands/fleet.d.ts +16 -0
- package/dist/commands/fleet.js +110 -0
- package/dist/commands/init.js +1 -0
- package/dist/commands/set-mode.d.ts +10 -0
- package/dist/commands/set-mode.js +41 -0
- package/dist/commands/update.d.ts +7 -2
- package/dist/commands/update.js +72 -19
- package/dist/core/config.js +1 -0
- package/dist/core/doctor.d.ts +36 -0
- package/dist/core/doctor.js +86 -0
- package/dist/core/file-generators.d.ts +3 -0
- package/dist/core/file-generators.js +13 -3
- package/dist/core/fleet.d.ts +62 -0
- package/dist/core/fleet.js +172 -0
- package/dist/core/migrations/index.d.ts +3 -0
- package/dist/core/migrations/index.js +2 -0
- package/dist/core/migrations/node20-to-node24.d.ts +15 -0
- package/dist/core/migrations/node20-to-node24.js +35 -0
- package/dist/core/migrations/registry.d.ts +11 -0
- package/dist/core/migrations/registry.js +18 -0
- package/dist/core/migrations/types.d.ts +55 -0
- package/dist/core/migrations/types.js +11 -0
- package/dist/core/site-mode.d.ts +23 -0
- package/dist/core/site-mode.js +37 -0
- package/dist/core/template-refresh.d.ts +41 -0
- package/dist/core/template-refresh.js +148 -0
- package/dist/core/template-scaffold.d.ts +11 -0
- package/dist/core/template-scaffold.js +12 -2
- package/dist/core/types.d.ts +2 -0
- package/dist/core/version-check.d.ts +31 -0
- package/dist/core/version-check.js +114 -0
- package/dist/index.js +41 -3
- package/dist/lib/npm-registry.d.ts +12 -0
- package/dist/lib/npm-registry.js +30 -0
- package/dist/lib/template-renderer.js +6 -1
- package/dist/lib/templates.d.ts +36 -0
- package/dist/lib/templates.js +171 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -251,7 +251,9 @@ my-site/
|
|
|
251
251
|
├── wrangler.toml # Cloudflare 設定(R2 バインディング)
|
|
252
252
|
├── _redirects # /admin/* → SPA, /* → /public/:splat
|
|
253
253
|
├── _routes.json # /api/*, /storage/* → Functions
|
|
254
|
-
├──
|
|
254
|
+
├── AGENTS.md # AI アシスタント用プロジェクト説明(正本・ツール非依存)
|
|
255
|
+
├── CLAUDE.md # → AGENTS.md を参照(Claude Code 用)
|
|
256
|
+
├── .github/copilot-instructions.md # → AGENTS.md を参照(GitHub Copilot 用)
|
|
255
257
|
└── version.json # バージョン管理
|
|
256
258
|
```
|
|
257
259
|
|
|
@@ -338,10 +340,184 @@ frelio update
|
|
|
338
340
|
#### オプション
|
|
339
341
|
|
|
340
342
|
```bash
|
|
341
|
-
frelio update #
|
|
342
|
-
frelio update --version v1.2.0 #
|
|
343
|
+
frelio update # バンドル+テンプレートを最新化
|
|
344
|
+
frelio update --version v1.2.0 # 特定バージョンのバンドルに更新
|
|
345
|
+
frelio update --templates-only # ワークフロー/functions/wrangler のみ再生成(バンドルは触らない)
|
|
346
|
+
frelio update --bundle-only # CMS Admin バンドルのみ更新(テンプレートは触らない)
|
|
347
|
+
frelio update --force # 差分があるファイルもバックアップせず上書き
|
|
343
348
|
```
|
|
344
349
|
|
|
350
|
+
`--templates-only` を含むテンプレート再生成では、ユーザー改変を保護するため、既存ファイルと
|
|
351
|
+
差分があるファイルは既定で `*.frelio-bak` にバックアップしてから上書きする(`--force` で無効化)。
|
|
352
|
+
`admin/config.json` / `frelio-data/` / `public/` / `functions/api/` は再生成対象外。
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
### `frelio doctor` — 生成物の自己診断
|
|
357
|
+
|
|
358
|
+
生成物(特に `.github/workflows/`)をスキャンし、期限付きの破壊的変更(例: GitHub Actions の
|
|
359
|
+
Node 20 廃止)や未適用のテンプレ更新を検出して、直し方を提示する。
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
cd my-site
|
|
363
|
+
frelio doctor # 検出結果を表示(終了コードは常に 0)
|
|
364
|
+
frelio doctor --ci # 対応が必要な指摘があれば非ゼロ終了(CI 用)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
出力例:
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
🔴 重大 GitHub Actions ワークフローを Node 24 対応へ更新 (期限 2026-06-16 — 残り 3 日)
|
|
371
|
+
該当ファイル(1):
|
|
372
|
+
• .github/workflows/build-staging.yml
|
|
373
|
+
直し方: `npx @c-time/frelio-cli update --templates-only` を実行して ...
|
|
374
|
+
→ 実行: npx @c-time/frelio-cli update --templates-only
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
指摘の修復はそのまま `frelio update --templates-only` で行う。
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
### `frelio set-mode <mode>` — 本番ゲート(公開状態の制御)
|
|
382
|
+
|
|
383
|
+
コンテンツサイトの公開状態をエッジで切り替える(Issue #93)。生成物に同梱されるエッジ
|
|
384
|
+
middleware(`functions/_middleware.ts`)が、ホスト判定したうえで以下を行う。
|
|
385
|
+
|
|
386
|
+
| mode | 挙動 | 用途 |
|
|
387
|
+
|---|---|---|
|
|
388
|
+
| `live`(既定) | 通常配信。`<project>.pages.dev` は独自ドメインへ **301** で正規化 | 公開運用 |
|
|
389
|
+
| `prelaunch` | `/storage/*` 以外を **403**("公開準備中" ページ) | 公開前のプレ公開ブロック |
|
|
390
|
+
| `maintenance` | `/storage/*` 以外を **503**("メンテナンス中" ページ) | 一時停止 |
|
|
391
|
+
| `closed` | `/storage/*` 含め **全遮断**(503) | サイト閉鎖キルスイッチ |
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
cd my-site
|
|
395
|
+
frelio set-mode prelaunch # プレ公開ブロック
|
|
396
|
+
frelio set-mode live # 公開(通常配信 + pages.dev 301)
|
|
397
|
+
frelio set-mode closed # 全遮断
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
`set-mode` は `config.json` の `siteMode` と `wrangler.toml` の `[vars]`
|
|
401
|
+
(`SITE_MODE` / `CANONICAL_HOST` / `CONTENT_PAGES_DEV`)、エッジ middleware を再生成する。
|
|
402
|
+
**エッジが読むのは `wrangler.toml [vars]` のため、変更の反映にはコンテンツ Pages の再デプロイが必要。**
|
|
403
|
+
|
|
404
|
+
#### 設計上の注意(地雷)
|
|
405
|
+
|
|
406
|
+
- **`functions/` は管理画面 Pages と共有**される。middleware は**ホスト判定で content ホスト
|
|
407
|
+
(独自ドメイン / `<project>.pages.dev`)のみ**をゲートし、admin / staging / localhost / 不明ホストは
|
|
408
|
+
素通りする。これにより CMS の `/api/*` を壊さない。
|
|
409
|
+
- **プレ公開・メンテナンスは `/storage/*` を除外**して配信を継続する(独自ドメイン配信の R2 画像が
|
|
410
|
+
CMS のサムネ・記事プレビューに使われるため)。**閉鎖(closed)は storage も遮断**する。
|
|
411
|
+
- **`pages.dev` は別ゾーンで WAF / Redirect Rules が効かない**ため、301 正規化は Pages Functions
|
|
412
|
+
(この middleware)で行う。`content` 側 `public/_routes.json` の include は `/*`(全リクエストを
|
|
413
|
+
middleware 経由にする。`live` では即素通り)。
|
|
414
|
+
- **最速の切替(キルスイッチ)**: 再ビルドを避けたい場合は `wrangler.toml` の `SITE_MODE` を直接変更して
|
|
415
|
+
`wrangler pages deploy public --project-name=<content>` で再デプロイするか、Cloudflare Dashboard の
|
|
416
|
+
Pages → 該当プロジェクト → Settings → Variables で `SITE_MODE` を変更して再デプロイする。
|
|
417
|
+
- CMS「基本設定」からの切替 UI は follow-up(現状は CLI / wrangler が切替の正本)。
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## 生成物のメンテナンスと自走(self-healing)
|
|
422
|
+
|
|
423
|
+
frelio が生成するワークフローや設定は各リポジトリにコピーされるため、frelio 本体の
|
|
424
|
+
テンプレート更新は自動では追従されない。これに対し、以下の3つの仕組みで「腐りにくく」する。
|
|
425
|
+
|
|
426
|
+
### 1. 依存の自動追従(Dependabot)
|
|
427
|
+
|
|
428
|
+
生成物には `.github/dependabot.yml` が同梱され、`github-actions` エコシステムを毎週監視する。
|
|
429
|
+
|
|
430
|
+
- frelio のワークフローはアクションを **major タグ**(例: `actions/checkout@v6`)で参照する。
|
|
431
|
+
patch/minor のセキュリティ修正は GitHub 側で自動的に流れる。
|
|
432
|
+
- major の更新は **各自のリポジトリに Dependabot が PR を立てる**。内容を確認してマージする
|
|
433
|
+
(**マージは利用者の責任**)。frelio 側から各リポジトリへ push して直すことはしない。
|
|
434
|
+
|
|
435
|
+
これにより、GitHub Actions ランタイムの廃止(例: Node 20 → 24)のような期限付きの破壊的変更も、
|
|
436
|
+
「次回以降」は自動 PR として各リポジトリに届く。
|
|
437
|
+
|
|
438
|
+
### 2. 自己診断(`frelio doctor`)
|
|
439
|
+
|
|
440
|
+
`frelio doctor` がテンプレートの期限付き廃止(例: ワークフローが Node 20 アクションのまま)を
|
|
441
|
+
検出し、直し方を提示する。コマンド詳細は後述の「コマンド」節を参照。
|
|
442
|
+
|
|
443
|
+
### 3. 最新版の取得(`frelio update`)
|
|
444
|
+
|
|
445
|
+
`frelio update --templates-only` でワークフロー・functions・wrangler.toml を最新テンプレートで再生成できる。
|
|
446
|
+
`frelio doctor` の指摘はこのコマンドで解消する。
|
|
447
|
+
|
|
448
|
+
### 4. 起動時バージョンチェック
|
|
449
|
+
|
|
450
|
+
CLI 実行時、npm の最新版を確認し、古ければ stderr にナッジを表示する(1日に1回・キャッシュ済み・
|
|
451
|
+
オフライン時は無言)。次の環境変数で無効化できる:
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
export FRELIO_NO_UPDATE_CHECK=1 # 起動時バージョンチェックを無効化(CI では自動的に無効)
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## メンテナー向け運用(ブロードキャスト)
|
|
460
|
+
|
|
461
|
+
匿名ユーザーには push で直せないため、**install/実行経路で警告を届ける**手段を運用で用意する。
|
|
462
|
+
|
|
463
|
+
### 期限付き破壊的変更の告知(`npm deprecate`)
|
|
464
|
+
|
|
465
|
+
期限付きの破壊的変更(例: Node 20 廃止)が出たら、影響する旧バージョン範囲を `npm deprecate` する。
|
|
466
|
+
これにより、該当バージョンを install したユーザーのターミナルへ警告が出る(数少ない強制告知手段)。
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
# 例: 1.4.x 以前を非推奨にし、移行先を案内する
|
|
470
|
+
npm deprecate '@c-time/frelio-cli@<=1.4.9' \
|
|
471
|
+
'Node 20 廃止対応のため 1.5.0 以降へ更新し、各サイトで `frelio doctor` を実行してください'
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### CHANGELOG / Releases に「移行」節を設ける
|
|
475
|
+
|
|
476
|
+
各リリースの CHANGELOG / GitHub Releases に、対応する `Migration.id`(例: `node20-to-node24`)と
|
|
477
|
+
期限・手順を記した「## 移行(Migration)」節を必ず設ける。`frelio doctor` の出力と対応づける。
|
|
478
|
+
|
|
479
|
+
### フリート在庫(`frelio fleet`)
|
|
480
|
+
|
|
481
|
+
管理(アクセス権のある)コンテンツリポジトリを横断スキャンし、各リポの**バンドルバージョン**と
|
|
482
|
+
**未対応マイグレーション**(期限付き廃止など)を集計する。期限付き廃止時の**露出特定・優先順位付け**に使う。
|
|
483
|
+
`frelio doctor` を 1 リポずつ流す代わりに、operator が手元から全顧客リポを一覧できる。
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
# org/user 配下を列挙(version.json を持つ frelio リポのみ対象)
|
|
487
|
+
frelio fleet --org my-org
|
|
488
|
+
|
|
489
|
+
# 明示指定 / ファイル指定(1 行 1 リポ、# でコメント可)
|
|
490
|
+
frelio fleet --repos owner/site-a,owner/site-b
|
|
491
|
+
frelio fleet --repo-list ./fleet-repos.txt
|
|
492
|
+
|
|
493
|
+
frelio fleet --org my-org --json # 機械可読(CI / 集計用)
|
|
494
|
+
frelio fleet --org my-org --ci # 未対応(critical/warning)があれば非ゼロ終了
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
出力例:
|
|
498
|
+
|
|
499
|
+
```
|
|
500
|
+
⚠ owner/site-b — v1.4.7
|
|
501
|
+
🔴 node20-to-node24 (期限 2026-06-16 / 残り 3 日)— 該当 4 ファイル
|
|
502
|
+
✓ owner/site-a — v1.5.0
|
|
503
|
+
|
|
504
|
+
サマリ: 2 スキャン / frelio 2 / 未対応あり 1
|
|
505
|
+
未対応マイグレ: 🔴 1 / 🟡 0 / ℹ️ 0
|
|
506
|
+
npm 直近1か月ダウンロード(匿名層の粗い母数):
|
|
507
|
+
@c-time/frelio-cli: 1529
|
|
508
|
+
@c-time/frelio-cms: 1727
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
- **前提**: `gh` CLI の認証(`gh auth login`)と対象リポへのアクセス権。検出には `version.json` と
|
|
512
|
+
`.github/workflows/*` を GitHub API(`gh api`)で取得する(読み取りのみ)。
|
|
513
|
+
- 検出ルールは `frelio doctor` と同じマイグレ定義(`Migration`)を共有する。
|
|
514
|
+
- 匿名ユーザーは原則 push 不可のため、本コマンドは主に**管理顧客**の在庫把握に効く。匿名層は npm
|
|
515
|
+
ダウンロード統計を粗い母数として併記する。
|
|
516
|
+
|
|
517
|
+
> **opt-in テレメトリ(phone-home)は follow-up。** 生成サイトからの匿名化情報の送信は、収集エンドポイント・
|
|
518
|
+
> 同意フロー・匿名化設計が前提(**無断収集はしない**)。本コマンドは GitHub アクセス権の範囲+npm 統計で
|
|
519
|
+
> 完結し、新規インフラを必要としない。
|
|
520
|
+
|
|
345
521
|
---
|
|
346
522
|
|
|
347
523
|
## セットアップ後の手動作業
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frelio doctor - 生成物の自己診断(#85)
|
|
3
|
+
*
|
|
4
|
+
* マイグレーション定義(#84)を読み、カレントプロジェクトをスキャンして
|
|
5
|
+
* 期限付き廃止や未適用のテンプレ更新を検出し、直し方を提示する。
|
|
6
|
+
*/
|
|
7
|
+
type DoctorOptions = {
|
|
8
|
+
ci?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare function doctorCommand(options: DoctorOptions): Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frelio doctor - 生成物の自己診断(#85)
|
|
3
|
+
*
|
|
4
|
+
* マイグレーション定義(#84)を読み、カレントプロジェクトをスキャンして
|
|
5
|
+
* 期限付き廃止や未適用のテンプレ更新を検出し、直し方を提示する。
|
|
6
|
+
*/
|
|
7
|
+
import { log, logSuccess, logError } from '../lib/shell.js';
|
|
8
|
+
import { runDoctor, daysUntil } from '../core/doctor.js';
|
|
9
|
+
const SEVERITY_ORDER = ['critical', 'warning', 'info'];
|
|
10
|
+
const SEVERITY_LABEL = {
|
|
11
|
+
critical: '🔴 重大',
|
|
12
|
+
warning: '🟡 警告',
|
|
13
|
+
info: 'ℹ️ 情報',
|
|
14
|
+
};
|
|
15
|
+
function formatDeadline(deadline) {
|
|
16
|
+
if (!deadline)
|
|
17
|
+
return '';
|
|
18
|
+
const days = daysUntil(deadline);
|
|
19
|
+
if (days < 0)
|
|
20
|
+
return `(期限 ${deadline} — ${-days} 日超過)`;
|
|
21
|
+
if (days === 0)
|
|
22
|
+
return `(期限 ${deadline} — 本日)`;
|
|
23
|
+
return `(期限 ${deadline} — 残り ${days} 日)`;
|
|
24
|
+
}
|
|
25
|
+
function printFinding(finding) {
|
|
26
|
+
const { migration, matchedFiles } = finding;
|
|
27
|
+
log('');
|
|
28
|
+
log(` ${SEVERITY_LABEL[migration.severity]} ${migration.title} ${formatDeadline(migration.deadline)}`);
|
|
29
|
+
log(` ${migration.description}`);
|
|
30
|
+
log(` 該当ファイル(${matchedFiles.length}):`);
|
|
31
|
+
for (const f of matchedFiles) {
|
|
32
|
+
log(` • ${f}`);
|
|
33
|
+
}
|
|
34
|
+
log(` 直し方: ${migration.remediation}`);
|
|
35
|
+
if (migration.command) {
|
|
36
|
+
log(` → 実行: ${migration.command}`);
|
|
37
|
+
}
|
|
38
|
+
if (migration.referenceUrl) {
|
|
39
|
+
log(` 参考: ${migration.referenceUrl}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function doctorCommand(options) {
|
|
43
|
+
log('');
|
|
44
|
+
log('🩺 Frelio doctor — 生成物の自己診断');
|
|
45
|
+
log('');
|
|
46
|
+
const projectDir = process.cwd();
|
|
47
|
+
const result = runDoctor(projectDir);
|
|
48
|
+
if (!result.success) {
|
|
49
|
+
logError(result.error);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const { findings, scannedFileCount, actionable } = result.data;
|
|
53
|
+
if (findings.length === 0) {
|
|
54
|
+
logSuccess(`問題は見つかりませんでした(${scannedFileCount} ファイルを確認)`);
|
|
55
|
+
log('');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// 重大度順に表示
|
|
59
|
+
const sorted = [...findings].sort((a, b) => SEVERITY_ORDER.indexOf(a.migration.severity) -
|
|
60
|
+
SEVERITY_ORDER.indexOf(b.migration.severity));
|
|
61
|
+
for (const f of sorted) {
|
|
62
|
+
printFinding(f);
|
|
63
|
+
}
|
|
64
|
+
log('');
|
|
65
|
+
log(` ${findings.length} 件の指摘が見つかりました(${scannedFileCount} ファイルを確認)。`);
|
|
66
|
+
log('');
|
|
67
|
+
// --ci 指定時のみ、対応が必要な指摘があれば非ゼロで終了する
|
|
68
|
+
if (options.ci && actionable) {
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frelio fleet - フリート在庫/可視化(#89・operator 向け)
|
|
3
|
+
*
|
|
4
|
+
* 管理(アクセス権のある)コンテンツリポジトリを横断スキャンし、各リポの
|
|
5
|
+
* バンドルバージョンと未対応マイグレーション(期限付き廃止等)を集計表示する。
|
|
6
|
+
* 期限付き廃止時の露出特定・優先順位付けに使う。
|
|
7
|
+
*/
|
|
8
|
+
type FleetOptions = {
|
|
9
|
+
org?: string;
|
|
10
|
+
repos?: string;
|
|
11
|
+
repoList?: string;
|
|
12
|
+
json?: boolean;
|
|
13
|
+
ci?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare function fleetCommand(options: FleetOptions): Promise<void>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frelio fleet - フリート在庫/可視化(#89・operator 向け)
|
|
3
|
+
*
|
|
4
|
+
* 管理(アクセス権のある)コンテンツリポジトリを横断スキャンし、各リポの
|
|
5
|
+
* バンドルバージョンと未対応マイグレーション(期限付き廃止等)を集計表示する。
|
|
6
|
+
* 期限付き廃止時の露出特定・優先順位付けに使う。
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import { log, logError } from '../lib/shell.js';
|
|
10
|
+
import { checkGhCli } from '../core/prerequisites.js';
|
|
11
|
+
import { daysUntil } from '../core/doctor.js';
|
|
12
|
+
import { createGhClient, resolveRepos, runFleetInventory, fleetHasActionable, } from '../core/fleet.js';
|
|
13
|
+
import { getMonthlyDownloads } from '../lib/npm-registry.js';
|
|
14
|
+
const SEVERITY_MARK = {
|
|
15
|
+
critical: '🔴',
|
|
16
|
+
warning: '🟡',
|
|
17
|
+
info: 'ℹ️',
|
|
18
|
+
};
|
|
19
|
+
function formatRepoLine(r) {
|
|
20
|
+
const lines = [];
|
|
21
|
+
const ver = r.version ?? (r.isFrelio ? '(version.json に version 無し)' : '(frelio リポではない)');
|
|
22
|
+
const head = r.findings.length === 0 ? '✓' : '⚠';
|
|
23
|
+
lines.push(` ${head} ${r.repo} — ${ver}`);
|
|
24
|
+
if (r.error) {
|
|
25
|
+
lines.push(` エラー: ${r.error}`);
|
|
26
|
+
}
|
|
27
|
+
for (const f of r.findings) {
|
|
28
|
+
const m = f.migration;
|
|
29
|
+
const dl = m.deadline ? `(期限 ${m.deadline} / 残り ${daysUntil(m.deadline)} 日)` : '';
|
|
30
|
+
lines.push(` ${SEVERITY_MARK[m.severity] ?? ''} ${m.id} ${dl} — 該当 ${f.matchedFiles.length} ファイル`);
|
|
31
|
+
}
|
|
32
|
+
return lines;
|
|
33
|
+
}
|
|
34
|
+
function printTable(report) {
|
|
35
|
+
// 未対応ありを先に、その中で critical を含むものを上に
|
|
36
|
+
const sorted = [...report.repos].sort((a, b) => {
|
|
37
|
+
const pa = a.findings.length > 0 ? 0 : 1;
|
|
38
|
+
const pb = b.findings.length > 0 ? 0 : 1;
|
|
39
|
+
if (pa !== pb)
|
|
40
|
+
return pa - pb;
|
|
41
|
+
return a.repo.localeCompare(b.repo);
|
|
42
|
+
});
|
|
43
|
+
for (const r of sorted) {
|
|
44
|
+
for (const line of formatRepoLine(r))
|
|
45
|
+
log(line);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function fleetCommand(options) {
|
|
49
|
+
log('');
|
|
50
|
+
log('🛰 Frelio フリート在庫');
|
|
51
|
+
log('');
|
|
52
|
+
// gh 認証チェック(operator が管理リポへアクセスするため必須)
|
|
53
|
+
const gh = checkGhCli();
|
|
54
|
+
if (!gh.success) {
|
|
55
|
+
logError(gh.error);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
// 対象リポを集める(--repos + --repo-list + --org)
|
|
59
|
+
const repos = [];
|
|
60
|
+
if (options.repos) {
|
|
61
|
+
repos.push(...options.repos.split(',').map((s) => s.trim()).filter(Boolean));
|
|
62
|
+
}
|
|
63
|
+
if (options.repoList) {
|
|
64
|
+
try {
|
|
65
|
+
const text = fs.readFileSync(options.repoList, 'utf-8');
|
|
66
|
+
repos.push(...text
|
|
67
|
+
.split('\n')
|
|
68
|
+
.map((s) => s.trim())
|
|
69
|
+
.filter((s) => s && !s.startsWith('#')));
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
logError(`--repo-list の読み込みに失敗: ${e.message}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const client = createGhClient();
|
|
77
|
+
const resolved = resolveRepos(client, { org: options.org, repos });
|
|
78
|
+
if (!resolved.success) {
|
|
79
|
+
logError(resolved.error);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
if (!options.json) {
|
|
83
|
+
log(` ${resolved.data.length} リポジトリをスキャン中...`);
|
|
84
|
+
log('');
|
|
85
|
+
}
|
|
86
|
+
const report = runFleetInventory(client, resolved.data);
|
|
87
|
+
const [cliDl, cmsDl] = await Promise.all([
|
|
88
|
+
getMonthlyDownloads('@c-time/frelio-cli'),
|
|
89
|
+
getMonthlyDownloads('@c-time/frelio-cms'),
|
|
90
|
+
]);
|
|
91
|
+
if (options.json) {
|
|
92
|
+
log(JSON.stringify({ ...report, npmDownloadsLastMonth: { '@c-time/frelio-cli': cliDl, '@c-time/frelio-cms': cmsDl } }, null, 2));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
printTable(report);
|
|
96
|
+
log('');
|
|
97
|
+
const t = report.totals;
|
|
98
|
+
log(` サマリ: ${t.scanned} スキャン / frelio ${t.frelio} / 未対応あり ${t.withPending}`);
|
|
99
|
+
log(` 未対応マイグレ: 🔴 ${t.bySeverity.critical} / 🟡 ${t.bySeverity.warning} / ℹ️ ${t.bySeverity.info}`);
|
|
100
|
+
log('');
|
|
101
|
+
log(' npm 直近1か月ダウンロード(匿名層の粗い母数):');
|
|
102
|
+
log(` @c-time/frelio-cli: ${cliDl ?? '取得不可'}`);
|
|
103
|
+
log(` @c-time/frelio-cms: ${cmsDl ?? '取得不可'}`);
|
|
104
|
+
log('');
|
|
105
|
+
}
|
|
106
|
+
// --ci: actionable(critical/warning)な未対応があれば非ゼロ終了
|
|
107
|
+
if (options.ci && fleetHasActionable(report)) {
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -463,6 +463,7 @@ async function promptConfig(options) {
|
|
|
463
463
|
ownerUsername: options.ownerUsername ?? response.ownerUsername ?? '',
|
|
464
464
|
stagingDomain,
|
|
465
465
|
cloudflareAccountId: options.cloudflareAccountId ?? response.cloudflareAccountId ?? '',
|
|
466
|
+
saveTimezone: '+09:00',
|
|
466
467
|
};
|
|
467
468
|
if (options.clientSecret) {
|
|
468
469
|
config.githubClientSecret = options.clientSecret;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frelio set-mode <mode> - 本番ゲートのサイト公開状態を変更(Issue #93)
|
|
3
|
+
*
|
|
4
|
+
* core/site-mode の changeSiteMode を呼ぶ薄いオーケストレーター。
|
|
5
|
+
* live 通常配信(既定)
|
|
6
|
+
* prelaunch 公開準備中。storage 以外を 403(プレ公開ブロック)
|
|
7
|
+
* maintenance メンテナンス中。storage 以外を 503
|
|
8
|
+
* closed 閉鎖。storage 含め全遮断(キルスイッチ)
|
|
9
|
+
*/
|
|
10
|
+
export declare function setModeCommand(mode: string): Promise<void>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frelio set-mode <mode> - 本番ゲートのサイト公開状態を変更(Issue #93)
|
|
3
|
+
*
|
|
4
|
+
* core/site-mode の changeSiteMode を呼ぶ薄いオーケストレーター。
|
|
5
|
+
* live 通常配信(既定)
|
|
6
|
+
* prelaunch 公開準備中。storage 以外を 403(プレ公開ブロック)
|
|
7
|
+
* maintenance メンテナンス中。storage 以外を 503
|
|
8
|
+
* closed 閉鎖。storage 含め全遮断(キルスイッチ)
|
|
9
|
+
*/
|
|
10
|
+
import { log, logSuccess, logError } from '../lib/shell.js';
|
|
11
|
+
import { changeSiteMode, SITE_MODES } from '../core/site-mode.js';
|
|
12
|
+
export async function setModeCommand(mode) {
|
|
13
|
+
log('');
|
|
14
|
+
log('🚦 本番ゲート: サイト公開状態の変更');
|
|
15
|
+
log('');
|
|
16
|
+
const projectDir = process.cwd();
|
|
17
|
+
const result = changeSiteMode(projectDir, mode);
|
|
18
|
+
if (!result.success) {
|
|
19
|
+
logError(result.error);
|
|
20
|
+
log('');
|
|
21
|
+
log(` 指定可能なモード: ${SITE_MODES.join(' | ')}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const { mode: applied, regeneratedPaths } = result.data;
|
|
25
|
+
logSuccess(`siteMode を "${applied}" に変更し、派生ファイルを再生成しました`);
|
|
26
|
+
log('');
|
|
27
|
+
log(' 再生成したファイル:');
|
|
28
|
+
for (const p of regeneratedPaths) {
|
|
29
|
+
log(` - ${p}`);
|
|
30
|
+
}
|
|
31
|
+
log('');
|
|
32
|
+
log(' ⚠ エッジ(functions/_middleware.ts)が読むのは wrangler.toml [vars] の SITE_MODE です。');
|
|
33
|
+
log(' 変更を反映するにはコンテンツ Pages の再デプロイが必要です:');
|
|
34
|
+
log(' 1. 差分を develop へ commit / push');
|
|
35
|
+
log(' 2. CMS の「直接デプロイ」フロー、または該当ブランチへの push でコンテンツを再デプロイ');
|
|
36
|
+
log('');
|
|
37
|
+
log(' 💡 最速の切替(キルスイッチ等で再ビルドを避けたい場合):');
|
|
38
|
+
log(' - wrangler.toml の SITE_MODE を変更し `wrangler pages deploy public --project-name=<content>` で再デプロイ');
|
|
39
|
+
log(' - もしくは Cloudflare Dashboard の Pages → 該当プロジェクト → Settings → Variables で SITE_MODE を変更し再デプロイ');
|
|
40
|
+
log('');
|
|
41
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* frelio update - CMS Admin
|
|
2
|
+
* frelio update - CMS Admin バンドル+テンプレートの更新
|
|
3
3
|
*
|
|
4
|
-
* core/bundle の updateBundle
|
|
4
|
+
* - バンドル: core/bundle の updateBundle(admin/ functions/api workers/)
|
|
5
|
+
* - テンプレート: core/template-refresh の refreshTemplates(.github ワークフロー /
|
|
6
|
+
* wrangler.toml / functions/storage / _redirects / _routes.json)
|
|
5
7
|
*/
|
|
6
8
|
type UpdateOptions = {
|
|
7
9
|
version?: string;
|
|
10
|
+
bundleOnly?: boolean;
|
|
11
|
+
templatesOnly?: boolean;
|
|
12
|
+
force?: boolean;
|
|
8
13
|
};
|
|
9
14
|
export declare function updateCommand(options: UpdateOptions): Promise<void>;
|
|
10
15
|
export {};
|
package/dist/commands/update.js
CHANGED
|
@@ -1,39 +1,92 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* frelio update - CMS Admin
|
|
2
|
+
* frelio update - CMS Admin バンドル+テンプレートの更新
|
|
3
3
|
*
|
|
4
|
-
* core/bundle の updateBundle
|
|
4
|
+
* - バンドル: core/bundle の updateBundle(admin/ functions/api workers/)
|
|
5
|
+
* - テンプレート: core/template-refresh の refreshTemplates(.github ワークフロー /
|
|
6
|
+
* wrangler.toml / functions/storage / _redirects / _routes.json)
|
|
5
7
|
*/
|
|
6
8
|
import fs from 'node:fs';
|
|
7
9
|
import path from 'node:path';
|
|
8
10
|
import { log, logSuccess, logError } from '../lib/shell.js';
|
|
9
11
|
import { updateBundle } from '../core/bundle.js';
|
|
12
|
+
import { readConfig } from '../core/config.js';
|
|
13
|
+
import { refreshTemplates } from '../core/template-refresh.js';
|
|
14
|
+
function printRefreshSummary(r) {
|
|
15
|
+
const list = (items) => items.map((f) => ` • ${f}`).join('\n');
|
|
16
|
+
if (r.written.length > 0) {
|
|
17
|
+
log(` ✓ 更新(${r.written.length}):`);
|
|
18
|
+
log(list(r.written));
|
|
19
|
+
}
|
|
20
|
+
if (r.backedUp.length > 0) {
|
|
21
|
+
log(` ℹ 上書き前にバックアップを作成(${r.backedUp.length}、*.frelio-bak):`);
|
|
22
|
+
log(list(r.backedUp));
|
|
23
|
+
}
|
|
24
|
+
if (r.skipped.length > 0) {
|
|
25
|
+
log(` ⚠ 差分ありのため据え置き(${r.skipped.length}、--force で上書き):`);
|
|
26
|
+
log(list(r.skipped));
|
|
27
|
+
}
|
|
28
|
+
if (r.unchanged.length > 0) {
|
|
29
|
+
log(` ・ 変更なし(${r.unchanged.length})`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
10
32
|
export async function updateCommand(options) {
|
|
11
33
|
log('');
|
|
12
|
-
log('🔄 Frelio
|
|
34
|
+
log('🔄 Frelio アップデート');
|
|
13
35
|
log('');
|
|
14
36
|
const projectDir = process.cwd();
|
|
15
|
-
// admin/
|
|
37
|
+
// admin/ の存在チェック(Frelio プロジェクトのルートか)
|
|
16
38
|
const adminDir = path.join(projectDir, 'admin');
|
|
17
39
|
if (!fs.existsSync(adminDir)) {
|
|
18
40
|
logError('admin/ ディレクトリが見つかりません。Frelio プロジェクトのルートで実行してください。');
|
|
19
41
|
process.exit(1);
|
|
20
42
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
log(' • CLOUDFLARE_ACCOUNT_ID … wrangler.toml の [vars]');
|
|
31
|
-
log(' • PAGES_PROJECT_NAME … wrangler.toml の [vars](コンテンツ配信 Pages プロジェクト名)');
|
|
32
|
-
log(' 未設定でも他機能は動作します。詳細は wrangler.toml のコメントを参照。');
|
|
43
|
+
// --- 1. CMS Admin バンドル更新 ---
|
|
44
|
+
if (!options.templatesOnly) {
|
|
45
|
+
log(' 📥 CMS Admin バンドルを取得・更新中...');
|
|
46
|
+
const result = await updateBundle(projectDir, options.version);
|
|
47
|
+
if (!result.success) {
|
|
48
|
+
logError(result.error);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
logSuccess(`config.json を保持したままバンドルを ${result.data.version} に更新しました`);
|
|
33
52
|
log('');
|
|
34
53
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
54
|
+
// --- 2. テンプレート(ワークフロー / functions / wrangler)再生成 ---
|
|
55
|
+
if (!options.bundleOnly) {
|
|
56
|
+
const configResult = readConfig(projectDir);
|
|
57
|
+
if (!configResult.success) {
|
|
58
|
+
logError(configResult.error);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const config = configResult.data.config;
|
|
62
|
+
if (!config) {
|
|
63
|
+
log(' ⚠ admin/config.json が無いため、テンプレート再生成をスキップしました。');
|
|
64
|
+
log(' (バンドルのみ更新。テンプレートを再生成するには config.json が必要です)');
|
|
65
|
+
log('');
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
log(' 📥 テンプレート(ワークフロー / functions / wrangler)を再生成中...');
|
|
69
|
+
const refresh = await refreshTemplates(projectDir, config, {
|
|
70
|
+
mode: options.force ? 'overwrite' : 'backup',
|
|
71
|
+
});
|
|
72
|
+
if (!refresh.success) {
|
|
73
|
+
logError(refresh.error);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
printRefreshSummary(refresh.data);
|
|
77
|
+
log('');
|
|
78
|
+
}
|
|
38
79
|
}
|
|
80
|
+
log(' ✅ アップデート完了');
|
|
81
|
+
log('');
|
|
82
|
+
log(' 次のステップ:');
|
|
83
|
+
log(' 1. 変更内容を確認(git diff、*.frelio-bak があれば差分を比較)');
|
|
84
|
+
log(' 2. `npx @c-time/frelio-cli doctor` で残課題が無いか確認');
|
|
85
|
+
log(' 3. develop ブランチでコミットし、各ブランチへ反映');
|
|
86
|
+
log('');
|
|
87
|
+
log(' ℹ デプロイ管理機能(Issue #27)を使う場合は次の環境変数が必要です:');
|
|
88
|
+
log(' • CLOUDFLARE_API_TOKEN … 管理画面 Pages のシークレット(wrangler pages secret put)');
|
|
89
|
+
log(' • CLOUDFLARE_ACCOUNT_ID … wrangler.toml の [vars]');
|
|
90
|
+
log(' • PAGES_PROJECT_NAME … wrangler.toml の [vars](コンテンツ配信 Pages プロジェクト名)');
|
|
91
|
+
log('');
|
|
39
92
|
}
|
package/dist/core/config.js
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/doctor — 生成物の自己診断(#85)
|
|
3
|
+
*
|
|
4
|
+
* #84 のマイグレーション定義を読み、対象リポジトリをスキャンして
|
|
5
|
+
* 未適用(期限付き廃止が残っている等)を検出する。
|
|
6
|
+
*/
|
|
7
|
+
import { type OperationResult } from './types.js';
|
|
8
|
+
import type { Migration } from './migrations/types.js';
|
|
9
|
+
export type Finding = {
|
|
10
|
+
migration: Migration;
|
|
11
|
+
/** ヒットしたファイル(プロジェクトルートからの相対パス) */
|
|
12
|
+
matchedFiles: string[];
|
|
13
|
+
};
|
|
14
|
+
export type DoctorReport = {
|
|
15
|
+
findings: Finding[];
|
|
16
|
+
/** スキャン対象となった(存在した)ファイル数 */
|
|
17
|
+
scannedFileCount: number;
|
|
18
|
+
/** info 以外(warning/critical)の指摘があるか = 対応が必要か */
|
|
19
|
+
actionable: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* グロブを展開する。`*` は basename(ファイル名部分)に 1 つだけ対応する。
|
|
23
|
+
* 例: `.github/workflows/*.yml`。マッチしたファイルの絶対パスを返す。
|
|
24
|
+
*/
|
|
25
|
+
export declare function expandGlob(projectDir: string, globRel: string): string[];
|
|
26
|
+
export declare function compilePatterns(m: Migration): RegExp[];
|
|
27
|
+
/**
|
|
28
|
+
* プロジェクトを診断し、未適用のマイグレーションを検出する。
|
|
29
|
+
* 欠損ファイルでは例外を投げない(best-effort)。
|
|
30
|
+
*/
|
|
31
|
+
export declare function runDoctor(projectDir: string): OperationResult<DoctorReport>;
|
|
32
|
+
/**
|
|
33
|
+
* 期限までの残日数を求める。`now` はテスト用に注入可能。
|
|
34
|
+
* 期限なし → null。負値 = 超過。
|
|
35
|
+
*/
|
|
36
|
+
export declare function daysUntil(deadline: string, now?: Date): number;
|