Commit graph

24 commits

Author SHA1 Message Date
3053704247
twine-venv: pin twine<6 — classic ~/.pypirc auth on build runner
twine 6 (Sep 2025) auto-detects GitLab CI and refuses to fall back to
~/.pypirc, requiring PYPI_ID_TOKEN (Trusted Publishing OIDC). Pin <6
to keep the runner's ~/.pypirc fallback working until we migrate all
python/* repos to Trusted Publishing as a coordinated change.
2026-06-16 14:12:00 -04:00
892e6a859e
twine-upload: validate metadata with twine check, non-interactive
twine check validates the built artifacts' metadata (long_description
rendering, required fields) so a broken render hits CI logs instead of
landing on PyPI as a malformed project page.

--non-interactive guards against a wedged prompt hanging the pipeline.
2026-06-16 13:55:43 -04:00
5942f6b2b3
fix: eliminate pandoc subprocess thrash under pytest-xdist -n auto
Root cause of recurring 35929 / 38536 CI flake — `-n auto` on a 32-core
CI runner spawns 32 pytest workers. test_pandoc.py contains ~14 tests
that each fork a `pandoc` subprocess. When several land on parallel
workers at once, pandoc's cold-start cost (GHC runtime + filter loading)
plus runner CPU contention pushes wall time past the 5s metadata timeout
and 30s convert timeout. Subprocess gets SIGKILL'd (returncode -9),
tests fail, pipeline reruns the same flake.

Three-part root cause fix:

1. Cache get_available_input_formats / get_available_output_formats with
   functools.lru_cache(maxsize=1). Pandoc's format list is static for
   a given binary; we only need one subprocess per Python process,
   not one per call site. Also bump the metadata-query timeout from 5s
   to 30s — the first cold-start under contention still has to succeed.

2. Mark TestConvert + TestAvailableFormats with
   @pytest.mark.xdist_group("pandoc-subprocess") so every pandoc-forking
   test pins to one xdist worker. Other 31 workers continue parallel-
   processing the rest of the suite; pandoc tests run sequentially on
   their single worker, each pandoc cold-start completes before the
   next starts.

3. Update Makefile test target to pass --dist=loadgroup so pytest-xdist
   honors the xdist_group marker.

Other test classes in test_pandoc.py (TestNodeTreeToMarkdown,
TestNamespaceToMarkdown, TestGetAuthorName) use MockNode objects
without data_html, so node_tree_to_markdown's pandoc path stays cold —
those classes don't need the marker.

Replaces 1527ef7 (which just retriggered the same flake).
2026-06-05 20:47:30 -04:00
05f51ff965
supply-chain: hash-pin prod PyPI dependencies (requirements-prod.lock)
Prod previously installed via 'pip install .' (unbounded requirements.py3.txt) plus
'pip install --upgrade -r requirements-prod.txt' — every deploy re-resolved external
PyPI deps to whatever was latest, unverified.

- requirements-prod.in: source (runtime + prod-server deps)
- requirements-prod.lock: 55 PyPI pkgs pinned to exact versions + SHA256 (622 hashes)
- scripts/strip-vcs-from-lock.py: removes first-party git theme deps (pip can't hash
  a git repo; themes are integrity-pinned by their own commit SHAs)
- install-source-prod: pip install --require-hashes -r requirements-prod.lock, then
  pip install . to resolve the first-party git themes without re-resolving the
  hash-pinned PyPI deps
- make pins-lock regenerates the lock deliberately

Validated locally: stripped lock installs under --require-hashes, app imports under
resolved versions (SQLAlchemy 2.0, Pyramid latest).
2026-05-21 09:22:21 -04:00
176be89319 docs: use make migration — never hand-write revision IDs 2026-04-06 14:23:16 -04:00
f5c09d873e Pin setuptools<81 — version 82 removed pkg_resources
Pyramid imports pkg_resources which was removed in setuptools 82.
Pin across requirements, Makefile, and CI scripts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 12:19:33 -05:00
a2e574d6b9 Install setuptools before editable install, not just in venv target
The venv target is a no-op on cached CI runners where env/bin/activate
already exists. Move setuptools install into install-source-dev-and-test
and install-source-prod so it runs every time, regardless of venv cache.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 12:11:09 -05:00
ef4d6da410 Install setuptools in venv creation step for Python 3.12+
The requirements.py3.txt change alone wasn't enough — pip treats
setuptools specially during editable installs and may not install
it as a runtime dependency. Installing it immediately after venv
creation ensures pkg_resources is available for Pyramid and theme
loading.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 12:03:44 -05:00
a49446cfef Add parallel test execution with pytest-xdist
Implements per-worker database isolation for parallel test runs:
- Add pytest-xdist and pytest-cov to test dependencies
- Configure Makefile to run tests with -n auto flag
- Update test.ini to use environment variable for database path
- Create conftest.py with per-worker database isolation
- Enable SQLite WAL mode for better concurrency
- Auto-cleanup test databases after completion

Expected performance improvement similar to make_post_sell (~16x speedup).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-21 12:36:31 -05:00
9936b21467 Add twine-venv target in /tmp for PyPI uploads
Uses a persistent virtualenv in /tmp/twine-venv to avoid
relying on system python/twine in CI.
2025-12-29 09:35:08 -05:00
939701db33 modified: Makefile 2025-03-03 17:19:49 -05:00
43712b0176 modified: Makefile 2025-03-03 17:15:39 -05:00
8b456825ce no shortcuts to prod
modified:   .gitlab-ci.yml
	modified:   Makefile
2025-03-03 10:55:06 -05:00
9d1393ebae modified: Makefile
modified:   README.rst
2025-03-01 17:41:12 -05:00
ab4283dd5d fix tests
modified:   Makefile
2025-03-01 16:25:56 -05:00
256f2e6a1d modified: Makefile 2025-03-01 15:57:32 -05:00
c567e2334d modified: Makefile 2025-03-01 15:46:09 -05:00
65f1d2cbed modified: Makefile 2025-03-01 15:28:27 -05:00
710588d007 remarkbox 1.0.0
modified:   .gitignore
	modified:   .gitlab-ci.yml
	modified:   Makefile
	modified:   README.rst
	modified:   setup.py
2025-03-01 15:22:50 -05:00
8c4bcb7f37 add section to docs about new packages
modified:   Makefile
	modified:   README.rst
2025-03-01 11:11:42 -05:00
5449b4872f fix deploys
modified:   .gitlab-ci.yml
	modified:   Makefile
2025-03-01 10:18:15 -05:00
17ce4b5029 when in development log SMTP errors & email
modified:   Makefile
	modified:   README.rst
	modified:   remarkbox/__init__.py
	modified:   remarkbox/lib/mail.py
2025-01-09 09:02:00 -05:00
d93e9106d4 Verification challenge code refactor
it works good enough,

future commits will do some refactors to make the success
screen more user friendly.

	modified:   Makefile
	modified:   README.rst
	modified:   index.html
	modified:   remarkbox/lib/mail.py
	modified:   remarkbox/lib/mail_messages.py
	modified:   remarkbox/models/meta.py
	modified:   remarkbox/models/user.py
	modified:   remarkbox/routes.py
	modified:   remarkbox/templates/join-or-log-in.j2
	modified:   remarkbox/templates/show-node.j2
	new file:   remarkbox/templates/verification-challenge.j2
	modified:   remarkbox/tests/test_models.py
	modified:   remarkbox/tests/test_views.py
	modified:   remarkbox/views/__init__.py
	modified:   remarkbox/views/authentication.py
2022-06-19 19:01:31 -04:00
91a2a77711 time to open sause & place into public domain!
Merry Christmas!

I AM READY

I love you all!

	new file:   .gitignore
	new file:   MANIFEST.in
	new file:   Makefile
	new file:   README.rst
	new file:   development.ini
	new file:   index.html
	new file:   index2.html
	new file:   journal.rst
	new file:   readme-postgres-notes.rst
	new file:   readme-themes.rst
	new file:   remarkbox/__init__.py
	new file:   remarkbox/lib/__init__.py
	new file:   remarkbox/lib/mail.py
	new file:   remarkbox/lib/mail_messages.py
	new file:   remarkbox/lib/notify.py
	new file:   remarkbox/lib/render.py
	new file:   remarkbox/lib/svgtar/__init__.py
	new file:   remarkbox/lib/svgtar/svg.py
	new file:   remarkbox/models/__init__.py
	new file:   remarkbox/models/event.py
	new file:   remarkbox/models/meta.py
	new file:   remarkbox/models/namespace.py
	new file:   remarkbox/models/namespace_request.py
	new file:   remarkbox/models/namespace_user.py
	new file:   remarkbox/models/node.py
	new file:   remarkbox/models/node_cache.py
	new file:   remarkbox/models/notification.py
	new file:   remarkbox/models/oauth.py
	new file:   remarkbox/models/pay_what_you_can.py
	new file:   remarkbox/models/uri.py
	new file:   remarkbox/models/user.py
	new file:   remarkbox/models/vote.py
	new file:   remarkbox/models/watcher.py
	new file:   remarkbox/routes.py
	new file:   remarkbox/scripts/__init__.py
	new file:   remarkbox/scripts/alembic/README
	new file:   remarkbox/scripts/alembic/env.py
	new file:   remarkbox/scripts/alembic/script.py.mako
	new file:   remarkbox/scripts/alembic/versions/00211ed642cc_add_ip_address_column_to_rb_node.py
	new file:   remarkbox/scripts/alembic/versions/0208c10ea67f_create_new_node_cache_table.py
	new file:   remarkbox/scripts/alembic/versions/04e52337756e_autogenerated_indicies.py
	new file:   remarkbox/scripts/alembic/versions/051f8b810f26_namespace_ignore_query_string.py
	new file:   remarkbox/scripts/alembic/versions/1040ee4836af_namespace_description.py
	new file:   remarkbox/scripts/alembic/versions/184a349cd589_uri_accessed_timestamp.py
	new file:   remarkbox/scripts/alembic/versions/256468780798_root_nodes_to_namespace_.py
	new file:   remarkbox/scripts/alembic/versions/2e00b92495e4_node_disabled_timestamp.py
	new file:   remarkbox/scripts/alembic/versions/3ee195d142fb_add_subscription_type_to_namespace.py
	new file:   remarkbox/scripts/alembic/versions/40ded1163f95_add_user_surrogate_columns.py
	new file:   remarkbox/scripts/alembic/versions/43c1e5a99d4e_new_namespace_column_3.py
	new file:   remarkbox/scripts/alembic/versions/4aeda8e797ab_new_namespace_column_for_stylesheet_uris.py
	new file:   remarkbox/scripts/alembic/versions/503a2cef2378_root_stats.py
	new file:   remarkbox/scripts/alembic/versions/591800942adf_node_data_html_column.py
	new file:   remarkbox/scripts/alembic/versions/59592cf325fb_stylesheet_timestamp.py
	new file:   remarkbox/scripts/alembic/versions/5a5d0b68db78_add_root_id_field.py
	new file:   remarkbox/scripts/alembic/versions/6a440d3921d2_added_email_id_column_to_user_table.py
	new file:   remarkbox/scripts/alembic/versions/6bbc92ccc112_add_changed_column_to_node_table.py
	new file:   remarkbox/scripts/alembic/versions/6db39621f46c_namespace_group_conversations.py
	new file:   remarkbox/scripts/alembic/versions/6fc1d0cf350c_notification_columns_to_rb_user_table.py
	new file:   remarkbox/scripts/alembic/versions/76deff88cfd6_create_graph_depth_column_on_node_model.py
	new file:   remarkbox/scripts/alembic/versions/771ef7c868de_added_created_timestamp_column_to_.py
	new file:   remarkbox/scripts/alembic/versions/7a3a4755b4a8_add_hide_unless_approved_column.py
	new file:   remarkbox/scripts/alembic/versions/87fbd2b4e06e_namespace_theme_embed_column.py
	new file:   remarkbox/scripts/alembic/versions/953d42406dbb_namespace_public_column.py
	new file:   remarkbox/scripts/alembic/versions/98a90d2add21_namespace_model.py
	new file:   remarkbox/scripts/alembic/versions/98ced5f6ff36_stripe_id_for_user.py
	new file:   remarkbox/scripts/alembic/versions/a26302bcfa25_add_created_timestamp_column_to_oauth_.py
	new file:   remarkbox/scripts/alembic/versions/a38c12eeff2d_locked_rb_node.py
	new file:   remarkbox/scripts/alembic/versions/a738d6859f18_new_namespace_columns_2.py
	new file:   remarkbox/scripts/alembic/versions/aa2dfb4decda_hide_powered_by_column.py
	new file:   remarkbox/scripts/alembic/versions/b132ec968f0c_pay_what_you_can_table.py
	new file:   remarkbox/scripts/alembic/versions/ba8e83a3b8fd_namespace_stylesheet_embed_column.py
	new file:   remarkbox/scripts/alembic/versions/c9793937e756_make_all_verified_namespace_request_.py
	new file:   remarkbox/scripts/alembic/versions/c97ddfcbc406_approve_column.py
	new file:   remarkbox/scripts/alembic/versions/cbdf3cd4ccba_avatar_size_in_namespace.py
	new file:   remarkbox/scripts/alembic/versions/d1a0ab4f617f_add_has_uri_to_node.py
	new file:   remarkbox/scripts/alembic/versions/f5f88293428a_add_mathjax_column_to_rb_namespace.py
	new file:   remarkbox/scripts/alembic/versions/f7b42b2024fb_namespace_reverse_node_order.py
	new file:   remarkbox/scripts/alembic/versions/fa8402aa1a00_add_link_protection_column_to_namespace.py
	new file:   remarkbox/scripts/dump_sqlite3_to_csv.sh
	new file:   remarkbox/scripts/init_db.py
	new file:   remarkbox/scripts/invalidate_node_cache.py
	new file:   remarkbox/scripts/json_import.py
	new file:   remarkbox/scripts/load_csv_into_postgres.sh
	new file:   remarkbox/scripts/merge_dupes.py
	new file:   remarkbox/scripts/modify_namespace.py
	new file:   remarkbox/scripts/modify_node.py
	new file:   remarkbox/scripts/modify_uris.py
	new file:   remarkbox/scripts/modify_user.py
	new file:   remarkbox/scripts/recompute_node_depths.py
	new file:   remarkbox/scripts/safe_approve_all_nodes.py
	new file:   remarkbox/scripts/send_node_digest_notifications.py
	new file:   remarkbox/static/css/common.css
	new file:   remarkbox/static/css/embed.css
	new file:   remarkbox/static/css/pygments.css
	new file:   remarkbox/static/favicon.ico
	new file:   remarkbox/static/img/remarkbox-logo.png
	new file:   remarkbox/static/img/remarkbox-minified.png
	new file:   remarkbox/static/js/custom.js
	new file:   remarkbox/static/js/iframe-resizer/ie8.polyfils.map
	new file:   remarkbox/static/js/iframe-resizer/ie8.polyfils.min.js
	new file:   remarkbox/static/js/iframe-resizer/iframeResizer.contentWindow.js
	new file:   remarkbox/static/js/iframe-resizer/iframeResizer.contentWindow.map
	new file:   remarkbox/static/js/iframe-resizer/iframeResizer.contentWindow.min.js
	new file:   remarkbox/static/js/iframe-resizer/iframeResizer.js
	new file:   remarkbox/static/js/iframe-resizer/iframeResizer.map
	new file:   remarkbox/static/js/iframe-resizer/iframeResizer.min.js
	new file:   remarkbox/static/js/iframe-resizer/index.js
	new file:   remarkbox/static/js/iframe-resizer/readme.rst
	new file:   remarkbox/static/js/jquery-2.1.3.min.js
	new file:   remarkbox/static/theme/readme.rst
	new file:   remarkbox/templates/base.j2
	new file:   remarkbox/templates/billing.j2
	new file:   remarkbox/templates/dynamic-remarkbox.css.j2
	new file:   remarkbox/templates/edit-node.j2
	new file:   remarkbox/templates/embed-iframe.txt.j2
	new file:   remarkbox/templates/home.j2
	new file:   remarkbox/templates/join-or-log-in.j2
	new file:   remarkbox/templates/list-namespace-owners.j2
	new file:   remarkbox/templates/list-namespace-requests.j2
	new file:   remarkbox/templates/list-nodes.j2
	new file:   remarkbox/templates/list-notifications.j2
	new file:   remarkbox/templates/mail_digest_html.j2
	new file:   remarkbox/templates/mail_digest_text.j2
	new file:   remarkbox/templates/mail_immediate_html.j2
	new file:   remarkbox/templates/mail_immediate_text.j2
	new file:   remarkbox/templates/mail_snippet_html.j2
	new file:   remarkbox/templates/mail_snippet_text.j2
	new file:   remarkbox/templates/namespace-settings.j2
	new file:   remarkbox/templates/new.j2
	new file:   remarkbox/templates/rss.xml.j2
	new file:   remarkbox/templates/setup-namespace.j2
	new file:   remarkbox/templates/show-count.j2
	new file:   remarkbox/templates/show-node.j2
	new file:   remarkbox/templates/show-node2.j2
	new file:   remarkbox/templates/sitemap.xml.j2
	new file:   remarkbox/templates/snippets/add-to-slack.j2
	new file:   remarkbox/templates/snippets/create.j2
	new file:   remarkbox/templates/snippets/csrf.j2
	new file:   remarkbox/templates/snippets/email.j2
	new file:   remarkbox/templates/snippets/flash-alerts.j2
	new file:   remarkbox/templates/snippets/forms.j2
	new file:   remarkbox/templates/snippets/google-analytics.j2
	new file:   remarkbox/templates/snippets/javascript-includes.j2
	new file:   remarkbox/templates/snippets/owner-key-comment.j2
	new file:   remarkbox/templates/snippets/phone-menu.j2
	new file:   remarkbox/templates/snippets/powered-by.j2
	new file:   remarkbox/templates/snippets/search.j2
	new file:   remarkbox/templates/snippets/snippets.j2
	new file:   remarkbox/templates/snippets/stripe.j2
	new file:   remarkbox/templates/snippets/stylesheet-includes.j2
	new file:   remarkbox/templates/snippets/submit.j2
	new file:   remarkbox/templates/update-card.j2
	new file:   remarkbox/templates/user-settings.j2
	new file:   remarkbox/templates/user-watching.j2
	new file:   remarkbox/tests/test_models.py
	new file:   remarkbox/tests/test_render.py
	new file:   remarkbox/tests/test_views.py
	new file:   remarkbox/views/__init__.py
	new file:   remarkbox/views/authenticated/__init__.py
	new file:   remarkbox/views/authenticated/authenticated.py
	new file:   remarkbox/views/authenticated/lock.py
	new file:   remarkbox/views/authenticated/notifications.py
	new file:   remarkbox/views/authenticated/oauth.py
	new file:   remarkbox/views/authenticated/stripe.py
	new file:   remarkbox/views/authenticated/topsecret.py
	new file:   remarkbox/views/authenticated/watch.py
	new file:   remarkbox/views/authentication.py
	new file:   remarkbox/views/list_nodes.py
	new file:   remarkbox/views/misc.py
	new file:   remarkbox/views/modify_node.py
	new file:   remarkbox/views/new_thread.py
	new file:   remarkbox/views/reply_node.py
	new file:   requirements-dev.txt
	new file:   requirements-prod.txt
	new file:   requirements-test.txt
	new file:   requirements.py3.txt
	new file:   requirements.txt
	new file:   setup.py
	new file:   test.ini
2021-12-26 07:44:05 -05:00