JupyterLab/jnotice
をテンプレートにして作成
[
トップ
] [
タイトル一覧
|
ページ一覧
|
新規
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
#notemd
# JupyterLab Notice プラグイン導入メモ(jnotice)
> **目的**: ユーザ HOME の `.jupyterhub_notice.txt` をポ...
---
## 1) 成果物の配置
```
/opt/conda/share/jupyter/labextensions/jnotice/
├─ package.json
└─ static/
└─ remoteEntry.jnotice.js
/opt/conda/etc/jupyter/jupyter_server_config.d/
├─ 90-allow-hidden.json
└─ 90-jnotice.json
```
### 90-allow-hidden.json
```json
{
"ContentsManager": { "allow_hidden": true },
"ServerApp": { "allow_hidden": true }
}
```
### 90-jnotice.json(**Server 側**の設定)
```json
{
"ServerApp": {
"tornado_settings": {
"page_config_data": {
"federated_extensions": [
{
"name": "jnotice",
"extension": "./extension",
"load": "extensions/jnotice/static/remoteEntr...
}
]
}
}
}
}
```
> **備考**: Lab4 環境では `jupyter_lab_config.d` ではなく...
### package.json(labextension 側)
```json
{
"name": "jnotice",
"version": "0.0.1",
"private": true,
"jupyterlab": {
"extension": true,
"outputDir": "static",
"_build": {
"name": "jnotice",
"load": "static/remoteEntry.jnotice.js",
"extension": "./extension"
}
},
"dependencies": {
"@jupyterlab/application": "^4.0.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"ts-loader": "^9.0.0",
"webpack": "^5.0.0",
"webpack-cli": "^5.0.0"
}
}
```
### webpack.config.cjs(assign + Banner で _JUPYTERLAB を...
```js
const path = require('path');
const webpack = require('webpack');
const { container } = webpack;
const { ModuleFederationPlugin } = container;
module.exports = {
output: {
path: path.resolve(__dirname, 'static'),
filename: 'remoteEntry.jnotice.js',
publicPath: 'auto'
},
resolve: { extensions: ['.ts', '.js'] },
module: { rules: [{ test: /\.ts$/, use: 'ts-loader', ex...
plugins: [
new webpack.BannerPlugin({ banner: 'window._JUPYTERLA...
new ModuleFederationPlugin({
name: 'jnotice',
filename: 'remoteEntry.jnotice.js',
shareScope: 'jupyterlab',
library: { type: 'assign', name: 'window._JUPYTERLA...
exposes: { './extension': './src/index.ts' },
shared: {
'@jupyterlab/application': { singleton: true, req...
'@jupyterlab/ui-components': { singleton: true, r...
'@lumino/widgets': { singleton: true, requiredVer...
}
})
],
mode: 'production',
optimization: { minimize: true }
};
```
### src/index.ts(ポーリング & トースト)
```ts
import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@...
const plugin: JupyterFrontEndPlugin<void> = {
id: 'jnotice:plugin',
autoStart: true,
activate: async (_app: JupyterFrontEnd) => {
const cfgEl = document.getElementById('jupyter-config...
const cfg = cfgEl && cfgEl.textContent ? JSON.parse(c...
const base: string = cfg.baseUrl || (location.pathnam...
const xsrf = (document.cookie.match(/(?:^|; )_xsrf=([...
const url = base + 'files/.jupyterhub_notice.txt';
let last = '';
async function poll() {
try {
const r = await fetch(url + '?_=' + Date.now(), {
credentials: 'same-origin',
headers: xsrf ? { 'X-XSRFToken': xsrf } : {}
});
if (!r.ok) return;
const t = (await r.text()).trim();
if (t && t !== last) { last = t; toast(t); }
} catch {}
}
function toast(msg: string) {
const el = document.createElement('div');
el.textContent = msg;
el.style.cssText = 'position:fixed;right:20px;botto...
document.body.appendChild(el);
setTimeout(() => el.remove(), 15000);
}
setTimeout(() => { poll(); setInterval(poll, 30000); ...
console.log('[jnotice] plugin loaded');
}
};
export default plugin;
```
---
## 2) ビルド(イメージ内)
```bash
# 例: /tmp/jnotice にソース一式を展開
npm install --no-audit --no-fund
npx webpack --config webpack.config.cjs --mode=production
# 生成物コピー
LABEXT=/opt/conda/share/jupyter/labextensions/jnotice
mkdir -p "$LABEXT/static"
cp -a static/remoteEntry.jnotice.js "$LABEXT/static/"
cp -a package.json "$LABEXT/"
chmod -R a+rX "$LABEXT"
```
---
## 3) 動作確認の手順
1. ブラウザで Lab を開く → **開発者ツール** を開く。
2. コンソールで以下を実行:
```js
const cfg = JSON.parse(document.getElementById('jupyte...
cfg.federated_extensions?.find(x=>x.name==='jnotice');...
window._JUPYTERLAB?.jnotice; ...
```
3. `~/.jupyterhub_notice.txt` にメッセージを書き込み → 1...
---
## 4) よくあるハマりどころ
* **federated_extensions が undefined**: 設定ファイルは *...
* **「init/get を読めない」**: remoteEntry が `window._JU...
* **403 /files**: XSRF ヘッダ不足。実装では `_xsrf` を `X...
* **キャッシュ**: remoteEntry を更新後は **ハードリロード...
---
## 5) health_check と通知ファイル
* health_check.sh が `.jupyterhub_notice.txt` に警告を書...
```bash
NOTICE_FILE="$NB_DIR/.jupyterhub_notice.txt"
mkdir -p "$(dirname "$NOTICE_FILE")" 2>/dev/null || true
printf '%s\n' '⚠️ あと5分で自動停止します' > "$NOTICE_FILE"
```
* Jupyter 側は hidden 許可済みなので `/user/<name>/files/...
---
## 6) 今後の改善
* メッセージにレベル(info/warn/error)や TTL、クリックで...
* Lab/Notebook 両対応の軽量バンドル(Notebook: `custom.js...
* i18n(日本語/英語切替)。
---
**最終確認フロー(ワンライナー)**
```js
(()=>{
const cfg=JSON.parse(document.getElementById('jupyter-co...
console.log(!!cfg.federated_extensions?.find(x=>x.name==...
})();
```
終了行:
#notemd
# JupyterLab Notice プラグイン導入メモ(jnotice)
> **目的**: ユーザ HOME の `.jupyterhub_notice.txt` をポ...
---
## 1) 成果物の配置
```
/opt/conda/share/jupyter/labextensions/jnotice/
├─ package.json
└─ static/
└─ remoteEntry.jnotice.js
/opt/conda/etc/jupyter/jupyter_server_config.d/
├─ 90-allow-hidden.json
└─ 90-jnotice.json
```
### 90-allow-hidden.json
```json
{
"ContentsManager": { "allow_hidden": true },
"ServerApp": { "allow_hidden": true }
}
```
### 90-jnotice.json(**Server 側**の設定)
```json
{
"ServerApp": {
"tornado_settings": {
"page_config_data": {
"federated_extensions": [
{
"name": "jnotice",
"extension": "./extension",
"load": "extensions/jnotice/static/remoteEntr...
}
]
}
}
}
}
```
> **備考**: Lab4 環境では `jupyter_lab_config.d` ではなく...
### package.json(labextension 側)
```json
{
"name": "jnotice",
"version": "0.0.1",
"private": true,
"jupyterlab": {
"extension": true,
"outputDir": "static",
"_build": {
"name": "jnotice",
"load": "static/remoteEntry.jnotice.js",
"extension": "./extension"
}
},
"dependencies": {
"@jupyterlab/application": "^4.0.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"ts-loader": "^9.0.0",
"webpack": "^5.0.0",
"webpack-cli": "^5.0.0"
}
}
```
### webpack.config.cjs(assign + Banner で _JUPYTERLAB を...
```js
const path = require('path');
const webpack = require('webpack');
const { container } = webpack;
const { ModuleFederationPlugin } = container;
module.exports = {
output: {
path: path.resolve(__dirname, 'static'),
filename: 'remoteEntry.jnotice.js',
publicPath: 'auto'
},
resolve: { extensions: ['.ts', '.js'] },
module: { rules: [{ test: /\.ts$/, use: 'ts-loader', ex...
plugins: [
new webpack.BannerPlugin({ banner: 'window._JUPYTERLA...
new ModuleFederationPlugin({
name: 'jnotice',
filename: 'remoteEntry.jnotice.js',
shareScope: 'jupyterlab',
library: { type: 'assign', name: 'window._JUPYTERLA...
exposes: { './extension': './src/index.ts' },
shared: {
'@jupyterlab/application': { singleton: true, req...
'@jupyterlab/ui-components': { singleton: true, r...
'@lumino/widgets': { singleton: true, requiredVer...
}
})
],
mode: 'production',
optimization: { minimize: true }
};
```
### src/index.ts(ポーリング & トースト)
```ts
import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@...
const plugin: JupyterFrontEndPlugin<void> = {
id: 'jnotice:plugin',
autoStart: true,
activate: async (_app: JupyterFrontEnd) => {
const cfgEl = document.getElementById('jupyter-config...
const cfg = cfgEl && cfgEl.textContent ? JSON.parse(c...
const base: string = cfg.baseUrl || (location.pathnam...
const xsrf = (document.cookie.match(/(?:^|; )_xsrf=([...
const url = base + 'files/.jupyterhub_notice.txt';
let last = '';
async function poll() {
try {
const r = await fetch(url + '?_=' + Date.now(), {
credentials: 'same-origin',
headers: xsrf ? { 'X-XSRFToken': xsrf } : {}
});
if (!r.ok) return;
const t = (await r.text()).trim();
if (t && t !== last) { last = t; toast(t); }
} catch {}
}
function toast(msg: string) {
const el = document.createElement('div');
el.textContent = msg;
el.style.cssText = 'position:fixed;right:20px;botto...
document.body.appendChild(el);
setTimeout(() => el.remove(), 15000);
}
setTimeout(() => { poll(); setInterval(poll, 30000); ...
console.log('[jnotice] plugin loaded');
}
};
export default plugin;
```
---
## 2) ビルド(イメージ内)
```bash
# 例: /tmp/jnotice にソース一式を展開
npm install --no-audit --no-fund
npx webpack --config webpack.config.cjs --mode=production
# 生成物コピー
LABEXT=/opt/conda/share/jupyter/labextensions/jnotice
mkdir -p "$LABEXT/static"
cp -a static/remoteEntry.jnotice.js "$LABEXT/static/"
cp -a package.json "$LABEXT/"
chmod -R a+rX "$LABEXT"
```
---
## 3) 動作確認の手順
1. ブラウザで Lab を開く → **開発者ツール** を開く。
2. コンソールで以下を実行:
```js
const cfg = JSON.parse(document.getElementById('jupyte...
cfg.federated_extensions?.find(x=>x.name==='jnotice');...
window._JUPYTERLAB?.jnotice; ...
```
3. `~/.jupyterhub_notice.txt` にメッセージを書き込み → 1...
---
## 4) よくあるハマりどころ
* **federated_extensions が undefined**: 設定ファイルは *...
* **「init/get を読めない」**: remoteEntry が `window._JU...
* **403 /files**: XSRF ヘッダ不足。実装では `_xsrf` を `X...
* **キャッシュ**: remoteEntry を更新後は **ハードリロード...
---
## 5) health_check と通知ファイル
* health_check.sh が `.jupyterhub_notice.txt` に警告を書...
```bash
NOTICE_FILE="$NB_DIR/.jupyterhub_notice.txt"
mkdir -p "$(dirname "$NOTICE_FILE")" 2>/dev/null || true
printf '%s\n' '⚠️ あと5分で自動停止します' > "$NOTICE_FILE"
```
* Jupyter 側は hidden 許可済みなので `/user/<name>/files/...
---
## 6) 今後の改善
* メッセージにレベル(info/warn/error)や TTL、クリックで...
* Lab/Notebook 両対応の軽量バンドル(Notebook: `custom.js...
* i18n(日本語/英語切替)。
---
**最終確認フロー(ワンライナー)**
```js
(()=>{
const cfg=JSON.parse(document.getElementById('jupyter-co...
console.log(!!cfg.federated_extensions?.find(x=>x.name==...
})();
```
ページ名: