mirror of
https://github.com/Heretek-Games/drop-plugin-sdk.git
synced 2026-11-07 10:06:00 -10:00
feat(templates): ship release/CI pipeline and scope switch in every scaffold
Every template now carries .github/workflows/release.yml, a matching ci.yml, .sdk-scope.json, scripts/switch-sdk-scope.mjs, and a typecheck script. initPlugin applies the scope config on scaffold so @drop-oss specifiers and workspace:* deps are rewritten to the concrete @droposs versions, letting a freshly scaffolded repo install, typecheck, build, and release without manual edits. Release/CI install steps fall back to npm install when no lockfile is present.
This commit is contained in:
1 parent
adb96c6b33
commit
dfeb00b326
45 files changed
+1517
-19
No files matched your search
@@ -48,7 +48,7 @@ The CLI:
|
||||
|
||||
You can automate building, testing, signing, and releasing `.dropplugin` packages on every Git tag release.
|
||||
|
||||
Use `templates/release-action.yml` as the canonical per-repository release workflow: it runs `npm ci`, builds, tests, validates the manifest, packs the archive, attaches a `.sha256` checksum, and publishes a prerelease automatically for tags like `v1.0.0-rc.1`.
|
||||
Use `templates/release-action.yml` as the canonical per-repository release workflow: it installs dependencies (`npm ci` when a lockfile exists, `npm install` otherwise), builds, tests, validates the manifest, packs the archive, attaches a `.sha256` checksum, and publishes a prerelease automatically for tags like `v1.0.0-rc.1`. Every scaffolded template under `templates/` ships a copy of this workflow, a matching `ci.yml`, a `typecheck` script, and a `.sdk-scope.json` + `scripts/switch-sdk-scope.mjs` pair, so a freshly scaffolded repo can tag `v1.0.0` and release without manual edits.
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import path from "node:path";
|
||||
import { cp, mkdir, readFile, writeFile, stat } from "node:fs/promises";
|
||||
import {
|
||||
cp,
|
||||
mkdir,
|
||||
readFile,
|
||||
writeFile,
|
||||
stat,
|
||||
readdir,
|
||||
realpath,
|
||||
} from "node:fs/promises";
|
||||
|
||||
export type TemplateType =
|
||||
| "starter"
|
||||
@@ -16,6 +24,139 @@ export interface InitOptions {
|
||||
template?: string;
|
||||
}
|
||||
|
||||
/** npm scopes the templates may resolve the SDK/CLI from. */
|
||||
const SDK_SCOPES = ["@drop-oss", "@droposs", "@drop"];
|
||||
const SKIP_DIRS = new Set([
|
||||
"node_modules",
|
||||
".git",
|
||||
"dist",
|
||||
"dist-package",
|
||||
"dist-packages",
|
||||
]);
|
||||
const TEXT_EXT = new Set([
|
||||
".ts",
|
||||
".tsx",
|
||||
".js",
|
||||
".mjs",
|
||||
".cjs",
|
||||
".json",
|
||||
".md",
|
||||
".yml",
|
||||
".yaml",
|
||||
]);
|
||||
|
||||
interface SdkScopeConfig {
|
||||
sdk: string;
|
||||
sdkVersion: string;
|
||||
cliVersion: string;
|
||||
}
|
||||
|
||||
function parseSdkScope(text: string): SdkScopeConfig | null {
|
||||
let parsed: Record<string, unknown>;
|
||||
try {
|
||||
parsed = JSON.parse(text);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
const { sdk, sdkVersion, cliVersion } = parsed;
|
||||
if (
|
||||
typeof sdk !== "string" ||
|
||||
typeof sdkVersion !== "string" ||
|
||||
typeof cliVersion !== "string" ||
|
||||
!SDK_SCOPES.includes(sdk)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return { sdk, sdkVersion, cliVersion };
|
||||
}
|
||||
|
||||
function rewriteSpecifiers(text: string, scope: string): string {
|
||||
let out = text;
|
||||
for (const other of SDK_SCOPES) {
|
||||
if (other === scope) continue;
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageDeps(text: string, config: SdkScopeConfig): string {
|
||||
let pkg: Record<string, unknown>;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of [
|
||||
"dependencies",
|
||||
"devDependencies",
|
||||
"peerDependencies",
|
||||
"optionalDependencies",
|
||||
]) {
|
||||
const section = pkg[sectionName] as Record<string, string> | undefined;
|
||||
if (!section) continue;
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = dep.endsWith("/plugin-sdk")
|
||||
? "plugin-sdk"
|
||||
: dep.endsWith("/plugin-cli")
|
||||
? "plugin-cli"
|
||||
: null;
|
||||
if (!kind) continue;
|
||||
delete section[dep];
|
||||
section[`${config.sdk}/${kind}`] =
|
||||
kind === "plugin-sdk" ? config.sdkVersion : config.cliVersion;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(pkg, null, 2) + "\n";
|
||||
}
|
||||
|
||||
async function walkFiles(
|
||||
dir: string,
|
||||
files: string[] = [],
|
||||
visited = new Set<string>(),
|
||||
): Promise<string[]> {
|
||||
const real = await realpath(dir).catch(() => dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) await walkFiles(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites a freshly copied template so its `@drop-oss` monorepo specifiers
|
||||
* and `workspace:*` dependencies point at the scope/version declared in the
|
||||
* template's `.sdk-scope.json`. Without this a scaffolded repo keeps
|
||||
* `workspace:*` and cannot install outside the SDK monorepo.
|
||||
*/
|
||||
export async function applySdkScope(targetPath: string): Promise<boolean> {
|
||||
const raw = await readFile(
|
||||
path.join(targetPath, ".sdk-scope.json"),
|
||||
"utf-8",
|
||||
).catch(() => null);
|
||||
if (raw === null) return false;
|
||||
const config = parseSdkScope(raw);
|
||||
if (!config) return false;
|
||||
|
||||
for (const file of await walkFiles(targetPath)) {
|
||||
if (path.basename(file).match(/lock\.(json|yaml|lockb)$/)) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = await readFile(file, "utf-8");
|
||||
let after = rewriteSpecifiers(before, config.sdk);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageDeps(after, config);
|
||||
}
|
||||
if (after !== before) await writeFile(file, after);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function resolveTemplateFolder(templateName: string = "starter"): string {
|
||||
const norm = templateName.replace(/^template-/, "").toLowerCase();
|
||||
switch (norm) {
|
||||
@@ -90,6 +231,10 @@ export async function initPlugin(
|
||||
},
|
||||
});
|
||||
|
||||
// Point the scaffolded repo at the configured npm scope/version instead of
|
||||
// the monorepo's `workspace:*` links.
|
||||
await applySdkScope(targetPath);
|
||||
|
||||
const pluginId =
|
||||
options.id ||
|
||||
path
|
||||
|
||||
@@ -135,6 +135,43 @@ test("initPlugin scaffolds various templates correctly", async () => {
|
||||
.catch(() => null),
|
||||
);
|
||||
|
||||
// Every template ships the publishing pipeline, scope switch, typecheck
|
||||
// script, and concrete (non-workspace) SDK dependencies.
|
||||
for (const [dir, template] of [
|
||||
[uiDir, "client-ui"],
|
||||
[metaDir, "metadata"],
|
||||
[storeDir, "store"],
|
||||
[runnerDir, "runner"],
|
||||
[fullDir, "fullstack"],
|
||||
] as const) {
|
||||
for (const rel of [
|
||||
".github/workflows/release.yml",
|
||||
".github/workflows/ci.yml",
|
||||
".sdk-scope.json",
|
||||
"scripts/switch-sdk-scope.mjs",
|
||||
]) {
|
||||
assert.ok(
|
||||
await fs.stat(path.join(dir, rel)).catch(() => null),
|
||||
`${template} is missing ${rel}`,
|
||||
);
|
||||
}
|
||||
const pkg = JSON.parse(
|
||||
await fs.readFile(path.join(dir, "package.json"), "utf-8"),
|
||||
);
|
||||
assert.ok(pkg.scripts.typecheck, `${template} is missing a typecheck script`);
|
||||
assert.equal(pkg.dependencies["@droposs/plugin-sdk"], "^0.7.0");
|
||||
assert.equal(pkg.devDependencies["@droposs/plugin-cli"], "^0.7.0");
|
||||
assert.equal(pkg.dependencies["@drop-oss/plugin-sdk"], undefined);
|
||||
assert.equal(pkg.devDependencies["@drop-oss/plugin-cli"], undefined);
|
||||
}
|
||||
|
||||
const runnerSrc = await fs.readFile(
|
||||
path.join(runnerDir, "src/client.ts"),
|
||||
"utf-8",
|
||||
);
|
||||
assert.match(runnerSrc, /@droposs\/plugin-sdk/);
|
||||
assert.doesNotMatch(runnerSrc, /@drop-oss\/plugin-sdk/);
|
||||
|
||||
await fs.rm(tmp, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -227,9 +227,20 @@ test("initPlugin scaffolds a new plugin repository", async () => {
|
||||
await fs.readFile(path.join(targetDir, "package.json"), "utf-8"),
|
||||
);
|
||||
assert.equal(pkg.name, "drop-my-custom-plugin");
|
||||
assert.equal(pkg.dependencies["@droposs/plugin-sdk"], "^0.7.0");
|
||||
assert.equal(pkg.devDependencies["@droposs/plugin-cli"], "^0.7.0");
|
||||
assert.equal(pkg.scripts.typecheck, "tsc --noEmit");
|
||||
|
||||
const srcExists = await fs.stat(path.join(targetDir, "src", "index.ts"));
|
||||
assert.ok(srcExists.isFile());
|
||||
const src = await fs.readFile(path.join(targetDir, "src", "index.ts"), "utf-8");
|
||||
assert.match(src, /@droposs\/plugin-sdk/);
|
||||
assert.doesNotMatch(src, /@drop-oss\/plugin-sdk/);
|
||||
assert.ok(
|
||||
await fs
|
||||
.stat(path.join(targetDir, ".github", "workflows", "release.yml"))
|
||||
.catch(() => null),
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
@@ -27,7 +27,12 @@ jobs:
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --ignore-scripts
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
+6
-1
@@ -27,7 +27,12 @@ jobs:
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --ignore-scripts
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
dist-package/
|
||||
*.dropplugin
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.6.2",
|
||||
"cliVersion": "^0.6.2"
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -27,16 +27,20 @@
|
||||
},
|
||||
"checksum": "639af61b6c28f29ede8d001f5b1545c6c78b3360af1dc68d56fcf502fadaefc7",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/client.css": "a7e80bda51554013cce91e4a153dd4598f63e8334da9a1d69b6f492d0f848629",
|
||||
"dist/src/client.css.map": "58159ed3c29742d2d044de6e304a03806cf1c6ef0405136cce3e2613e94ffc4f",
|
||||
"dist/src/client.js": "639af61b6c28f29ede8d001f5b1545c6c78b3360af1dc68d56fcf502fadaefc7",
|
||||
"dist/src/client.js.map": "0a5a2907dea7b026cccbf4469520b5a4b4a97185a2a474b6b1d25ce38ac6b640",
|
||||
"dist/test/client.test.js": "7234144f610006dd7b56e689580a68e6bc090c5c4ead137c90313d0e32260708",
|
||||
"package.json": "45f7f8d41de9a5a4c6c246faee3359a3de9a6e89cc2bcc38caeeaff92dabccb4",
|
||||
"package.json": "4cdb8a519181b4e51355d034ff00e4159bde5fc4676e843fc62258c31655afc3",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/client.ts": "d6584c900faf826bcc5f45a900853110ff586591a295414c7386ef4c7d76e4c3",
|
||||
"src/components/MyPanel.vue": "f92d837fe157e4cf48ceef4c77d2b1f46e52d1b34e6548163a928ee61df8fe55",
|
||||
"test/client.test.ts": "b357cbe8049dc5f58d98fc3e7f2168e2d94c3c4c4353056794e757cf5b89b2a9",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
"build": "drop-plugin build",
|
||||
"dev": "drop-plugin dev",
|
||||
"test": "node --test test/client.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execSync("npm install --no-audit --no-fund", { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execSync("npm install --no-audit --no-fund --legacy-peer-deps", { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execSync("pnpm install", { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -79,19 +79,23 @@
|
||||
},
|
||||
"checksum": "4be6e9eb395333d01656762da93debc7f50c101ae3e222230e2b75217770580b",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/client.css": "16fe4969e12025eba710f5d71939827c8e3db98344fb4cb2f6622ff199f49ad3",
|
||||
"dist/src/client.css.map": "aef5ae0e46e30bafcd54d1c0d8a712ca40ac7df0b10ec5254b112345980ef2e0",
|
||||
"dist/src/client.js": "ba9114a7de9420db06682108ae4e4952bf60b570041a79d1efee536bf01f220e",
|
||||
"dist/src/client.js.map": "e83e8e665da64f3cb85692f8529a32f0e10336cafc430a97a34179f4685dc2e5",
|
||||
"dist/src/index.js": "4be6e9eb395333d01656762da93debc7f50c101ae3e222230e2b75217770580b",
|
||||
"dist/src/index.js.map": "0c6c706d98afc1913a45cf484c12d6d79b5ef5186c32c4ef35af7b4ff1c1e18f",
|
||||
"package.json": "e1a2e1aaf319cca92c451a11bebe22a65dcf8e51d6794f7ae90c35355f0e64b1",
|
||||
"package.json": "5cf0bd19d8e597dac4cb1c60c67e8ef0255a5a47c5507720a481599ac7623bfb",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/client.ts": "a8f767ad5ded4ae6ef767a46fcaa0c945fd9098e4cd923473053c9e437e21815",
|
||||
"src/components/StatusPanel.vue": "2b8374cfad18715c235b7909ddcd2c45332d664d0f4e89b0e3e6418e5ab02fb6",
|
||||
"src/index.ts": "cff4a6b75e96bae971da4272d6ededde5cd88358b466ba861db54d9051d84d60",
|
||||
"test/client.test.ts": "8fe9659122b3e331688866813195b101a1ca9c543b5f79787bde8c8538211a29",
|
||||
"test/index.test.ts": "a0cc24ae8d19ff423bb717a3edb4c8e029144ed639ff02200d5d5fa4b028528b",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"test/index.test.ts": "2ca92c3a328f623e82da8d2b38e155dc24ddd91cbfedd69619b56393137bd777",
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
"build": "drop-plugin build",
|
||||
"dev": "drop-plugin dev",
|
||||
"test": "node --test test/index.test.ts test/client.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execSync("npm install --no-audit --no-fund", { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execSync("npm install --no-audit --no-fund --legacy-peer-deps", { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execSync("pnpm install", { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -19,7 +19,7 @@ test("MyFullstackPlugin registers status and config routes", async () => {
|
||||
const statusRes = (await status.handler(null, {
|
||||
params: {},
|
||||
query: {},
|
||||
readJson: async () => ({}),
|
||||
readJson: async <T = unknown>() => ({}) as T,
|
||||
})) as { ok: boolean };
|
||||
assert.equal(statusRes.ok, true);
|
||||
|
||||
@@ -28,7 +28,7 @@ test("MyFullstackPlugin registers status and config routes", async () => {
|
||||
const configRes = (await config.handler(null, {
|
||||
params: {},
|
||||
query: {},
|
||||
readJson: async () => ({ mode: "full" }),
|
||||
readJson: async <T = unknown>() => ({ mode: "full" }) as T,
|
||||
})) as { saved: boolean; synced: number };
|
||||
assert.equal(configRes.saved, true);
|
||||
assert.equal(configRes.synced, 1);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -28,13 +28,17 @@
|
||||
},
|
||||
"checksum": "4b4d8aa64f747b9611e00609a3036b493ba030a3e5aadf4d68fcf7752fef88a9",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/index.js": "4b4d8aa64f747b9611e00609a3036b493ba030a3e5aadf4d68fcf7752fef88a9",
|
||||
"dist/src/index.js.map": "904c14711dda7e966d9d534e53f277e895c87411b486d76424eee15a1963bafb",
|
||||
"dist/test/index.test.js": "640511eaf5d061c82d6cdf609bd518cd8033e05a59f7642418231ba69e9ed59a",
|
||||
"package.json": "86dbe2c6a0412914c40ee2692b730262602437b19e5c06c15e0f654b27feb427",
|
||||
"package.json": "d6635b8ea742bc70120ae47bfdacecc434833f0913f1d1db9c4903cce76d84c5",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/index.ts": "546e73733177bd21ec77bcef1c77b11453ad15dbf3204d5aef8671e75ba19d48",
|
||||
"test/index.test.ts": "efd5b6120feaee7b74fb7e774d3ff243d1bf6d9195d28181c2ab615d45908b0f",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"build": "drop-plugin build",
|
||||
"test": "node --test test/index.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execSync("npm install --no-audit --no-fund", { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execSync("npm install --no-audit --no-fund --legacy-peer-deps", { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execSync("pnpm install", { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -28,13 +28,17 @@
|
||||
},
|
||||
"checksum": "1aed97c00c0d65e276bbe6cc4fb856bcc311eba57dd02aced1050739c25b656e",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/client.js": "1aed97c00c0d65e276bbe6cc4fb856bcc311eba57dd02aced1050739c25b656e",
|
||||
"dist/src/client.js.map": "a33fa4205bdff524362e0ae3d9fa43160dfa494635b1119be712c91a47d5b7cf",
|
||||
"dist/test/client.test.js": "30a5d956c06e06aa230bacb27537a2e02b0865c99215dcb5175733caa99769d1",
|
||||
"package.json": "2f3b4ffaa8a135c11f07818c690841259402200f371bf64c832cf18731d7190c",
|
||||
"package.json": "b129d1090d19b77e37c162af3a91e613f6f5955f3f8b052f0da3798e2028e899",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/client.ts": "458ac509935463f9b2e2bd6c56144fb12b3434819155432f08403d7b67b03a6e",
|
||||
"test/client.test.ts": "de8e38567558d71a95c36ea414f555bda0cb8b46c5e85c73b19d5cbab6bf814c",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"build": "drop-plugin build",
|
||||
"test": "node --test test/client.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execSync("npm install --no-audit --no-fund", { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execSync("npm install --no-audit --no-fund --legacy-peer-deps", { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execSync("pnpm install", { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Build, typecheck, and test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Release Drop Plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build, validate, and publish .dropplugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # actions/[email protected]
|
||||
|
||||
- name: Set up Node.js 22
|
||||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # actions/[email protected]
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
if [ -f package-lock.json ]; then
|
||||
npm ci --ignore-scripts
|
||||
else
|
||||
npm install --no-audit --no-fund --ignore-scripts
|
||||
fi
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Validate manifest
|
||||
run: npx --no-install drop-plugin validate .
|
||||
|
||||
- name: Pack plugin archive
|
||||
run: npx --no-install drop-plugin pack . ./dist-package
|
||||
|
||||
- name: Generate checksums
|
||||
id: pkg
|
||||
run: |
|
||||
FILE=$(ls dist-package/*.dropplugin | head -n 1)
|
||||
echo "path=$FILE" >> "$GITHUB_OUTPUT"
|
||||
echo "name=$(basename "$FILE")" >> "$GITHUB_OUTPUT"
|
||||
sha256sum "$FILE" > "$FILE.sha256"
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
|
||||
with:
|
||||
files: |
|
||||
${{ steps.pkg.outputs.path }}
|
||||
${{ steps.pkg.outputs.path }}.sha256
|
||||
name: ${{ steps.pkg.outputs.name }}
|
||||
prerelease: ${{ contains(github.ref_name, '-') }}
|
||||
generate_release_notes: true
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": "@droposs",
|
||||
"sdkVersion": "^0.7.0",
|
||||
"cliVersion": "^0.7.0"
|
||||
}
|
||||
@@ -28,13 +28,17 @@
|
||||
},
|
||||
"checksum": "d6dd6f7ea2fce5f4d36b5e1c5d67acb4437a00dfbb908593dd6ac3191168ed2b",
|
||||
"files": {
|
||||
".github/workflows/ci.yml": "4741172bed309e809eb7d7a1ca02ce241fc8edeb2430060bbd680a08a9c20f10",
|
||||
".github/workflows/release.yml": "999abcf55a425c3c0fb4dcb8442aca189a252a4967a5a7d82c709223ef46c871",
|
||||
".gitignore": "c7ecb5dd064aa6e25e6d7afb4258dc3132295a306a5c1d343676ef541763d0bd",
|
||||
".sdk-scope.json": "f13936572aca04f0f47da440ed27eb69ea7d5c5b1d0901b69ebd4aa55ce455b6",
|
||||
"dist/src/client.js": "d6dd6f7ea2fce5f4d36b5e1c5d67acb4437a00dfbb908593dd6ac3191168ed2b",
|
||||
"dist/src/client.js.map": "d9c5c3e51398e7987190d3d2ee0f78d0151060fd662e44a94bcbff7ee78b4cff",
|
||||
"dist/test/client.test.js": "3f0b0d88df89542ad652022d50a1fa245fadc1743c9373ef37f82b1c8c15eefe",
|
||||
"package.json": "9bd246d0eeba2becc4d50e63869683cb66d2aba81a780dfabd61699da91a9dfb",
|
||||
"package.json": "82b8b701d226bbf45dbf9e605588f0b249a780c4dd457601202ba71d56b64a10",
|
||||
"scripts/switch-sdk-scope.mjs": "ab740ee477a7f5eb07b5344bd12f8c311d51f49279ad4778668e7e2ea4fef798",
|
||||
"src/client.ts": "982c9dd2afd1afd77d9d9071c7cc97ede6143648ea8a4e71a5e80e664cb449d9",
|
||||
"test/client.test.ts": "b0cd225d32c199605e35532d1cab89fc5513cfffeb91f10c76e0cb4435ed9611",
|
||||
"tsconfig.json": "a31a559ca71e6b0d196fffa84189fbaa4e117b6def7c4f45bd13645fbbfe30b9"
|
||||
"tsconfig.json": "ffd407ef4014fe75cbdc6ad897632157a2d8b8f66a03a4d711c6ac1374f4cde2"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"scripts": {
|
||||
"build": "drop-plugin build",
|
||||
"test": "node --test test/client.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"validate": "drop-plugin validate"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
/* global process, console */
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
const LEGACY_SCOPE = "@droposs";
|
||||
const NEW_SCOPE = "@drop-oss";
|
||||
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "dist-package", "dist-packages"]);
|
||||
const LOCKFILES = new Set(["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "deno.lock"]);
|
||||
const TEXT_EXT = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json", ".md", ".yml", ".yaml"]);
|
||||
|
||||
const configPath = path.resolve(process.cwd(), ".sdk-scope.json");
|
||||
if (!existsSync(configPath)) {
|
||||
console.error("Missing .sdk-scope.json in repo root");
|
||||
process.exit(1);
|
||||
}
|
||||
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
||||
const scope = config.sdk;
|
||||
if (scope !== NEW_SCOPE && scope !== LEGACY_SCOPE) {
|
||||
console.error(`Unknown scope '${scope}'; .sdk-scope.json sdk must be '${NEW_SCOPE}' or '${LEGACY_SCOPE}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
const sdkVersion = config["sdkVersion"];
|
||||
const cliVersion = config["cliVersion"];
|
||||
if (typeof sdkVersion !== "string" || typeof cliVersion !== "string") {
|
||||
console.error(".sdk-scope.json must define sdkVersion and cliVersion");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const OTHER_SCOPES = scope === NEW_SCOPE ? [LEGACY_SCOPE] : [NEW_SCOPE];
|
||||
|
||||
function walk(dir, files = [], visited = new Set()) {
|
||||
const real = realpathSync(dir);
|
||||
if (visited.has(real)) return files;
|
||||
visited.add(real);
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (SKIP_DIRS.has(entry.name)) continue;
|
||||
if (entry.isSymbolicLink()) continue;
|
||||
if (entry.isDirectory()) walk(full, files, visited);
|
||||
else files.push(full);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function normalizeSpecifiers(text) {
|
||||
let out = text;
|
||||
for (const other of OTHER_SCOPES) {
|
||||
out = out
|
||||
.replaceAll(`${other}/plugin-sdk`, `${scope}/plugin-sdk`)
|
||||
.replaceAll(`${other}/plugin-cli`, `${scope}/plugin-cli`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function rewritePackageJson(file, text) {
|
||||
const indent = detectIndent(text);
|
||||
let pkg;
|
||||
try {
|
||||
pkg = JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
for (const sectionName of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
||||
const section = pkg[sectionName];
|
||||
if (!section) continue;
|
||||
const rebuilt = {};
|
||||
for (const dep of Object.keys(section)) {
|
||||
const kind = parseSdkDep(dep);
|
||||
if (!kind) {
|
||||
rebuilt[dep] = section[dep];
|
||||
continue;
|
||||
}
|
||||
const target = `${scope}/${kind}`;
|
||||
rebuilt[target] = kind === "plugin-sdk" ? sdkVersion : cliVersion;
|
||||
}
|
||||
pkg[sectionName] = rebuilt;
|
||||
}
|
||||
return JSON.stringify(pkg, null, indent) + (text.endsWith("\n") ? "\n" : "");
|
||||
}
|
||||
|
||||
function parseSdkDep(dep) {
|
||||
for (const scopeName of [LEGACY_SCOPE, NEW_SCOPE, "@drop"]) {
|
||||
if (dep === `${scopeName}/plugin-sdk`) return "plugin-sdk";
|
||||
if (dep === `${scopeName}/plugin-cli`) return "plugin-cli";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectIndent(text) {
|
||||
const match = text.match(/\n([ \t]+)"/);
|
||||
return match ? match[1] : "\t";
|
||||
}
|
||||
|
||||
const edited = [];
|
||||
|
||||
const files = walk(process.cwd());
|
||||
for (const file of files) {
|
||||
if (LOCKFILES.has(path.basename(file))) continue;
|
||||
if (!TEXT_EXT.has(path.extname(file))) continue;
|
||||
const before = readFileSync(file, "utf-8");
|
||||
let after = normalizeSpecifiers(before);
|
||||
if (path.basename(file) === "package.json") {
|
||||
after = rewritePackageJson(file, after);
|
||||
}
|
||||
if (after !== before) {
|
||||
writeFileSync(file, after);
|
||||
edited.push(path.relative(process.cwd(), file));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Scope set to '${scope}' (sdk ${sdkVersion}, cli ${cliVersion})`);
|
||||
if (edited.length) {
|
||||
for (const f of edited) console.log(` rewrote ${f}`);
|
||||
} else {
|
||||
console.log(" no specifier changes needed");
|
||||
}
|
||||
|
||||
const installDir = process.cwd();
|
||||
|
||||
if (existsSync(path.join(installDir, "package-lock.json"))) {
|
||||
try {
|
||||
execSync("npm install --no-audit --no-fund", { cwd: installDir, stdio: "inherit" });
|
||||
} catch {
|
||||
execSync("npm install --no-audit --no-fund --legacy-peer-deps", { cwd: installDir, stdio: "inherit" });
|
||||
}
|
||||
} else if (existsSync(path.join(installDir, "pnpm-lock.yaml"))) {
|
||||
execSync("pnpm install", { cwd: installDir, stdio: "inherit" });
|
||||
} else {
|
||||
console.warn("no lockfile found; skipping dependency install");
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*", "test/**/*"]
|
||||
|
||||
Reference in new issue
Block a user