connections-export 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- connections_export-0.1.0/.github/workflows/binaries.yml +144 -0
- connections_export-0.1.0/.github/workflows/ci.yml +82 -0
- connections_export-0.1.0/.github/workflows/packages.yml +181 -0
- connections_export-0.1.0/.github/workflows/release.yml +41 -0
- connections_export-0.1.0/.gitignore +29 -0
- connections_export-0.1.0/LICENSE +28 -0
- connections_export-0.1.0/PKG-INFO +373 -0
- connections_export-0.1.0/README.md +334 -0
- connections_export-0.1.0/connections-export.example.toml +73 -0
- connections_export-0.1.0/connections_export/__init__.py +1 -0
- connections_export-0.1.0/connections_export/_licenses/Markdown/LICENSE.md +30 -0
- connections_export-0.1.0/connections_export/_licenses/NOTICE.txt +80 -0
- connections_export-0.1.0/connections_export/_licenses/annotated-doc/LICENSE +21 -0
- connections_export-0.1.0/connections_export/_licenses/annotated-types/LICENSE +21 -0
- connections_export-0.1.0/connections_export/_licenses/anyio/LICENSE +20 -0
- connections_export-0.1.0/connections_export/_licenses/beautifulsoup4/LICENSE +31 -0
- connections_export-0.1.0/connections_export/_licenses/certifi/LICENSE +20 -0
- connections_export-0.1.0/connections_export/_licenses/click/LICENSE.txt +28 -0
- connections_export-0.1.0/connections_export/_licenses/connections-export/LICENSE +28 -0
- connections_export-0.1.0/connections_export/_licenses/fastapi/LICENSE +21 -0
- connections_export-0.1.0/connections_export/_licenses/greenlet/LICENSE +30 -0
- connections_export-0.1.0/connections_export/_licenses/greenlet/LICENSE.PSF +47 -0
- connections_export-0.1.0/connections_export/_licenses/h11/LICENSE.txt +22 -0
- connections_export-0.1.0/connections_export/_licenses/httpcore/LICENSE.md +27 -0
- connections_export-0.1.0/connections_export/_licenses/httpx/LICENSE.md +12 -0
- connections_export-0.1.0/connections_export/_licenses/idna/LICENSE.md +31 -0
- connections_export-0.1.0/connections_export/_licenses/lxml/LICENSE.txt +31 -0
- connections_export-0.1.0/connections_export/_licenses/lxml/LICENSES.txt +29 -0
- connections_export-0.1.0/connections_export/_licenses/markdownify/LICENSE +21 -0
- connections_export-0.1.0/connections_export/_licenses/paged.js/LICENSE.txt +29 -0
- connections_export-0.1.0/connections_export/_licenses/pdf.js/LICENSE.txt +193 -0
- connections_export-0.1.0/connections_export/_licenses/playwright/LICENSE +202 -0
- connections_export-0.1.0/connections_export/_licenses/playwright/NOTICE +5 -0
- connections_export-0.1.0/connections_export/_licenses/pydantic/LICENSE +21 -0
- connections_export-0.1.0/connections_export/_licenses/pydantic_core/LICENSE +21 -0
- connections_export-0.1.0/connections_export/_licenses/pyee/LICENSE +21 -0
- connections_export-0.1.0/connections_export/_licenses/pypdf/LICENSE +29 -0
- connections_export-0.1.0/connections_export/_licenses/sbom.cyclonedx.json +550 -0
- connections_export-0.1.0/connections_export/_licenses/six/LICENSE +18 -0
- connections_export-0.1.0/connections_export/_licenses/soupsieve/LICENSE.md +21 -0
- connections_export-0.1.0/connections_export/_licenses/starlette/LICENSE.md +27 -0
- connections_export-0.1.0/connections_export/_licenses/typing-inspection/LICENSE +21 -0
- connections_export-0.1.0/connections_export/_licenses/typing_extensions/LICENSE +279 -0
- connections_export-0.1.0/connections_export/_licenses/uvicorn/LICENSE.md +27 -0
- connections_export-0.1.0/connections_export/adapters/__init__.py +114 -0
- connections_export-0.1.0/connections_export/adapters/atom.py +380 -0
- connections_export-0.1.0/connections_export/adapters/blogs.py +279 -0
- connections_export-0.1.0/connections_export/adapters/communities.py +120 -0
- connections_export-0.1.0/connections_export/adapters/directory.py +97 -0
- connections_export-0.1.0/connections_export/adapters/errors.py +33 -0
- connections_export-0.1.0/connections_export/adapters/files.py +177 -0
- connections_export-0.1.0/connections_export/adapters/forums.py +449 -0
- connections_export-0.1.0/connections_export/adapters/model.py +391 -0
- connections_export-0.1.0/connections_export/adapters/profiles.py +85 -0
- connections_export-0.1.0/connections_export/adapters/rte.py +265 -0
- connections_export-0.1.0/connections_export/adapters/search.py +165 -0
- connections_export-0.1.0/connections_export/adapters/since.py +110 -0
- connections_export-0.1.0/connections_export/adapters/wikis.py +453 -0
- connections_export-0.1.0/connections_export/apps.py +174 -0
- connections_export-0.1.0/connections_export/archive/__init__.py +11 -0
- connections_export-0.1.0/connections_export/archive/blobs.py +42 -0
- connections_export-0.1.0/connections_export/archive/feeds.py +60 -0
- connections_export-0.1.0/connections_export/archive/records.py +167 -0
- connections_export-0.1.0/connections_export/archive/redact.py +40 -0
- connections_export-0.1.0/connections_export/archive/source.py +296 -0
- connections_export-0.1.0/connections_export/archive/store.py +313 -0
- connections_export-0.1.0/connections_export/cli.py +1496 -0
- connections_export-0.1.0/connections_export/compare/__init__.py +1 -0
- connections_export-0.1.0/connections_export/compare/author_compare.py +131 -0
- connections_export-0.1.0/connections_export/config.py +376 -0
- connections_export-0.1.0/connections_export/crawler/__init__.py +33 -0
- connections_export-0.1.0/connections_export/crawler/apps/__init__.py +27 -0
- connections_export-0.1.0/connections_export/crawler/apps/_shared.py +70 -0
- connections_export-0.1.0/connections_export/crawler/apps/blogs.py +517 -0
- connections_export-0.1.0/connections_export/crawler/apps/community.py +82 -0
- connections_export-0.1.0/connections_export/crawler/apps/files.py +201 -0
- connections_export-0.1.0/connections_export/crawler/apps/forums.py +517 -0
- connections_export-0.1.0/connections_export/crawler/apps/rich_content.py +237 -0
- connections_export-0.1.0/connections_export/crawler/apps/wikis.py +561 -0
- connections_export-0.1.0/connections_export/crawler/assets.py +227 -0
- connections_export-0.1.0/connections_export/crawler/author_plan.py +157 -0
- connections_export-0.1.0/connections_export/crawler/community.py +500 -0
- connections_export-0.1.0/connections_export/crawler/crawl.py +59 -0
- connections_export-0.1.0/connections_export/crawler/dispatch.py +143 -0
- connections_export-0.1.0/connections_export/crawler/engine.py +715 -0
- connections_export-0.1.0/connections_export/crawler/events.py +311 -0
- connections_export-0.1.0/connections_export/crawler/provenance.py +118 -0
- connections_export-0.1.0/connections_export/crawler/report.py +48 -0
- connections_export-0.1.0/connections_export/crawler/session.py +52 -0
- connections_export-0.1.0/connections_export/derive/__init__.py +48 -0
- connections_export-0.1.0/connections_export/derive/apps/_shared.py +354 -0
- connections_export-0.1.0/connections_export/derive/apps/blogs.py +281 -0
- connections_export-0.1.0/connections_export/derive/apps/communities.py +105 -0
- connections_export-0.1.0/connections_export/derive/apps/files.py +151 -0
- connections_export-0.1.0/connections_export/derive/apps/forums.py +461 -0
- connections_export-0.1.0/connections_export/derive/apps/rich_content.py +143 -0
- connections_export-0.1.0/connections_export/derive/apps/wikis.py +291 -0
- connections_export-0.1.0/connections_export/derive/assemble.py +459 -0
- connections_export-0.1.0/connections_export/derive/assets.py +65 -0
- connections_export-0.1.0/connections_export/derive/author_filter.py +126 -0
- connections_export-0.1.0/connections_export/derive/crosslink.py +103 -0
- connections_export-0.1.0/connections_export/derive/index.py +529 -0
- connections_export-0.1.0/connections_export/derive/links.py +184 -0
- connections_export-0.1.0/connections_export/derive/model.py +477 -0
- connections_export-0.1.0/connections_export/derive/scope.py +312 -0
- connections_export-0.1.0/connections_export/derive/traverse.py +92 -0
- connections_export-0.1.0/connections_export/fakeserver/__init__.py +29 -0
- connections_export-0.1.0/connections_export/fakeserver/app.py +1034 -0
- connections_export-0.1.0/connections_export/fakeserver/atom.py +1182 -0
- connections_export-0.1.0/connections_export/fakeserver/content.py +3464 -0
- connections_export-0.1.0/connections_export/fakeserver/faults.py +39 -0
- connections_export-0.1.0/connections_export/fakeserver/model.py +540 -0
- connections_export-0.1.0/connections_export/fakeserver/navjson.py +122 -0
- connections_export-0.1.0/connections_export/fakeserver/prototype.py +815 -0
- connections_export-0.1.0/connections_export/fakeserver/searchfeed.py +76 -0
- connections_export-0.1.0/connections_export/fakeserver/synth.py +1329 -0
- connections_export-0.1.0/connections_export/gui/__init__.py +10 -0
- connections_export-0.1.0/connections_export/gui/app.py +220 -0
- connections_export-0.1.0/connections_export/gui/archives.py +512 -0
- connections_export-0.1.0/connections_export/gui/bridge.py +38 -0
- connections_export-0.1.0/connections_export/gui/demo.py +567 -0
- connections_export-0.1.0/connections_export/gui/events.py +167 -0
- connections_export-0.1.0/connections_export/gui/manual.py +68 -0
- connections_export-0.1.0/connections_export/gui/model_source.py +286 -0
- connections_export-0.1.0/connections_export/gui/requests.py +179 -0
- connections_export-0.1.0/connections_export/gui/routes/_lookup.py +81 -0
- connections_export-0.1.0/connections_export/gui/routes/archives.py +552 -0
- connections_export-0.1.0/connections_export/gui/routes/lookup.py +578 -0
- connections_export-0.1.0/connections_export/gui/routes/model.py +118 -0
- connections_export-0.1.0/connections_export/gui/routes/pdf.py +334 -0
- connections_export-0.1.0/connections_export/gui/routes/run.py +1161 -0
- connections_export-0.1.0/connections_export/gui/routes/settings.py +173 -0
- connections_export-0.1.0/connections_export/gui/routes/static.py +208 -0
- connections_export-0.1.0/connections_export/gui/security.py +96 -0
- connections_export-0.1.0/connections_export/gui/settings_store.py +90 -0
- connections_export-0.1.0/connections_export/gui/static/console.css +1598 -0
- connections_export-0.1.0/connections_export/gui/static/console.html +658 -0
- connections_export-0.1.0/connections_export/gui/static/console.js +7156 -0
- connections_export-0.1.0/connections_export/gui/static/vendor/pdf.min.LICENSE +193 -0
- connections_export-0.1.0/connections_export/gui/static/vendor/pdf.min.mjs +28 -0
- connections_export-0.1.0/connections_export/gui/static/vendor/pdf.worker.min.mjs +28 -0
- connections_export-0.1.0/connections_export/gui/static/vendor/pdfjs-boot.mjs +23 -0
- connections_export-0.1.0/connections_export/gui/support.py +463 -0
- connections_export-0.1.0/connections_export/gui/wiki_url.py +353 -0
- connections_export-0.1.0/connections_export/http/__init__.py +32 -0
- connections_export-0.1.0/connections_export/http/auth.py +167 -0
- connections_export-0.1.0/connections_export/http/client.py +210 -0
- connections_export-0.1.0/connections_export/http/results.py +52 -0
- connections_export-0.1.0/connections_export/identity.py +26 -0
- connections_export-0.1.0/connections_export/ingest/__init__.py +8 -0
- connections_export-0.1.0/connections_export/ingest/obsidian.py +419 -0
- connections_export-0.1.0/connections_export/interchange/__init__.py +21 -0
- connections_export-0.1.0/connections_export/interchange/filenames.py +206 -0
- connections_export-0.1.0/connections_export/interchange/manifest.py +218 -0
- connections_export-0.1.0/connections_export/interchange/package.py +250 -0
- connections_export-0.1.0/connections_export/paging.py +156 -0
- connections_export-0.1.0/connections_export/pdf/__init__.py +36 -0
- connections_export-0.1.0/connections_export/pdf/browser.py +300 -0
- connections_export-0.1.0/connections_export/pdf/browser_fidelity.py +255 -0
- connections_export-0.1.0/connections_export/pdf/html.py +1138 -0
- connections_export-0.1.0/connections_export/pdf/live.py +329 -0
- connections_export-0.1.0/connections_export/pdf/marks.py +350 -0
- connections_export-0.1.0/connections_export/pdf/paged.py +309 -0
- connections_export-0.1.0/connections_export/pdf/sample.py +75 -0
- connections_export-0.1.0/connections_export/pdf/vendor/paged.polyfill.LICENSE +29 -0
- connections_export-0.1.0/connections_export/pdf/vendor/paged.polyfill.js +33251 -0
- connections_export-0.1.0/connections_export/probes/__init__.py +26 -0
- connections_export-0.1.0/connections_export/probes/files_since.py +222 -0
- connections_export-0.1.0/connections_export/quick.py +205 -0
- connections_export-0.1.0/connections_export/sbom.py +748 -0
- connections_export-0.1.0/docs/manual.md +457 -0
- connections_export-0.1.0/docs/reference/architecture.md +485 -0
- connections_export-0.1.0/docs/reference/interchange-format.md +977 -0
- connections_export-0.1.0/justfile +45 -0
- connections_export-0.1.0/licenses/overrides/README.md +25 -0
- connections_export-0.1.0/pyproject.toml +151 -0
- connections_export-0.1.0/tests/__init__.py +0 -0
- connections_export-0.1.0/tests/_hostcheck.py +183 -0
- connections_export-0.1.0/tests/adapters/__init__.py +0 -0
- connections_export-0.1.0/tests/adapters/fixtures/blogs_entries_feed.xml +67 -0
- connections_export-0.1.0/tests/adapters/fixtures/blogs_entry_comments_feed.xml +39 -0
- connections_export-0.1.0/tests/adapters/fixtures/blogs_list_feed.xml +26 -0
- connections_export-0.1.0/tests/adapters/fixtures/forums_replies_feed_flat_chain.xml +58 -0
- connections_export-0.1.0/tests/adapters/fixtures/forums_topics_feed.xml +47 -0
- connections_export-0.1.0/tests/adapters/fixtures/nav_feed_real_80.json +26 -0
- connections_export-0.1.0/tests/adapters/fixtures/nav_feed_verbatim.json +18 -0
- connections_export-0.1.0/tests/adapters/fixtures/navigation_entry_verbatim.xml +10 -0
- connections_export-0.1.0/tests/adapters/fixtures/page_entry_reference_fields.xml +67 -0
- connections_export-0.1.0/tests/adapters/fixtures/page_entry_scheme_sn_type.xml +36 -0
- connections_export-0.1.0/tests/adapters/test_atom.py +323 -0
- connections_export-0.1.0/tests/adapters/test_blogs.py +170 -0
- connections_export-0.1.0/tests/adapters/test_directory.py +74 -0
- connections_export-0.1.0/tests/adapters/test_feeds_artifacts.py +139 -0
- connections_export-0.1.0/tests/adapters/test_files.py +117 -0
- connections_export-0.1.0/tests/adapters/test_forums.py +362 -0
- connections_export-0.1.0/tests/adapters/test_hierarchy_sources.py +57 -0
- connections_export-0.1.0/tests/adapters/test_model.py +225 -0
- connections_export-0.1.0/tests/adapters/test_navigation.py +117 -0
- connections_export-0.1.0/tests/adapters/test_page_entry.py +111 -0
- connections_export-0.1.0/tests/adapters/test_profiles.py +44 -0
- connections_export-0.1.0/tests/adapters/test_resilience.py +90 -0
- connections_export-0.1.0/tests/adapters/test_roundtrip.py +145 -0
- connections_export-0.1.0/tests/adapters/test_rte.py +222 -0
- connections_export-0.1.0/tests/adapters/test_rte_against_fake.py +104 -0
- connections_export-0.1.0/tests/adapters/test_rte_layout_shapes.py +138 -0
- connections_export-0.1.0/tests/adapters/test_search.py +94 -0
- connections_export-0.1.0/tests/adapters/test_since_parameters.py +211 -0
- connections_export-0.1.0/tests/adapters/test_subcommunities.py +79 -0
- connections_export-0.1.0/tests/adapters/test_urls.py +107 -0
- connections_export-0.1.0/tests/archive/__init__.py +0 -0
- connections_export-0.1.0/tests/archive/conftest.py +17 -0
- connections_export-0.1.0/tests/archive/test_blobs.py +61 -0
- connections_export-0.1.0/tests/archive/test_capability_roundtrip.py +162 -0
- connections_export-0.1.0/tests/archive/test_feeds.py +86 -0
- connections_export-0.1.0/tests/archive/test_open_source.py +77 -0
- connections_export-0.1.0/tests/archive/test_records.py +210 -0
- connections_export-0.1.0/tests/archive/test_redact.py +67 -0
- connections_export-0.1.0/tests/archive/test_report.py +82 -0
- connections_export-0.1.0/tests/archive/test_run_metadata.py +50 -0
- connections_export-0.1.0/tests/archive/test_run_provenance.py +99 -0
- connections_export-0.1.0/tests/archive/test_seen_urls.py +44 -0
- connections_export-0.1.0/tests/archive/test_source.py +75 -0
- connections_export-0.1.0/tests/archive/test_store.py +211 -0
- connections_export-0.1.0/tests/archive/test_zip_source.py +113 -0
- connections_export-0.1.0/tests/cli/test_compare_author.py +18 -0
- connections_export-0.1.0/tests/cli/test_it_calls_itself_by_its_own_name.py +57 -0
- connections_export-0.1.0/tests/cli/test_licenses_cmd.py +53 -0
- connections_export-0.1.0/tests/cli/test_open_cmd.py +67 -0
- connections_export-0.1.0/tests/cli/test_the_bare_launch_finds_a_port.py +79 -0
- connections_export-0.1.0/tests/cli/test_the_commands_agree.py +52 -0
- connections_export-0.1.0/tests/cli/test_zip_cli.py +65 -0
- connections_export-0.1.0/tests/compare/__init__.py +0 -0
- connections_export-0.1.0/tests/compare/test_author_compare.py +127 -0
- connections_export-0.1.0/tests/compare/test_search_roundtrip.py +41 -0
- connections_export-0.1.0/tests/config/__init__.py +0 -0
- connections_export-0.1.0/tests/config/test_cli.py +90 -0
- connections_export-0.1.0/tests/config/test_discovery.py +96 -0
- connections_export-0.1.0/tests/config/test_env_parsing.py +138 -0
- connections_export-0.1.0/tests/config/test_example_toml.py +56 -0
- connections_export-0.1.0/tests/config/test_model.py +130 -0
- connections_export-0.1.0/tests/config/test_precedence.py +86 -0
- connections_export-0.1.0/tests/config/test_secrets.py +121 -0
- connections_export-0.1.0/tests/config/test_the_sample_config_matches_the_defaults.py +72 -0
- connections_export-0.1.0/tests/conftest.py +50 -0
- connections_export-0.1.0/tests/crawler/__init__.py +0 -0
- connections_export-0.1.0/tests/crawler/conftest.py +113 -0
- connections_export-0.1.0/tests/crawler/test_assets.py +152 -0
- connections_export-0.1.0/tests/crawler/test_assets_in_crawl.py +335 -0
- connections_export-0.1.0/tests/crawler/test_author_plan.py +127 -0
- connections_export-0.1.0/tests/crawler/test_author_two_pass.py +293 -0
- connections_export-0.1.0/tests/crawler/test_blog_comment_index.py +320 -0
- connections_export-0.1.0/tests/crawler/test_blogs_forums.py +578 -0
- connections_export-0.1.0/tests/crawler/test_cache_policy.py +183 -0
- connections_export-0.1.0/tests/crawler/test_cli.py +197 -0
- connections_export-0.1.0/tests/crawler/test_community_discovery.py +197 -0
- connections_export-0.1.0/tests/crawler/test_community_logo_capture.py +114 -0
- connections_export-0.1.0/tests/crawler/test_dispatch.py +154 -0
- connections_export-0.1.0/tests/crawler/test_events.py +126 -0
- connections_export-0.1.0/tests/crawler/test_every_capture_can_anchor_an_update.py +112 -0
- connections_export-0.1.0/tests/crawler/test_every_discovery_concludes.py +105 -0
- connections_export-0.1.0/tests/crawler/test_failures_and_retries.py +126 -0
- connections_export-0.1.0/tests/crawler/test_fetch_modes.py +53 -0
- connections_export-0.1.0/tests/crawler/test_file_link_document_id.py +42 -0
- connections_export-0.1.0/tests/crawler/test_file_link_scan.py +63 -0
- connections_export-0.1.0/tests/crawler/test_files_author_filter.py +121 -0
- connections_export-0.1.0/tests/crawler/test_files_crawl.py +140 -0
- connections_export-0.1.0/tests/crawler/test_into_archive.py +89 -0
- connections_export-0.1.0/tests/crawler/test_into_archive_end_to_end.py +92 -0
- connections_export-0.1.0/tests/crawler/test_linked_file_capture.py +111 -0
- connections_export-0.1.0/tests/crawler/test_pagination.py +417 -0
- connections_export-0.1.0/tests/crawler/test_recheck_comments.py +173 -0
- connections_export-0.1.0/tests/crawler/test_report.py +54 -0
- connections_export-0.1.0/tests/crawler/test_report_orphans.py +106 -0
- connections_export-0.1.0/tests/crawler/test_resumability.py +86 -0
- connections_export-0.1.0/tests/crawler/test_roundtrip.py +526 -0
- connections_export-0.1.0/tests/crawler/test_rte_crawl.py +170 -0
- connections_export-0.1.0/tests/crawler/test_run_provenance.py +126 -0
- connections_export-0.1.0/tests/crawler/test_run_provenance_written.py +200 -0
- connections_export-0.1.0/tests/crawler/test_scoping.py +123 -0
- connections_export-0.1.0/tests/crawler/test_stop_ends_the_run.py +130 -0
- connections_export-0.1.0/tests/crawler/test_traversal.py +130 -0
- connections_export-0.1.0/tests/crawler/test_update_cutoff_is_real.py +84 -0
- connections_export-0.1.0/tests/crawler/test_update_finds_new_pages.py +251 -0
- connections_export-0.1.0/tests/crawler/test_update_run.py +339 -0
- connections_export-0.1.0/tests/crawler/test_update_sees_every_change.py +383 -0
- connections_export-0.1.0/tests/crawler/test_version_policy.py +127 -0
- connections_export-0.1.0/tests/crawler/test_wiki_community_attribution.py +66 -0
- connections_export-0.1.0/tests/crawler/test_wiki_page_index.py +145 -0
- connections_export-0.1.0/tests/derive/__init__.py +0 -0
- connections_export-0.1.0/tests/derive/test_assemble.py +594 -0
- connections_export-0.1.0/tests/derive/test_assets.py +135 -0
- connections_export-0.1.0/tests/derive/test_author_filter.py +131 -0
- connections_export-0.1.0/tests/derive/test_blogs_forums_assemble.py +397 -0
- connections_export-0.1.0/tests/derive/test_blogs_forums_model.py +87 -0
- connections_export-0.1.0/tests/derive/test_blogs_forums_roundtrip.py +233 -0
- connections_export-0.1.0/tests/derive/test_community_logo.py +78 -0
- connections_export-0.1.0/tests/derive/test_crosslink.py +126 -0
- connections_export-0.1.0/tests/derive/test_crosslink_files.py +69 -0
- connections_export-0.1.0/tests/derive/test_file_absence_reason.py +84 -0
- connections_export-0.1.0/tests/derive/test_file_link_resolution.py +62 -0
- connections_export-0.1.0/tests/derive/test_files_derive.py +126 -0
- connections_export-0.1.0/tests/derive/test_highlights_links_resolve.py +108 -0
- connections_export-0.1.0/tests/derive/test_index.py +178 -0
- connections_export-0.1.0/tests/derive/test_index_source.py +40 -0
- connections_export-0.1.0/tests/derive/test_links.py +182 -0
- connections_export-0.1.0/tests/derive/test_model.py +276 -0
- connections_export-0.1.0/tests/derive/test_pagination_cap.py +151 -0
- connections_export-0.1.0/tests/derive/test_paginator_contract.py +162 -0
- connections_export-0.1.0/tests/derive/test_phased_crosslinks.py +154 -0
- connections_export-0.1.0/tests/derive/test_reconstructed_reads_announce_themselves.py +230 -0
- connections_export-0.1.0/tests/derive/test_replays_the_recorded_walk.py +138 -0
- connections_export-0.1.0/tests/derive/test_roundtrip.py +254 -0
- connections_export-0.1.0/tests/derive/test_rte_derive.py +108 -0
- connections_export-0.1.0/tests/derive/test_run_provenance.py +155 -0
- connections_export-0.1.0/tests/derive/test_scope.py +166 -0
- connections_export-0.1.0/tests/derive/test_scope_files.py +109 -0
- connections_export-0.1.0/tests/derive/test_scope_rich_content_page.py +105 -0
- connections_export-0.1.0/tests/derive/test_scoped_derive.py +91 -0
- connections_export-0.1.0/tests/derive/test_select_scope.py +112 -0
- connections_export-0.1.0/tests/derive/test_self_describing.py +148 -0
- connections_export-0.1.0/tests/derive/test_traverse.py +38 -0
- connections_export-0.1.0/tests/derive/test_update_pages_are_read_back.py +92 -0
- connections_export-0.1.0/tests/derive/test_zip_parity.py +63 -0
- connections_export-0.1.0/tests/derive/test_zip_without_feeds_record.py +88 -0
- connections_export-0.1.0/tests/fakeserver/__init__.py +0 -0
- connections_export-0.1.0/tests/fakeserver/conftest.py +20 -0
- connections_export-0.1.0/tests/fakeserver/test_app.py +229 -0
- connections_export-0.1.0/tests/fakeserver/test_atom.py +208 -0
- connections_export-0.1.0/tests/fakeserver/test_blogs_forums.py +699 -0
- connections_export-0.1.0/tests/fakeserver/test_body_html.py +44 -0
- connections_export-0.1.0/tests/fakeserver/test_capability_integration.py +115 -0
- connections_export-0.1.0/tests/fakeserver/test_faults.py +125 -0
- connections_export-0.1.0/tests/fakeserver/test_model.py +172 -0
- connections_export-0.1.0/tests/fakeserver/test_navjson.py +89 -0
- connections_export-0.1.0/tests/fakeserver/test_one_person_one_id.py +182 -0
- connections_export-0.1.0/tests/fakeserver/test_prototype.py +153 -0
- connections_export-0.1.0/tests/fakeserver/test_since_routes.py +131 -0
- connections_export-0.1.0/tests/fakeserver/test_subcommunities_route.py +65 -0
- connections_export-0.1.0/tests/fakeserver/test_synth.py +231 -0
- connections_export-0.1.0/tests/fakeserver/test_visible_wave.py +118 -0
- connections_export-0.1.0/tests/fakeserver/test_waves.py +82 -0
- connections_export-0.1.0/tests/gui/__init__.py +0 -0
- connections_export-0.1.0/tests/gui/_served_assets.py +48 -0
- connections_export-0.1.0/tests/gui/test_a_run_ends_once.py +98 -0
- connections_export-0.1.0/tests/gui/test_app.py +323 -0
- connections_export-0.1.0/tests/gui/test_archive_ledger.py +101 -0
- connections_export-0.1.0/tests/gui/test_archive_naming.py +135 -0
- connections_export-0.1.0/tests/gui/test_archive_only_is_reachable.py +201 -0
- connections_export-0.1.0/tests/gui/test_archive_only_mode.py +157 -0
- connections_export-0.1.0/tests/gui/test_archive_rename.py +92 -0
- connections_export-0.1.0/tests/gui/test_archives.py +122 -0
- connections_export-0.1.0/tests/gui/test_archives_base_config.py +60 -0
- connections_export-0.1.0/tests/gui/test_archives_drop_zone.py +57 -0
- connections_export-0.1.0/tests/gui/test_archives_location.py +95 -0
- connections_export-0.1.0/tests/gui/test_author_filter_wiring.py +71 -0
- connections_export-0.1.0/tests/gui/test_author_picker_merges.py +126 -0
- connections_export-0.1.0/tests/gui/test_author_picker_scrolls.py +59 -0
- connections_export-0.1.0/tests/gui/test_capture_collision.py +80 -0
- connections_export-0.1.0/tests/gui/test_cli_default_launch.py +123 -0
- connections_export-0.1.0/tests/gui/test_cli_echo.py +145 -0
- connections_export-0.1.0/tests/gui/test_cli_serve.py +123 -0
- connections_export-0.1.0/tests/gui/test_console_real_update.py +102 -0
- connections_export-0.1.0/tests/gui/test_demo.py +167 -0
- connections_export-0.1.0/tests/gui/test_demo_chip_ingest.py +248 -0
- connections_export-0.1.0/tests/gui/test_demo_community.py +222 -0
- connections_export-0.1.0/tests/gui/test_demo_discovery_parity.py +117 -0
- connections_export-0.1.0/tests/gui/test_demo_files.py +96 -0
- connections_export-0.1.0/tests/gui/test_demo_rich_content.py +76 -0
- connections_export-0.1.0/tests/gui/test_demo_scoping.py +85 -0
- connections_export-0.1.0/tests/gui/test_demo_update.py +73 -0
- connections_export-0.1.0/tests/gui/test_demo_urls.py +181 -0
- connections_export-0.1.0/tests/gui/test_drawer_content_after_a_run_finishes.py +51 -0
- connections_export-0.1.0/tests/gui/test_drop_an_archive.py +51 -0
- connections_export-0.1.0/tests/gui/test_drop_url_vs_archive.py +44 -0
- connections_export-0.1.0/tests/gui/test_event_serialization.py +63 -0
- connections_export-0.1.0/tests/gui/test_events.py +242 -0
- connections_export-0.1.0/tests/gui/test_feed_info.py +178 -0
- connections_export-0.1.0/tests/gui/test_file_link_rendering_static.py +29 -0
- connections_export-0.1.0/tests/gui/test_files_surfaces.py +115 -0
- connections_export-0.1.0/tests/gui/test_identity_chip.py +109 -0
- connections_export-0.1.0/tests/gui/test_in_export_links_reach_every_component.py +52 -0
- connections_export-0.1.0/tests/gui/test_ledger_endpoint.py +124 -0
- connections_export-0.1.0/tests/gui/test_ledger_static.py +144 -0
- connections_export-0.1.0/tests/gui/test_licenses_page.py +167 -0
- connections_export-0.1.0/tests/gui/test_live_endpoints.py +101 -0
- connections_export-0.1.0/tests/gui/test_live_ingest_parity_static.py +202 -0
- connections_export-0.1.0/tests/gui/test_live_model.py +141 -0
- connections_export-0.1.0/tests/gui/test_live_reader_static.py +70 -0
- connections_export-0.1.0/tests/gui/test_live_tree_groups_by_community.py +52 -0
- connections_export-0.1.0/tests/gui/test_manual.py +108 -0
- connections_export-0.1.0/tests/gui/test_manual_documents_the_cli.py +52 -0
- connections_export-0.1.0/tests/gui/test_manual_interchange_spec.py +147 -0
- connections_export-0.1.0/tests/gui/test_manual_source.py +58 -0
- connections_export-0.1.0/tests/gui/test_model_endpoints.py +349 -0
- connections_export-0.1.0/tests/gui/test_model_source.py +243 -0
- connections_export-0.1.0/tests/gui/test_multi_app_setup.py +120 -0
- connections_export-0.1.0/tests/gui/test_multi_community_capture.py +99 -0
- connections_export-0.1.0/tests/gui/test_multi_community_picker_static.py +97 -0
- connections_export-0.1.0/tests/gui/test_multi_community_request.py +73 -0
- connections_export-0.1.0/tests/gui/test_open_archive_api.py +352 -0
- connections_export-0.1.0/tests/gui/test_open_external_archive.py +203 -0
- connections_export-0.1.0/tests/gui/test_overview_actions.py +55 -0
- connections_export-0.1.0/tests/gui/test_paste_does_not_duplicate.py +133 -0
- connections_export-0.1.0/tests/gui/test_pdf_appearance.py +136 -0
- connections_export-0.1.0/tests/gui/test_pdf_endpoint.py +243 -0
- connections_export-0.1.0/tests/gui/test_reader_blogs_forums_static.py +218 -0
- connections_export-0.1.0/tests/gui/test_reader_community_grouping_static.py +57 -0
- connections_export-0.1.0/tests/gui/test_reader_files_static.py +95 -0
- connections_export-0.1.0/tests/gui/test_reader_nav_stays_reachable.py +162 -0
- connections_export-0.1.0/tests/gui/test_reader_pure_js.py +445 -0
- connections_export-0.1.0/tests/gui/test_reader_rich_content_static.py +152 -0
- connections_export-0.1.0/tests/gui/test_reader_static.py +262 -0
- connections_export-0.1.0/tests/gui/test_recent_archives_cap.py +66 -0
- connections_export-0.1.0/tests/gui/test_rich_content_preview_endpoint.py +92 -0
- connections_export-0.1.0/tests/gui/test_saved_delay_reaches_the_run.py +111 -0
- connections_export-0.1.0/tests/gui/test_security.py +161 -0
- connections_export-0.1.0/tests/gui/test_select_screen_while_running.py +58 -0
- connections_export-0.1.0/tests/gui/test_settings_panel.py +152 -0
- connections_export-0.1.0/tests/gui/test_settings_persist.py +79 -0
- connections_export-0.1.0/tests/gui/test_setup_screen.py +211 -0
- connections_export-0.1.0/tests/gui/test_shell_static.py +963 -0
- connections_export-0.1.0/tests/gui/test_static_assets.py +56 -0
- connections_export-0.1.0/tests/gui/test_subcommunities_endpoint.py +63 -0
- connections_export-0.1.0/tests/gui/test_superseded_archives.py +94 -0
- connections_export-0.1.0/tests/gui/test_the_demo_is_signed_in.py +133 -0
- connections_export-0.1.0/tests/gui/test_throughput_is_honest.py +68 -0
- connections_export-0.1.0/tests/gui/test_update_counts_are_honest.py +89 -0
- connections_export-0.1.0/tests/gui/test_update_never_becomes_a_demo.py +168 -0
- connections_export-0.1.0/tests/gui/test_update_stays_inside_the_archive.py +96 -0
- connections_export-0.1.0/tests/gui/test_wiki_url.py +173 -0
- connections_export-0.1.0/tests/gui/test_zip_archives_listing.py +83 -0
- connections_export-0.1.0/tests/gui/test_zip_is_read_only.py +143 -0
- connections_export-0.1.0/tests/gui/test_zip_model_source.py +96 -0
- connections_export-0.1.0/tests/http/__init__.py +0 -0
- connections_export-0.1.0/tests/http/conftest.py +39 -0
- connections_export-0.1.0/tests/http/test_auth.py +96 -0
- connections_export-0.1.0/tests/http/test_capability_integration.py +63 -0
- connections_export-0.1.0/tests/http/test_cookies.py +24 -0
- connections_export-0.1.0/tests/http/test_fetch.py +46 -0
- connections_export-0.1.0/tests/http/test_results.py +105 -0
- connections_export-0.1.0/tests/http/test_retry.py +212 -0
- connections_export-0.1.0/tests/http/test_throttle.py +85 -0
- connections_export-0.1.0/tests/http/test_tls.py +45 -0
- connections_export-0.1.0/tests/ingest/__init__.py +0 -0
- connections_export-0.1.0/tests/ingest/test_obsidian.py +241 -0
- connections_export-0.1.0/tests/interchange/__init__.py +0 -0
- connections_export-0.1.0/tests/interchange/test_blogs_forums_package.py +105 -0
- connections_export-0.1.0/tests/interchange/test_end_to_end.py +141 -0
- connections_export-0.1.0/tests/interchange/test_filenames.py +185 -0
- connections_export-0.1.0/tests/interchange/test_files_package.py +133 -0
- connections_export-0.1.0/tests/interchange/test_hcl_agnostic.py +122 -0
- connections_export-0.1.0/tests/interchange/test_it_names_where_it_came_from.py +134 -0
- connections_export-0.1.0/tests/interchange/test_manifest.py +266 -0
- connections_export-0.1.0/tests/interchange/test_manifest_counts_all_apps.py +103 -0
- connections_export-0.1.0/tests/interchange/test_package.py +286 -0
- connections_export-0.1.0/tests/interchange/test_package_declares_what_it_contains.py +104 -0
- connections_export-0.1.0/tests/pdf/__init__.py +0 -0
- connections_export-0.1.0/tests/pdf/test_blogs_forums.py +244 -0
- connections_export-0.1.0/tests/pdf/test_browser.py +65 -0
- connections_export-0.1.0/tests/pdf/test_browser_fidelity.py +97 -0
- connections_export-0.1.0/tests/pdf/test_browser_launch.py +73 -0
- connections_export-0.1.0/tests/pdf/test_browser_quiet.py +36 -0
- connections_export-0.1.0/tests/pdf/test_cli_pdf.py +58 -0
- connections_export-0.1.0/tests/pdf/test_end_to_end.py +86 -0
- connections_export-0.1.0/tests/pdf/test_files_render.py +139 -0
- connections_export-0.1.0/tests/pdf/test_html.py +527 -0
- connections_export-0.1.0/tests/pdf/test_links_showcase.py +47 -0
- connections_export-0.1.0/tests/pdf/test_live.py +29 -0
- connections_export-0.1.0/tests/pdf/test_live_merge.py +106 -0
- connections_export-0.1.0/tests/pdf/test_marks.py +90 -0
- connections_export-0.1.0/tests/pdf/test_marks_logo.py +147 -0
- connections_export-0.1.0/tests/pdf/test_marks_surface.py +191 -0
- connections_export-0.1.0/tests/pdf/test_paged.py +93 -0
- connections_export-0.1.0/tests/pdf/test_rich_content_render.py +103 -0
- connections_export-0.1.0/tests/pdf/test_style_surface.py +65 -0
- connections_export-0.1.0/tests/pdf/test_template.py +356 -0
- connections_export-0.1.0/tests/probes/__init__.py +0 -0
- connections_export-0.1.0/tests/probes/test_files_since.py +180 -0
- connections_export-0.1.0/tests/sbom/__init__.py +0 -0
- connections_export-0.1.0/tests/sbom/test_a_package_with_no_code_needs_no_licence.py +71 -0
- connections_export-0.1.0/tests/sbom/test_license_gaps.py +130 -0
- connections_export-0.1.0/tests/sbom/test_sbom.py +252 -0
- connections_export-0.1.0/tests/sbom/test_the_executable_carries_its_documents.py +80 -0
- connections_export-0.1.0/tests/sbom/test_vendored_assets.py +145 -0
- connections_export-0.1.0/tests/test_apps_registry.py +117 -0
- connections_export-0.1.0/tests/test_identity.py +38 -0
- connections_export-0.1.0/tests/test_import_every_module.py +53 -0
- connections_export-0.1.0/tests/test_it_compiles_without_warnings.py +42 -0
- connections_export-0.1.0/tests/test_quick.py +107 -0
- connections_export-0.1.0/tests/test_readme_links.py +89 -0
- connections_export-0.1.0/tests/test_the_suite_leaves_the_user_alone.py +49 -0
- connections_export-0.1.0/tools/build_binary.py +346 -0
- connections_export-0.1.0/tools/write_license_bundle.py +58 -0
- connections_export-0.1.0/uv.lock +1079 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
name: Binaries
|
|
2
|
+
|
|
3
|
+
# Single-file executables for people who will not install Python, attached to
|
|
4
|
+
# the GitHub Release on `v*`.
|
|
5
|
+
#
|
|
6
|
+
# When they are built depends on what it costs. Four platform builds -- two of
|
|
7
|
+
# them macOS -- is the most expensive thing here, and on a private repository
|
|
8
|
+
# macOS bills at 10x, so there they run on a tag or on request only. On a
|
|
9
|
+
# public repository standard runners are free, so they run on every push to
|
|
10
|
+
# main as well: a packaging fault found on a push is found before a tag exists,
|
|
11
|
+
# and a tag is a bad place to discover one.
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
branches: [main]
|
|
15
|
+
tags: ["v*"]
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
# Free on a public repo, so every push; metered on a private one, so tags
|
|
24
|
+
# and explicit requests only.
|
|
25
|
+
if: >-
|
|
26
|
+
github.event.repository.private == false
|
|
27
|
+
|| startsWith(github.ref, 'refs/tags/v')
|
|
28
|
+
|| github.event_name == 'workflow_dispatch'
|
|
29
|
+
strategy:
|
|
30
|
+
fail-fast: false
|
|
31
|
+
matrix:
|
|
32
|
+
include:
|
|
33
|
+
- os: ubuntu-latest
|
|
34
|
+
target: linux-x86_64
|
|
35
|
+
- os: macos-latest
|
|
36
|
+
target: macos-arm64
|
|
37
|
+
# Intel macOS. NOT `macos-13` -- GitHub retired those runners, and a
|
|
38
|
+
# retired label does not fail, it queues forever: the first two runs
|
|
39
|
+
# here sat unassigned for over an hour while the other three
|
|
40
|
+
# platforms had long since finished, and the run never reported.
|
|
41
|
+
- os: macos-15-intel
|
|
42
|
+
target: macos-x86_64
|
|
43
|
+
- os: windows-latest
|
|
44
|
+
target: windows-x86_64
|
|
45
|
+
runs-on: ${{ matrix.os }}
|
|
46
|
+
# A PyInstaller build is minutes, not tens of minutes. The cap turns a hung
|
|
47
|
+
# or unservable cell into a failure someone can see.
|
|
48
|
+
timeout-minutes: 30
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
- uses: astral-sh/setup-uv@v5
|
|
52
|
+
with:
|
|
53
|
+
# PyInstaller embeds the interpreter it builds against, so the
|
|
54
|
+
# executable IS this Python version. Pinned rather than matrixed:
|
|
55
|
+
# one binary per platform, not one per Python.
|
|
56
|
+
python-version: "3.13"
|
|
57
|
+
enable-cache: true
|
|
58
|
+
|
|
59
|
+
# `requests-kerberos` pulls `krb5`, which builds against system Kerberos
|
|
60
|
+
# headers that the Linux runners do not ship -- `--all-extras` therefore
|
|
61
|
+
# fails at resolve time on Linux alone. Same wall a Linux user installing
|
|
62
|
+
# `connections-export[sspi]` hits, so the fix belongs in the docs too.
|
|
63
|
+
- name: Kerberos headers (Linux only, for the sspi extra)
|
|
64
|
+
if: runner.os == 'Linux'
|
|
65
|
+
run: sudo apt-get update -qq && sudo apt-get install -y -qq libkrb5-dev
|
|
66
|
+
|
|
67
|
+
# `--all-extras` is what puts markdownify in the environment, and so in
|
|
68
|
+
# the bundle: the Obsidian ingester imports it lazily, which means a
|
|
69
|
+
# bundle built without it still starts and still passes `--help`.
|
|
70
|
+
# build_binary.py asserts the module is present rather than trusting it.
|
|
71
|
+
- name: Build executable
|
|
72
|
+
run: uv run --all-extras --python 3.13 python tools/build_binary.py --dist dist
|
|
73
|
+
|
|
74
|
+
# The point of the executable is that a user with no Python and no
|
|
75
|
+
# install can run it, so the smoke test uses the binary ALONE -- no uv,
|
|
76
|
+
# no source, no helper script. `--demo` renders the synthetic deployment
|
|
77
|
+
# that ships inside it; PDF rendering is the one thing that reaches
|
|
78
|
+
# outside, for a Chromium-based browser. On Windows that is the
|
|
79
|
+
# preinstalled Edge, which is the path most users will take.
|
|
80
|
+
- name: Smoke-test the executable, standalone
|
|
81
|
+
shell: bash
|
|
82
|
+
run: |
|
|
83
|
+
set -euo pipefail
|
|
84
|
+
exe="dist/connections-export"
|
|
85
|
+
[ "${{ runner.os }}" = "Windows" ] && exe="dist/connections-export.exe"
|
|
86
|
+
"$exe" --help > /dev/null
|
|
87
|
+
"$exe" crawl --demo --output-dir demo-archive
|
|
88
|
+
"$exe" pdf --demo --output smoke.pdf
|
|
89
|
+
# A PDF header and a plausible size: the demo set renders ~100 pages,
|
|
90
|
+
# so anything tiny means it rendered nothing and still exited 0.
|
|
91
|
+
head -c 5 smoke.pdf | grep -q '%PDF' || { echo "not a PDF"; exit 1; }
|
|
92
|
+
size=$(wc -c < smoke.pdf)
|
|
93
|
+
echo "smoke.pdf: $size bytes on ${{ matrix.target }}"
|
|
94
|
+
[ "$size" -gt 100000 ] || { echo "PDF suspiciously small"; exit 1; }
|
|
95
|
+
rm -rf demo-archive smoke.pdf
|
|
96
|
+
|
|
97
|
+
- name: Name the artifact after the version and platform
|
|
98
|
+
shell: bash
|
|
99
|
+
run: |
|
|
100
|
+
# `uv run --no-project`, not bare `python`: setup-uv provisions a
|
|
101
|
+
# managed interpreter without guaranteeing `python` on PATH, and
|
|
102
|
+
# --no-project skips a pointless re-sync just to read a TOML key.
|
|
103
|
+
version="$(uv run --no-project python -c 'import tomllib; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')"
|
|
104
|
+
src="dist/connections-export"; ext=""
|
|
105
|
+
if [ "${{ runner.os }}" = "Windows" ]; then src="dist/connections-export.exe"; ext=".exe"; fi
|
|
106
|
+
mv "$src" "dist/connections-export-${version}-${{ matrix.target }}${ext}"
|
|
107
|
+
ls -la dist/
|
|
108
|
+
|
|
109
|
+
- uses: actions/upload-artifact@v4
|
|
110
|
+
with:
|
|
111
|
+
name: connections-export-${{ matrix.target }}
|
|
112
|
+
path: dist/connections-export-*
|
|
113
|
+
if-no-files-found: error
|
|
114
|
+
# Four executables are ~290 MB a run, and GitHub keeps artifacts for
|
|
115
|
+
# 90 days by default. On a PRIVATE repo that is metered storage: seven
|
|
116
|
+
# runs had accumulated 2 GB against a 500 MB allowance. These are
|
|
117
|
+
# disposable -- rebuildable from any tag -- and on a `v*` tag the
|
|
118
|
+
# release assets are the copy that lasts.
|
|
119
|
+
retention-days: 7
|
|
120
|
+
|
|
121
|
+
attach-to-release:
|
|
122
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
123
|
+
needs: build
|
|
124
|
+
runs-on: ubuntu-latest
|
|
125
|
+
permissions:
|
|
126
|
+
contents: write
|
|
127
|
+
steps:
|
|
128
|
+
- uses: actions/download-artifact@v4
|
|
129
|
+
with:
|
|
130
|
+
path: dist/
|
|
131
|
+
merge-multiple: true
|
|
132
|
+
# `gh release upload` fails if no Release object exists, and release.yml
|
|
133
|
+
# only publishes to PyPI -- pushing a tag does not create one. Create it
|
|
134
|
+
# if absent, then upload; `--clobber` makes a re-run idempotent.
|
|
135
|
+
- name: Attach executables to the release
|
|
136
|
+
env:
|
|
137
|
+
GH_TOKEN: ${{ github.token }}
|
|
138
|
+
run: |
|
|
139
|
+
gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 \
|
|
140
|
+
|| gh release create "$GITHUB_REF_NAME" \
|
|
141
|
+
--repo "$GITHUB_REPOSITORY" \
|
|
142
|
+
--title "$GITHUB_REF_NAME" \
|
|
143
|
+
--generate-notes
|
|
144
|
+
gh release upload "$GITHUB_REF_NAME" dist/* --clobber --repo "$GITHUB_REPOSITORY"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# How much of the matrix runs depends on whether the repository is private,
|
|
4
|
+
# because that is what decides whether it costs anything.
|
|
5
|
+
#
|
|
6
|
+
# public -> everything, on every push. GitHub-hosted standard runners are
|
|
7
|
+
# free for public repositories, so there is no reason to prove
|
|
8
|
+
# less than the whole matrix.
|
|
9
|
+
# private -> Linux only on a push; the full 3 OS x 3 Python matrix on a tag
|
|
10
|
+
# or on request. Minutes are metered there, and metered unevenly:
|
|
11
|
+
# macOS bills at 10x and Windows at 2x, so a nine-cell matrix on
|
|
12
|
+
# every push costs roughly 350 metered minutes and exhausts a
|
|
13
|
+
# modest allowance in a day.
|
|
14
|
+
#
|
|
15
|
+
# Linux alone catches most of it -- a dependency without wheels for a new
|
|
16
|
+
# Python fails at resolve time on any OS -- but the claim that `requires-python`
|
|
17
|
+
# is honoured can only be proved by running there.
|
|
18
|
+
on:
|
|
19
|
+
push:
|
|
20
|
+
branches: [main]
|
|
21
|
+
tags: ["v*"]
|
|
22
|
+
pull_request:
|
|
23
|
+
workflow_dispatch:
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
test:
|
|
27
|
+
strategy:
|
|
28
|
+
fail-fast: false
|
|
29
|
+
matrix:
|
|
30
|
+
python: ["3.12", "3.13", "3.14"]
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: astral-sh/setup-uv@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python }}
|
|
37
|
+
enable-cache: true
|
|
38
|
+
# `requests-kerberos` pulls `krb5`, which builds against system Kerberos
|
|
39
|
+
# headers that the Linux runners do not ship -- `--all-extras` therefore
|
|
40
|
+
# fails at resolve time on Linux alone. Same wall a Linux user installing
|
|
41
|
+
# `connections-export[sspi]` hits, so the fix belongs in the docs too.
|
|
42
|
+
- name: Kerberos headers (Linux only, for the sspi extra)
|
|
43
|
+
if: runner.os == 'Linux'
|
|
44
|
+
run: sudo apt-get update -qq && sudo apt-get install -y -qq libkrb5-dev
|
|
45
|
+
|
|
46
|
+
# No Playwright browsers installed on purpose: the suite runs offline
|
|
47
|
+
# against the in-process fake server; the ~2 browser-PDF tests skip.
|
|
48
|
+
- name: Lint
|
|
49
|
+
run: |
|
|
50
|
+
uv run --all-extras ruff check .
|
|
51
|
+
uv run --all-extras ruff format --check .
|
|
52
|
+
- name: Tests
|
|
53
|
+
run: uv run --all-extras pytest
|
|
54
|
+
|
|
55
|
+
# The expensive half: only before a release, or when asked. macOS bills at
|
|
56
|
+
# 10x and Windows at 2x, so this job costs more than everything else in the
|
|
57
|
+
# repo combined -- which is exactly why it does not run on every push.
|
|
58
|
+
cross-platform:
|
|
59
|
+
# Free on a public repo, so always; metered on a private one, so only
|
|
60
|
+
# when it is worth paying for.
|
|
61
|
+
if: >-
|
|
62
|
+
github.event.repository.private == false
|
|
63
|
+
|| startsWith(github.ref, 'refs/tags/v')
|
|
64
|
+
|| github.event_name == 'workflow_dispatch'
|
|
65
|
+
strategy:
|
|
66
|
+
fail-fast: false
|
|
67
|
+
matrix:
|
|
68
|
+
os: [macos-latest, windows-latest]
|
|
69
|
+
python: ["3.12", "3.13", "3.14"]
|
|
70
|
+
runs-on: ${{ matrix.os }}
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v4
|
|
73
|
+
- uses: astral-sh/setup-uv@v5
|
|
74
|
+
with:
|
|
75
|
+
python-version: ${{ matrix.python }}
|
|
76
|
+
enable-cache: true
|
|
77
|
+
- name: Lint
|
|
78
|
+
run: |
|
|
79
|
+
uv run --all-extras ruff check .
|
|
80
|
+
uv run --all-extras ruff format --check .
|
|
81
|
+
- name: Tests
|
|
82
|
+
run: uv run --all-extras pytest
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
name: Packages
|
|
2
|
+
|
|
3
|
+
# Wheel + sdist on every push to main, so staging proves the distributions the
|
|
4
|
+
# same way it proves the executables -- and `twine check` renders the README the
|
|
5
|
+
# way PyPI will, which is the one packaging fault a local install cannot show.
|
|
6
|
+
#
|
|
7
|
+
# The TestPyPI upload below is deliberately DORMANT: it never runs on a push,
|
|
8
|
+
# only when someone dispatches it by hand with publish=true. Before it can work,
|
|
9
|
+
# two things must exist (neither is created by this file):
|
|
10
|
+
# * a TestPyPI Trusted Publisher for this owner/repo naming `packages.yml`
|
|
11
|
+
# and the `testpypi` environment
|
|
12
|
+
# * a repository Environment named `testpypi`
|
|
13
|
+
on:
|
|
14
|
+
push:
|
|
15
|
+
branches: [main]
|
|
16
|
+
tags: ["v*"]
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
inputs:
|
|
19
|
+
publish:
|
|
20
|
+
description: "Upload the built distributions to TestPyPI"
|
|
21
|
+
type: boolean
|
|
22
|
+
default: false
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
build:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
- uses: astral-sh/setup-uv@v5
|
|
33
|
+
with:
|
|
34
|
+
python-version: "3.13"
|
|
35
|
+
enable-cache: true
|
|
36
|
+
|
|
37
|
+
- name: Build wheel and sdist
|
|
38
|
+
run: uv build --out-dir dist
|
|
39
|
+
|
|
40
|
+
# A package index renders the README as the project description. A
|
|
41
|
+
# malformed one is rejected at upload, or silently renders wrong, and a
|
|
42
|
+
# relative link that resolves in a checkout may not resolve there.
|
|
43
|
+
- name: Validate metadata and description rendering
|
|
44
|
+
run: uvx twine check --strict dist/*
|
|
45
|
+
|
|
46
|
+
- name: Report what was built
|
|
47
|
+
run: ls -l dist/
|
|
48
|
+
|
|
49
|
+
- uses: actions/upload-artifact@v4
|
|
50
|
+
with:
|
|
51
|
+
name: distributions
|
|
52
|
+
path: dist/
|
|
53
|
+
if-no-files-found: error
|
|
54
|
+
# Small (~2 MB) but uploaded on every push; no reason to keep 90 days
|
|
55
|
+
# of them on metered storage.
|
|
56
|
+
retention-days: 7
|
|
57
|
+
|
|
58
|
+
# What only a real install can show. The tests run against the source tree,
|
|
59
|
+
# where the working copy has answered every question the same way for so long
|
|
60
|
+
# that some of them stopped being asked: bytecode is cached, the repository is
|
|
61
|
+
# on `sys.path`, and a data file is found because `docs/` happens to be next
|
|
62
|
+
# door. A fresh venv with nothing but the wheel in it has none of that.
|
|
63
|
+
#
|
|
64
|
+
# Runs on every push, before the dormant upload below, so a fault of this kind
|
|
65
|
+
# is found while the version number is still free to reuse.
|
|
66
|
+
smoke:
|
|
67
|
+
needs: build
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
steps:
|
|
70
|
+
- uses: astral-sh/setup-uv@v5
|
|
71
|
+
with:
|
|
72
|
+
python-version: "3.13"
|
|
73
|
+
- uses: actions/download-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
name: distributions
|
|
76
|
+
path: dist/
|
|
77
|
+
|
|
78
|
+
- name: Install the built wheel into an empty environment
|
|
79
|
+
run: |
|
|
80
|
+
uv venv /tmp/smoke
|
|
81
|
+
VIRTUAL_ENV=/tmp/smoke uv pip install dist/*.whl
|
|
82
|
+
|
|
83
|
+
# Deliberately not from the repository root: Python puts the working
|
|
84
|
+
# directory on `sys.path`, so a check run there imports the checkout and
|
|
85
|
+
# the wheel is never touched.
|
|
86
|
+
- name: It runs, and says nothing it was not asked to say
|
|
87
|
+
working-directory: /tmp
|
|
88
|
+
run: |
|
|
89
|
+
set -euo pipefail
|
|
90
|
+
/tmp/smoke/bin/connections-export --help >/tmp/out.txt 2>/tmp/err.txt
|
|
91
|
+
echo "--- stdout ---"; cat /tmp/out.txt
|
|
92
|
+
if [ -s /tmp/err.txt ]; then
|
|
93
|
+
echo "::error::the console script wrote to stderr; a user sees this"
|
|
94
|
+
cat /tmp/err.txt
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
if grep -qi stub /tmp/out.txt; then
|
|
98
|
+
echo "::error::a stub command is advertised in the public help"
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# A SyntaxWarning is raised once, when the `.pyc` is written -- long ago
|
|
103
|
+
# in a working tree, and on a new install either during it or on stderr of
|
|
104
|
+
# every command until a cache warms. Compiling on purpose catches both.
|
|
105
|
+
- name: Every installed module compiles without warnings
|
|
106
|
+
working-directory: /tmp
|
|
107
|
+
run: |
|
|
108
|
+
SITE=$(/tmp/smoke/bin/python -P -c \
|
|
109
|
+
"import connections_export,pathlib;print(pathlib.Path(connections_export.__file__).parent)")
|
|
110
|
+
/tmp/smoke/bin/python -W error::SyntaxWarning -m compileall -q -f -x vendor "$SITE"
|
|
111
|
+
|
|
112
|
+
# The documents the application READS, not merely ships beside: the
|
|
113
|
+
# manual is the console's Manual tab, and the spec is copied into every
|
|
114
|
+
# package written.
|
|
115
|
+
- name: Its own documents are inside it, and readable
|
|
116
|
+
working-directory: /tmp
|
|
117
|
+
run: |
|
|
118
|
+
/tmp/smoke/bin/python -P - <<'PY'
|
|
119
|
+
import pathlib
|
|
120
|
+
import connections_export
|
|
121
|
+
from connections_export.gui.manual import MANUAL_DOC_PATH, manual_html
|
|
122
|
+
from connections_export.interchange.package import SPEC_DOC_PATH
|
|
123
|
+
|
|
124
|
+
root = pathlib.Path(connections_export.__file__).parent
|
|
125
|
+
assert MANUAL_DOC_PATH.is_file(), f"no manual at {MANUAL_DOC_PATH}"
|
|
126
|
+
assert SPEC_DOC_PATH.is_file(), f"no interchange spec at {SPEC_DOC_PATH}"
|
|
127
|
+
assert (root / "architecture.md").is_file(), "no architecture document"
|
|
128
|
+
html = manual_html()
|
|
129
|
+
assert html.count("<h2") >= 10, f"the manual rendered {html.count('<h2')} sections"
|
|
130
|
+
print(f"manual renders: {len(html)} chars, {html.count('<h2')} sections")
|
|
131
|
+
PY
|
|
132
|
+
|
|
133
|
+
# The console serves its own page, with the manual already in it -- and
|
|
134
|
+
# refuses a caller that is not local. The console reads archives and can
|
|
135
|
+
# start a capture with the user's own credentials, so it answers to this
|
|
136
|
+
# machine and nothing else; that guard is worth proving in the artefact
|
|
137
|
+
# that ships rather than only in the tests.
|
|
138
|
+
- name: The console starts, serves a complete page, and stays local
|
|
139
|
+
working-directory: /tmp
|
|
140
|
+
run: |
|
|
141
|
+
/tmp/smoke/bin/python -P - <<'PY'
|
|
142
|
+
import asyncio
|
|
143
|
+
import httpx
|
|
144
|
+
from connections_export.gui.app import make_app
|
|
145
|
+
|
|
146
|
+
async def get(base):
|
|
147
|
+
transport = httpx.ASGITransport(app=make_app(demo=True))
|
|
148
|
+
async with httpx.AsyncClient(transport=transport, base_url=base) as c:
|
|
149
|
+
return await c.get("/")
|
|
150
|
+
|
|
151
|
+
r = asyncio.run(get("http://127.0.0.1"))
|
|
152
|
+
assert r.status_code == 200, r.status_code
|
|
153
|
+
assert 'id="man-intro"' in r.text, "the manual is not in the served page"
|
|
154
|
+
assert "<!--MANUAL-->" not in r.text, "the manual placeholder was never filled"
|
|
155
|
+
print(f"console serves {len(r.text)} chars with the manual in place")
|
|
156
|
+
|
|
157
|
+
elsewhere = asyncio.run(get("http://elsewhere.example"))
|
|
158
|
+
assert elsewhere.status_code == 403, (
|
|
159
|
+
f"a non-local caller got {elsewhere.status_code}, not 403"
|
|
160
|
+
)
|
|
161
|
+
print("a non-local caller is refused")
|
|
162
|
+
PY
|
|
163
|
+
|
|
164
|
+
publish-testpypi:
|
|
165
|
+
# Dormant until dispatched by hand. Nothing on a push can reach this job.
|
|
166
|
+
if: github.event_name == 'workflow_dispatch' && inputs.publish
|
|
167
|
+
needs: [build, smoke]
|
|
168
|
+
runs-on: ubuntu-latest
|
|
169
|
+
environment:
|
|
170
|
+
name: testpypi
|
|
171
|
+
url: https://test.pypi.org/p/connections-export
|
|
172
|
+
permissions:
|
|
173
|
+
id-token: write
|
|
174
|
+
steps:
|
|
175
|
+
- uses: actions/download-artifact@v4
|
|
176
|
+
with:
|
|
177
|
+
name: distributions
|
|
178
|
+
path: dist/
|
|
179
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
180
|
+
with:
|
|
181
|
+
repository-url: https://test.pypi.org/legacy/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions: {}
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: astral-sh/setup-uv@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
- name: Confirm tag matches package version
|
|
19
|
+
run: |
|
|
20
|
+
version="$(uv run --no-project python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
|
|
21
|
+
test "v${version}" = "$GITHUB_REF_NAME"
|
|
22
|
+
- name: Build distributions
|
|
23
|
+
run: uv build
|
|
24
|
+
- uses: actions/upload-artifact@v4
|
|
25
|
+
with:
|
|
26
|
+
name: python-package-distributions
|
|
27
|
+
path: dist/
|
|
28
|
+
|
|
29
|
+
publish-to-pypi:
|
|
30
|
+
needs: build
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
environment:
|
|
33
|
+
name: pypi
|
|
34
|
+
permissions:
|
|
35
|
+
id-token: write
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/download-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: python-package-distributions
|
|
40
|
+
path: dist/
|
|
41
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.venv/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
.ruff_cache/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
|
|
11
|
+
# Node
|
|
12
|
+
node_modules/
|
|
13
|
+
*.log
|
|
14
|
+
|
|
15
|
+
# Crawl output holds exported content and session tokens -- never commit.
|
|
16
|
+
# Anchored to the repository root so it does not match the
|
|
17
|
+
# connections_export/archive source package or tests/archive.
|
|
18
|
+
/archive/
|
|
19
|
+
*.ltpa
|
|
20
|
+
.env
|
|
21
|
+
.env.*
|
|
22
|
+
|
|
23
|
+
# Your own config: copy connections-export.example.toml and fill it in. It
|
|
24
|
+
# names your deployment, so it is never committed.
|
|
25
|
+
connections-export.toml
|
|
26
|
+
|
|
27
|
+
# hcl-serve's default run-archive location when run from the repo root
|
|
28
|
+
# (CONNECTIONS_EXPORT_ARCHIVES_DIR unset) — dev/testing archives, never commit.
|
|
29
|
+
/connections-export-archives/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Jörg Türmer
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|