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.
Files changed (114) hide show
  1. pineforge_data-0.3.1/.github/workflows/ci.yml +206 -0
  2. pineforge_data-0.3.1/.github/workflows/native-feed.yml +80 -0
  3. pineforge_data-0.3.1/.github/workflows/pineforge-release.yml +86 -0
  4. pineforge_data-0.3.1/.github/workflows/release-report.yml +96 -0
  5. pineforge_data-0.3.1/.github/workflows/release.yml +399 -0
  6. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/.gitignore +1 -0
  7. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/PKG-INFO +6 -3
  8. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/README.md +4 -2
  9. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docker/server.Dockerfile +3 -1
  10. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/providers.md +16 -0
  11. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/backtesting.md +5 -2
  12. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/data-model.md +2 -2
  13. pineforge_data-0.3.1/docs/providers/fxmacrodata.md +143 -0
  14. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers.md +9 -0
  15. pineforge_data-0.3.1/docs/release-receipt-report.md +363 -0
  16. pineforge_data-0.3.1/docs/releasing.md +262 -0
  17. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/server.md +15 -0
  18. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/mkdocs.yml +2 -0
  19. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/pyproject.toml +6 -2
  20. pineforge_data-0.3.1/scripts/exercise_distribution.py +82 -0
  21. pineforge_data-0.3.1/scripts/release_adapter.py +60 -0
  22. pineforge_data-0.3.1/scripts/release_chain/__init__.py +1 -0
  23. pineforge_data-0.3.1/scripts/release_chain/automation.py +528 -0
  24. pineforge_data-0.3.1/scripts/release_chain/contract.py +473 -0
  25. pineforge_data-0.3.1/scripts/release_chain/evidence.py +206 -0
  26. pineforge_data-0.3.1/scripts/release_chain/publishing.py +498 -0
  27. pineforge_data-0.3.1/scripts/release_chain/receipts.py +116 -0
  28. pineforge_data-0.3.1/scripts/release_chain/reporting.py +369 -0
  29. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/__init__.py +14 -0
  30. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/docker_runtime.py +7 -2
  31. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/__init__.py +18 -0
  32. pineforge_data-0.3.1/src/pineforge_data/providers/fxmacrodata.py +493 -0
  33. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/release_contract.py +20 -3
  34. pineforge_data-0.3.1/src/pineforge_data/release_lock.json +55 -0
  35. pineforge_data-0.3.1/src/pineforge_data/release_manifest.json +1 -0
  36. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/server.py +6 -1
  37. pineforge_data-0.3.1/tests/fixtures/fxmacrodata_usd_non_farm_payrolls.json +91 -0
  38. pineforge_data-0.3.1/tests/fixtures/release_chain/README.md +20 -0
  39. pineforge_data-0.3.1/tests/fixtures/release_chain/amd64-config.json +13 -0
  40. pineforge_data-0.3.1/tests/fixtures/release_chain/amd64-manifest.json +8 -0
  41. pineforge_data-0.3.1/tests/fixtures/release_chain/arm64-config.json +13 -0
  42. pineforge_data-0.3.1/tests/fixtures/release_chain/arm64-manifest.json +8 -0
  43. pineforge_data-0.3.1/tests/fixtures/release_chain/facts.json +7 -0
  44. pineforge_data-0.3.1/tests/fixtures/release_chain/index.json +20 -0
  45. pineforge_data-0.3.1/tests/fixtures/release_chain/ready-event.json +47 -0
  46. pineforge_data-0.3.1/tests/fixtures/release_chain/receipt-policy.json +48 -0
  47. pineforge_data-0.3.1/tests/fixtures/release_chain/release-manifest.json +42 -0
  48. pineforge_data-0.3.1/tests/fixtures/release_chain/sample-release-receipt.json +112 -0
  49. pineforge_data-0.3.1/tests/hub_receipt_validator.py +267 -0
  50. pineforge_data-0.3.1/tests/test_consumer_receipt.py +422 -0
  51. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_docker_integration.py +34 -2
  52. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_docker_runtime.py +5 -1
  53. pineforge_data-0.3.1/tests/test_fxmacrodata.py +443 -0
  54. pineforge_data-0.3.1/tests/test_release_adapter.py +691 -0
  55. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_release_contract.py +40 -0
  56. pineforge_data-0.3.0/.github/workflows/ci.yml +0 -90
  57. pineforge_data-0.3.0/.github/workflows/release.yml +0 -231
  58. pineforge_data-0.3.0/docs/releasing.md +0 -87
  59. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/.dockerignore +0 -0
  60. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/.github/release.yml +0 -0
  61. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/.github/workflows/docs.yml +0 -0
  62. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/CONTRIBUTING.md +0 -0
  63. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/LICENSE +0 -0
  64. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/backtesting.md +0 -0
  65. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/index.md +0 -0
  66. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/models.md +0 -0
  67. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/requests.md +0 -0
  68. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/server.md +0 -0
  69. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/api/streaming.md +0 -0
  70. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/getting-started.md +0 -0
  71. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/index.md +0 -0
  72. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/provider-contract.md +0 -0
  73. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/ccxt.md +0 -0
  74. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/csv.md +0 -0
  75. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/sqlalchemy.md +0 -0
  76. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/sqlite.md +0 -0
  77. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/docs/providers/tabular-schema.md +0 -0
  78. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/scripts/backtest.py +0 -0
  79. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/scripts/bump_version.py +0 -0
  80. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/scripts/run_database_e2e.sh +0 -0
  81. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/scripts/verify_release.py +0 -0
  82. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/backtest.py +0 -0
  83. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/cli/__init__.py +0 -0
  84. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/cli/backtest.py +0 -0
  85. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/compile_cache.py +0 -0
  86. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/engine.py +0 -0
  87. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/errors.py +0 -0
  88. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/models.py +0 -0
  89. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/base.py +0 -0
  90. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/ccxt.py +0 -0
  91. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/local.py +0 -0
  92. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/registry.py +0 -0
  93. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/sqlalchemy.py +0 -0
  94. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/providers/tabular.py +0 -0
  95. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/py.typed +0 -0
  96. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/requests.py +0 -0
  97. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/src/pineforge_data/server_client.py +0 -0
  98. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/database/docker-compose.yml +0 -0
  99. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/fixtures/CORPUS.md +0 -0
  100. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/fixtures/corpus_ohlcv_ETH-USDT-USDT_1m_sample.csv +0 -0
  101. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/fixtures/sma_cross.pine +0 -0
  102. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/in_process_release_check.py +0 -0
  103. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_backtest.py +0 -0
  104. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_ccxt.py +0 -0
  105. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_compile_cache.py +0 -0
  106. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_database_e2e.py +0 -0
  107. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_documentation.py +0 -0
  108. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_engine.py +0 -0
  109. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_models.py +0 -0
  110. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_providers.py +0 -0
  111. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_release.py +0 -0
  112. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_server.py +0 -0
  113. {pineforge_data-0.3.0 → pineforge_data-0.3.1}/tests/test_server_client.py +0 -0
  114. {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