Commit Graph
16 Commits
Author SHA1 Message Date
tao.chen 570ed2183b ci: trigger condition
CI / CI (push) Successful in 32m55s
v0.0.1-beta
2026-07-22 11:22:20 +08:00
tao.chenandClaude d7a74a6971 ci: drop Upload wheel artifacts step (redundant with release)
CI / CI (push) Successful in 32m9s
artifact-upload served two purposes:
 1. download wheel from Actions UI for debugging
 2. (future) hand off to cross-job tests

Neither is currently used: distribution already goes through the
Gitea release via create-release.sh, and there are no downstream
jobs that need the artifact.

Removes the actions/upload-artifact@v4 step + the if: success()
gate. The build is now: uv build -> release (tag only) -> bark
notify. Cleaner, one fewer thing to fail on.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 10:29:08 +08:00
tao.chen 18963ae03b ci: fix
CI / CI (push) Failing after 44m36s
2026-07-21 19:17:34 +08:00
tao.chenandClaude f8c186fe2c ci: switch runner from astral-uv to ubuntu-latest
CI / CI (push) Failing after 11s
Replaces the self-hosted astral-uv runner (which had uv
pre-installed) with the standard GitHub-hosted ubuntu-latest.
Adds back the astral-sh/setup-uv step that was omitted in the
self-hosted case.

The astral-uv runner was timing out / unreachable from the
GitHub Actions side; ubuntu-latest is universally available.

Trade-off: ubuntu-latest runners start fresh each run (~30s cold
start) and the setup-uv step takes a few seconds to download uv,
versus a self-hosted runner that boots in <1s with uv ready. For
a single CI job that runs on tag push only, the extra ~10s is
not a problem.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 19:14:49 +08:00
tao.chenandClaude d40d19ae96 ci: point uv build at Tsinghua PyPI mirror via UV_DEFAULT_INDEX
CI / CI (push) Failing after 4s
The self-hosted astral-uv runner is in China; pulling build deps
from pypi.org is slow and often times out. Set UV_DEFAULT_INDEX
on the 'Build package' step so uv's build-environment resolution
goes through the Tsinghua mirror.

This complements the [tool.uv.index] url=... block in
pyproject.toml — env wins over the file config when both are
present, so the mirror is used consistently.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 19:06:49 +08:00
tao.chenandClaude ac17a954b0 ci: extract release script to fix YAML parse error (line 51)
CI / CI (push) Canceled after 11m29s
The 'Upload release assets via Gitea API' step embedded a JSON
heredoc inside a YAML block scalar (run: |). Gitea's YAML parser
bailed at line 51 with 'could not find expected :'' when it saw
the leading '{' of the JSON body — it tried to interpret the
heredoc content as a flow mapping instead of literal text.

Fix: move the release logic into .github/scripts/create-release.sh
and pass the GitHub Actions context values as env vars. The step
becomes:

  env:
    GITEA_TOKEN: ...
    TAG_NAME: ...
    API_URL: ...
    REPOSITORY: ...
    SHA: ...
  run: ./.github/scripts/create-release.sh

Validates cleanly with PyYAML (yaml.safe_load). Pattern matches
bark-notify.sh from earlier — CI helpers live in .github/scripts/,
workflows stay slim.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:57:18 +08:00
tao.chenandClaude 72fd363600 ci: move bark-notify.sh to .github/scripts, add Gitea release asset upload
Two changes:

1. Move scripts/bark-notify.sh -> .github/scripts/bark-notify.sh
   (CI helper belongs next to the workflow that calls it). History
   preserved via git mv.

2. Add a 'Upload release assets via Gitea API' step that runs only
   on tag pushes (if: startsWith(github.ref, 'refs/tags/v')). It:
     - POSTs /releases to create a Gitea release bound to the tag
     - extracts the release id from the response (with explicit error
       path if the API call fails)
     - loops over dist/snapshot-*.whl and dist/snapshot-*.tar.gz and
       uploads each as a release asset
   Uses ${{ secrets.GITHUB_TOKEN }} exposed as the GITEA_TOKEN env
   var (the Gitea convention for the repo token secret). The
   Notify Bark step still runs last with if: always() so a release
   upload failure is reported via the phone.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:51:57 +08:00
