Skip to content

Commit fa7401d

Browse files
ci: add weekly spec-sync workflow, least-privilege permissions, and documentation
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 1d4676b commit fa7401d

7 files changed

Lines changed: 176 additions & 10 deletions

File tree

.github/workflows/create_release.yml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@ on:
33
push:
44
tags:
55
- '*'
6+
7+
permissions:
8+
contents: write
9+
610
jobs:
711
build:
812
runs-on: ubuntu-latest
913
steps:
10-
- uses: actions/checkout@v6
11-
- name: Create a Release
12-
uses: elgohr/Github-Release-Action@v5
13-
env:
14-
GITHUB_TOKEN: "${{ secrets.RELEASE_TOKEN }}"
15-
with:
16-
title: ${{ github.ref }}
14+
- uses: actions/checkout@v6
15+
- name: Create a Release
16+
uses: elgohr/Github-Release-Action@v5
17+
env:
18+
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
19+
with:
20+
title: ${{ github.ref }}

.github/workflows/dart.yml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
pull_request:
77
branches: [ master ]
88

9+
permissions:
10+
contents: read
11+
912
jobs:
1013
build:
1114
runs-on: ${{ matrix.os }}
@@ -22,12 +25,26 @@ jobs:
2225

2326
- name: Install dependencies
2427
run: dart pub get
25-
- name: Dart Analyzer
26-
run: dart analyze
28+
29+
- name: Dart Analyzer (Strict mode)
30+
run: dart analyze --fatal-infos
31+
2732
- name: Check Dart Format
2833
if: ${{ matrix.sdk == 'stable' }}
2934
run: dart format --set-exit-if-changed -onone .
30-
- name: Unit tests
35+
36+
- name: Verify Spec Sync Drift
37+
if: ${{ matrix.sdk == 'stable' }}
38+
run: dart run tool/spec_sync.dart --check --offline
39+
40+
- name: Unit and Contract Tests
3141
run: dart test
42+
43+
- name: Coverage Ratchet Check
44+
if: ${{ matrix.sdk == 'stable' }}
45+
run: |
46+
dart test --coverage=coverage
47+
dart run tool/coverage_check.dart coverage/lcov.info --min=30.0
48+
3249
- name: Check if Publishable
3350
run: dart pub publish --dry-run

.github/workflows/publish_demos.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ on:
33
push:
44
branches:
55
- master
6+
7+
permissions:
8+
contents: write
9+
610
jobs:
711
build-and-deploy:
812
runs-on: ubuntu-latest

.github/workflows/spec_sync.yml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
name: Weekly GitHub OpenAPI Spec Sync
2+
3+
on:
4+
schedule:
5+
# Run every Monday at 04:00 UTC
6+
- cron: '0 4 * * 1'
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
spec-sync:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: write
17+
pull-requests: write
18+
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v6
22+
with:
23+
fetch-depth: 0
24+
25+
- name: Setup Dart SDK
26+
uses: dart-lang/setup-dart@v1
27+
with:
28+
sdk: stable
29+
30+
- name: Install dependencies
31+
run: dart pub get
32+
33+
- name: Check for upstream OpenAPI specification updates
34+
id: check_upstream
35+
run: |
36+
mkdir -p .dart_tool
37+
dart run tool/spec_sync.dart --offline --report-json > .dart_tool/spec_report_before.json
38+
echo "Checked current spec baseline."
39+
40+
- name: Regenerate OpenAPI catalog and docs
41+
id: run_sync
42+
run: |
43+
dart run tool/spec_sync.dart --update
44+
git status --porcelain > .dart_tool/git_status.txt
45+
if [ -s .dart_tool/git_status.txt ]; then
46+
echo "changes_detected=true" >> "$GITHUB_OUTPUT"
47+
else
48+
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
49+
fi
50+
51+
- name: Validate generated code and contracts
52+
if: steps.run_sync.outputs.changes_detected == 'true'
53+
run: |
54+
dart format --output=none --set-exit-if-changed .
55+
dart analyze --fatal-infos
56+
dart test
57+
dart pub publish --dry-run
58+
59+
- name: Create or update spec-sync pull request
60+
if: steps.run_sync.outputs.changes_detected == 'true'
61+
env:
62+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63+
run: |
64+
BRANCH_NAME="automation/github-openapi-spec-sync"
65+
git config user.name "github-actions[bot]"
66+
git config user.email "github-actions[bot]@users.noreply.github.com"
67+
68+
git checkout -B "$BRANCH_NAME"
69+
git add lib/src/common/generated/rest_contracts.g.dart docs/api-coverage.md tool/spec/
70+
git commit -m "chore(spec): synchronize GitHub OpenAPI specification contracts"
71+
git push --force origin "$BRANCH_NAME"
72+
73+
PR_TITLE="chore(spec): synchronize GitHub OpenAPI specification contracts"
74+
PR_BODY="## GitHub OpenAPI Specification Sync
75+
76+
This automated pull request synchronizes the GitHub OpenAPI description contracts and coverage reports.
77+
78+
### Summary of Changes
79+
- Regenerated \`lib/src/common/generated/rest_contracts.g.dart\`
80+
- Updated API coverage documentation in \`docs/api-coverage.md\`
81+
82+
### Validation
83+
- \`dart format\` verified
84+
- \`dart analyze --fatal-infos\` passed
85+
- \`dart test\` passed
86+
- \`dart pub publish --dry-run\` verified
87+
88+
**Notice**: Review only. Never auto-merged."
89+
90+
gh pr create \
91+
--title "$PR_TITLE" \
92+
--body "$PR_BODY" \
93+
--base master \
94+
--head "$BRANCH_NAME" \
95+
--label "automation,spec-sync" || \
96+
gh pr edit "$BRANCH_NAME" \
97+
--title "$PR_TITLE" \
98+
--body "$PR_BODY"

.github/workflows/triage.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ on:
33
issues:
44
types: [opened]
55

6+
permissions:
7+
issues: write
8+
69
jobs:
710
assignRob:
811
name: Assign Rob

CONTRIBUTING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ To regenerate the JSON logic for the models, run:
3838
dart run build_runner build -d
3939
```
4040

