github-actions-ingester 0.2.0__tar.gz → 0.2.1__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.
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/CHANGELOG.md +13 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/PKG-INFO +6 -2
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/README.md +5 -1
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/pyproject.toml +1 -1
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/__init__.py +1 -1
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/store.py +37 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_store.py +6 -1
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/.gitignore +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/LICENSE +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/__main__.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/app_manifest.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/collector.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/config.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/github.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/metrics.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/migrations/0001_initial.sql +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/ratelimit.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/server.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/workflow_schedule.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/__init__.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/conftest.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/helpers.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_app_manifest.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_cli.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_collector.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_config.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_dashboard.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_github.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_metrics.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_ratelimit.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_server.py +0 -0
- {github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_workflow_schedule.py +0 -0
|
@@ -4,6 +4,19 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.2.1] — 2026-09-05
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Chart: `deploymentAnnotations`, for a Reloader annotation on the
|
|
11
|
+
Deployment.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- A read role with a name different from the schema could not resolve the
|
|
15
|
+
views by bare name, and the Grafana PostgreSQL datasource has no
|
|
16
|
+
search_path setting. After granting read access the ingester now sets
|
|
17
|
+
the database's default `search_path` to the schema (needs the ingester
|
|
18
|
+
role to own the database; skipped with a warning otherwise).
|
|
19
|
+
|
|
7
20
|
## [0.2.0] — 2026-09-05
|
|
8
21
|
|
|
9
22
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: github-actions-ingester
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Standalone GitHub Actions ingester — pulls workflows, runs and jobs from the GitHub API into PostgreSQL for Grafana dashboards, FinOps and alerting. Prometheus /metrics for ingester health and scheduled-workflow liveness.
|
|
5
5
|
Project-URL: Homepage, https://github.com/danielgines/github-actions-ingester
|
|
6
6
|
Project-URL: Repository, https://github.com/danielgines/github-actions-ingester
|
|
@@ -162,7 +162,11 @@ table and view after each migration run, plus a default privilege so
|
|
|
162
162
|
tables added by later migrations are readable too. Point Grafana at the
|
|
163
163
|
database with one of those roles and it never needs the ingester's
|
|
164
164
|
credentials. The roles themselves are created by whoever manages the
|
|
165
|
-
database (the ingester never runs `CREATE ROLE`).
|
|
165
|
+
database (the ingester never runs `CREATE ROLE`). In the same step the
|
|
166
|
+
ingester sets the database's default `search_path` to the schema, so a
|
|
167
|
+
Grafana datasource (which cannot send one) resolves the views by bare
|
|
168
|
+
name; that statement needs the ingester's role to own the database, and
|
|
169
|
+
is skipped with a warning otherwise.
|
|
166
170
|
|
|
167
171
|
### Tables and views
|
|
168
172
|
|
|
@@ -99,7 +99,11 @@ table and view after each migration run, plus a default privilege so
|
|
|
99
99
|
tables added by later migrations are readable too. Point Grafana at the
|
|
100
100
|
database with one of those roles and it never needs the ingester's
|
|
101
101
|
credentials. The roles themselves are created by whoever manages the
|
|
102
|
-
database (the ingester never runs `CREATE ROLE`).
|
|
102
|
+
database (the ingester never runs `CREATE ROLE`). In the same step the
|
|
103
|
+
ingester sets the database's default `search_path` to the schema, so a
|
|
104
|
+
Grafana datasource (which cannot send one) resolves the views by bare
|
|
105
|
+
name; that statement needs the ingester's role to own the database, and
|
|
106
|
+
is skipped with a warning otherwise.
|
|
103
107
|
|
|
104
108
|
### Tables and views
|
|
105
109
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "github-actions-ingester"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.1"
|
|
4
4
|
description = "Standalone GitHub Actions ingester — pulls workflows, runs and jobs from the GitHub API into PostgreSQL for Grafana dashboards, FinOps and alerting. Prometheus /metrics for ingester health and scheduled-workflow liveness."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
{github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/src/github_actions_ingester/store.py
RENAMED
|
@@ -200,8 +200,45 @@ class Store:
|
|
|
200
200
|
)
|
|
201
201
|
granted.append(role)
|
|
202
202
|
logger.info("store.read_access_granted", roles=granted, schema=self._schema)
|
|
203
|
+
self._set_database_search_path()
|
|
203
204
|
return granted
|
|
204
205
|
|
|
206
|
+
def _set_database_search_path(self) -> None:
|
|
207
|
+
"""Make the schema resolve by bare name for every session on the database.
|
|
208
|
+
|
|
209
|
+
The ingester sets ``search_path`` on its own connections, and a role
|
|
210
|
+
named like the schema gets it for free (``"$user"`` is first in the
|
|
211
|
+
default path), which is why the Compose example works without this.
|
|
212
|
+
A read role with a different name (``grafana`` reading schema
|
|
213
|
+
``gha``) resolves ``minion_workflow_runs`` to nothing, and the
|
|
214
|
+
Grafana PostgreSQL datasource has no search_path setting.
|
|
215
|
+
|
|
216
|
+
``ALTER DATABASE ... SET`` needs the database owner. When the ingester
|
|
217
|
+
is not the owner this logs and moves on: the grants above still hold,
|
|
218
|
+
and the operator can run the statement once by hand.
|
|
219
|
+
"""
|
|
220
|
+
conn = self.connect()
|
|
221
|
+
try:
|
|
222
|
+
with conn.transaction(), conn.cursor() as cur:
|
|
223
|
+
cur.execute("SELECT current_database() AS db")
|
|
224
|
+
row = cur.fetchone()
|
|
225
|
+
database = row["db"] if row else ""
|
|
226
|
+
cur.execute(
|
|
227
|
+
sql.SQL("ALTER DATABASE {} SET search_path TO {}, public").format(
|
|
228
|
+
sql.Identifier(database), sql.Identifier(self._schema)
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
except psycopg.errors.InsufficientPrivilege:
|
|
232
|
+
conn.rollback()
|
|
233
|
+
logger.warning(
|
|
234
|
+
"store.search_path_not_set",
|
|
235
|
+
schema=self._schema,
|
|
236
|
+
hint="ingester role does not own the database; run "
|
|
237
|
+
"ALTER DATABASE <db> SET search_path TO <schema>, public as the owner",
|
|
238
|
+
)
|
|
239
|
+
return
|
|
240
|
+
logger.info("store.search_path_set", schema=self._schema)
|
|
241
|
+
|
|
205
242
|
def schema_version(self) -> int | None:
|
|
206
243
|
"""Highest applied migration, or None when the schema was never bootstrapped."""
|
|
207
244
|
conn = self.connect()
|
|
@@ -250,7 +250,10 @@ def test_grant_read_access_lets_a_role_read_views_and_future_tables(
|
|
|
250
250
|
schema = migrated_store.schema
|
|
251
251
|
reader_url = re.sub(r"//[^@]*@", f"//{role}:reader@", _with_userinfo(database_url))
|
|
252
252
|
with psycopg.connect(reader_url, autocommit=True) as reader:
|
|
253
|
-
|
|
253
|
+
# No SET search_path on purpose: Grafana cannot send one, so the
|
|
254
|
+
# database-level default the ingester sets must resolve the views.
|
|
255
|
+
path = reader.execute("SHOW search_path").fetchone()[0]
|
|
256
|
+
assert path.split(",")[0].strip().strip('"') == schema
|
|
254
257
|
assert reader.execute("SELECT count(*) FROM minion_workflow_runs").fetchone()[0] == 0
|
|
255
258
|
assert reader.execute("SELECT count(*) FROM workflow_jobs").fetchone()[0] == 0
|
|
256
259
|
assert reader.execute("SELECT count(*) FROM later_migration").fetchone()[0] == 0
|
|
@@ -262,6 +265,8 @@ def test_grant_read_access_lets_a_role_read_views_and_future_tables(
|
|
|
262
265
|
with psycopg.connect(database_url, autocommit=True) as admin:
|
|
263
266
|
admin.execute(f"DROP OWNED BY {role}")
|
|
264
267
|
admin.execute(f"DROP ROLE {role}")
|
|
268
|
+
db = admin.execute("SELECT current_database()").fetchone()[0]
|
|
269
|
+
admin.execute(f'ALTER DATABASE "{db}" RESET search_path')
|
|
265
270
|
|
|
266
271
|
|
|
267
272
|
def test_grant_read_access_requires_an_existing_role(migrated_store: Store) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{github_actions_ingester-0.2.0 → github_actions_ingester-0.2.1}/tests/test_workflow_schedule.py
RENAMED
|
File without changes
|