Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions .github/workflows/create_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ on:
push:
tags:
- '*'

permissions:
contents: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Create a Release
uses: elgohr/Github-Release-Action@v5
env:
GITHUB_TOKEN: "${{ secrets.RELEASE_TOKEN }}"
with:
title: ${{ github.ref }}
- uses: actions/checkout@v6
- name: Create a Release
uses: elgohr/Github-Release-Action@v5
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
with:
title: ${{ github.ref }}
23 changes: 20 additions & 3 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: [ master ]

permissions:
contents: read

jobs:
build:
runs-on: ${{ matrix.os }}
Expand All @@ -22,12 +25,26 @@ jobs:

- name: Install dependencies
run: dart pub get
- name: Dart Analyzer
run: dart analyze

- name: Dart Analyzer (Strict mode)
run: dart analyze --fatal-infos

- name: Check Dart Format
if: ${{ matrix.sdk == 'stable' }}
run: dart format --set-exit-if-changed -onone .
- name: Unit tests

- name: Verify Spec Sync Drift
if: ${{ matrix.sdk == 'stable' }}
run: dart run tool/spec_sync.dart --check --offline

- name: Unit and Contract Tests
run: dart test

- name: Coverage Ratchet Check
if: ${{ matrix.sdk == 'stable' }}
run: |
dart test --coverage=coverage
dart run tool/coverage_check.dart coverage/lcov.info --min=30.0

- name: Check if Publishable
run: dart pub publish --dry-run
4 changes: 4 additions & 0 deletions .github/workflows/publish_demos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ on:
push:
branches:
- master

permissions:
contents: write

jobs:
build-and-deploy:
runs-on: ubuntu-latest
Expand Down
98 changes: 98 additions & 0 deletions .github/workflows/spec_sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Weekly GitHub OpenAPI Spec Sync

on:
schedule:
# Run every Monday at 04:00 UTC
- cron: '0 4 * * 1'
workflow_dispatch:

permissions:
contents: read

jobs:
spec-sync:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write

steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Setup Dart SDK
uses: dart-lang/setup-dart@v1
with:
sdk: stable

- name: Install dependencies
run: dart pub get

- name: Check for upstream OpenAPI specification updates
id: check_upstream
run: |
mkdir -p .dart_tool
dart run tool/spec_sync.dart --offline --report-json > .dart_tool/spec_report_before.json
echo "Checked current spec baseline."

- name: Regenerate OpenAPI catalog and docs
id: run_sync
run: |
dart run tool/spec_sync.dart --update
git status --porcelain > .dart_tool/git_status.txt
if [ -s .dart_tool/git_status.txt ]; then
echo "changes_detected=true" >> "$GITHUB_OUTPUT"
else
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
fi

- name: Validate generated code and contracts
if: steps.run_sync.outputs.changes_detected == 'true'
run: |
dart format --output=none --set-exit-if-changed .
dart analyze --fatal-infos
dart test
dart pub publish --dry-run

- name: Create or update spec-sync pull request
if: steps.run_sync.outputs.changes_detected == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH_NAME="automation/github-openapi-spec-sync"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git checkout -B "$BRANCH_NAME"
git add lib/src/common/generated/rest_contracts.g.dart docs/api-coverage.md tool/spec/
git commit -m "chore(spec): synchronize GitHub OpenAPI specification contracts"
git push --force origin "$BRANCH_NAME"

PR_TITLE="chore(spec): synchronize GitHub OpenAPI specification contracts"
PR_BODY="## GitHub OpenAPI Specification Sync

This automated pull request synchronizes the GitHub OpenAPI description contracts and coverage reports.

### Summary of Changes
- Regenerated \`lib/src/common/generated/rest_contracts.g.dart\`
- Updated API coverage documentation in \`docs/api-coverage.md\`

### Validation
- \`dart format\` verified
- \`dart analyze --fatal-infos\` passed
- \`dart test\` passed
- \`dart pub publish --dry-run\` verified

**Notice**: Review only. Never auto-merged."

gh pr create \
--title "$PR_TITLE" \
--body "$PR_BODY" \
--base master \
--head "$BRANCH_NAME" \
--label "automation,spec-sync" || \
gh pr edit "$BRANCH_NAME" \
--title "$PR_TITLE" \
--body "$PR_BODY"
3 changes: 3 additions & 0 deletions .github/workflows/triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ on:
issues:
types: [opened]

permissions:
issues: write

jobs:
assignRob:
name: Assign Rob
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.dart_tool
.packages
.pub
.dart_pub_cache
tool/spec/.cache
packages
pubspec.lock

14 changes: 14 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ To regenerate the JSON logic for the models, run:
dart run build_runner build -d
```

## OpenAPI Specification & Contract Sync

This repository uses an OpenAPI-driven contract verification system pinned to the official GitHub REST API specification.

- To check that code and documentation match the pinned OpenAPI specification:
```sh
dart run tool/spec_sync.dart --check --offline
```
- To update or regenerate the REST contracts catalog and coverage report:
```sh
dart run tool/spec_sync.dart --update
```
- Automated weekly pull requests (`spec_sync.yml`) monitor upstream API specification releases. Automated PRs are review-only and never auto-merged.

## Tests

`dart test` will only run the unit tests.
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,35 @@ We are looking for contributors. If you're interested or have questions, head ov

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

## OpenAPI Conformance and Spec Synchronization

This library tracks the official [GitHub REST API v3 description](https://github.com/github/rest-api-description).
- The OpenAPI specification baseline is pinned in `tool/spec/github-openapi.lock.json`.
- Operation bindings and conformance are tracked in `tool/spec/bindings.yaml`.
- Immutable REST contract metadata is generated into `lib/src/common/generated/rest_contracts.g.dart`.
- Conformance and coverage metrics are documented in `docs/api-coverage.md`.

To check or update the OpenAPI contracts locally:
```bash
# Verify no drift between specification and codebase
dart run tool/spec_sync.dart --check --offline

# Regenerate contracts and documentation
dart run tool/spec_sync.dart --update
```

A weekly GitHub Actions workflow runs the spec sync check and opens review-only PRs when upstream API descriptions evolve.

## Security and Best Practices

- **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.
- **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.
- **Browser Authentication**: Token extraction from URL query parameters is disabled by default for security. Tokens should be passed via `Authentication.withToken()` or session storage.

## Examples

See the examples in the example directory to learn how to use some of the features!
Expand Down
4 changes: 3 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ include: package:lints/recommended.yaml

analyzer:
language:
#strict-casts: true
strict-casts: true
strict-inference: true
strict-raw-types: true

linter:
rules:
Expand Down
Loading
Loading