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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
.env.*
.pnpmfile.cjs
.release
.ci-candidates
.turbo
.visual-delta
coverage
Expand Down
140 changes: 140 additions & 0 deletions .github/workflows/lapis-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ defaults:
shell: bash

env:
LAPIS_PLUGIN_CANDIDATE_COMMIT: "61bf175f21d91f17e06a071bdaadbaf5d87645a1"
LAPIS_CONTROLLER_CANDIDATE_COMMIT: "11c70f4390190b8f9e1bc0d06ed349df5c142754"
TURBO_API: ${{ vars.TURBO_API }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
Expand Down Expand Up @@ -158,6 +160,135 @@ jobs:
- name: Report Storybook cache result
run: node scripts/report-turbo-cache.mjs --github-summary

registry-fixture:
runs-on: ubuntu-latest
timeout-minutes: 30
container:
image: ghcr.io/lapismd/lapis-ci@sha256:8fb0ecf5e978c9b44acd8394d78f689d93404f30f8014861a10a7eb2b90b96b8
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v7
- name: Checkout the exact Plugins validation candidate
uses: actions/checkout@v7
with:
repository: lapismd/lapis-plugins
ref: ${{ env.LAPIS_PLUGIN_CANDIDATE_COMMIT }}
path: .ci-candidates/plugins
- name: Build test-signed registry candidate
working-directory: .ci-candidates/plugins
run: |
test "$(git rev-parse HEAD)" = "$LAPIS_PLUGIN_CANDIDATE_COMMIT"
export GITHUB_SHA="$LAPIS_PLUGIN_CANDIDATE_COMMIT"
pnpm install --frozen-lockfile
node scripts/check-framework-publication.mjs
pnpm build
pnpm packages:pack -- --already-built
pnpm plugin:build
node scripts/create-registry-acceptance-fixture.mjs
- uses: actions/upload-artifact@v7
with:
name: signed-registry-acceptance-fixture
path: .ci-candidates/plugins/.release/registry-acceptance
if-no-files-found: error
retention-days: 14

web-acceptance:
needs: [build-cache, registry-fixture]
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
with:
version: 10.34.5
- uses: actions/setup-node@v7
with:
node-version: "24.15.0"
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2
with:
deno-version: v2.9.7
- run: pnpm install --frozen-lockfile
- run: pnpm exec playwright install --with-deps chromium
- name: Restore package builds
run: node scripts/run-turbo.mjs build --summarize
- name: Build the server-host acceptance image
run: docker build -f packages/server-deno/Dockerfile -t lapis-server:e2e .
- uses: actions/download-artifact@v8
with:
name: signed-registry-acceptance-fixture
path: .ci-candidates/registry-fixture
- run: pnpm test:web:e2e
env:
LAPIS_WEB_REGISTRY_FIXTURE: .ci-candidates/registry-fixture
- uses: actions/upload-artifact@v7
if: failure()
with:
name: web-acceptance-diagnostics
path: packages/web/test-results
retention-days: 14

desktop-packaged:
needs: build-cache
strategy:
fail-fast: false
matrix:
os: [macos-15, ubuntu-24.04]
runs-on: ${{ matrix.os }}
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
- name: Checkout the exact controller validation source
uses: actions/checkout@v7
with:
repository: lapismd/ai-controller
ref: ${{ env.LAPIS_CONTROLLER_CANDIDATE_COMMIT }}
path: .ci-candidates/controller
- uses: pnpm/action-setup@v6
with:
version: 10.34.5
- uses: actions/setup-node@v7
with:
node-version: "24.15.0"
- uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2
with:
deno-version: v2.9.7
- name: Install Linux native acceptance dependencies
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.1-0 libgtk-3-0t64 libayatana-appindicator3-1 libfuse2t64 xvfb dbus-x11
- run: pnpm install --frozen-lockfile
- name: Provision the controller CLI candidate
working-directory: .ci-candidates/controller
run: |
test "$(git rev-parse HEAD)" = "$LAPIS_CONTROLLER_CANDIDATE_COMMIT"
pnpm install --frozen-lockfile
pnpm build:cli
node scripts/install-cli.mjs --home "$GITHUB_WORKSPACE/.ci-candidates/controller-home" --install-path "$GITHUB_WORKSPACE/.ci-candidates/bin/lapis-ai-controller"
printf 'LAPIS_AI_CONTROLLER_BIN=%s\n' "$GITHUB_WORKSPACE/.ci-candidates/bin/lapis-ai-controller" >> "$GITHUB_ENV"
- name: Build validation-only native package
run: pnpm package:desktop
- name: Cold packaged acceptance on macOS
if: runner.os == 'macOS'
run: pnpm test:desktop:packaged
- name: Cold packaged acceptance on Linux
if: runner.os == 'Linux'
run: xvfb-run -a dbus-run-session -- pnpm test:desktop:packaged
env:
APPIMAGE_EXTRACT_AND_RUN: "1"
- uses: actions/upload-artifact@v7
if: always()
with:
name: desktop-validation-${{ matrix.os }}
include-hidden-files: true
path: |
packages/desktop-deno/release/*.zip
packages/desktop-deno/release/*.AppImage
packages/desktop-deno/release/*.tar.gz
.ci-candidates/controller/artifacts/runtime/runtime-manifest.json
if-no-files-found: error
retention-days: 14

release-artifacts:
needs: build-cache
runs-on: ubuntu-latest
Expand Down Expand Up @@ -201,6 +332,9 @@ jobs:
- unit
- storybook-static
- storybook-interaction
- web-acceptance
- registry-fixture
- desktop-packaged
- release-artifacts
runs-on: ubuntu-latest
steps:
Expand All @@ -212,6 +346,9 @@ jobs:
UNIT: ${{ needs.unit.result }}
STORYBOOK_STATIC: ${{ needs.storybook-static.result }}
STORYBOOK_INTERACTION: ${{ needs.storybook-interaction.result }}
WEB_ACCEPTANCE: ${{ needs.web-acceptance.result }}
REGISTRY_FIXTURE: ${{ needs.registry-fixture.result }}
DESKTOP_PACKAGED: ${{ needs.desktop-packaged.result }}
RELEASE_ARTIFACTS: ${{ needs.release-artifacts.result }}
run: |
set -euo pipefail
Expand All @@ -222,6 +359,9 @@ jobs:
"$UNIT"
"$STORYBOOK_STATIC"
"$STORYBOOK_INTERACTION"
"$WEB_ACCEPTANCE"
"$REGISTRY_FIXTURE"
"$DESKTOP_PACKAGED"
"$RELEASE_ARTIFACTS"
)
for result in "${results[@]}"; do
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ dist-server
.svelte-kit
storybook-static
.release/
.ci-candidates/
.ci/dependency-context/
spec/book
.qmd/index.sqlite*
Expand Down
1 change: 1 addition & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@
"test:visual:run": "node ./scripts/test-visual.mjs",
"test:visual:update:run": "node ./scripts/update-visual-baselines.mjs",
"test:spec-validation:run": "node --test scripts/spec-validation/*.test.mjs",
"release:test:run": "node --test ./scripts/storybook-turbo-cache.test.mjs ./scripts/ci-images.test.mjs ./scripts/ci-container.test.mjs ./scripts/check-release-intent.spec.mjs ./scripts/check-release-workflow.spec.mjs ./scripts/create-github-releases.spec.mjs ./scripts/publish-release.spec.mjs ./scripts/release-manifest.spec.mjs ./scripts/verify-release.spec.mjs ./scripts/release-plan.spec.mjs",
"release:test:run": "node --test ./scripts/web-registry-acceptance.test.mjs ./scripts/storybook-turbo-cache.test.mjs ./scripts/ci-images.test.mjs ./scripts/ci-container.test.mjs ./scripts/check-release-intent.spec.mjs ./scripts/check-release-workflow.spec.mjs ./scripts/create-github-releases.spec.mjs ./scripts/publish-release.spec.mjs ./scripts/release-manifest.spec.mjs ./scripts/verify-release.spec.mjs ./scripts/release-plan.spec.mjs",
"test:desktop:packaged:run": "pnpm --filter @lapis-notes/desktop-deno test:packaged",
"test:local-plugin-consumer:run": "node --test scripts/local-plugin-pnpmfile.test.mjs scripts/lib/local-plugin-consumer.test.mjs",
"test:web:e2e:run": "pnpm --filter @lapis-notes/web test:e2e",
"test:web:e2e:run": "node scripts/run-web-registry-acceptance.mjs",
"test:specification:dev:run": "playwright test -c playwright.specification.config.ts",
"test:specification:static:run": "SPECIFICATION_STATIC=1 playwright test -c playwright.specification.config.ts",
"build-storybook:run": "node -e \"require('node:fs').mkdirSync('.visual-delta/cache',{recursive:true})\" && NODE_OPTIONS=\"${NODE_OPTIONS:---max-old-space-size=8192}\" storybook build --stats-json .visual-delta/cache && pnpm check:storybook-index",
Expand Down
6 changes: 2 additions & 4 deletions packages/desktop-deno/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@lapis-notes/deno-host-services/vault-fs": "../deno-host-services/src/vault-fs.ts",
"@lapis-notes/deno-host-services/vault-resources": "../deno-host-services/src/vault-resources.ts",
"@lapis-notes/language-service/markdownlint/runtime": "../language-service/src/markdownlint/runtime.ts",
"@lapismd/terminal-host/deno": "../../../terminal-host/src/deno.ts",
"@lapismd/terminal-host/deno": "npm:@lapismd/terminal-host@^0.1.0/deno",
"@lezer/common": "npm:@lezer/common@1.2.3",
"@lezer/highlight": "npm:@lezer/highlight@1.2.1",
"@lezer/lr": "npm:@lezer/lr@1.4.2",
Expand All @@ -43,8 +43,6 @@
},
"compilerOptions": {
"strict": true,
"lib": [
"deno.window"
]
"lib": ["deno.window"]
}
}
1 change: 1 addition & 0 deletions packages/desktop-deno/scripts/distribution.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export function createDistributionPlan({
target,
baseName,
icon: path.join(sharedBuildDir, "icon.icns"),
desktopOutput: path.join(releaseDir, baseName),
appBundle: path.join(releaseDir, `${baseName}.app`),
archive: path.join(releaseDir, `${baseName}.zip`),
};
Expand Down
5 changes: 3 additions & 2 deletions packages/desktop-deno/scripts/distribution.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ describe("Deno desktop distribution", () => {
...common,
target: "aarch64-apple-darwin",
});
expect(mac.desktopOutput).toBe(path.join(common.releaseDir, mac.baseName));
expect(
mac.appBundle.endsWith(
path.join("release", "Lapis-Notes-2026.31.5-macos-arm64.app"),
Expand Down Expand Up @@ -91,9 +92,9 @@ describe("Deno desktop distribution", () => {
"utf8",
);
expect(source).toContain(
'path.resolve(packageDir, "../../../terminal-host")',
'import.meta.resolve("@lapismd/terminal-host/native-artifacts.json")',
);
expect(source).toContain('"native-artifacts.json"');
expect(source).not.toContain("../../../terminal-host");
expect(source).toContain('"--include"');
expect(source).toContain('"native"');
expect(source).toContain('"build/icon-light.png"');
Expand Down
15 changes: 9 additions & 6 deletions packages/desktop-deno/scripts/package-app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,18 @@ const releaseDir = path.join(packageDir, "release");
const packageMetadata = JSON.parse(
await readFile(path.join(packageDir, "package.json"), "utf8"),
);
const terminalHostDir = path.resolve(packageDir, "../../../terminal-host");
const terminalArtifacts = JSON.parse(
await readFile(path.join(terminalHostDir, "native-artifacts.json"), "utf8"),
await readFile(
new URL(
import.meta.resolve("@lapismd/terminal-host/native-artifacts.json"),
),
"utf8",
),
);

function resolveDenoExecutable() {
const configured = process.env.DENO_BIN?.trim();
if (configured) return configured;
const userInstall = path.join(os.homedir(), ".deno", "bin", "deno");
return fs.existsSync(userInstall) ? userInstall : "deno";
return configured || "deno";
}

async function run(command, args) {
Expand Down Expand Up @@ -213,7 +215,8 @@ async function notarizeMacApplication(appBundle) {
}

async function packageMac(plan) {
await buildDesktopOutput(plan, plan.appBundle);
await rm(plan.appBundle, { force: true, recursive: true });
await buildDesktopOutput(plan, plan.desktopOutput);
await declareMacApplicationMetadata(plan.appBundle);
await writeFile(
path.join(
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop-deno/scripts/smoke-packaged.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ try {
await waitForCleanExit(secondary, secondaryDiagnostics);

const report = await waitForReport(reportPath, child, diagnostics);
assert.equal(report.ok, true, report.detail);
assert.equal(report.ok, true, JSON.stringify(report, null, 2));
assert.equal(report.runtime, "deno-desktop");
assert.equal(report.vault, "vault");
assert.deepEqual(report.plugins, expectedPlugins);
Expand Down
10 changes: 10 additions & 0 deletions packages/desktop-deno/src/desktop-recovery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ describe("Deno desktop startup recovery", () => {
expect(diagnostic.detail).toContain("/Users/example/Desktop/notes");
});

it("preserves the error message when WebKit stacks contain only locations", () => {
const error = new Error("Native database could not open");
error.stack =
"@user-script:1:58:33\nglobal code@http://127.0.0.1:1234/:1:23";
const failure = createDesktopStartupFailure("vault", error);
expect(failure.detail).toContain("Native database could not open");
expect(failure.detail).toContain(error.stack);
expect(failure.message).toBe(error.message);
});

it("describes layout-only recovery without unavailable policies", () => {
const storage = memoryStorage();
const state = updateDesktopSafeModeState(
Expand Down
8 changes: 7 additions & 1 deletion packages/desktop-deno/src/desktop-recovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,16 @@ export function createDesktopStartupFailure(
error: unknown,
): AppSafeModeFailure {
const message = error instanceof Error ? error.message : String(error);
let detail = message;
if (error instanceof Error && error.stack) {
detail = error.stack;
if (!detail.includes(message))
detail = `${error.name}: ${message}\n${detail}`;
}
return {
task,
message,
detail: error instanceof Error ? (error.stack ?? error.message) : message,
detail,
pluginId: null,
};
}
Expand Down
Loading
Loading