hkex-filing-scraper 2.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.
Files changed (127) hide show
  1. hkex_filing_scraper-2.1.0/.editorconfig +16 -0
  2. hkex_filing_scraper-2.1.0/.env.example +140 -0
  3. hkex_filing_scraper-2.1.0/.gitattributes +18 -0
  4. hkex_filing_scraper-2.1.0/.github/CODEOWNERS +8 -0
  5. hkex_filing_scraper-2.1.0/.github/FUNDING.yml +5 -0
  6. hkex_filing_scraper-2.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +78 -0
  7. hkex_filing_scraper-2.1.0/.github/ISSUE_TEMPLATE/config.yml +11 -0
  8. hkex_filing_scraper-2.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
  9. hkex_filing_scraper-2.1.0/.github/PULL_REQUEST_TEMPLATE.md +35 -0
  10. hkex_filing_scraper-2.1.0/.github/dependabot.yml +30 -0
  11. hkex_filing_scraper-2.1.0/.github/workflows/canary.yml +70 -0
  12. hkex_filing_scraper-2.1.0/.github/workflows/ci.yml +374 -0
  13. hkex_filing_scraper-2.1.0/.github/workflows/codeql.yml +26 -0
  14. hkex_filing_scraper-2.1.0/.github/workflows/pages.yml +50 -0
  15. hkex_filing_scraper-2.1.0/.github/workflows/pypi.yml +59 -0
  16. hkex_filing_scraper-2.1.0/.github/workflows/release.yml +140 -0
  17. hkex_filing_scraper-2.1.0/.github/workflows/scorecard.yml +48 -0
  18. hkex_filing_scraper-2.1.0/.github/workflows/wiki.yml +76 -0
  19. hkex_filing_scraper-2.1.0/.gitignore +53 -0
  20. hkex_filing_scraper-2.1.0/.markdownlint-cli2.jsonc +32 -0
  21. hkex_filing_scraper-2.1.0/.pre-commit-config.yaml +27 -0
  22. hkex_filing_scraper-2.1.0/AGENTS.md +124 -0
  23. hkex_filing_scraper-2.1.0/CHANGELOG.md +192 -0
  24. hkex_filing_scraper-2.1.0/CITATION.cff +27 -0
  25. hkex_filing_scraper-2.1.0/CODE_OF_CONDUCT.md +127 -0
  26. hkex_filing_scraper-2.1.0/CONTRIBUTING.md +95 -0
  27. hkex_filing_scraper-2.1.0/GOVERNANCE.md +65 -0
  28. hkex_filing_scraper-2.1.0/LICENSE +9 -0
  29. hkex_filing_scraper-2.1.0/PKG-INFO +464 -0
  30. hkex_filing_scraper-2.1.0/README.md +401 -0
  31. hkex_filing_scraper-2.1.0/SECURITY.md +92 -0
  32. hkex_filing_scraper-2.1.0/SUPPORT.md +47 -0
  33. hkex_filing_scraper-2.1.0/constitution.md +147 -0
  34. hkex_filing_scraper-2.1.0/docs/STYLE.md +64 -0
  35. hkex_filing_scraper-2.1.0/docs/adr/0001-versioning-and-release-automation.md +45 -0
  36. hkex_filing_scraper-2.1.0/docs/adr/0002-multi-sink-architecture.md +62 -0
  37. hkex_filing_scraper-2.1.0/docs/adr/0003-sink-support-policy.md +59 -0
  38. hkex_filing_scraper-2.1.0/docs/architecture.md +120 -0
  39. hkex_filing_scraper-2.1.0/docs/assets/banner.svg +70 -0
  40. hkex_filing_scraper-2.1.0/docs/assets/favicon.svg +8 -0
  41. hkex_filing_scraper-2.1.0/docs/assets/javascripts/a11y.js +62 -0
  42. hkex_filing_scraper-2.1.0/docs/assets/stylesheets/terminal.css +307 -0
  43. hkex_filing_scraper-2.1.0/docs/cli.md +62 -0
  44. hkex_filing_scraper-2.1.0/docs/configuration.md +137 -0
  45. hkex_filing_scraper-2.1.0/docs/de-risking.md +51 -0
  46. hkex_filing_scraper-2.1.0/docs/getting-started.md +133 -0
  47. hkex_filing_scraper-2.1.0/docs/index.md +54 -0
  48. hkex_filing_scraper-2.1.0/docs/legal.md +58 -0
  49. hkex_filing_scraper-2.1.0/docs/release-automation.md +185 -0
  50. hkex_filing_scraper-2.1.0/docs/releasing.md +129 -0
  51. hkex_filing_scraper-2.1.0/docs/sinks/README.md +84 -0
  52. hkex_filing_scraper-2.1.0/docs/sinks/clickhouse.md +78 -0
  53. hkex_filing_scraper-2.1.0/docs/sinks/duckdb.md +68 -0
  54. hkex_filing_scraper-2.1.0/docs/sinks/mongodb.md +82 -0
  55. hkex_filing_scraper-2.1.0/docs/sinks/mysql.md +69 -0
  56. hkex_filing_scraper-2.1.0/docs/sinks/neo4j.md +84 -0
  57. hkex_filing_scraper-2.1.0/docs/sinks/postgresql.md +130 -0
  58. hkex_filing_scraper-2.1.0/docs/sinks/sqlite.md +72 -0
  59. hkex_filing_scraper-2.1.0/docs/sinks/surrealdb.md +106 -0
  60. hkex_filing_scraper-2.1.0/docs/social_preview.png +0 -0
  61. hkex_filing_scraper-2.1.0/docs/testing.md +150 -0
  62. hkex_filing_scraper-2.1.0/docs/troubleshooting.md +117 -0
  63. hkex_filing_scraper-2.1.0/docs/upgrading.md +74 -0
  64. hkex_filing_scraper-2.1.0/examples/README.md +60 -0
  65. hkex_filing_scraper-2.1.0/examples/docker-compose.yml +74 -0
  66. hkex_filing_scraper-2.1.0/examples/quickstart.sh +45 -0
  67. hkex_filing_scraper-2.1.0/mkdocs.yml +71 -0
  68. hkex_filing_scraper-2.1.0/pyproject.toml +137 -0
  69. hkex_filing_scraper-2.1.0/scripts/canary.py +127 -0
  70. hkex_filing_scraper-2.1.0/scripts/check_licenses.py +77 -0
  71. hkex_filing_scraper-2.1.0/scripts/mirror_wiki.py +260 -0
  72. hkex_filing_scraper-2.1.0/src/hkex_scraper/__init__.py +10 -0
  73. hkex_filing_scraper-2.1.0/src/hkex_scraper/_version.py +24 -0
  74. hkex_filing_scraper-2.1.0/src/hkex_scraper/api.py +302 -0
  75. hkex_filing_scraper-2.1.0/src/hkex_scraper/config.py +240 -0
  76. hkex_filing_scraper-2.1.0/src/hkex_scraper/db.py +267 -0
  77. hkex_filing_scraper-2.1.0/src/hkex_scraper/db_postgres.py +713 -0
  78. hkex_filing_scraper-2.1.0/src/hkex_scraper/extractor.py +732 -0
  79. hkex_filing_scraper-2.1.0/src/hkex_scraper/graph.py +175 -0
  80. hkex_filing_scraper-2.1.0/src/hkex_scraper/http.py +149 -0
  81. hkex_filing_scraper-2.1.0/src/hkex_scraper/main.py +425 -0
  82. hkex_filing_scraper-2.1.0/src/hkex_scraper/pipeline.py +702 -0
  83. hkex_filing_scraper-2.1.0/src/hkex_scraper/py.typed +0 -0
  84. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/__init__.py +96 -0
  85. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/base.py +158 -0
  86. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/clickhouse.py +474 -0
  87. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/dialects.py +540 -0
  88. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/duckdb.py +122 -0
  89. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/mongodb.py +428 -0
  90. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/mysql.py +158 -0
  91. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/neo4j.py +413 -0
  92. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/postgres.py +90 -0
  93. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/registry.py +192 -0
  94. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/relational.py +340 -0
  95. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/sqlite.py +111 -0
  96. hkex_filing_scraper-2.1.0/src/hkex_scraper/sinks/surrealdb.py +606 -0
  97. hkex_filing_scraper-2.1.0/src/hkex_scraper/utils.py +211 -0
  98. hkex_filing_scraper-2.1.0/tests/__init__.py +0 -0
  99. hkex_filing_scraper-2.1.0/tests/conftest.py +50 -0
  100. hkex_filing_scraper-2.1.0/tests/test_api_contract.py +226 -0
  101. hkex_filing_scraper-2.1.0/tests/test_canary.py +137 -0
  102. hkex_filing_scraper-2.1.0/tests/test_cli.py +164 -0
  103. hkex_filing_scraper-2.1.0/tests/test_config.py +96 -0
  104. hkex_filing_scraper-2.1.0/tests/test_coverage.py +101 -0
  105. hkex_filing_scraper-2.1.0/tests/test_dialects.py +154 -0
  106. hkex_filing_scraper-2.1.0/tests/test_docs_consistency.py +213 -0
  107. hkex_filing_scraper-2.1.0/tests/test_duckdb_integration.py +163 -0
  108. hkex_filing_scraper-2.1.0/tests/test_extra_sinks.py +215 -0
  109. hkex_filing_scraper-2.1.0/tests/test_extra_sinks_integration.py +153 -0
  110. hkex_filing_scraper-2.1.0/tests/test_fault_injection.py +183 -0
  111. hkex_filing_scraper-2.1.0/tests/test_fidelity.py +129 -0
  112. hkex_filing_scraper-2.1.0/tests/test_http.py +233 -0
  113. hkex_filing_scraper-2.1.0/tests/test_hypothesis.py +163 -0
  114. hkex_filing_scraper-2.1.0/tests/test_multi_sink_integration.py +92 -0
  115. hkex_filing_scraper-2.1.0/tests/test_mysql_integration.py +104 -0
  116. hkex_filing_scraper-2.1.0/tests/test_postgres_integration.py +148 -0
  117. hkex_filing_scraper-2.1.0/tests/test_postgres_sink.py +292 -0
  118. hkex_filing_scraper-2.1.0/tests/test_registry.py +94 -0
  119. hkex_filing_scraper-2.1.0/tests/test_relational_sink.py +62 -0
  120. hkex_filing_scraper-2.1.0/tests/test_sink_dispatch.py +234 -0
  121. hkex_filing_scraper-2.1.0/tests/test_sqlite_integration.py +203 -0
  122. hkex_filing_scraper-2.1.0/tests/test_supply_chain.py +167 -0
  123. hkex_filing_scraper-2.1.0/tests/test_surrealdb_integration.py +131 -0
  124. hkex_filing_scraper-2.1.0/tests/test_theme.py +147 -0
  125. hkex_filing_scraper-2.1.0/tests/test_utils.py +149 -0
  126. hkex_filing_scraper-2.1.0/tests/test_verify.py +263 -0
  127. hkex_filing_scraper-2.1.0/uv.lock +2471 -0