tao.chenandClaude 044c83cd22 ci: bark notification on build success / failure
Adds scripts/bark-notify.sh (deployment detail, hardcoded
device_key + endpoint — per the script header it's a deployment
constant, not a secret) and a final workflow step that pushes
a / notification on every job result.

The notify step:
- runs unconditionally (if: always()) so failures also notify
- is a side-effect — if Bark itself is unreachable, the
  curl failure is swallowed so CI does not turn red on a
  notification outage
- uses portable context vars (github.repository / ref_name /
  sha / actor / job.status) which work on Gitea Actions

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:50:25 +08:00
tao.chenandClaude 121b835494 ci: rewrite workflows for Gitea, use astral-uv runner, uv build only
Replaces the previous five JupyterLab-template workflows
(build / check-release / enforce-label / prep-release /
publish-release / update-integration-tests) with a single minimal
CI workflow tuned for a self-hosted Gitea runner.

What's in the new build.yml:
- Triggers: push to main, push of v* tag, pull_request
- runs-on: astral-uv (self-hosted runner with uv pre-installed;
  we skip the setup-uv step)
- Steps: checkout -> uv build -> upload-artifact
- uv build alone is enough because pyproject.toml's
  [tool.hatch.build.hooks.jupyter-builder] drives jlpm install
  and jlpm build:prod before wheel assembly, and uv caches the
  build environment between runs.

What's removed: all jupyter-server/jupyter_releaser steps and the
check-links / update-snapshots / test_isolated / integration-tests
jobs from the template — they depended on jupyterlab/maintainer-tools
and GitHub-specific secrets (GITHUB_TOKEN, APP_PRIVATE_KEY, NPM_TOKEN)
that don't apply to a Gitea deployment.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:48:15 +08:00
tao.chenandClaude 5ae9f94023 docs: add TODO.md (pending items, prioritized by user value)
Captures the open work items raised across earlier sessions:
- S3 consistency fix (return list in commit response — the real
  workaround behind the 'refresh button' shim)
- POST /notebook-version/restore endpoint (deferred in design.md)
- Settings schema (schema/plugin.json is currently empty)
- jest + pnpm linker incompatibility (blocks jlpm test)
- Frontend diff.ts unit tests (blocked on the jest fix)
- ui-tests (Playwright + Galata)
- CI pytest step
- v0.0.1 tag + PyPI release

Each item is labeled with trigger conditions (what real user need
would justify doing it) so we don't gold-plate features.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:44:03 +08:00
tao.chenandClaude 263232bea4 feat: replace text buttons with icons (tooltip on hover)
Replaces the two visible 'button-like' affordances with LabIcon glyphs
so the sidebar and toolbar stay compact; the explanatory text moves
to the hover tooltip.

Changes:
- Toolbar 'Save Snapshot' button: text label -> saveIcon (floppy),
  tooltip '保存当前 notebook 为快照'
- Sidebar panel '快照历史' text label -> saveIcon + caption='快照历史'
  (the panel tab is now icon-only)
- Sidebar refresh button: text -> refreshIcon with a 1s rotate animation
  while loading

Dependencies:
- Adds @jupyterlab/ui-components to package.json (source of the
  standard LabIcon set: saveIcon, refreshIcon, historyIcon)

Style:
- New .jp-snapshot-panel-refresh-button sizing (28x24 fixed box,
  centered icon) and .is-loading spin keyframes
- @keyframes jp-snapshot-spin applied to the icon's .jp-Icon element

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:41:01 +08:00
tao.chenandClaude 2fc584d70f chore: bump version to 0.0.1 to match CHANGELOG
Build / Check Links (push) Failing after 5s
Build / build (push) Failing after 8s
Build / test_isolated (push) Skipped
Build / Integration tests (push) Skipped
Check Release / check_release (push) Failing after 10s
package.json was still at the copier-template default 0.1.0
while CHANGELOG and DEVELOPER.md document 0.0.1. pyproject.toml
sources the version from package.json via hatch-nodejs-version, so
no edit needed there.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:29:05 +08:00
tao.chenandClaude 6e6c79f43a docs: add v0.0.1 changelog entry
Records the initial development release covering all features
shipped in commits cd1aba6 / bc1828b / 3476517: manual snapshot
commit flow, sidebar timeline, cell diff, restore, skip-on-duplicate,
Local + S3 storage adapters, S3ConnectionError diagnostics, envelope-
embedded snapshot files, traitlets config, and the DEVELOPER.md
manual.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:28:22 +08:00
tao.chenandClaude 3476517479 docs: add DEVELOPER.md (architecture, build, debug, packaging)
Developer-facing manual covering:

