Compare commits

..

3 Commits

Author SHA1 Message Date
d302614b99 feat: automate tag release pipeline
Some checks failed
continuous-integration/drone/tag Build is failing
2026-05-25 11:26:02 +08:00
57e4dc72aa feat: switch extension updates to COS 2026-05-25 10:32:39 +08:00
8fa9fc4469 docs: add COS update design spec 2026-05-25 10:19:14 +08:00
24 changed files with 1509 additions and 24 deletions

34
.drone.yml Normal file
View File

@ -0,0 +1,34 @@
kind: pipeline
type: docker
name: release-tag
trigger:
event:
- tag
steps:
- name: install
image: node:20-alpine
commands:
- npm ci
- name: test
image: node:20-alpine
depends_on:
- install
commands:
- npm test
- name: release
image: node:20-alpine
depends_on:
- test
environment:
COS_BUCKET: wksgx-1343191620
COS_REGION: ap-nanjing
COS_SECRET_ID:
from_secret: cos_secret_id
COS_SECRET_KEY:
from_secret: cos_secret_key
commands:
- npm run release:tag

View File

@ -93,6 +93,8 @@ npm run write:latest
- 这个压缩包不是给 Chrome 商店上传的 - 这个压缩包不是给 Chrome 商店上传的
- 它是发给公司内部同事使用的交付包 - 它是发给公司内部同事使用的交付包
- 同事收到后需要解压,再到 `chrome://extensions``Load unpacked` - 同事收到后需要解压,再到 `chrome://extensions``Load unpacked`
- COS 发布时,`latest.json` 放在 `star-chart-search-enhancer/latest.json`ZIP 和 PDF 放在对应版本目录下
- 打 tag 后会触发 Drone 发布,推荐格式:`0.MMDD.N`
--- ---

View File

