Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56d1072e58 | ||
|
|
e71d0ca6b6 | ||
|
|
6f442bf423 |
@@ -0,0 +1,25 @@
|
||||
# Left out of the Comfy Registry package (comfy node publish). Git clones still
|
||||
# have everything. tests/test_package_contents.py holds these rules to what
|
||||
# ComfyUI needs at run time; keep each one anchored to the root with a leading /.
|
||||
|
||||
# Development folders
|
||||
/tests/
|
||||
/tools/
|
||||
/docs/
|
||||
/.github/
|
||||
|
||||
# Screenshots used only by README-legacy.md on GitHub
|
||||
/images/
|
||||
|
||||
# Development files
|
||||
/TESTING.md
|
||||
/Makefile
|
||||
/requirements-dev.txt
|
||||
/.coveragerc
|
||||
/.flake8
|
||||
/.pre-commit-config.yaml
|
||||
/.gitignore
|
||||
|
||||
# Stray images no code uses
|
||||
/image_meta_data.png
|
||||
/test_metadata_file.png
|
||||
@@ -668,6 +668,10 @@ Browser tests: `workspace-stale-source.spec.mjs` (`/__test__/sources/reverse` an
|
||||
`/__test__/scope-key/rotate`, both undone by every reset); the manual-Refresh
|
||||
recovery specs in `workspace-images.spec.mjs` answer every rescope 409.
|
||||
|
||||
## Registry package
|
||||
|
||||
`comfy node publish` zips the tracked files minus `.comfyignore`, so a new development file or folder at the root ships until it is listed there. `tests/test_package_contents.py` keeps each rule anchored to the root, checks that development paths are left out and that nothing ComfyUI needs at run time is. Git clones are unaffected.
|
||||
|
||||
## Windows portability preflight
|
||||
|
||||
Before changing paths, SQLite, streams, restore or cleanup:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "promptmanager"
|
||||
description = "A powerful ComfyUI custom node that extends the standard text encoder with persistent prompt storage, advanced search capabilities, and an automatic image gallery system using SQLite."
|
||||
version = "3.5.0"
|
||||
version = "3.5.1"
|
||||
license = {file = "LICENSE"}
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["watchdog>=2.1.0", "Pillow>=10.1.0"]
|
||||
|
||||
@@ -4,15 +4,15 @@ Pillow>=10.1.0
|
||||
aiohttp>=3.11.8
|
||||
|
||||
# Tests and coverage
|
||||
coverage>=7.16.2
|
||||
coverage>=7.17.0
|
||||
numpy>=1.25.0 # WD14 engine tests
|
||||
|
||||
# Code quality
|
||||
black==26.10.1
|
||||
flake8==7.4.1
|
||||
flake8==7.5.0
|
||||
|
||||
# Security scanning
|
||||
bandit[toml]>=1.9.4
|
||||
|
||||
# Pre-commit hooks
|
||||
pre-commit>=4.6.2
|
||||
pre-commit>=4.7.0
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// #615: each old page says it will be removed and links to where its job lives now. The page keeps working, and the
|
||||
// notice takes its own room: nothing is pushed off a page whose layout fills the window.
|
||||
import { startCoverage, saveCoverage } from '../../tools/vendor/browser-coverage.mjs';
|
||||
import { test, expect } from './fixtures.mjs';
|
||||
|
||||
const PAGES = Object.freeze([
|
||||
{ path: '/prompt_manager/admin', section: 'library', heading: 'Prompt library' },
|
||||
{ path: '/prompt_manager/gallery', section: 'images', heading: 'Images' },
|
||||
{ path: '/prompt_manager/gallery.html', section: 'inspect', heading: 'Inspect a file' },
|
||||
]);
|
||||
|
||||
test.beforeEach(async ({ page, request, browserName }) => {
|
||||
await startCoverage(page, browserName);
|
||||
await expect(await request.post('/__test__/reset', {})).toBeOK();
|
||||
await page.addInitScript(() => {
|
||||
localStorage.setItem('pm_update_notice_dismissed', '3.2.0');
|
||||
localStorage.setItem('gallerySettings', JSON.stringify({ checkThumbnailsAtStartup: false }));
|
||||
});
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page, browserName }) => {
|
||||
await saveCoverage(page, browserName);
|
||||
});
|
||||
|
||||
for (const { path, section, heading } of PAGES) {
|
||||
test(`${path} says it will be removed and links to ${section}`, async ({ page }) => {
|
||||
await page.goto(path);
|
||||
const notice = page.locator('#legacy-removal-notice');
|
||||
await expect(notice).toBeVisible();
|
||||
await expect(notice).toContainText('This page will be removed in a future release.');
|
||||
await notice.getByRole('link', { name: 'Open the workspace' }).click();
|
||||
await expect(page).toHaveURL(new RegExp(`/prompt_manager/workspace#/${section}$`));
|
||||
await expect(page.locator('#workspace-main h1')).toHaveText(heading);
|
||||
});
|
||||
}
|
||||
|
||||
test('the notice leaves the metadata page filling the window', async ({ page }) => {
|
||||
await page.goto('/prompt_manager/gallery.html');
|
||||
await expect(page.locator('#legacy-removal-notice')).toBeVisible();
|
||||
const { bottom, height, scroll } = await page.locator('#image-area').evaluate(area => ({
|
||||
bottom: area.getBoundingClientRect().bottom,
|
||||
height: window.innerHeight,
|
||||
scroll: document.documentElement.scrollHeight,
|
||||
}));
|
||||
expect(bottom).toBeLessThanOrEqual(height);
|
||||
expect(scroll).toBeLessThanOrEqual(height);
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
"""What the Registry package leaves out (#615).
|
||||
|
||||
``comfy node publish`` zips the tracked files minus ``.comfyignore``. The checks
|
||||
here hold the two ways that can go wrong: a development folder creeping back
|
||||
into the download, or a rule that drops something ComfyUI needs at run time.
|
||||
"""
|
||||
|
||||
import ast
|
||||
import fnmatch
|
||||
import re
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RUNTIME_DIRS = ("py", "database", "utils", "web")
|
||||
DEVELOPMENT = (
|
||||
"tests/test_package_contents.py",
|
||||
"tools/vendor/package.json",
|
||||
"docs/application-redesign.md",
|
||||
".github/workflows/publish.yml",
|
||||
"images/x.png",
|
||||
"TESTING.md",
|
||||
"requirements-dev.txt",
|
||||
"image_meta_data.png",
|
||||
"test_metadata_file.png",
|
||||
)
|
||||
KEPT = (
|
||||
"__init__.py",
|
||||
"prompt_manager.py",
|
||||
"pyproject.toml",
|
||||
"requirements.txt",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"README-legacy.md",
|
||||
"assets/readme/hero.webp",
|
||||
)
|
||||
|
||||
|
||||
def _patterns():
|
||||
lines = (ROOT / ".comfyignore").read_text(encoding="utf-8").splitlines()
|
||||
return [line.strip() for line in lines if line.strip() and line[0] != "#"]
|
||||
|
||||
|
||||
def _ignored(path):
|
||||
"""The rules this file allows: ``folder/`` from the root, or a root file."""
|
||||
for pattern in _patterns():
|
||||
if pattern.endswith("/"):
|
||||
if path.startswith(pattern.lstrip("/")):
|
||||
return True
|
||||
elif fnmatch.fnmatchcase(path, pattern.lstrip("/")):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class TestPackageContents(unittest.TestCase):
|
||||
def test_every_rule_is_anchored_to_the_root(self):
|
||||
# An unanchored "tests/" would also drop web/js/tests, and "images/" a
|
||||
# runtime folder of that name deeper down.
|
||||
for pattern in _patterns():
|
||||
self.assertTrue(pattern.startswith("/"), pattern)
|
||||
self.assertNotIn("!", pattern)
|
||||
|
||||
def test_development_files_are_left_out(self):
|
||||
for path in DEVELOPMENT:
|
||||
self.assertTrue(_ignored(path), path)
|
||||
|
||||
def test_what_comfyui_needs_stays(self):
|
||||
for path in KEPT:
|
||||
self.assertFalse(_ignored(path), path)
|
||||
for folder in RUNTIME_DIRS:
|
||||
for file in (ROOT / folder).rglob("*"):
|
||||
if file.is_file():
|
||||
relative = file.relative_to(ROOT).as_posix()
|
||||
self.assertFalse(_ignored(relative), relative)
|
||||
|
||||
def test_runtime_code_imports_nothing_left_out(self):
|
||||
sources = list(ROOT.glob("*.py"))
|
||||
for folder in ("py", "database", "utils"):
|
||||
sources.extend((ROOT / folder).rglob("*.py"))
|
||||
for source in sources:
|
||||
tree = ast.parse(source.read_text(encoding="utf-8"))
|
||||
for node in ast.walk(tree):
|
||||
names = []
|
||||
if isinstance(node, ast.Import):
|
||||
names = [alias.name for alias in node.names]
|
||||
elif isinstance(node, ast.ImportFrom) and not node.level:
|
||||
names = [node.module or ""]
|
||||
for name in names:
|
||||
self.assertNotIn(name.split(".")[0], ("tests", "tools"), source)
|
||||
|
||||
def test_the_old_pages_name_their_replacement(self):
|
||||
for page, section in (
|
||||
("admin", "library"),
|
||||
("gallery", "images"),
|
||||
("metadata", "inspect"),
|
||||
):
|
||||
html = (ROOT / "web" / f"{page}.html").read_text(encoding="utf-8")
|
||||
self.assertRegex(html, r'id="legacy-removal-notice"')
|
||||
self.assertRegex(html, re.escape(f'href="workspace#/{section}"'))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(unittest.main())
|
||||
@@ -20,6 +20,7 @@
|
||||
<script src="lib/viewerjs/viewer.min.js"></script>
|
||||
</head>
|
||||
<body class="bg-pm-primary text-pm min-h-screen text-[13px]">
|
||||
<div id="legacy-removal-notice" role="note" class="bg-pm-surface border-b border-pm px-4 py-2 text-pm" style="font-size: 13px;">This page will be removed in a future release. Its features are moving to the new workspace. <a href="workspace#/library" style="text-decoration: underline;">Open the workspace</a></div>
|
||||
<div class="min-h-screen flex flex-col">
|
||||
<!-- Header -->
|
||||
<header class="bg-pm-surface border-b border-pm px-4 py-2" style="min-height: 2.5rem;">
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-pm-primary text-pm min-h-screen">
|
||||
<div id="legacy-removal-notice" role="note" class="bg-pm-surface border-b border-pm px-4 py-2 text-pm" style="font-size: 13px;">This page will be removed in a future release. Its features are moving to the new workspace. <a href="workspace#/images" style="text-decoration: underline;">Open the workspace</a></div>
|
||||
<div class="min-h-screen flex flex-col">
|
||||
<!-- Header -->
|
||||
<header class="bg-pm-surface border-b border-pm px-4 py-2">
|
||||
|
||||
+3
-2
@@ -361,8 +361,9 @@ Seed: ${currentWorkflowData.seed || 'Unknown'}`;
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-pm-primary text-pm font-sans overflow-hidden">
|
||||
<div class="h-screen flex">
|
||||
<body class="bg-pm-primary text-pm font-sans overflow-hidden" style="display: flex; flex-direction: column; height: 100vh;">
|
||||
<div id="legacy-removal-notice" role="note" class="bg-pm-surface border-b border-pm px-4 py-2 text-pm" style="font-size: 13px;">This page will be removed in a future release. Its features are moving to the new workspace. <a href="workspace#/inspect" style="text-decoration: underline;">Open the workspace</a></div>
|
||||
<div class="flex" style="flex: 1; min-height: 0;">
|
||||
<!-- Main Image Area -->
|
||||
<div id="image-area" class="flex-1 flex items-center justify-center p-3 bg-pm-primary relative border-2 border-dashed border-transparent transition-colors min-w-0">
|
||||
<!-- Upload prompt overlay -->
|
||||
|
||||
Reference in New Issue
Block a user