Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,34 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: "22"
- name: Detect changed scope
id: scope
env:
CI_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
CI_HEAD_SHA: ${{ github.sha }}
run: node scripts/ci-scope.mjs
- name: Install dependencies
if: steps.scope.outputs.package_any == 'true'
run: npm ci --no-audit --no-fund
- name: Verify package and official extensions
if: steps.scope.outputs.package_full == 'true'
run: npm run typecheck && npm test && npm run build && npm run smoke:package && npm run extensions:verify && npm run docs:evaluate
- name: Verify package documentation
if: steps.scope.outputs.package_full != 'true' && (steps.scope.outputs.package_docs == 'true' || steps.scope.outputs.package_smoke == 'true')
run: |
if [ "${{ steps.scope.outputs.package_docs }}" = "true" ]; then
npm run docs:evaluate
fi
if [ "${{ steps.scope.outputs.package_smoke }}" = "true" ]; then
npm run smoke:package
fi
- name: Check package contents
if: steps.scope.outputs.package_full == 'true' || steps.scope.outputs.package_smoke == 'true'
env:
npm_config_cache: ${{ runner.temp }}/npm-cache
run: npm pack -w @interactive-os/json-document --dry-run
Expand All @@ -37,23 +57,47 @@ jobs:
- uses: actions/setup-node@v6
with:
node-version: "22"
- name: Detect changed scope
id: scope
env:
CI_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
CI_HEAD_SHA: ${{ github.sha }}
run: node scripts/ci-scope.mjs
- name: Install dependencies
if: steps.scope.outputs.lab_extensions == 'true'
run: npm ci --no-audit --no-fund
- name: Verify changed lab extension dist imports
if: steps.scope.outputs.lab_extensions == 'true'
env:
LAB_EXTENSIONS_BASE: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
LAB_EXTENSIONS_HEAD: ${{ github.sha }}
run: npm run build && npm run labs:extensions:verify:changed
run: npm run labs:extensions:verify:changed

playground-site:
name: Playgrounds and site
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: "22"
- name: Detect changed scope
id: scope
env:
CI_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
CI_HEAD_SHA: ${{ github.sha }}
run: node scripts/ci-scope.mjs
- name: Install dependencies
if: steps.scope.outputs.site == 'true' || steps.scope.outputs.playground == 'true' || steps.scope.outputs.browser == 'true'
run: npm ci --no-audit --no-fund
- name: Verify playgrounds and site
run: npm run playground:typecheck && npm run playground:test && npm run playground:build && npm run site:evaluate && npm run site:verify:pages && npm run browser:test
if: steps.scope.outputs.playground == 'true'
run: npm run playground:typecheck && npm run playground:test && npm run playground:build
- name: Verify site
if: steps.scope.outputs.site == 'true'
run: npm run site:verify:pages
- name: Verify browser behavior
if: steps.scope.outputs.browser == 'true'
run: npm run browser:test
150 changes: 150 additions & 0 deletions scripts/ci-scope.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { appendFileSync } from "node:fs";
import { spawnSync } from "node:child_process";

const root = new URL("..", import.meta.url).pathname;
const base = option("--base") ?? process.env.CI_BASE_SHA ?? null;
const head = option("--head") ?? process.env.CI_HEAD_SHA ?? "HEAD";

const diff = changedFiles();
const all = diff.files === null;
const files = diff.files ?? [];