@ -36,7 +36,6 @@
"identity", "identity",
"storage" "storage"
], ],
"version": "0.2.0421.2",
"web_accessible_resources": [ "web_accessible_resources": [
{ {
"matches": [ "matches": [
@ -48,6 +47,7 @@
] ]
} }
], ],
"version": "0.2.0421.2",
"host_permissions": [ "host_permissions": [
"https://xingtu.cn/ad/creator/market*", "https://xingtu.cn/ad/creator/market*",
"https://*.xingtu.cn/ad/creator/market*", "https://*.xingtu.cn/ad/creator/market*",

View File

@ -263,7 +263,7 @@
} }
// src/shared/update-config.ts // src/shared/update-config.ts
var UPDATE_MANIFEST_URL = "https://example.com/star-chart-search-enhancer/latest.json"; var UPDATE_MANIFEST_URL = "https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/latest.json";
// src/popup/index.ts // src/popup/index.ts
async function bootPopup(options = {}) { async function bootPopup(options = {}) {

View File

@ -23,19 +23,50 @@ The popup checks `src/shared/update-config.ts` for the update manifest URL.
Before publishing the COS-based update flow: Before publishing the COS-based update flow:
1. Upload these files to COS: 1. Upload these files to COS:
- `release/latest.json` - `star-chart-search-enhancer/latest.json`
- `release/star-chart-search-enhancer-internal.zip` - `star-chart-search-enhancer/releases/<version>/star-chart-search-enhancer-internal.zip`
- `release/星图增强插件-超简单安装使用指南.pdf` - `star-chart-search-enhancer/releases/<version>/星图增强插件-超简单安装使用指南.pdf`
2. Make the COS path publicly readable. 2. Make the COS path publicly readable.
3. Replace the placeholder `UPDATE_MANIFEST_URL` in `src/shared/update-config.ts`. 3. Replace the placeholder `UPDATE_MANIFEST_URL` in `src/shared/update-config.ts` if your COS bucket changes.
4. Rebuild and package the extension. 4. Rebuild and package the extension.
The release manifest can be generated with a real public base URL: The release manifest can be generated with a real public base URL:
```bash ```bash
UPDATE_PUBLIC_BASE_URL="https://<your-cos-domain>/star-chart-search-enhancer/releases/<version>" npm run write:latest UPDATE_PUBLIC_BASE_URL="https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/releases/<version>" npm run write:latest
``` ```
Quick access check:
```bash
curl -I https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/latest.json
```
## Drone Release Flow
Tag the repo to trigger the release pipeline:
```bash
git tag 0.0525.1
git push origin 0.0525.1
```
The Drone job will:
1. Run `npm ci`.
2. Run `npm test`.
3. Run `npm run release:tag`.
4. Build the release bundle.
5. Write `release/latest.json`.
6. Upload `latest.json`, the ZIP, and the PDF to COS.
Drone secrets required:
- `cos_secret_id`
- `cos_secret_key`
The pipeline uses the tag as the release version. Recommended format: `0.MMDD.N`.
## Coworker Install Steps ## Coworker Install Steps
1. Unzip `star-chart-search-enhancer-internal.zip`. 1. Unzip `star-chart-search-enhancer-internal.zip`.

View File

@ -0,0 +1,128 @@
# COS Extension Update 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:** Point the popup update flow at the real COS release bucket and keep the generated release manifest, docs, and tests aligned with that COS-based distribution path.
**Architecture:** Reuse the existing update-check flow already in `src/shared/update-check.ts`, `src/popup/index.ts`, `src/popup/view.ts`, and `src/background/index.ts`. The only behavior change is the source of truth: the popup should fetch a stable COS-hosted `latest.json`, while `scripts/write-latest-manifest.mjs` should keep generating versioned asset URLs under the COS release folder. Everything else stays manual and user-driven.
**Tech Stack:** TypeScript, Chrome MV3, Vitest, Node.js ESM scripts, Tencent COS public HTTPS URLs
---
### Task 1: Lock the popup manifest URL to COS
**Files:**
- Modify: `src/shared/update-config.ts`
- Test: `tests/update-config.test.ts`
- [ ] **Step 1: Write the failing URL test**
Add a small test that asserts `UPDATE_MANIFEST_URL` points at the stable COS-hosted `latest.json` URL for this bucket:
```ts
expect(UPDATE_MANIFEST_URL).toBe(
"https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/latest.json"
);
```
- [ ] **Step 2: Run the focused test**
Run:
```bash
npm test -- tests/update-config.test.ts
```
Expected: FAIL because the current constant still uses the placeholder example URL.
- [ ] **Step 3: Update the constant**
Replace the placeholder string in `src/shared/update-config.ts` with the COS `latest.json` URL above.
- [ ] **Step 4: Verify**
Run:
```bash
npm test -- tests/update-config.test.ts tests/popup-entry.test.ts
```
Expected: PASS.
### Task 2: Generate release assets from the COS base
**Files:**
- Modify: `scripts/write-latest-manifest.mjs`
- Modify: `release/latest.json`
- Test: `tests/update-check.test.ts`
- [ ] **Step 1: Write a manifest generation regression test**
Add or extend a test that proves the generated manifest uses the COS release base for assets, not `example.com`. Use the COS base:
```ts
https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/releases/0.2.0421.2
```
Assert that `zipUrl` and `guideUrl` are derived from that base.
- [ ] **Step 2: Run the focused test**
Run:
```bash
npm test -- tests/update-check.test.ts
```
Expected: FAIL until the generator default points at COS.
- [ ] **Step 3: Update the generator default**
Change `publicBaseUrl` in `scripts/write-latest-manifest.mjs` to default to the COS release base for this bucket and region, while keeping `UPDATE_PUBLIC_BASE_URL` as the override path for future releases.
- [ ] **Step 4: Regenerate the tracked manifest**
Run:
```bash
npm run write:latest
```
Then confirm `release/latest.json` contains the COS URLs.
- [ ] **Step 5: Verify**
Run:
```bash
npm test -- tests/update-check.test.ts tests/popup-entry.test.ts tests/background-index.test.ts
```
Expected: PASS.
### Task 3: Update distribution docs and verify COS access
**Files:**
- Modify: `docs/internal-extension-distribution.md`
- Modify: `README.md`
- [ ] **Step 1: Update the release instructions**
Document the stable manifest URL, the versioned asset base, and the upload flow to COS. Keep the user-facing manual install steps unchanged.
- [ ] **Step 2: Add the COS verification command**
Document a `curl -I` check for the public `latest.json` URL and the uploaded ZIP/PDF so a failed COS ACL is caught before release.
- [ ] **Step 3: Run the final verification**
Run:
```bash
npm test
npm run build:release
```
Expected: PASS, and the generated release bundle should still open the popup update card correctly.

View File

@ -0,0 +1,84 @@
# COS Extension Update Design
## Goal
Use COS as the release source for extension updates. When the popup opens, it checks a public `latest.json` on COS. If the COS version is newer than the installed extension version, the popup shows an update card with download actions for the ZIP and the PDF guide, plus manual reload instructions.
## Confirmed Scope
- Update prompt appears only in the extension popup.
- No star chart page banner in this change.
- The user keeps the current manual install flow: download, unzip, replace the folder, then reload in `chrome://extensions`.
## Reusable Implementation
The current repo already has most of the flow:
- `src/shared/update-check.ts` parses the manifest and compares versions.
- `src/popup/index.ts` checks for updates when the popup boots.
- `src/popup/view.ts` renders the update status and download actions.
- `src/background/index.ts` downloads the ZIP/PDF through `chrome.downloads`.
- `scripts/write-latest-manifest.mjs` generates `release/latest.json`.
This change is mostly activation and configuration, not a rewrite.
## Manifest Contract
The public COS manifest must keep these fields:
- `latestVersion`
- `minSupportedVersion`
- `publishedAt`
- `releaseNotes`
- `zipUrl`
- `guideUrl`
Rules:
- All asset URLs must be public HTTPS URLs.
- Version comparison stays numeric dotted comparison.
- Popup logic only needs `latestVersion` to decide whether to show the update card.
- `minSupportedVersion` stays in the manifest for forward compatibility.
## COS Layout
Use a fixed release layout like:
- `https://<cos-domain>/star-chart-search-enhancer/releases/<version>/latest.json`
- `https://<cos-domain>/star-chart-search-enhancer/releases/<version>/star-chart-search-enhancer-internal.zip`
- `https://<cos-domain>/star-chart-search-enhancer/releases/<version>/星图增强插件-超简单安装使用指南.pdf`
## User Flow
1. User opens the popup.
2. Popup reads the current extension version.
3. Popup fetches COS `latest.json` with `no-store`.
4. If `latestVersion` is not newer, show “当前已是最新版本”.
5. If `latestVersion` is newer, show “发现新版本” plus release notes.
6. User clicks:
- `下载更新包` for the ZIP
- `下载使用说明` for the PDF
7. Popup shows the manual upgrade instructions after download starts.
## Error Handling
- If the manifest is missing, invalid, or unreachable, the popup should show a non-blocking update error.
- If download fails, the popup should show a download error and keep the plugin usable.
- Update check failures must not block auth or normal plugin behavior.
## Release Process
1. Build the release package.
2. Package the internal ZIP.
3. Generate `latest.json` with the real COS base URL.
4. Upload `latest.json`, the ZIP, and the PDF to the COS folder.
5. Replace the placeholder manifest URL in `src/shared/update-config.ts`.
6. Rebuild and verify the popup update card.
## Out of Scope
- Automatic in-place extension updates.
- Auto-reload after download.
- Star chart page update prompts.
- Chrome Web Store publishing.

871
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
"build": "node scripts/build.mjs", "build": "node scripts/build.mjs",
"build:release": "BUILD_TARGET=release node scripts/build.mjs", "build:release": "BUILD_TARGET=release node scripts/build.mjs",
"mock:protected-api": "node scripts/mock-protected-api.mjs", "mock:protected-api": "node scripts/mock-protected-api.mjs",
"release:tag": "node scripts/ci/release-tag.mjs",
"package:internal": "npm run build:release && node scripts/package-release.mjs", "package:internal": "npm run build:release && node scripts/package-release.mjs",
"package:release": "npm run build:release && node scripts/package-release.mjs", "package:release": "npm run build:release && node scripts/package-release.mjs",
"write:latest": "node scripts/write-latest-manifest.mjs", "write:latest": "node scripts/write-latest-manifest.mjs",
@ -19,6 +20,7 @@
"license": "UNLICENSED", "license": "UNLICENSED",
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.59.1", "@playwright/test": "^1.59.1",
"cos-nodejs-sdk-v5": "^2.15.4",
"jsdom": "^29.0.2", "jsdom": "^29.0.2",
"tsup": "^8.5.1", "tsup": "^8.5.1",
"typescript": "^6.0.3", "typescript": "^6.0.3",

View File

@ -1,11 +1,11 @@
{ {
"guideUrl": "https://example.com/star-chart-search-enhancer/releases/0.2.0421.2/星图增强插件-超简单安装使用指南.pdf", "guideUrl": "https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/releases/0.2.0421.2/星图增强插件-超简单安装使用指南.pdf",
"latestVersion": "0.2.0421.2", "latestVersion": "0.2.0421.2",
"minSupportedVersion": "0.2.0421.2", "minSupportedVersion": "0.2.0421.2",
"publishedAt": "2026-05-19", "publishedAt": "2026-05-25",
"releaseNotes": [ "releaseNotes": [
"支持在插件弹窗中检查新版本", "支持在插件弹窗中检查新版本",
"支持一键下载最新版插件压缩包和使用说明" "支持一键下载最新版插件压缩包和使用说明"
], ],
"zipUrl": "https://example.com/star-chart-search-enhancer/releases/0.2.0421.2/star-chart-search-enhancer-internal.zip" "zipUrl": "https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/releases/0.2.0421.2/star-chart-search-enhancer-internal.zip"
} }

View File

@ -0,0 +1,71 @@
import { execFile } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { resolveReleaseVersion } from "../release-version.mjs";
import { uploadReleaseAssets } from "./upload-release-assets.mjs";
const execFileAsync = promisify(execFile);
export async function runReleaseTagPipeline(env = process.env) {
const projectRoot = resolveProjectRoot();
const releaseVersion = resolveReleaseVersion(env);
const publicBaseUrl =
env.UPDATE_PUBLIC_BASE_URL ??
`https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/releases/${releaseVersion}`;
console.log(`release version: ${releaseVersion}`);
console.log("running build:release");
await runNpmScript("build:release", projectRoot, {
...env,
EXTENSION_VERSION: releaseVersion
});
console.log("running package-release");
await runNodeScript("scripts/package-release.mjs", projectRoot, {
...env,
EXTENSION_VERSION: releaseVersion
});
console.log("writing latest manifest");
await runNpmScript("write:latest", projectRoot, {
...env,
EXTENSION_VERSION: releaseVersion,
UPDATE_PUBLIC_BASE_URL: publicBaseUrl
});
console.log("uploading release assets to COS");
await uploadReleaseAssets({
env: {
...env,
EXTENSION_VERSION: releaseVersion
},
projectRoot,
releaseVersion
});
}
async function runNpmScript(scriptName, cwd, env) {
await execFileAsync("npm", ["run", scriptName], {
cwd,
env,
stdio: "inherit"
});
}
async function runNodeScript(scriptPath, cwd, env) {
await execFileAsync("node", [scriptPath], {
cwd,
env,
stdio: "inherit"
});
}
function resolveProjectRoot() {
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
await runReleaseTagPipeline();
}

View File

@ -0,0 +1,83 @@
import COS from "cos-nodejs-sdk-v5";
import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import path from "node:path";
import { buildReleaseUploadTargets } from "../release-assets.mjs";
export async function uploadReleaseAssets(options = {}) {
const env = options.env ?? process.env;
const projectRoot = options.projectRoot ?? resolveProjectRoot();
const releaseVersion = options.releaseVersion ?? env.EXTENSION_VERSION ?? env.DRONE_TAG;
if (!releaseVersion) {
throw new Error("release version is required for COS upload");
}
const cos = options.cosClient ?? createCosClient(env);
const targets =
options.targets ??
buildReleaseUploadTargets({
projectRoot,
releaseVersion
});
for (const target of targets) {
const body = await readFile(target.localPath);
await putObjectAsync(cos, {
Bucket: getRequiredEnv(env, "COS_BUCKET"),
Body: body,
ContentType: getContentType(target.cosKey),
Key: target.cosKey,
Region: getRequiredEnv(env, "COS_REGION")
});
}
}
async function putObjectAsync(client, params) {
return await new Promise((resolve, reject) => {
client.putObject(params, (error, data) => {
if (error) {
reject(error);
return;
}
resolve(data);
});
});
}
function createCosClient(env) {
return new COS({
SecretId: getRequiredEnv(env, "COS_SECRET_ID"),
SecretKey: getRequiredEnv(env, "COS_SECRET_KEY")
});
}
function getContentType(key) {
if (key.endsWith(".json")) {
return "application/json";
}
if (key.endsWith(".pdf")) {
return "application/pdf";
}
if (key.endsWith(".zip")) {
return "application/zip";
}
return "application/octet-stream";
}
function getRequiredEnv(env, name) {
const value = env[name];
if (!value) {
throw new Error(`${name} is required`);
}
return value;
}
function resolveProjectRoot() {
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
}

View File

@ -1,3 +1,5 @@
import { resolveReleaseVersion } from "./release-version.mjs";
const sharedIcons = { const sharedIcons = {
16: "assets/icons/icon-16.png", 16: "assets/icons/icon-16.png",
32: "assets/icons/icon-32.png", 32: "assets/icons/icon-32.png",
@ -34,7 +36,6 @@ const sharedManifest = {
manifest_version: 3, manifest_version: 3,
name: "Star Chart Search Enhancer", name: "Star Chart Search Enhancer",
permissions: ["downloads", "identity", "storage"], permissions: ["downloads", "identity", "storage"],
version: "0.2.0421.2",
web_accessible_resources: [ web_accessible_resources: [
{ {
matches: [ matches: [
@ -72,6 +73,7 @@ export function createManifest(options = {}) {
return { return {
...sharedManifest, ...sharedManifest,
version: resolveReleaseVersion(),
host_permissions: hostPermissions host_permissions: hostPermissions
}; };
} }

View File

@ -0,0 +1,25 @@
import path from "node:path";
export function buildReleaseUploadTargets({
projectRoot,
releaseVersion
}) {
const releaseDir = path.join(projectRoot, "release");
const releasePrefix = "star-chart-search-enhancer";
const releaseVersionPrefix = `${releasePrefix}/releases/${releaseVersion}`;
return [
{
cosKey: `${releasePrefix}/latest.json`,
localPath: path.join(releaseDir, "latest.json")
},
{
cosKey: `${releaseVersionPrefix}/star-chart-search-enhancer-internal.zip`,
localPath: path.join(releaseDir, "star-chart-search-enhancer-internal.zip")
},
{
cosKey: `${releaseVersionPrefix}/星图增强插件-超简单安装使用指南.pdf`,
localPath: path.join(releaseDir, "星图增强插件-超简单安装使用指南.pdf")
}
];
}

View File

@ -0,0 +1,30 @@
const RELEASE_VERSION_PATTERN = /^\d+(?:\.\d+)*$/;
export function normalizeReleaseVersionTag(value) {
if (typeof value !== "string") {
return null;
}
const normalized = value.trim().replace(/^v/i, "");
if (!RELEASE_VERSION_PATTERN.test(normalized)) {
return null;
}
return normalized;
}
export function resolveReleaseVersion(
env = process.env,
fallbackVersion = "0.2.0421.2"
) {
const candidates = [env.EXTENSION_VERSION, env.DRONE_TAG, fallbackVersion];
for (const candidate of candidates) {
const normalized = normalizeReleaseVersionTag(candidate);
if (normalized) {
return normalized;
}
}
throw new Error("unable to resolve a valid release version");
}

View File

@ -0,0 +1,15 @@
export function createLatestManifest(options) {
const publishedAt = options.publishedAt ?? new Date().toISOString().slice(0, 10);
return {
guideUrl: `${options.publicBaseUrl}/星图增强插件-超简单安装使用指南.pdf`,
latestVersion: options.latestVersion,
minSupportedVersion: options.minSupportedVersion,
publishedAt,
releaseNotes: [
"支持在插件弹窗中检查新版本",
"支持一键下载最新版插件压缩包和使用说明"
],
zipUrl: `${options.publicBaseUrl}/star-chart-search-enhancer-internal.zip`
};
}

View File

@ -2,28 +2,24 @@ import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path"; import path from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { createManifest } from "./manifest.mjs"; import { createManifest } from "./manifest.mjs";
import { createLatestManifest } from "./write-latest-manifest-data.mjs";
import { resolveReleaseVersion } from "./release-version.mjs";
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
const projectRoot = path.resolve(__dirname, ".."); const projectRoot = path.resolve(__dirname, "..");
const releaseDir = path.join(projectRoot, "release"); const releaseDir = path.join(projectRoot, "release");
const releaseManifest = createManifest({ target: "release" }); const releaseManifest = createManifest({ target: "release" });
const latestVersion = process.env.LATEST_VERSION ?? releaseManifest.version; const latestVersion =
process.env.LATEST_VERSION ?? resolveReleaseVersion(process.env, releaseManifest.version);
const publicBaseUrl = const publicBaseUrl =
process.env.UPDATE_PUBLIC_BASE_URL ?? process.env.UPDATE_PUBLIC_BASE_URL ??
`https://example.com/star-chart-search-enhancer/releases/${latestVersion}`; `https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/releases/${latestVersion}`;
const latestManifest = createLatestManifest({
const latestManifest = {
guideUrl: `${publicBaseUrl}/星图增强插件-超简单安装使用指南.pdf`,
latestVersion, latestVersion,
minSupportedVersion: releaseManifest.version, minSupportedVersion: releaseManifest.version,
publishedAt: new Date().toISOString().slice(0, 10), publicBaseUrl
releaseNotes: [ });
"支持在插件弹窗中检查新版本",
"支持一键下载最新版插件压缩包和使用说明"
],
zipUrl: `${publicBaseUrl}/star-chart-search-enhancer-internal.zip`
};
await mkdir(releaseDir, { recursive: true }); await mkdir(releaseDir, { recursive: true });
await writeFile( await writeFile(

View File

@ -1,2 +1,2 @@
export const UPDATE_MANIFEST_URL = export const UPDATE_MANIFEST_URL =
"https://example.com/star-chart-search-enhancer/latest.json"; "https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/latest.json";

View File

@ -66,4 +66,20 @@ describe("manifest", () => {
"32": "assets/icons/icon-32.png" "32": "assets/icons/icon-32.png"
}); });
}); });
test("uses EXTENSION_VERSION for release builds", () => {
const previousVersion = process.env.EXTENSION_VERSION;
process.env.EXTENSION_VERSION = "v0.0525.1";
try {
const releaseManifest = createManifest({ target: "release" });
expect(releaseManifest.version).toBe("0.0525.1");
} finally {
if (previousVersion === undefined) {
delete process.env.EXTENSION_VERSION;
} else {
process.env.EXTENSION_VERSION = previousVersion;
}
}
});
}); });

View File

@ -0,0 +1,30 @@
import path from "node:path";
import { describe, expect, test } from "vitest";
import { buildReleaseUploadTargets } from "../scripts/release-assets.mjs";
describe("release-assets", () => {
test("maps release files to the COS object keys", () => {
expect(
buildReleaseUploadTargets({
projectRoot: "/repo",
releaseVersion: "0.0525.1"
})
).toEqual([
{
cosKey: "star-chart-search-enhancer/latest.json",
localPath: path.join("/repo", "release", "latest.json")
},
{
cosKey:
"star-chart-search-enhancer/releases/0.0525.1/star-chart-search-enhancer-internal.zip",
localPath: path.join("/repo", "release", "star-chart-search-enhancer-internal.zip")
},
{
cosKey:
"star-chart-search-enhancer/releases/0.0525.1/星图增强插件-超简单安装使用指南.pdf",
localPath: path.join("/repo", "release", "星图增强插件-超简单安装使用指南.pdf")
}
]);
});
});

View File

@ -0,0 +1,25 @@
import { describe, expect, test } from "vitest";
import {
normalizeReleaseVersionTag,
resolveReleaseVersion
} from "../scripts/release-version.mjs";
describe("release-version", () => {
test("normalizes a tag by stripping a leading v", () => {
expect(normalizeReleaseVersionTag("v0.0525.1")).toBe("0.0525.1");
expect(normalizeReleaseVersionTag("0.0525.1")).toBe("0.0525.1");
});
test("prefers EXTENSION_VERSION over DRONE_TAG and fallback", () => {
expect(
resolveReleaseVersion(
{
DRONE_TAG: "0.0525.2",
EXTENSION_VERSION: "v0.0525.3"
},
"0.2.0421.2"
)
).toBe("0.0525.3");
});
});

View File

@ -0,0 +1,11 @@
import { describe, expect, test } from "vitest";
import { UPDATE_MANIFEST_URL } from "../src/shared/update-config";
describe("update-config", () => {
test("points popup update checks at the COS latest manifest", () => {
expect(UPDATE_MANIFEST_URL).toBe(
"https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/latest.json"
);
});
});

View File

@ -0,0 +1,29 @@
import { describe, expect, test } from "vitest";
import { createLatestManifest } from "../scripts/write-latest-manifest-data.mjs";
describe("write-latest-manifest-data", () => {
test("builds COS asset URLs from the release base", () => {
expect(
createLatestManifest({
latestVersion: "0.2.0421.2",
minSupportedVersion: "0.2.0421.2",
publicBaseUrl:
"https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/releases/0.2.0421.2",
publishedAt: "2026-05-25"
})
).toEqual({
guideUrl:
"https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/releases/0.2.0421.2/星图增强插件-超简单安装使用指南.pdf",
latestVersion: "0.2.0421.2",
minSupportedVersion: "0.2.0421.2",
publishedAt: "2026-05-25",
releaseNotes: [
"支持在插件弹窗中检查新版本",
"支持一键下载最新版插件压缩包和使用说明"
],
zipUrl:
"https://wksgx-1343191620.cos.ap-nanjing.myqcloud.com/star-chart-search-enhancer/releases/0.2.0421.2/star-chart-search-enhancer-internal.zip"
});
});
});