pineforge-data 0.3.0__tar.gz → 0.3.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.
- pineforge_data-0.3.1/.github/workflows/ci.yml +206 -0
- pineforge_data-0.3.1/.github/workflows/native-feed.yml +80 -0
- pineforge_data-0.3.1/.github/workflows/pineforge-release.yml +86 -0
- pineforge_data-0.3.1/.github/workflows/release-report.yml +96 -0
- pineforge_data-0.3.1/.github/workflows/release.yml +399 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/.gitignore +1 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/PKG-INFO +6 -3
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/README.md +4 -2
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docker/server.Dockerfile +3 -1
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/providers.md +16 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/backtesting.md +5 -2
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/data-model.md +2 -2
- pineforge_data-0.3.1/docs/providers/fxmacrodata.md +143 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers.md +9 -0
- pineforge_data-0.3.1/docs/release-receipt-report.md +363 -0
- pineforge_data-0.3.1/docs/releasing.md +262 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/server.md +15 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/mkdocs.yml +2 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/pyproject.toml +6 -2
- pineforge_data-0.3.1/scripts/exercise_distribution.py +82 -0
- pineforge_data-0.3.1/scripts/release_adapter.py +60 -0
- pineforge_data-0.3.1/scripts/release_chain/__init__.py +1 -0
- pineforge_data-0.3.1/scripts/release_chain/automation.py +528 -0
- pineforge_data-0.3.1/scripts/release_chain/contract.py +473 -0
- pineforge_data-0.3.1/scripts/release_chain/evidence.py +206 -0
- pineforge_data-0.3.1/scripts/release_chain/publishing.py +498 -0
- pineforge_data-0.3.1/scripts/release_chain/receipts.py +116 -0
- pineforge_data-0.3.1/scripts/release_chain/reporting.py +369 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/__init__.py +14 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/docker_runtime.py +7 -2
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/__init__.py +18 -0
- pineforge_data-0.3.1/src/pineforge_data/providers/fxmacrodata.py +493 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/release_contract.py +20 -3
- pineforge_data-0.3.1/src/pineforge_data/release_lock.json +55 -0
- pineforge_data-0.3.1/src/pineforge_data/release_manifest.json +1 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/server.py +6 -1
- pineforge_data-0.3.1/tests/fixtures/fxmacrodata_usd_non_farm_payrolls.json +91 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/README.md +20 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/amd64-config.json +13 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/amd64-manifest.json +8 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/arm64-config.json +13 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/arm64-manifest.json +8 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/facts.json +7 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/index.json +20 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/ready-event.json +47 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/receipt-policy.json +48 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/release-manifest.json +42 -0
- pineforge_data-0.3.1/tests/fixtures/release_chain/sample-release-receipt.json +112 -0
- pineforge_data-0.3.1/tests/hub_receipt_validator.py +267 -0
- pineforge_data-0.3.1/tests/test_consumer_receipt.py +422 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_docker_integration.py +34 -2
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_docker_runtime.py +5 -1
- pineforge_data-0.3.1/tests/test_fxmacrodata.py +443 -0
- pineforge_data-0.3.1/tests/test_release_adapter.py +691 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_release_contract.py +40 -0
- pineforge_data-0.3.0/.github/workflows/ci.yml +0 -90
- pineforge_data-0.3.0/.github/workflows/release.yml +0 -231
- pineforge_data-0.3.0/docs/releasing.md +0 -87
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/.dockerignore +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/.github/release.yml +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/.github/workflows/docs.yml +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/CONTRIBUTING.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/LICENSE +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/backtesting.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/index.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/models.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/requests.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/server.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/streaming.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/getting-started.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/index.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/provider-contract.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/ccxt.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/csv.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/sqlalchemy.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/sqlite.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/tabular-schema.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/scripts/backtest.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/scripts/bump_version.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/scripts/run_database_e2e.sh +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/scripts/verify_release.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/backtest.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/cli/__init__.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/cli/backtest.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/compile_cache.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/engine.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/errors.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/models.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/base.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/ccxt.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/local.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/registry.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/sqlalchemy.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/tabular.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/py.typed +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/requests.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/server_client.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/database/docker-compose.yml +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/fixtures/CORPUS.md +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/fixtures/corpus_ohlcv_ETH-USDT-USDT_1m_sample.csv +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/fixtures/sma_cross.pine +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/in_process_release_check.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_backtest.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_ccxt.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_compile_cache.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_database_e2e.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_documentation.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_engine.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_models.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_providers.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_release.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_server.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_server_client.py +0 -0
- {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_tabular_providers.py +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
actions: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: Python ${{ matrix.python-version }}
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v7
|
|
22
|
+
- uses: actions/setup-python@v6
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
cache: pip
|
|
26
|
+
- run: python -m pip install --upgrade pip
|
|
27
|
+
- run: python -m pip install -e '.[dev,ccxt,release]'
|
|
28
|
+
- run: ruff check .
|
|
29
|
+
- run: mypy src
|
|
30
|
+
- run: pytest
|
|
31
|
+
- if: matrix.python-version == '3.13'
|
|
32
|
+
run: python -m build
|
|
33
|
+
- if: matrix.python-version == '3.13'
|
|
34
|
+
name: The sdist does not ship the native feed
|
|
35
|
+
run: |
|
|
36
|
+
if tar -tzf dist/*.tar.gz | grep -E '^[^/]+/native/'; then
|
|
37
|
+
echo "native/ must stay out of the Python sdist" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
- if: matrix.python-version == '3.13'
|
|
41
|
+
run: python -m twine check --strict dist/*
|
|
42
|
+
|
|
43
|
+
docker:
|
|
44
|
+
name: Docker runtime
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v7
|
|
48
|
+
- uses: actions/setup-python@v6
|
|
49
|
+
with:
|
|
50
|
+
python-version: "3.13"
|
|
51
|
+
cache: pip
|
|
52
|
+
- run: python -m pip install -e '.[dev]'
|
|
53
|
+
- run: PINEFORGE_DOCKER_TEST=1 pytest tests/test_docker_integration.py --junitxml=docker.xml
|
|
54
|
+
- name: Require every real runtime test with zero skips
|
|
55
|
+
env:
|
|
56
|
+
PYTHONPATH: scripts
|
|
57
|
+
run: python -m release_chain.evidence junit --suite docker --path docker.xml
|
|
58
|
+
|
|
59
|
+
database-e2e:
|
|
60
|
+
name: Database E2E
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
services:
|
|
63
|
+
mysql:
|
|
64
|
+
image: mysql:8.4
|
|
65
|
+
env:
|
|
66
|
+
MYSQL_DATABASE: pineforge
|
|
67
|
+
MYSQL_USER: pineforge
|
|
68
|
+
MYSQL_PASSWORD: pineforge
|
|
69
|
+
MYSQL_ROOT_PASSWORD: pineforge-root
|
|
70
|
+
ports:
|
|
71
|
+
- 3306:3306
|
|
72
|
+
options: >-
|
|
73
|
+
--health-cmd="mysqladmin ping --host=127.0.0.1 --user=pineforge --password=pineforge --silent"
|
|
74
|
+
--health-interval=2s
|
|
75
|
+
--health-timeout=5s
|
|
76
|
+
--health-retries=30
|
|
77
|
+
--health-start-period=15s
|
|
78
|
+
postgres:
|
|
79
|
+
image: postgres:17
|
|
80
|
+
env:
|
|
81
|
+
POSTGRES_DB: pineforge
|
|
82
|
+
POSTGRES_USER: pineforge
|
|
83
|
+
POSTGRES_PASSWORD: pineforge
|
|
84
|
+
ports:
|
|
85
|
+
- 5432:5432
|
|
86
|
+
options: >-
|
|
87
|
+
--health-cmd="pg_isready --username=pineforge --dbname=pineforge"
|
|
88
|
+
--health-interval=2s
|
|
89
|
+
--health-timeout=5s
|
|
90
|
+
--health-retries=30
|
|
91
|
+
--health-start-period=10s
|
|
92
|
+
env:
|
|
93
|
+
PINEFORGE_DATABASE_E2E: "1"
|
|
94
|
+
PINEFORGE_CORPUS_OHLCV: tests/fixtures/corpus_ohlcv_ETH-USDT-USDT_1m_sample.csv
|
|
95
|
+
PINEFORGE_MYSQL_URL: mysql+pymysql://pineforge:pineforge@127.0.0.1:3306/pineforge
|
|
96
|
+
PINEFORGE_POSTGRES_URL: postgresql+psycopg://pineforge:pineforge@127.0.0.1:5432/pineforge
|
|
97
|
+
steps:
|
|
98
|
+
- uses: actions/checkout@v7
|
|
99
|
+
- uses: actions/setup-python@v6
|
|
100
|
+
with:
|
|
101
|
+
python-version: "3.13"
|
|
102
|
+
cache: pip
|
|
103
|
+
- run: python -m pip install --upgrade pip
|
|
104
|
+
- run: python -m pip install -e '.[dev,database-e2e]'
|
|
105
|
+
- run: pytest tests/test_database_e2e.py --junitxml=database.xml
|
|
106
|
+
- name: Require the disposable database E2E inventory with zero skips
|
|
107
|
+
env:
|
|
108
|
+
PYTHONPATH: scripts
|
|
109
|
+
run: python -m release_chain.evidence junit --suite database --path database.xml
|
|
110
|
+
|
|
111
|
+
contract:
|
|
112
|
+
name: Release contract
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
steps:
|
|
115
|
+
- uses: actions/checkout@v7
|
|
116
|
+
with:
|
|
117
|
+
persist-credentials: false
|
|
118
|
+
- uses: actions/setup-python@v6
|
|
119
|
+
with:
|
|
120
|
+
python-version: '3.13'
|
|
121
|
+
- name: Verify synchronized defaults, immutable manifest, source, facts and both OCI children
|
|
122
|
+
env:
|
|
123
|
+
GH_TOKEN: ${{ github.token }}
|
|
124
|
+
run: python scripts/release_adapter.py local
|
|
125
|
+
|
|
126
|
+
distribution:
|
|
127
|
+
name: Distribution
|
|
128
|
+
runs-on: ubuntu-latest
|
|
129
|
+
steps:
|
|
130
|
+
- uses: actions/checkout@v7
|
|
131
|
+
with:
|
|
132
|
+
persist-credentials: false
|
|
133
|
+
- uses: actions/setup-python@v6
|
|
134
|
+
with:
|
|
135
|
+
python-version: '3.13'
|
|
136
|
+
cache: pip
|
|
137
|
+
- run: python -m pip install -e '.[release]'
|
|
138
|
+
- name: Set reproducible archive timestamps
|
|
139
|
+
run: echo "SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)" >> "$GITHUB_ENV"
|
|
140
|
+
- run: python -m build
|
|
141
|
+
- run: python -m twine check --strict dist/*
|
|
142
|
+
- name: The canonical sdist does not ship the native feed
|
|
143
|
+
run: |
|
|
144
|
+
if tar -tzf dist/*.tar.gz | grep -E '^[^/]+/native/'; then
|
|
145
|
+
echo "native/ must stay out of the Python sdist" >&2
|
|
146
|
+
exit 1
|
|
147
|
+
fi
|
|
148
|
+
- name: Install and backtest both built distributions without importing checkout source
|
|
149
|
+
env:
|
|
150
|
+
PYTHONPATH: scripts
|
|
151
|
+
run: |
|
|
152
|
+
set -euo pipefail
|
|
153
|
+
version="$(python scripts/bump_version.py current)"
|
|
154
|
+
image="$(python -c 'from pathlib import Path; from release_chain.contract import runtime_pin; print(runtime_pin(Path.cwd()))')"
|
|
155
|
+
for artifact in "$GITHUB_WORKSPACE"/dist/*; do
|
|
156
|
+
case "$artifact" in
|
|
157
|
+
*.whl) kind=wheel ;;
|
|
158
|
+
*.tar.gz) kind=sdist ;;
|
|
159
|
+
*) exit 1 ;;
|
|
160
|
+
esac
|
|
161
|
+
environment="$RUNNER_TEMP/distribution-$kind"
|
|
162
|
+
python -m venv "$environment"
|
|
163
|
+
"$environment/bin/python" -m pip install "$artifact"
|
|
164
|
+
"$environment/bin/python" -I scripts/exercise_distribution.py \
|
|
165
|
+
--version "$version" --image "$image" --source "$GITHUB_WORKSPACE/src" \
|
|
166
|
+
--fixtures "$GITHUB_WORKSPACE/tests/fixtures" \
|
|
167
|
+
--output "$RUNNER_TEMP/exercise-$kind.json"
|
|
168
|
+
done
|
|
169
|
+
- uses: actions/upload-artifact@v5
|
|
170
|
+
with:
|
|
171
|
+
name: distribution-runtime-evidence-${{ github.run_attempt }}
|
|
172
|
+
path: ${{ runner.temp }}/exercise-*.json
|
|
173
|
+
if-no-files-found: error
|
|
174
|
+
- uses: actions/upload-artifact@v5
|
|
175
|
+
with:
|
|
176
|
+
name: python-package-distributions-${{ github.run_attempt }}
|
|
177
|
+
path: dist/
|
|
178
|
+
if-no-files-found: error
|
|
179
|
+
|
|
180
|
+
ready:
|
|
181
|
+
name: release/ready
|
|
182
|
+
if: always()
|
|
183
|
+
needs: [test, contract, docker, database-e2e, distribution]
|
|
184
|
+
runs-on: ubuntu-latest
|
|
185
|
+
steps:
|
|
186
|
+
- uses: actions/checkout@v7
|
|
187
|
+
with:
|
|
188
|
+
persist-credentials: false
|
|
189
|
+
- uses: actions/setup-python@v6
|
|
190
|
+
with:
|
|
191
|
+
python-version: '3.13'
|
|
192
|
+
- uses: actions/download-artifact@v6
|
|
193
|
+
with:
|
|
194
|
+
name: python-package-distributions-${{ github.run_attempt }}
|
|
195
|
+
path: dist/
|
|
196
|
+
- name: Require the complete job inventory and bind evidence to this head/base/tree
|
|
197
|
+
env:
|
|
198
|
+
PYTHONPATH: scripts
|
|
199
|
+
NEEDS_JSON: ${{ toJSON(needs) }}
|
|
200
|
+
GH_TOKEN: ${{ github.token }}
|
|
201
|
+
run: python -m release_chain.evidence aggregate --path dist
|
|
202
|
+
- uses: actions/upload-artifact@v5
|
|
203
|
+
with:
|
|
204
|
+
name: release-evidence-${{ github.run_attempt }}
|
|
205
|
+
path: evidence.json
|
|
206
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
name: Native feed
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
paths: ['native/feed/**', '.github/workflows/native-feed.yml']
|
|
6
|
+
pull_request:
|
|
7
|
+
paths: ['native/feed/**', '.github/workflows/native-feed.yml']
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
release:
|
|
15
|
+
name: Release (${{ matrix.os }})
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-14]
|
|
20
|
+
runs-on: ${{ matrix.os }}
|
|
21
|
+
timeout-minutes: 30
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v7
|
|
24
|
+
- name: Linux prerequisites
|
|
25
|
+
if: runner.os == 'Linux'
|
|
26
|
+
run: sudo apt-get update && sudo apt-get install -y cmake ninja-build libssl-dev
|
|
27
|
+
- name: macOS prerequisites
|
|
28
|
+
if: runner.os == 'macOS'
|
|
29
|
+
run: |
|
|
30
|
+
brew install ninja openssl@3
|
|
31
|
+
echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV"
|
|
32
|
+
- name: Pinned WebSocket curl
|
|
33
|
+
run: bash native/feed/tests/build_ci_curl.sh "$RUNNER_TEMP/curl"
|
|
34
|
+
- name: Configure, build and test
|
|
35
|
+
run: |
|
|
36
|
+
export LD_LIBRARY_PATH="$RUNNER_TEMP/curl/lib:${LD_LIBRARY_PATH:-}"
|
|
37
|
+
export DYLD_LIBRARY_PATH="$RUNNER_TEMP/curl/lib:${DYLD_LIBRARY_PATH:-}"
|
|
38
|
+
cmake -S native/feed -B build-feed -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
|
39
|
+
-DCURL_DIR="$RUNNER_TEMP/curl/lib/cmake/CURL"
|
|
40
|
+
cmake --build build-feed -j4
|
|
41
|
+
ctest --test-dir build-feed --output-on-failure
|
|
42
|
+
|
|
43
|
+
asan-ubsan:
|
|
44
|
+
name: ASan + UBSan
|
|
45
|
+
runs-on: ubuntu-24.04
|
|
46
|
+
timeout-minutes: 30
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v7
|
|
49
|
+
- run: sudo apt-get update && sudo apt-get install -y ninja-build libssl-dev clang-18
|
|
50
|
+
- run: bash native/feed/tests/build_ci_curl.sh "$RUNNER_TEMP/curl"
|
|
51
|
+
- name: Instrumented core and mock venue
|
|
52
|
+
env:
|
|
53
|
+
ASAN_OPTIONS: detect_leaks=1:abort_on_error=1
|
|
54
|
+
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
|
|
55
|
+
run: |
|
|
56
|
+
export LD_LIBRARY_PATH="$RUNNER_TEMP/curl/lib:${LD_LIBRARY_PATH:-}"
|
|
57
|
+
cmake -S native/feed -B build-feed -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
|
58
|
+
-DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_C_COMPILER=clang-18 -DCURL_DIR="$RUNNER_TEMP/curl/lib/cmake/CURL" \
|
|
59
|
+
-DPINEFORGE_FEED_SANITIZER=address,undefined
|
|
60
|
+
cmake --build build-feed -j4
|
|
61
|
+
ctest --test-dir build-feed --output-on-failure
|
|
62
|
+
|
|
63
|
+
tsan:
|
|
64
|
+
name: TSan
|
|
65
|
+
runs-on: ubuntu-24.04
|
|
66
|
+
timeout-minutes: 30
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v7
|
|
69
|
+
- run: sudo apt-get update && sudo apt-get install -y ninja-build libssl-dev clang-18
|
|
70
|
+
- run: bash native/feed/tests/build_ci_curl.sh "$RUNNER_TEMP/curl"
|
|
71
|
+
- name: Threaded receive, healing, termination and serve fan-out
|
|
72
|
+
env:
|
|
73
|
+
TSAN_OPTIONS: halt_on_error=1
|
|
74
|
+
run: |
|
|
75
|
+
export LD_LIBRARY_PATH="$RUNNER_TEMP/curl/lib:${LD_LIBRARY_PATH:-}"
|
|
76
|
+
cmake -S native/feed -B build-feed -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
|
77
|
+
-DCMAKE_CXX_COMPILER=clang++-18 -DCMAKE_C_COMPILER=clang-18 -DCURL_DIR="$RUNNER_TEMP/curl/lib/cmake/CURL" \
|
|
78
|
+
-DPINEFORGE_FEED_SANITIZER=thread
|
|
79
|
+
cmake --build build-feed -j4
|
|
80
|
+
ctest --test-dir build-feed --output-on-failure
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: PineForge release adapter
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
repository_dispatch:
|
|
5
|
+
types: [pineforge-release]
|
|
6
|
+
workflow_run:
|
|
7
|
+
workflows: [CI, Release]
|
|
8
|
+
types: [completed]
|
|
9
|
+
# schedule (9,24,39,54 * * * *) removed 2026-10-18: every run failed and Actions cost is capped (owner); dispatch and the triggers above remain.
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
inputs:
|
|
12
|
+
publication_run:
|
|
13
|
+
description: Completed Release run to verify (automated repair uses the immutable package tag)
|
|
14
|
+
required: false
|
|
15
|
+
type: string
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
concurrency:
|
|
21
|
+
group: data-release-adapter
|
|
22
|
+
cancel-in-progress: false
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
reconcile:
|
|
26
|
+
# Automatic triggers (schedule, workflow_run) run only once the chain is
|
|
27
|
+
# configured (RELEASE_APP_ID set); a hub dispatch or a manual run without it
|
|
28
|
+
# still fails loudly.
|
|
29
|
+
if: >-
|
|
30
|
+
(vars.RELEASE_APP_ID != '' || (github.event_name != 'schedule' && github.event_name != 'workflow_run')) &&
|
|
31
|
+
(github.event_name != 'workflow_run' ||
|
|
32
|
+
(github.event.workflow_run.name == 'CI' && github.event.workflow_run.event == 'pull_request') ||
|
|
33
|
+
github.event.workflow_run.name == 'Release')
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/create-github-app-token@v2
|
|
37
|
+
id: app
|
|
38
|
+
with:
|
|
39
|
+
app-id: ${{ vars.RELEASE_APP_ID }}
|
|
40
|
+
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
|
41
|
+
owner: ${{ github.repository_owner }}
|
|
42
|
+
repositories: ${{ github.event.repository.name }}
|
|
43
|
+
permission-contents: write
|
|
44
|
+
permission-pull-requests: write
|
|
45
|
+
permission-actions: write
|
|
46
|
+
- uses: actions/checkout@v7
|
|
47
|
+
with:
|
|
48
|
+
ref: main
|
|
49
|
+
fetch-depth: 0
|
|
50
|
+
token: ${{ steps.app.outputs.token }}
|
|
51
|
+
- uses: actions/setup-python@v6
|
|
52
|
+
with:
|
|
53
|
+
python-version: '3.13'
|
|
54
|
+
- name: Verify ready event, reconcile update PR and respect release gates
|
|
55
|
+
if: github.event.workflow_run.name != 'Release' && inputs.publication_run == ''
|
|
56
|
+
env:
|
|
57
|
+
GH_TOKEN: ${{ steps.app.outputs.token }}
|
|
58
|
+
RELEASE_BOT_LOGIN: ${{ steps.app.outputs.app-slug }}[bot]
|
|
59
|
+
run: python scripts/release_adapter.py reconcile --receipt "$RUNNER_TEMP/release-receipt.json"
|
|
60
|
+
- name: Verify the completed publication from trusted coordinator code
|
|
61
|
+
if: github.event.workflow_run.name == 'Release' || inputs.publication_run != ''
|
|
62
|
+
env:
|
|
63
|
+
PYTHONPATH: scripts
|
|
64
|
+
GH_TOKEN: ${{ steps.app.outputs.token }}
|
|
65
|
+
PUBLICATION_RUN: ${{ inputs.publication_run || github.event.workflow_run.id }}
|
|
66
|
+
run: python -m release_chain.reporting publication --run "$PUBLICATION_RUN" --receipt "$RUNNER_TEMP/release-receipt.json"
|
|
67
|
+
- uses: actions/upload-artifact@v5
|
|
68
|
+
if: always()
|
|
69
|
+
with:
|
|
70
|
+
name: release-stage-${{ github.run_attempt }}-10-adapter
|
|
71
|
+
path: ${{ runner.temp }}/release-receipt.json
|
|
72
|
+
if-no-files-found: warn
|
|
73
|
+
|
|
74
|
+
report:
|
|
75
|
+
if: always() && needs.reconcile.result != 'skipped'
|
|
76
|
+
needs: reconcile
|
|
77
|
+
uses: ./.github/workflows/release-report.yml
|
|
78
|
+
permissions:
|
|
79
|
+
contents: read
|
|
80
|
+
actions: read
|
|
81
|
+
issues: write
|
|
82
|
+
with:
|
|
83
|
+
hub: true
|
|
84
|
+
failed: ${{ needs.reconcile.result != 'success' }}
|
|
85
|
+
stage: reconcile
|
|
86
|
+
secrets: inherit
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
name: Release receipt and incident
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
failed:
|
|
7
|
+
type: boolean
|
|
8
|
+
required: true
|
|
9
|
+
stage:
|
|
10
|
+
type: string
|
|
11
|
+
required: true
|
|
12
|
+
hub:
|
|
13
|
+
type: boolean
|
|
14
|
+
default: false
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
actions: read
|
|
19
|
+
issues: write
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
report:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
|
+
with:
|
|
27
|
+
ref: main
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: actions/setup-python@v6
|
|
30
|
+
with:
|
|
31
|
+
python-version: '3.13'
|
|
32
|
+
- uses: actions/download-artifact@v6
|
|
33
|
+
with:
|
|
34
|
+
pattern: release-stage-${{ github.run_attempt }}-*
|
|
35
|
+
path: receipts
|
|
36
|
+
- name: Preserve contract evidence and alert the configured owner
|
|
37
|
+
id: receipt
|
|
38
|
+
env:
|
|
39
|
+
PYTHONPATH: scripts
|
|
40
|
+
GH_TOKEN: ${{ github.token }}
|
|
41
|
+
FAILED: ${{ inputs.failed }}
|
|
42
|
+
STAGE: ${{ inputs.stage }}
|
|
43
|
+
RELEASE_ALERT_OWNER: ${{ vars.RELEASE_ALERT_OWNER }}
|
|
44
|
+
run: python -m release_chain.reporting collect
|
|
45
|
+
- name: Upload the single hub receipt snapshot
|
|
46
|
+
id: upload
|
|
47
|
+
if: inputs.hub && steps.receipt.outputs.has_event == 'true'
|
|
48
|
+
uses: actions/upload-artifact@v5
|
|
49
|
+
with:
|
|
50
|
+
name: release-receipt-data
|
|
51
|
+
path: release-receipt.json
|
|
52
|
+
if-no-files-found: error
|
|
53
|
+
overwrite: true
|
|
54
|
+
retention-days: 90
|
|
55
|
+
- name: Mint a consumer-only token for the immutable completion asset
|
|
56
|
+
if: inputs.hub && steps.upload.outcome == 'success'
|
|
57
|
+
uses: actions/create-github-app-token@v2
|
|
58
|
+
id: consumer
|
|
59
|
+
with:
|
|
60
|
+
app-id: ${{ vars.RELEASE_APP_ID }}
|
|
61
|
+
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
|
62
|
+
owner: ${{ github.repository_owner }}
|
|
63
|
+
repositories: ${{ github.event.repository.name }}
|
|
64
|
+
permission-contents: write
|
|
65
|
+
permission-pull-requests: read
|
|
66
|
+
- name: Retain this same verified contract as an immutable distribution asset
|
|
67
|
+
if: inputs.hub && steps.upload.outcome == 'success'
|
|
68
|
+
env:
|
|
69
|
+
PYTHONPATH: scripts
|
|
70
|
+
GH_TOKEN: ${{ steps.consumer.outputs.token }}
|
|
71
|
+
run: python -m release_chain.reporting retain
|
|
72
|
+
- name: Mint a separate token scoped only to the hub
|
|
73
|
+
if: inputs.hub && steps.upload.outcome == 'success'
|
|
74
|
+
uses: actions/create-github-app-token@v2
|
|
75
|
+
id: hub
|
|
76
|
+
with:
|
|
77
|
+
app-id: ${{ vars.RELEASE_APP_ID }}
|
|
78
|
+
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
|
79
|
+
owner: pineforge-4pass
|
|
80
|
+
repositories: pineforge-release
|
|
81
|
+
permission-contents: write
|
|
82
|
+
- name: Wake the hub collector with the exact consumer-signal/v1 callback
|
|
83
|
+
if: inputs.hub && steps.upload.outcome == 'success'
|
|
84
|
+
env:
|
|
85
|
+
PYTHONPATH: scripts
|
|
86
|
+
GH_TOKEN: ${{ steps.hub.outputs.token }}
|
|
87
|
+
run: python -m release_chain.reporting callback
|
|
88
|
+
- name: Alert locally if receipt delivery or reporting failed
|
|
89
|
+
if: failure()
|
|
90
|
+
env:
|
|
91
|
+
PYTHONPATH: scripts
|
|
92
|
+
GH_TOKEN: ${{ github.token }}
|
|
93
|
+
FAILED: 'true'
|
|
94
|
+
STAGE: receipt-transport
|
|
95
|
+
RELEASE_ALERT_OWNER: ${{ vars.RELEASE_ALERT_OWNER }}
|
|
96
|
+
run: python -m release_chain.reporting collect --receipt incident-release-receipt.json
|