1. Architecture overview with the four-layer data flow diagram
2. Repository layout (src/, snapshot/, storage/, etc.)
3. Dev environment setup (one-time + daily) and the
   'what to restart after what type of change' table
4. Common commands quick reference
5. Debugging: backend (server logs, curl), frontend (browser
   DevTools, console.log diagnostic points, React/Lumino
   inspection), and end-to-end symptom -> cause table
6. Packaging and release (PyPI wheel via hatch + jupyter-builder,
   version sourced from package.json, git tag flow, CI)
7. Known pitfalls: jest+pnpm linker incompatibility, S3 strong
   consistency variance, conda/npm gotchas
8. 5-minute quickstart for new contributors

User-facing concerns (install/config/troubleshoot) stay in README.md;
design goals stay in design.md; AI coding rules stay in AGENTS.md.
DEVELOPER.md slots in for engineer handoff / onboarding.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:26:11 +08:00
tao.chenandClaude bc1828bde6 fix: surface hash-duplicate warning and add manual refresh
Build / Check Links (push) Failing after 6s
Check Release / check_release (push) Failing after 1m1s
Build / build (push) Failing after 1m25s
Build / test_isolated (push) Skipped
Build / Integration tests (push) Skipped
Three issues fixed:

1. **Skipped-commit warning not visible** — When the user commits
   unchanged content (hash matches most recent version), the backend
   correctly returns {skipped: true, reason: "unchanged", message: ...}
   but the frontend Notification.warning was easy to miss (default
   4-5s autoClose, small toast at top-right). Now uses {autoClose: false}
   so the warning stays until manually dismissed, plus a console.log
   of the raw commit response for debugging. Added a fallback branch
   that warns clearly if the response shape is unexpected.

2. **No manual refresh on the panel** — In S3 setups the list call
   can occasionally lag the just-committed entry (manifest strong
   consistency is a per-implementation property). Added a "刷新"
   button at the top of the sidebar that calls model.refresh(), letting
   the user force a re-fetch when the auto-refresh misses.

3. **S3 addressing_style** — user-side config addition: S3Storage
   now uses path-style addressing (endpoint/bucket/key) which is the
   format required by MinIO and several other S3-compatible services.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 18:23:34 +08:00
tao.chenandClaude cd1aba6698 dev: initial implementation of notebook-snapshot extension
Manual-commit notebook snapshot extension (JupyterLab 4):

Backend
- Storage adapter layer (Local + S3 via boto3, pip extra)
  - Dumb put/get/list/delete contract; key validation
  - S3Storage: lazy boto3 import, S3ConnectionError with friendly
    messages + connectivity check at startup
- SnapshotStore
  - Strip code outputs/execution_count, MD5 on cleaned JSON
  - Gzipped envelope (id/timestamp/name/description/hash/size/notebook)
  - Append-only manifest.json (rebuildable from version files)
  - SnapshotUnchangedError when new hash matches most recent version
- REST API (commit / list / content) under /snapshot/ namespace
- traitlets config (storage_type, local_root, s3_* + S3_* env fallback)

Frontend
- snapshot:commit command (toolbar button + command palette)
- Dialog for name + description
- SnapshotPanel (left sidebar timeline) + DiffWidget (main area)
- Cell diff: id-match first, LCS fallback; jsdiff for line diff
- Restore via model.fromJSON() + context.save() (no refresh)
- All AGENTS.md hard conventions enforced

Tests: 33 backend pytest passing (storage + store + routes)

Docs: AGENTS.md, design.md, README.md synced with implementation.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 17:33:55 +08:00