const rootDependency = [/^package(?:-lock)?\.json$/];
const ciWorkflow = [/^\.github\/workflows\/ci\.yml$/, /^scripts\/ci-scope\.mjs$/];
const coreRuntime = [
/^packages\/json-document\/src\//,
/^packages\/json-document\/dist\//,
/^packages\/json-document\/tests\//,
/^packages\/json-document\/(?:package\.json|public-contract\.json|tsconfig(?:\.test)?\.json|vitest\.config\.ts|eslint\.config\.js)$/,
];
const coreDependency = [
/^packages\/json-document\/src\//,
/^packages\/json-document\/dist\//,
/^packages\/json-document\/(?:package\.json|public-contract\.json|tsconfig\.json)$/,
];
const officialExtensionRuntime = [
/^packages\/(?!json-document\/)[^/]+\/(?:src|tests)\//,
/^packages\/(?!json-document\/)[^/]+\/(?:package\.json|tsconfig(?:\.test)?\.json|vitest\.config\.ts|eslint\.config\.js)$/,
];
const packageDocs = [
/^docs\//,
/^llms\.txt$/,
/^packages\/[^/]+\/README\.md$/,
/^apps\/site\/src\/generated\//,
/^scripts\/(?:generate-docs|evaluate-docs)\.mjs$/,
];
const packageSmoke = [
/^packages\/json-document\/README\.md$/,
/^packages\/json-document\/tests\/smoke\//,
];
const packageTooling = [
/^scripts\/(?:run-workspace-scripts|evaluate-extensions)\.mjs$/,
];
const labRuntime = [
/^labs\/extensions\//,
/^scripts\/evaluate-extension-lab\.mjs$/,
];
const siteRuntime = [
/^apps\/site\//,
/^docs\//,
/^llms\.txt$/,
/^packages\/[^/]+\/README\.md$/,
/^scripts\/(?:generate-docs|evaluate-docs|evaluate-site|evaluate-site-http|evaluate-live-site)\.mjs$/,
];
const browserRuntime = [
/^tests\/browser\//,
/^playwright\.config\.ts$/,
/^apps\/site\/(?:src\/(?!generated\/)|tests\/)/,
/^apps\/site\/(?:package\.json|vite\.config\.ts|tsconfig(?:\.node)?\.json)$/,
];
const playgroundRuntime = [
/^apps\/(?!site\/)[^/]+\//,
];

const packageFull = matches([
...rootDependency,
...ciWorkflow,
...coreRuntime,
...officialExtensionRuntime,
...packageTooling,
]);
const packageDocsChanged = matches(packageDocs);
const packageSmokeChanged = matches(packageSmoke);
const packageAny = packageFull || packageDocsChanged || packageSmokeChanged;
const labExtensions = matches([
...rootDependency,
...ciWorkflow,
...coreDependency,
...labRuntime,
]);
const site = matches([
...ciWorkflow,
...siteRuntime,
]);
const playground = matches([
...ciWorkflow,
...coreRuntime,
...officialExtensionRuntime,
...playgroundRuntime,
]);
const browser = matches([
...ciWorkflow,
...coreRuntime,
...officialExtensionRuntime,
...browserRuntime,
]);

const outputs = {
package_full: packageFull,
package_docs: packageDocsChanged,
package_smoke: packageSmokeChanged,
package_any: packageAny,
lab_extensions: labExtensions,
site,
playground,
browser,
};

console.log(`ci scope: ${diff.reason}`);
if (files.length > 0) {
for (const file of files) console.log(`- ${file}`);
}
for (const [name, value] of Object.entries(outputs)) {
console.log(`${name}=${value}`);
}

if (process.env.GITHUB_OUTPUT) {
const content = Object.entries(outputs)
.map(([name, value]) => `${name}=${value ? "true" : "false"}`)
.join("\n");
appendFileSync(process.env.GITHUB_OUTPUT, `${content}\n`);
}

function option(name) {
const index = process.argv.indexOf(name);
if (index === -1) return null;
return process.argv[index + 1] ?? null;
}

function changedFiles() {
if (base === null || /^0+$/.test(base)) {
return { files: null, reason: "missing diff base; running all scopes" };
}

const result = spawnSync("git", ["diff", "--name-only", base, head], {
cwd: root,
encoding: "utf8",
});
if (result.status !== 0) {
return { files: null, reason: `git diff failed for ${base}..${head}; running all scopes` };
}

return {
files: result.stdout.split(/\r?\n/).filter((path) => path.length > 0),
reason: `${base}..${head}`,
};
}

function matches(patterns) {
return all || files.some((file) => patterns.some((pattern) => pattern.test(file)));
}
4 changes: 3 additions & 1 deletion scripts/evaluate-extension-lab.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ const verify = process.argv.includes("--verify");
const verifyChanged = process.argv.includes("--changed");
const fullVerificationPathPatterns = [
/^package(?:-lock)?\.json$/,
/^packages\/json-document\//,
/^packages\/json-document\/src\//,
/^packages\/json-document\/dist\//,
/^packages\/json-document\/(?:package\.json|public-contract\.json|tsconfig\.json)$/,
/^scripts\/evaluate-extension-lab\.mjs$/,
];
const retiredLabNames = new Set([
Expand Down