diff options
| -rw-r--r-- | api/course/event.php | 6 | ||||
| -rw-r--r-- | modules/ext/course.js | 1 | ||||
| -rw-r--r-- | scripts/newos4-maintain.py | 19 |
3 files changed, 20 insertions, 6 deletions
diff --git a/api/course/event.php b/api/course/event.php index 696d1c5..ce11041 100644 --- a/api/course/event.php +++ b/api/course/event.php @@ -30,7 +30,11 @@ if (random_int(1, 50) === 1) { } $detail = isset($p['detail']) && is_array($p['detail']) ? array_slice($p['detail'], 0, 8, true) : []; -foreach ($detail as $k => $v) { if (is_string($v)) $detail[$k] = substr($v, 0, 300); } +foreach ($detail as $k => $v) { + if (is_string($v)) $detail[$k] = substr($v, 0, 300); + elseif (is_scalar($v)) $detail[$k] = substr((string)$v, 0, 300); + else $detail[$k] = gettype($v); +} $row = ['ts' => gmdate('c'), 'type' => $type, 'sid' => $sid, 'version' => substr((string)($p['version'] ?? ''), 0, 40), 'detail' => $detail]; $out = $dir . '/events-' . date('Ym') . '.jsonl'; diff --git a/modules/ext/course.js b/modules/ext/course.js index 3879cb5..3e00229 100644 --- a/modules/ext/course.js +++ b/modules/ext/course.js @@ -108,6 +108,7 @@ class CourseModule { let m; try { m = await this._load(); } catch (e) { return this.terminal.print('Course content unreachable (' + e.message + '). Try `course list` again.', 'error'); } + if (localStorage.getItem('webx11_course_skills_ver') !== m.version) this._installSkills().catch(() => {}); const p = this._progress(); if (sub === 'list' || sub === 'chapters') { this.terminal.print('📚 newOS4 Course — v' + m.version + ' (' + m.updated + ')', 'header'); diff --git a/scripts/newos4-maintain.py b/scripts/newos4-maintain.py index 6321a3f..10ac62b 100644 --- a/scripts/newos4-maintain.py +++ b/scripts/newos4-maintain.py @@ -55,10 +55,11 @@ def check_index(): idxp = os.path.join(ROOT, 'modules-index.json') atlas = json.load(open(os.path.join(ROOT, 'course/atlas.json'))) idx = json.load(open(idxp)) - cur = len(set(c for m in idx.get('modules', {}).values() for c in m.get('commands', []))) - if cur == atlas['coverage']['commands']: - add('PASS', f'modules-index {cur} commands'); return - add('WARN', f'modules-index stale {cur} vs {atlas["coverage"]["commands"]}') + index_cmds = set(c for m in idx.get('modules', {}).values() for c in m.get('commands', [])) + missing = sorted(c for c, e in atlas.get('commands', {}).items() if not e.get('builtin') and c not in index_cmds) + if not missing: + add('PASS', f'modules-index covers {len(index_cmds)} commands'); return + add('WARN', f'modules-index missing {len(missing)}: ' + ', '.join(missing[:5])) if not FIX: return for name, m in atlas['modules'].items(): e = idx['modules'].get(name, {'name': name, 'file': m['file'], 'type': 'core' if m['tier'] == 'core-bundle' else 'ext', @@ -87,6 +88,14 @@ def check_live(): code = sh(f"curl -s -o /dev/null -w '%{{http_code}}' '{url}'") add('PASS' if code == '200' else 'FAIL', f'HTTP {code} {url.replace(WEB, "")}') +def check_atlas(): + p = os.path.join(ROOT, 'course/atlas.json') + before = (json.load(open(p)).get('coverage', {}).get('commands') if os.path.exists(p) else None) + r = run([sys.executable, 'scripts/gen-course-atlas.py'], cwd=ROOT) + after = (json.load(open(p)).get('coverage', {}).get('commands') if os.path.exists(p) else None) + add('PASS' if r.returncode == 0 else 'FAIL', + f'atlas {after} commands' + (' (refreshed from ' + str(before) + ')' if before != after else '')) + def check_course(): r = run([sys.executable, 'scripts/course-validate.py'], cwd=ROOT) add('PASS' if r.returncode == 0 else 'FAIL', 'course ' + ('valid' if r.returncode == 0 else 'INVALID: ' + r.stdout[-160:])) @@ -106,7 +115,7 @@ def check_links(): def main(): os.chdir(ROOT) - check_versions(); check_bundles(); check_syntax(); check_perms(); check_index(); check_data(); check_live(); check_course(); check_links() + check_versions(); check_bundles(); check_syntax(); check_perms(); check_index(); check_data(); check_live(); check_atlas(); check_course(); check_links() fails = [m for l, m in issues if l == 'FAIL'] print('newOS4 maintenance — ' + datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%MZ') + (' [fix]' if FIX else '') + (' [deep]' if DEEP else '')) for l, m in issues: print(f'{l}: {m}') |