41+
## OpenAPI Specification & Contract Sync
42+
43+
This repository uses an OpenAPI-driven contract verification system pinned to the official GitHub REST API specification.
44+
45+
- To check that code and documentation match the pinned OpenAPI specification:
46+
```sh
47+
dart run tool/spec_sync.dart --check --offline
48+
```
49+
- To update or regenerate the REST contracts catalog and coverage report:
50+
```sh
51+
dart run tool/spec_sync.dart --update
52+
```
53+
- Automated weekly pull requests (`spec_sync.yml`) monitor upstream API specification releases. Automated PRs are review-only and never auto-merged.
54+
4155
## Tests
4256

4357
`dart test` will only run the unit tests.

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,35 @@ We are looking for contributors. If you're interested or have questions, head ov
2222

2323
- [Library Demos](https://spinlocklabs.github.io/github.dart/) (based on the [sample code](https://github.com/SpinlockLabs/github.dart/tree/master/example))
2424
- [Pub Package](https://pub.dev/packages/github)
25+
- [API Coverage Report](docs/api-coverage.md)
2526
- [Wiki](https://github.com/SpinlockLabs/github.dart/wiki)
2627
- [Latest API reference](https://pub.dev/documentation/github/latest/)
2728

29+
## OpenAPI Conformance and Spec Synchronization
30+
31+
This library tracks the official [GitHub REST API v3 description](https://github.com/github/rest-api-description).
32+
- The OpenAPI specification baseline is pinned in `tool/spec/github-openapi.lock.json`.
33+
- Operation bindings and conformance are tracked in `tool/spec/bindings.yaml`.
34+
- Immutable REST contract metadata is generated into `lib/src/common/generated/rest_contracts.g.dart`.
35+
- Conformance and coverage metrics are documented in `docs/api-coverage.md`.
36+
37+
To check or update the OpenAPI contracts locally:
38+
```bash
39+
# Verify no drift between specification and codebase
40+
dart run tool/spec_sync.dart --check --offline
41+
42+
# Regenerate contracts and documentation
43+
dart run tool/spec_sync.dart --update
44+
```
45+
46+
A weekly GitHub Actions workflow runs the spec sync check and opens review-only PRs when upstream API descriptions evolve.
47+
48+
## Security and Best Practices
49+
50+
- **Credential Boundaries**: Authenticated requests are constrained to the configured API origin (`api.github.com` or custom enterprise endpoint). Cross-origin redirects and pagination links automatically strip credentials. Insecure HTTP authentication is disallowed by default.
51+
- **Webhooks**: Always configure a webhook secret. Webhooks are validated using constant-time HMAC-SHA256 signature verification, enforce configurable payload limits, and support replay attack detection.
52+
- **Browser Authentication**: Token extraction from URL query parameters is disabled by default for security. Tokens should be passed via `Authentication.withToken()` or session storage.
53+
2854
## Examples
2955

3056
See the examples in the example directory to learn how to use some of the features!

0 commit comments

Comments
 (0)