@@ -0,0 +1,16 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = space
9
+ indent_size = 4
10
+ max_line_length = 100
11
+
12
+ [*.{yml,yaml,json,toml}]
13
+ indent_size = 2
14
+
15
+ [*.md]
16
+ trim_trailing_whitespace = false
@@ -0,0 +1,140 @@
1
+ # =============================================================================
2
+ # HKEx Filing Scraper — Environment Configuration
3
+ # =============================================================================
4
+ # Copy this file to .env and fill in your values:
5
+ # cp .env.example .env
6
+ #
7
+ # .env is loaded from the CURRENT WORKING DIRECTORY, not the repo root.
8
+ # =============================================================================
9
+
10
+ # ---------------------------------------------------------------------------
11
+ # Database sink selection (REQUIRED)
12
+ # ---------------------------------------------------------------------------
13
+ # An ordered, comma-separated list of sink ids. Every configured sink is
14
+ # written to; reads come from the first sink that supports them.
15
+ # Valid ids (documented popularity order):
16
+ # postgres PostgreSQL 13+ (extra: postgres)
17
+ # mysql MySQL 8 (extra: mysql)
18
+ # sqlite SQLite file (no extra)
19
+ # mongodb MongoDB 6+ (extra: mongodb)
20
+ # mariadb MariaDB 10.5+ (extra: mysql)
21
+ # neo4j Neo4j 5 (extra: neo4j)
22
+ # clickhouse ClickHouse 24+ (extra: clickhouse)
23
+ # duckdb DuckDB file (extra: duckdb)
24
+ # surrealdb SurrealDB (no extra)
25
+ #
26
+ # Examples:
27
+ # DATABASE_TARGET=postgres,sqlite
28
+ # DATABASE_TARGET=mysql,clickhouse
29
+ # DATABASE_TARGET=sqlite
30
+ DATABASE_TARGET=postgres,sqlite
31
+
32
+ # ---------------------------------------------------------------------------
33
+ # PostgreSQL (required when DATABASE_TARGET includes postgres)
34
+ # ---------------------------------------------------------------------------
35
+ # Install the driver first: pip install "hkex-filing-scraper[postgres]"
36
+ #
37
+ # Either provide a full connection string...
38
+ POSTGRES_DSN=
39
+ # ...or the discrete settings below (used when POSTGRES_DSN is empty).
40
+ POSTGRES_HOST=localhost
41
+ POSTGRES_PORT=5432
42
+ POSTGRES_DATABASE=
43
+ POSTGRES_USER=
44
+ POSTGRES_PASSWORD=
45
+ POSTGRES_SCHEMA=public
46
+ # Connection pool sizing (OPTIONAL)
47
+ # POSTGRES_MIN_POOL=1
48
+ # POSTGRES_MAX_POOL=15
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # MySQL / MariaDB (required when DATABASE_TARGET includes mysql or mariadb)
52
+ # ---------------------------------------------------------------------------
53
+ # Install the driver first: pip install "hkex-filing-scraper[mysql]"
54
+ #
55
+ # MYSQL_DSN=mysql://user:password@localhost:3306/hkex
56
+ MYSQL_HOST=localhost
57
+ MYSQL_PORT=3306
58
+ MYSQL_DATABASE=
59
+ MYSQL_USER=
60
+ MYSQL_PASSWORD=
61
+ # The mariadb sink reads MARIADB_* and falls back to MYSQL_* when its own are unset.
62
+ # MARIADB_DSN=
63
+ # MARIADB_HOST=localhost
64
+ # MARIADB_PORT=3306
65
+ # MARIADB_DATABASE=
66
+ # MARIADB_USER=
67
+ # MARIADB_PASSWORD=
68
+
69
+ # ---------------------------------------------------------------------------
70
+ # SQLite / DuckDB (required when DATABASE_TARGET includes sqlite or duckdb)
71
+ # ---------------------------------------------------------------------------
72
+ # SQLite needs no extra dependency.
73
+ # DuckDB: pip install "hkex-filing-scraper[duckdb]"
74
+ #
75
+ # SQLITE_PATH=hkex.db # a file path, or :memory:
76
+ # DUCKDB_PATH=hkex.duckdb # a file path, or :memory:
77
+
78
+ # ---------------------------------------------------------------------------
79
+ # MongoDB (required when DATABASE_TARGET includes mongodb)
80
+ # ---------------------------------------------------------------------------
81
+ # Install the driver first: pip install "hkex-filing-scraper[mongodb]"
82
+ #
83
+ # MONGODB_URI=mongodb://localhost:27017
84
+ # MONGODB_DATABASE=hkex
85
+
86
+ # ---------------------------------------------------------------------------
87
+ # ClickHouse (required when DATABASE_TARGET includes clickhouse)
88
+ # ---------------------------------------------------------------------------
89
+ # Install the driver first: pip install "hkex-filing-scraper[clickhouse]"
90
+ #
91
+ # CLICKHOUSE_HOST=localhost
92
+ # CLICKHOUSE_PORT=8123
93
+ # CLICKHOUSE_DATABASE=hkex
94
+ # CLICKHOUSE_USER=default
95
+ # CLICKHOUSE_PASSWORD=
96
+
97
+ # ---------------------------------------------------------------------------
98
+ # Neo4j (required when DATABASE_TARGET includes neo4j)
99
+ # ---------------------------------------------------------------------------
100
+ # Install the driver first: pip install "hkex-filing-scraper[neo4j]"
101
+ #
102
+ # NEO4J_URI=bolt://localhost:7687
103
+ # NEO4J_USER=neo4j
104
+ # NEO4J_PASSWORD=
105
+ # NEO4J_DATABASE=
106
+
107
+ # ---------------------------------------------------------------------------
108
+ # SurrealDB (required when DATABASE_TARGET includes surrealdb)
109
+ # ---------------------------------------------------------------------------
110
+ # No extra dependency.
111
+ #
112
+ # SURREAL_ENDPOINT=http://localhost:8000
113
+ # SURREAL_NAMESPACE=default
114
+ # SURREAL_DATABASE=default
115
+ # SURREAL_USERNAME=root
116
+ # SURREAL_PASSWORD=
117
+
118
+ # ---------------------------------------------------------------------------
119
+ # Graph linking (OPTIONAL)
120
+ # ---------------------------------------------------------------------------
121
+ # Set COMPANY_TABLE to the name of your existing company table to enable
122
+ # graph edges (has_filing, references_filing) between companies and filings.
123
+ # Leave empty to disable graph linking entirely.
124
+ #
125
+ # COMPANY_TABLE=my_company
126
+ #
127
+ # Pattern for converting a ticker like "0451.HK" into a company key.
128
+ # Placeholders: {code} = leading-zero-stripped stock code, {exchange} = exchange suffix.
129
+ # Default: "{code}_{exchange}" produces "451_HK"
130
+ #
131
+ # COMPANY_ID_PATTERN={code}_{exchange}
132
+
133
+ # ---------------------------------------------------------------------------
134
+ # Performance tuning (OPTIONAL)
135
+ # ---------------------------------------------------------------------------
136
+ # MAX_DOWNLOAD_WORKERS=15
137
+ #
138
+ # Minimum seconds between request starts, process-wide. 0 disables pacing (default).
139
+ # Set a small value (e.g. 0.25) to be deliberately polite to the endpoint.
140
+ # REQUEST_DELAY_SECONDS=0
@@ -0,0 +1,18 @@
1
+ # Auto-detect text files and normalise line endings.
2
+ * text=auto eol=lf
3
+
4
+ # Windows scripts keep CRLF.
5
+ *.bat text eol=crlf
6
+ *.cmd text eol=crlf
7
+
8
+ # Binary assets.
9
+ *.png binary
10
+ *.jpg binary
11
+ *.gif binary
12
+ *.pdf binary
13
+ *.whl binary
14
+ *.gz binary
15
+
16
+ # Keep generated/vendored files out of language stats.
17
+ src/hkex_scraper/_version.py linguist-generated=true
18
+ docs/*.png linguist-documentation
@@ -0,0 +1,8 @@
1
+ # Default owner for everything in the repo.
2
+ * @simonplmak-cloud
3
+
4
+ # Sink architecture and per-engine adapters.
5
+ /src/hkex_scraper/sinks/ @simonplmak-cloud
6
+
7
+ # CI, release, and GitHub configuration.
8
+ /.github/ @simonplmak-cloud
@@ -0,0 +1,5 @@
1
+ # Funding options for this project.
2
+ # The Sponsor button appears only for platforms that are actually enrolled,
3
+ # so leaving a handle here is safe; remove a line if you are not enrolled.
4
+ github: [simonplmak-cloud]
5
+ # custom: ["https://simonplmak-cloud.github.io/hkex-filing-scraper/"]
@@ -0,0 +1,78 @@
1
+ name: Bug report
2
+ description: Report a reproducible problem with the scraper
3
+ title: "[Bug] "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Thanks for the report. Please do not include credentials or `.env` contents.
10
+ - type: textarea
11
+ id: what-happened
12
+ attributes:
13
+ label: What happened?
14
+ description: A clear description of the bug and what you expected instead.
15
+ validations:
16
+ required: true
17
+ - type: textarea
18
+ id: reproduce
19
+ attributes:
20
+ label: Steps to reproduce
21
+ description: Exact commands and configuration (redact secrets).
22
+ placeholder: |
23
+ 1. DATABASE_TARGET=...
24
+ 2. hkex-scraper ...
25
+ 3. ...
26
+ validations:
27
+ required: true
28
+ - type: dropdown
29
+ id: sink
30
+ attributes:
31
+ label: Database target
32
+ options:
33
+ - postgres
34
+ - mysql
35
+ - mariadb
36
+ - sqlite
37
+ - duckdb
38
+ - mongodb
39
+ - clickhouse
40
+ - neo4j
41
+ - surrealdb
42
+ - multiple / other
43
+ validations:
44
+ required: true
45
+ - type: input
46
+ id: version
47
+ attributes:
48
+ label: Version
49
+ description: Output of `hkex-scraper --version` or the release tag.
50
+ placeholder: "1.1.0"
51
+ validations:
52
+ required: true
53
+ - type: input
54
+ id: python
55
+ attributes:
56
+ label: Python version
57
+ placeholder: "3.11.9"
58
+ validations:
59
+ required: true
60
+ - type: dropdown
61
+ id: os
62
+ attributes:
63
+ label: Operating system
64
+ options:
65
+ - Linux
66
+ - macOS
67
+ - Windows
68
+ - Other
69
+ validations:
70
+ required: true
71
+ - type: textarea
72
+ id: logs
73
+ attributes:
74
+ label: Relevant log output
75
+ description: Paste the relevant lines from `logs/hkex_filings_*.log` (redact secrets).
76
+ render: shell
77
+ validations:
78
+ required: false
@@ -0,0 +1,11 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Question or usage help
4
+ url: https://github.com/simonplmak-cloud/hkex-filing-scraper/discussions
5
+ about: Ask a question, share a setup, or discuss an idea in Discussions. Please do not open an issue for usage questions.
6
+ - name: Documentation
7
+ url: https://simonplmak-cloud.github.io/hkex-filing-scraper/
8
+ about: Getting started, configuration, CLI, sink guides, and troubleshooting.
9
+ - name: Security vulnerability
10
+ url: https://github.com/simonplmak-cloud/hkex-filing-scraper/security/policy
11
+ about: Report a vulnerability privately per the security policy. Never open a public issue for security.
@@ -0,0 +1,46 @@
1
+ name: Feature request
2
+ description: Suggest an idea or improvement
3
+ title: "[Feature] "
4
+ labels: ["enhancement"]
5
+ body:
6
+ - type: textarea
7
+ id: problem
8
+ attributes:
9
+ label: Problem / motivation
10
+ description: What problem does this solve? Who benefits?
11
+ validations:
12
+ required: true
13
+ - type: textarea
14
+ id: proposal
15
+ attributes:
16
+ label: Proposed solution
17
+ description: Describe the change you would like. Include CLI/config sketches if relevant.
18
+ validations:
19
+ required: true
20
+ - type: textarea
21
+ id: alternatives
22
+ attributes:
23
+ label: Alternatives considered
24
+ description: Other approaches or workarounds you have tried.
25
+ validations:
26
+ required: false
27
+ - type: dropdown
28
+ id: area
29
+ attributes:
30
+ label: Area
31
+ options:
32
+ - Scraping / API
33
+ - Document extraction
34
+ - PostgreSQL sink
35
+ - MySQL / MariaDB sink
36
+ - SQLite / DuckDB sink
37
+ - MongoDB sink
38
+ - ClickHouse sink
39
+ - Neo4j sink
40
+ - SurrealDB sink
41
+ - Graph linking
42
+ - CLI / configuration
43
+ - Documentation
44
+ - Other
45
+ validations:
46
+ required: true
@@ -0,0 +1,35 @@
1
+ <!--
2
+ Thanks for contributing. Keep the description focused on the why and the impact.
3
+ -->
4
+
5
+ ## Summary
6
+
7
+ <!-- One or two sentences: what changes, and why. -->
8
+
9
+ ## Type of change
10
+
11
+ - [ ] `feat` — new feature
12
+ - [ ] `fix` — bug fix
13
+ - [ ] `docs` — documentation only
14
+ - [ ] `refactor` — no behavior change
15
+ - [ ] `chore` — tooling, dependencies, maintenance
16
+ - [ ] `test` — tests only
17
+
18
+ ## Related issues
19
+
20
+ <!-- e.g. Closes #123 -->
21
+
22
+ ## How this was tested
23
+
24
+ - [ ] `ruff check && ruff format --check` passes
25
+ - [ ] `pytest -q` passes
26
+ - [ ] Added or updated tests for the change
27
+ - [ ] Verified against a live sink (which one? — PostgreSQL / MySQL / SQLite / DuckDB / MongoDB / ClickHouse / Neo4j / SurrealDB)
28
+
29
+ ## Checklist
30
+
31
+ - [ ] New or changed sink fields are mirrored across every schema (SurrealDB, PostgreSQL, dialect DDL, MongoDB/Neo4j/ClickHouse)
32
+ - [ ] SQL is parameterised; no values interpolated; credentials are never logged
33
+ - [ ] Docs, templates, and `.env.example` updated (the docs consistency test covers this)
34
+ - [ ] `CHANGELOG.md` updated under `Unreleased`
35
+ - [ ] No secrets, tokens, or real credentials committed
@@ -0,0 +1,30 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: "/"
5
+ schedule:
6
+ interval: weekly
7
+ open-pull-requests-limit: 5
8
+ groups:
9
+ python-minor:
10
+ patterns: ["*"]
11
+ update-types: ["minor", "patch"]
12
+ labels: ["dependencies"]
13
+
14
+ - package-ecosystem: uv
15
+ directory: "/"
16
+ schedule:
17
+ interval: weekly
18
+ open-pull-requests-limit: 5
19
+ labels: ["dependencies"]
20
+
21
+ - package-ecosystem: github-actions
22
+ directory: "/"
23
+ schedule:
24
+ interval: weekly
25
+ labels: ["dependencies", "ci"]
26
+
27
+ # Container images are pinned by digest in .github/workflows/ci.yml and
28
+ # examples/docker-compose.yml. Dependabot's `docker` ecosystem only understands Dockerfiles
29
+ # and Kubernetes YAML (not compose files), so those digests are bumped manually when an
30
+ # engine release is worth adopting.
@@ -0,0 +1,70 @@
1
+ name: Canary
2
+
3
+ # Early-warning control for risk R1: the undocumented HKEx API changing shape.
4
+ # Runs the real fetch path against a small recent window once a day, and opens an issue
5
+ # (or comments on the existing one) when the response no longer matches what the scraper
6
+ # depends on. Target: detect an API break within 24 hours.
7
+
8
+ on:
9
+ schedule:
10
+ - cron: "17 1 * * *"
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ canary:
18
+ name: HKEx API canary
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ contents: read
22
+ issues: write # create/comment on the tracking issue
23
+ steps:
24
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
25
+
26
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
27
+ with:
28
+ python-version: "3.12"
29
+
30
+ - name: Install the package
31
+ run: pip install --quiet .
32
+
33
+ - name: Run the canary
34
+ id: canary
35
+ run: |
36
+ set +e
37
+ set -o pipefail
38
+ python scripts/canary.py --days 7 --max 5 --json | tee canary.json
39
+ code=$?
40
+ echo "exit_code=$code" >> "$GITHUB_OUTPUT"
41
+
42
+ - name: Report a failure on the tracking issue
43
+ if: steps.canary.outputs.exit_code != '0'
44
+ env:
45
+ GH_TOKEN: ${{ github.token }}
46
+ run: |
47
+ set -euo pipefail
48
+ TITLE="Canary: HKEx API response shape changed"
49
+ BODY="$(printf 'The scheduled canary failed on %s.\n\n```json\n%s\n```\n\nSee the workflow run: %s/%s/actions/runs/%s' \
50
+ "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(cat canary.json)" \
51
+ "$GITHUB_SERVER_URL" "$GITHUB_REPOSITORY" "$GITHUB_RUN_ID")"
52
+ EXISTING="$(gh issue list --state open --search "in:title \"$TITLE\"" --json number --jq '.[0].number // empty')"
53
+ if [ -n "$EXISTING" ]; then
54
+ gh issue comment "$EXISTING" --body "$BODY"
55
+ else
56
+ gh issue create --title "$TITLE" --body "$BODY" --label bug
57
+ fi
58
+
59
+ - name: Keep the report
60
+ if: always()
61
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
62
+ with:
63
+ name: canary-report
64
+ path: canary.json
65
+ if-no-files-found: ignore
66
+ retention-days: 14
67
+
68
+ - name: Fail the run when the canary failed
69
+ if: steps.canary.outputs.exit_code != '0'
70
+ run: exit 1