chore: add Obsidian scorecard preflight checks

This commit is contained in:
waaraawa 2026-06-16 16:22:03 +09:00 committed by waaraawa
parent e68a0f3733
commit 98e42aea3d
7 changed files with 6011 additions and 5 deletions

60
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,60 @@
name: Release
on:
push:
tags:
- '*'
permissions:
contents: write
id-token: write
attestations: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Prepare release assets
run: |
mkdir -p release
cp packages/obsidian-plugin/main.js release/main.js
cp packages/obsidian-plugin/styles.css release/styles.css
cp manifest.json release/manifest.json
- name: Attest release assets
uses: actions/attest-build-provenance@v2
with:
subject-path: release/*
- name: Ensure GitHub release exists
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release view "${GITHUB_REF_NAME}" ||
gh release create "${GITHUB_REF_NAME}" \
--title "${GITHUB_REF_NAME}" \
--notes "Release ${GITHUB_REF_NAME}"
- name: Upload release assets
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${GITHUB_REF_NAME}" release/* --clobber

1
.gitignore vendored
View file

@ -1,6 +1,5 @@
# Dependencies # Dependencies
node_modules/ node_modules/
package-lock.json
yarn.lock yarn.lock
# Build outputs # Build outputs

5852
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@
"build": "npm run build --workspaces", "build": "npm run build --workspaces",
"test": "npm run test --workspaces --if-present", "test": "npm run test --workspaces --if-present",
"lint": "eslint . --ext .ts", "lint": "eslint . --ext .ts",
"check:scorecard": "node scripts/check-scorecard.mjs",
"format": "prettier --write \"**/*.{ts,json,md}\"", "format": "prettier --write \"**/*.{ts,json,md}\"",
"format:check": "prettier --check \"**/*.{ts,json,md}\"", "format:check": "prettier --check \"**/*.{ts,json,md}\"",
"clean": "rm -rf packages/*/dist packages/*/node_modules node_modules" "clean": "rm -rf packages/*/dist packages/*/node_modules node_modules"
@ -27,8 +28,9 @@
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.11", "@types/jest": "^29.5.11",
"@types/node": "^20.10.6", "@types/node": "^20.10.6",
"@typescript-eslint/eslint-plugin": "^6.17.0", "@typescript-eslint/eslint-plugin": "^8.61.1",
"@typescript-eslint/parser": "^6.17.0", "@typescript-eslint/parser": "^8.61.1",
"esbuild": "^0.28.1",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"jest": "^29.7.0", "jest": "^29.7.0",
"prettier": "^3.1.1", "prettier": "^3.1.1",

View file

@ -20,7 +20,7 @@
"devDependencies": { "devDependencies": {
"@types/node": "^20.10.6", "@types/node": "^20.10.6",
"builtin-modules": "^3.3.0", "builtin-modules": "^3.3.0",
"esbuild": "^0.19.11", "esbuild": "^0.28.1",
"obsidian": "^1.4.16", "obsidian": "^1.4.16",
"typescript": "^5.3.3" "typescript": "^5.3.3"
} }

View file

@ -25,7 +25,7 @@
} }
.theme-dark .bytegrid-source { .theme-dark .bytegrid-source {
background-color: #374151 !important; background-color: #374151;
color: #e5e7eb; color: #e5e7eb;
} }

View file

@ -0,0 +1,93 @@
import fs from 'node:fs';
import path from 'node:path';
const ROOT = process.cwd();
const IGNORED_DIRS = new Set(['coverage', 'dist', 'node_modules']);
const checks = [];
function addCheck(name, run) {
checks.push({ name, run });
}
function listFiles(dir, predicate = () => true) {
if (!fs.existsSync(dir)) {
return [];
}
const entries = fs.readdirSync(dir, { withFileTypes: true });
return entries.flatMap((entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (IGNORED_DIRS.has(entry.name)) {
return [];
}
return listFiles(fullPath, predicate);
}
return predicate(fullPath) ? [fullPath] : [];
});
}
addCheck('No CSS !important usage', () => {
const cssFiles = listFiles(path.join(ROOT, 'packages'), (file) => file.endsWith('.css'));
const violations = cssFiles.flatMap((file) => {
const content = fs.readFileSync(file, 'utf8');
return content
.split('\n')
.map((line, index) => ({ file, line, lineNumber: index + 1 }))
.filter(({ line }) => line.includes('!important'));
});
if (violations.length === 0) {
return;
}
const details = violations
.map(({ file, lineNumber }) => `${path.relative(ROOT, file)}:${lineNumber}`)
.join('\n');
throw new Error(`Avoid !important in plugin CSS:\n${details}`);
});
addCheck('Committed npm lockfile exists', () => {
const lockfile = path.join(ROOT, 'package-lock.json');
if (!fs.existsSync(lockfile)) {
throw new Error('package-lock.json is required for reproducible dependency resolution');
}
});
addCheck('Release workflow includes artifact attestation', () => {
const workflowDir = path.join(ROOT, '.github', 'workflows');
const workflowFiles = listFiles(workflowDir, (file) => /\.(ya?ml)$/.test(file));
const hasAttestation = workflowFiles.some((file) => {
const content = fs.readFileSync(file, 'utf8');
return content.includes('actions/attest-build-provenance');
});
if (!hasAttestation) {
throw new Error(
'release workflow should attest release assets with actions/attest-build-provenance'
);
}
});
const failures = [];
for (const check of checks) {
try {
check.run();
console.log(`PASS ${check.name}`);
} catch (error) {
failures.push({ name: check.name, error });
console.error(`FAIL ${check.name}`);
console.error(error instanceof Error ? error.message : String(error));
}
}
if (failures.length > 0) {
process.exitCode = 1;
}