Compare commits
4 Commits
e1cf2970da
...
8aca116949
| Author | SHA1 | Date | |
|---|---|---|---|
| 8aca116949 | |||
| 3a80ef9859 | |||
| 1a7b025aee | |||
| b3b916c6bc |
@ -104,7 +104,7 @@ npm run write:latest
|
|||||||
2. 打开 `chrome://extensions`
|
2. 打开 `chrome://extensions`
|
||||||
3. 打开右上角 `开发者模式`
|
3. 打开右上角 `开发者模式`
|
||||||
4. 点击 `加载已解压的扩展程序`
|
4. 点击 `加载已解压的扩展程序`
|
||||||
5. 选择解压后的插件文件夹
|
5. 选择解压后的 `dist/` 文件夹
|
||||||
|
|
||||||
安装后请确认扩展 ID 是:
|
安装后请确认扩展 ID 是:
|
||||||
|
|
||||||
|
|||||||
@ -73,7 +73,7 @@ The pipeline uses the tag as the release version. Recommended format: `0.MMDD.N`
|
|||||||
2. Open `chrome://extensions`.
|
2. Open `chrome://extensions`.
|
||||||
3. Enable developer mode.
|
3. Enable developer mode.
|
||||||
4. Click `Load unpacked`.
|
4. Click `Load unpacked`.
|
||||||
5. Select the unzipped folder.
|
5. Select the unzipped `dist/` folder.
|
||||||
6. Confirm the extension ID is `pkjopdibdnomhogjheclhnknmejccffg`.
|
6. Confirm the extension ID is `pkjopdibdnomhogjheclhnknmejccffg`.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|||||||
188
docs/superpowers/plans/2026-05-25-unified-dist-distribution.md
Normal file
188
docs/superpowers/plans/2026-05-25-unified-dist-distribution.md
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
# Unified Dist Distribution Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Make first-time install, Git-based install, ZIP-based install, and later updates all use the same unpacked extension directory named `dist/`.
|
||||||
|
|
||||||
|
**Architecture:** Standardize the extension output path through one shared build-path helper, then make ZIP packaging wrap that exact `dist/` directory, and finally update all teammate-facing docs to reference only that path. The release pipeline keeps its existing behavior, but the user-facing artifact shape becomes stable and singular.
|
||||||
|
|
||||||
|
**Tech Stack:** TypeScript, Node.js ESM scripts, Chrome MV3 extension packaging, Vitest
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Map
|
||||||
|
|
||||||
|
- Create: `scripts/build-output-path.mjs`
|
||||||
|
- Single source of truth for the unpacked extension output path.
|
||||||
|
- Modify: `scripts/build.mjs`
|
||||||
|
- Write release builds to `dist/`.
|
||||||
|
- Modify: `scripts/package-release.mjs`
|
||||||
|
- Package the shared `dist/` output instead of any alternate directory.
|
||||||
|
- Modify: `scripts/package-release-archive.mjs`
|
||||||
|
- Ensure ZIP output preserves a top-level `dist/` folder.
|
||||||
|
- Modify: `tests/package-release-archive.test.ts`
|
||||||
|
- Verify ZIP layout unpacks as `dist/...`.
|
||||||
|
- Create: `tests/build-output-path.test.ts`
|
||||||
|
- Verify the shared helper resolves `dist/`.
|
||||||
|
- Modify: `playwright.config.js`
|
||||||
|
- Load the extension from `dist/`.
|
||||||
|
- Modify: `e2e-tests/extension-load.spec.js`
|
||||||
|
- Verify files under `dist/`.
|
||||||
|
- Modify: `README.md`
|
||||||
|
- Remove `dist-release` references and describe `dist/` as the only unpacked directory.
|
||||||
|
- Modify: `docs/internal-extension-distribution.md`
|
||||||
|
- Align the release flow language with `dist/`.
|
||||||
|
- Modify: `docs/【给同事】从Git下载使用说明.md`
|
||||||
|
- Instruct coworkers to load `dist/`.
|
||||||
|
- Modify: `.gitignore`
|
||||||
|
- Remove obsolete `dist-release/` ignore entry if no longer needed.
|
||||||
|
- Delete tracked directory: `dist-release/`
|
||||||
|
- Remove the confusing second unpacked extension directory from the repo.
|
||||||
|
|
||||||
|
### Task 1: Lock the single output path in tests
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `tests/build-output-path.test.ts`
|
||||||
|
- Modify: `tests/package-release-archive.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Write the failing helper test**
|
||||||
|
|
||||||
|
Create a small test asserting both development and release builds resolve to:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
path.join("/repo", "dist")
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Write the failing ZIP layout assertion**
|
||||||
|
|
||||||
|
Update the archive test so it expects:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
"dist/hello.txt"
|
||||||
|
```
|
||||||
|
|
||||||
|
not a flat file list or any alternate top-level folder.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Run focused tests**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test -- tests/build-output-path.test.ts tests/package-release-archive.test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL until the shared path helper and ZIP layout are implemented.
|
||||||
|
|
||||||
|
### Task 2: Standardize build and package scripts
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `scripts/build-output-path.mjs`
|
||||||
|
- Modify: `scripts/build.mjs`
|
||||||
|
- Modify: `scripts/package-release.mjs`
|
||||||
|
- Modify: `scripts/package-release-archive.mjs`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Implement the shared output-path helper**
|
||||||
|
|
||||||
|
Add `resolveExtensionBuildDir(projectRoot, buildTarget)` and return `dist/` for both release and development flows.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Update the build script**
|
||||||
|
|
||||||
|
Replace any hard-coded directory switching logic so the release build writes into `dist/`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Update the package script**
|
||||||
|
|
||||||
|
Point ZIP packaging at the same `dist/` directory.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Keep ZIP layout stable**
|
||||||
|
|
||||||
|
Ensure the archive helper stores files under a top-level `dist/` directory inside the ZIP.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Verify**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test -- tests/build-output-path.test.ts tests/package-release-archive.test.ts
|
||||||
|
npm run build:release
|
||||||
|
npm run package:internal
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS, and the ZIP should unpack as `dist/...`.
|
||||||
|
|
||||||
|
### Task 3: Remove `dist-release` from tooling and docs
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `playwright.config.js`
|
||||||
|
- Modify: `e2e-tests/extension-load.spec.js`
|
||||||
|
- Modify: `README.md`
|
||||||
|
- Modify: `docs/internal-extension-distribution.md`
|
||||||
|
- Modify: `docs/【给同事】从Git下载使用说明.md`
|
||||||
|
- Modify: `.gitignore`
|
||||||
|
- Delete tracked directory: `dist-release/`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Update tool references**
|
||||||
|
|
||||||
|
Point Playwright and debug paths at `dist/`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Update teammate docs**
|
||||||
|
|
||||||
|
Make every install/update instruction reference only `dist/`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Remove the obsolete tracked directory**
|
||||||
|
|
||||||
|
Delete the committed `dist-release/` tree so future contributors cannot mistake it for the live extension directory.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Verify references**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rg -n "dist-release" README.md docs package.json scripts e2e-tests playwright.config.js src tests .gitignore
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: no remaining user-facing `dist-release` references.
|
||||||
|
|
||||||
|
### Task 4: Final verification
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify any of the above only if fixes are required
|
||||||
|
|
||||||
|
- [ ] **Step 1: Run targeted tests**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test -- tests/build-output-path.test.ts tests/package-release-archive.test.ts tests/manifest.test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Rebuild and inspect the artifact**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build:release
|
||||||
|
npm run package:internal
|
||||||
|
unzip -l release/star-chart-search-enhancer-internal.zip | sed -n '1,20p'
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: the archive contents start with `dist/...`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Full verification**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS, or if unrelated pre-existing failures remain, capture them explicitly before completion.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add scripts/build-output-path.mjs scripts/build.mjs scripts/package-release.mjs scripts/package-release-archive.mjs tests/build-output-path.test.ts tests/package-release-archive.test.ts README.md docs/internal-extension-distribution.md docs/【给同事】从Git下载使用说明.md playwright.config.js e2e-tests/extension-load.spec.js .gitignore
|
||||||
|
git commit -m "chore: unify extension distribution around dist"
|
||||||
|
```
|
||||||
|
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
# Unified Dist Distribution Design
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Make first-time installation and later updates use the exact same extension directory structure so coworkers never need to learn different paths or loading rules.
|
||||||
|
|
||||||
|
## Confirmed Direction
|
||||||
|
|
||||||
|
- The only user-facing unpacked extension directory should be `dist/`.
|
||||||
|
- First install and later update must produce the same directory name and shape.
|
||||||
|
- Coworkers should always load the same directory in `chrome://extensions`.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
The current delivery flow has created multiple mental models:
|
||||||
|
|
||||||
|
- sometimes coworkers are told to load `dist-release/`
|
||||||
|
- sometimes the ZIP extracts to a differently named top-level folder
|
||||||
|
- sometimes Git-based installation and ZIP-based installation do not look identical
|
||||||
|
|
||||||
|
This is not user-friendly and increases support cost.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This change standardizes the user-facing extension directory across:
|
||||||
|
|
||||||
|
- local release builds
|
||||||
|
- ZIP packaging
|
||||||
|
- Git-based teammate installation
|
||||||
|
- later update downloads
|
||||||
|
- installation and update documentation
|
||||||
|
|
||||||
|
This change does not alter:
|
||||||
|
|
||||||
|
- extension runtime behavior
|
||||||
|
- update-check logic itself
|
||||||
|
- popup auth flow
|
||||||
|
- COS manifest format
|
||||||
|
|
||||||
|
## Distribution Rule
|
||||||
|
|
||||||
|
There must be exactly one user-facing extension directory:
|
||||||
|
|
||||||
|
- `dist/`
|
||||||
|
|
||||||
|
This rule must hold in all paths:
|
||||||
|
|
||||||
|
1. Git install
|
||||||
|
- teammate runs the release build
|
||||||
|
- resulting unpacked extension directory is `dist/`
|
||||||
|
2. ZIP install
|
||||||
|
- teammate unzips the package
|
||||||
|
- resulting top-level extension directory is `dist/`
|
||||||
|
3. ZIP update
|
||||||
|
- teammate downloads the newer ZIP
|
||||||
|
- unzip result is also `dist/`
|
||||||
|
- teammate replaces the previous `dist/`
|
||||||
|
|
||||||
|
## Packaging Rule
|
||||||
|
|
||||||
|
The release ZIP should unpack into a single top-level folder named `dist/`.
|
||||||
|
|
||||||
|
Expected unpack result:
|
||||||
|
|
||||||
|
- `dist/manifest.json`
|
||||||
|
- `dist/background/`
|
||||||
|
- `dist/content/`
|
||||||
|
- `dist/popup/`
|
||||||
|
- `dist/assets/`
|
||||||
|
|
||||||
|
The ZIP must not unpack as:
|
||||||
|
|
||||||
|
- a flat file list
|
||||||
|
- `dist-release/`
|
||||||
|
- `star-chart-search-enhancer-internal/`
|
||||||
|
- any other user-facing top-level folder name
|
||||||
|
|
||||||
|
## Build Rule
|
||||||
|
|
||||||
|
Release builds should write the unpacked extension directly to `dist/`.
|
||||||
|
|
||||||
|
There should not be a second extension build directory that teammates might mistake as the correct one.
|
||||||
|
|
||||||
|
If internal scripts need a concept of “release build,” that should be represented by:
|
||||||
|
|
||||||
|
- environment/config
|
||||||
|
- release manifest values
|
||||||
|
- packaging flow
|
||||||
|
|
||||||
|
but not by exposing a second unpacked directory name to users.
|
||||||
|
|
||||||
|
## Documentation Rule
|
||||||
|
|
||||||
|
All teammate-facing docs must say the same thing:
|
||||||
|
|
||||||
|
- first install: load `dist/`
|
||||||
|
- later update: replace `dist/` and reload
|
||||||
|
|
||||||
|
No user-facing doc should mention `dist-release/`.
|
||||||
|
|
||||||
|
## CI / Release Rule
|
||||||
|
|
||||||
|
Drone and local release scripts must publish only one ZIP layout:
|
||||||
|
|
||||||
|
- unzip result is `dist/`
|
||||||
|
|
||||||
|
The release flow must not generate a user ZIP whose folder layout differs from the Git-based install path.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- `npm run build:release` writes the unpacked extension to `dist/`
|
||||||
|
- release ZIP unpacks into a top-level `dist/` folder
|
||||||
|
- first install and later update ZIPs unpack to the same structure
|
||||||
|
- coworker docs consistently reference `dist/`
|
||||||
|
- repository no longer contains a second tracked unpacked extension directory that conflicts with `dist/`
|
||||||
|
|
||||||
|
## Out of Scope
|
||||||
|
|
||||||
|
- changing extension features
|
||||||
|
- changing the update notification UX
|
||||||
|
- switching away from unpacked-extension installation
|
||||||
|
- Chrome Web Store or enterprise force-install deployment
|
||||||
@ -18,9 +18,9 @@
|
|||||||
|
|
||||||
1. 在桌面上找到这个压缩包
|
1. 在桌面上找到这个压缩包
|
||||||
2. **右键** → 选择"解压到当前文件夹"(或"Extract Here")
|
2. **右键** → 选择"解压到当前文件夹"(或"Extract Here")
|
||||||
3. 会多出一个文件夹,名字类似 `star-chart-search-enhancer-internal`
|
3. 会多出一个文件夹,名字是 `dist`
|
||||||
|
|
||||||
⚠️ **重要**:这个文件夹要一直放在桌面,不要删、不要改名
|
⚠️ **重要**:这个 `dist` 文件夹要一直放在桌面,不要删、不要改名
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -37,9 +37,9 @@
|
|||||||
|
|
||||||
4. 点击左上角出现的 **"加载已解压的扩展程序"**
|
4. 点击左上角出现的 **"加载已解压的扩展程序"**
|
||||||
|
|
||||||
5. 选择刚才解压出来的插件文件夹
|
5. 选择刚才解压出来的 `dist` 文件夹
|
||||||
|
|
||||||
⚠️ **重要**:如果文件夹里能看到 `manifest.json`、`content`、`background`、`popup` 这些文件和文件夹,说明选对了。
|
⚠️ **重要**:如果 `dist` 文件夹里能看到 `manifest.json`、`content`、`background`、`popup` 这些文件和文件夹,说明选对了。
|
||||||
|
|
||||||
6. 看到绿色的插件卡片出现,就装好了!
|
6. 看到绿色的插件卡片出现,就装好了!
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ https://xingtu.cn/ad/creator/market
|
|||||||
- 解压下载到的新版本 zip
|
- 解压下载到的新版本 zip
|
||||||
- 打开 `chrome://extensions`
|
- 打开 `chrome://extensions`
|
||||||
- 找到 `Star Chart Search Enhancer`
|
- 找到 `Star Chart Search Enhancer`
|
||||||
- 点击 **"重新加载"**,或重新选择解压后的新插件文件夹
|
- 点击 **"重新加载"**,或重新选择解压后的 `dist` 文件夹
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ https://xingtu.cn/ad/creator/market
|
|||||||
⚠️ **如果重新加载后还是旧版本**:
|
⚠️ **如果重新加载后还是旧版本**:
|
||||||
- 先点击插件卡片的 **"移除"** 删除旧版本
|
- 先点击插件卡片的 **"移除"** 删除旧版本
|
||||||
- 然后重新点击 **"加载已解压的扩展程序"**
|
- 然后重新点击 **"加载已解压的扩展程序"**
|
||||||
- 再次选择新解压出来的插件文件夹
|
- 再次选择新解压出来的 `dist` 文件夹
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -6,7 +6,7 @@ import yazl from "yazl";
|
|||||||
|
|
||||||
export async function createReleaseArchive({
|
export async function createReleaseArchive({
|
||||||
archivePath,
|
archivePath,
|
||||||
rootDirName = "star-chart-search-enhancer-internal",
|
rootDirName = "dist",
|
||||||
sourceDir
|
sourceDir
|
||||||
}) {
|
}) {
|
||||||
const zip = new yazl.ZipFile();
|
const zip = new yazl.ZipFile();
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { describe, expect, test } from "vitest";
|
|||||||
import { createReleaseArchive } from "../scripts/package-release-archive.mjs";
|
import { createReleaseArchive } from "../scripts/package-release-archive.mjs";
|
||||||
|
|
||||||
describe("package-release-archive", () => {
|
describe("package-release-archive", () => {
|
||||||
test("creates a zip archive with a top-level release folder", async () => {
|
test("creates a zip archive with a top-level dist folder", async () => {
|
||||||
const tempDir = await mkdtemp(path.join(os.tmpdir(), "release-archive-"));
|
const tempDir = await mkdtemp(path.join(os.tmpdir(), "release-archive-"));
|
||||||
const sourceDir = path.join(tempDir, "source");
|
const sourceDir = path.join(tempDir, "source");
|
||||||
const archivePath = path.join(tempDir, "archive.zip");
|
const archivePath = path.join(tempDir, "archive.zip");
|
||||||
@ -21,8 +21,6 @@ describe("package-release-archive", () => {
|
|||||||
|
|
||||||
const archive = await readFile(archivePath);
|
const archive = await readFile(archivePath);
|
||||||
expect(archive.byteLength).toBeGreaterThan(0);
|
expect(archive.byteLength).toBeGreaterThan(0);
|
||||||
expect(archive.toString("utf8")).toContain(
|
expect(archive.toString("utf8")).toContain("dist/hello.txt");
|
||||||
"star-chart-search-enhancer-internal/hello.txt"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user