Skip to content
Merged
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
73 changes: 73 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# CI/CD Workflows

## OpenAPI CI/CD Pipeline

This workflow (`openapi-ci.yml`) provides automated validation, testing, and documentation deployment for the Green Button OpenAPI specification.

### Workflow Jobs

#### 1. Validate (runs on all PRs and pushes)
- **Checkout code** - Gets the latest code
- **Setup Node.js** - Installs Node.js 20 with npm caching
- **Install dependencies** - Runs `npm ci` for clean install
- **Lint OpenAPI spec** - Runs `npm test` (executes `redocly lint`)
- **Bundle OpenAPI spec** - Runs `npm run build` (executes `redocly bundle`)
- **Upload bundled spec** - Saves the bundled spec as an artifact (30-day retention)

#### 2. Deploy Documentation (runs only on main branch pushes)
- **Build documentation** - Creates the documentation bundle
- **Deploy to GitHub Pages** - Publishes the documentation to GitHub Pages

### Branch Protection

The validation job ensures that:
- βœ… OpenAPI spec is valid
- βœ… No linting errors
- βœ… Spec can be bundled successfully

### GitHub Pages Setup

To enable GitHub Pages deployment:

1. Go to repository **Settings** β†’ **Pages**
2. Under **Source**, select **GitHub Actions**
3. Save the settings

After the first successful deployment to `main`, your documentation will be available at:
```
https://<organization>.github.io/<repository>/
```

For this repository:
```
https://greenbuttonalliance.github.io/openapi-starter/
```

### Local Development

Run the same checks locally before pushing:

```bash
# Validate and lint
npm test

# Build bundle
npm run build

# Preview documentation
npm start
```

### Artifacts

Each workflow run produces:
- **openapi-bundle** - The bundled OpenAPI spec (available for 30 days)
- **github-pages** - The deployed documentation (on main branch only)

### Status Badge

Add this badge to your README to show the build status:

```markdown
![OpenAPI CI/CD](https://github.com/GreenButtonAlliance/openapi-starter/workflows/OpenAPI%20CI%2FCD/badge.svg)
```
86 changes: 86 additions & 0 deletions .github/workflows/openapi-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: OpenAPI CI/CD

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
validate:
name: Validate OpenAPI Spec
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Lint OpenAPI spec
run: npm test

- name: Bundle OpenAPI spec
run: npm run build

- name: Upload bundled spec as artifact
uses: actions/upload-artifact@v4
with:
name: openapi-bundle
path: dist/
retention-days: 30

deploy-docs:
name: Deploy Documentation
runs-on: ubuntu-latest
needs: validate
if: github.ref == 'refs/heads/main' && github.event_name == 'push'

environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build documentation
run: npm run build

- name: Setup Pages
uses: actions/configure-pages@v4

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: 'dist'

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Dir for bundles
dist
node_modules

# IDE
.idea

# Claude Code
.claude
4 changes: 0 additions & 4 deletions openapi/components/headers/ExpiresAfter.yaml

This file was deleted.

8 changes: 8 additions & 0 deletions openapi/components/parameters/depth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: depth
in: query
description: >-
The maximum number of entries to be transferred in the response.
required: false
schema:
type: integer
format: int64
8 changes: 8 additions & 0 deletions openapi/components/parameters/max-results.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: max-results
in: query
description: >-
The upper bound on the number of entries to be contained in the response.
required: false
schema:
type: integer
format: int64
7 changes: 7 additions & 0 deletions openapi/components/parameters/published-max.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: published-max
in: query
description: The upper bound on the published date of the resource.
required: false
schema:
type: string
format: date-time
7 changes: 7 additions & 0 deletions openapi/components/parameters/published-min.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: published-min
in: query
description: The lower bound on the published date of the resource.
required: false
schema:
type: string
format: date-time
9 changes: 9 additions & 0 deletions openapi/components/parameters/start-index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: start-index
in: query
description: >-
The one-based offset in the DataCustodian's collection of resources
that should be transferred as the first entry of the response.
required: false
schema:
type: integer
format: int64
7 changes: 7 additions & 0 deletions openapi/components/parameters/updated-max.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: updated-max
in: query
description: The upper bound on the updated date of the resource.
required: false
schema:
type: string
format: date-time
7 changes: 7 additions & 0 deletions openapi/components/parameters/updated-min.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: updated-min
in: query
description: The lower bound on the updated date of the resource.
required: false
schema:
type: string
format: date-time
117 changes: 117 additions & 0 deletions openapi/components/responses/AtomEntry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
description: >-
OK - Returns an Atom entry wrapping a single ESPI resource.
content:
application/atom+xml:
schema:
$ref: '../schemas/AtomEntry.yaml'
examples:
UsagePointEntry:
summary: A UsagePoint entry for an electricity service
value: |
<entry xmlns="http://www.w3.org/2005/Atom">
<id>urn:uuid:e6896122-a094-5d30-be14-e44544247e14</id>
<link rel="self" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246" type="espi-entry/UsagePoint"/>
<link rel="up" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint" type="espi-feed/UsagePoint"/>
<link rel="related" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246/MeterReading" type="espi-feed/MeterReading"/>
<link rel="related" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246/UsageSummary" type="espi-feed/UsageSummary"/>
<link rel="related" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246/ElectricPowerQualitySummary" type="espi-feed/ElectricPowerQualitySummary"/>
<link rel="related" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/LocalTimeParameters/TIMEZONE_407593_1455246" type="espi-entry/LocalTimeParameters"/>
<published>2023-10-20T17:32:35.448812Z</published>
<updated>2023-10-20T17:33:23.054424Z</updated>
<title>UsagePoint</title>
<content>
<UsagePoint xmlns="http://naesb.org/espi">
<ServiceCategory>
<kind>0</kind>
</ServiceCategory>
</UsagePoint>
</content>
</entry>
LocalTimeParametersEntry:
summary: A LocalTimeParameters entry with US Eastern timezone DST rules
value: |
<entry xmlns="http://www.w3.org/2005/Atom">
<id>urn:uuid:305129b8-503c-5b50-b293-333cd7fb6adf</id>
<link rel="self" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/LocalTimeParameters/TIMEZONE_407593_1455246" type="espi-entry/LocalTimeParameters"/>
<link rel="up" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/LocalTimeParameters" type="espi-feed/LocalTimeParameters"/>
<published>2023-10-20T17:32:35.448812Z</published>
<updated>2023-10-20T17:33:23.054424Z</updated>
<title>LocalTimeParameters</title>
<content>
<LocalTimeParameters xmlns="http://naesb.org/espi">
<dstEndRule>B40E2000</dstEndRule>
<dstOffset>3600</dstOffset>
<dstStartRule>360E2000</dstStartRule>
<tzOffset>-18000</tzOffset>
</LocalTimeParameters>
</content>
</entry>
MeterReadingEntry:
summary: A MeterReading entry with empty content and related links
value: |
<entry xmlns="http://www.w3.org/2005/Atom">
<id>urn:uuid:6052217b-aadf-5cca-b803-c611f9fcf89e</id>
<link rel="self" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246/MeterReading/Ba020673_kwh_1" type="espi-entry/MeterReading"/>
<link rel="up" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246/MeterReading" type="espi-feed/MeterReading"/>
<link rel="related" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246" type="espi-entry/UsagePoint"/>
<link rel="related" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246/MeterReading/Ba020673_kwh_1/IntervalBlock" type="espi-feed/IntervalBlock"/>
<link rel="related" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/ReadingType/407593_1455246_Ba020673_kwh_1" type="espi-entry/ReadingType"/>
<published>2023-10-20T17:32:44.801089Z</published>
<updated>2023-10-20T17:32:44.801089Z</updated>
<title>MeterReading</title>
<content>
<MeterReading xmlns="http://naesb.org/espi"/>
</content>
</entry>
ReadingTypeEntry:
summary: A ReadingType entry for monthly kWh electricity consumption
value: |
<entry xmlns="http://www.w3.org/2005/Atom">
<id>urn:uuid:06ff0942-04fc-5f91-bc35-ca5e51b49dbb</id>
<link rel="self" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/ReadingType/407593_1455246_Ba020673_kwh_1" type="espi-entry/ReadingType"/>
<link rel="up" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/ReadingType" type="espi-feed/ReadingType"/>
<published>2023-10-20T17:32:44.801089Z</published>
<updated>2023-10-20T17:32:44.801089Z</updated>
<title>ReadingType</title>
<content>
<ReadingType xmlns="http://naesb.org/espi">
<accumulationBehaviour>1</accumulationBehaviour>
<commodity>1</commodity>
<currency>840</currency>
<flowDirection>1</flowDirection>
<intervalLength>2332800</intervalLength>
<kind>12</kind>
<phase>0</phase>
<powerOfTenMultiplier>-3</powerOfTenMultiplier>
<uom>72</uom>
</ReadingType>
</content>
</entry>
IntervalBlockEntry:
summary: An IntervalBlock entry with a single IntervalReading
value: |
<entry xmlns="http://www.w3.org/2005/Atom">
<id>urn:uuid:1e530409-5dbe-572f-aad8-fa06f6a68cfc</id>
<link rel="self" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246/MeterReading/Ba020673_kwh_1/IntervalBlock/000001" type="espi-entry/IntervalBlock"/>
<link rel="up" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246/MeterReading/Ba020673_kwh_1/IntervalBlock" type="espi-feed/IntervalBlock"/>
<link rel="related" href="https://utilityapi.com/DataCustodian/espi/1_1/resource/Subscription/407593/UsagePoint/1455246/MeterReading/Ba020673_kwh_1" type="espi-feed/MeterReading"/>
<published>2023-10-20T17:32:44.801089Z</published>
<updated>2023-10-20T17:32:44.801089Z</updated>
<title>IntervalBlock</title>
<content>
<IntervalBlock xmlns="http://naesb.org/espi">
<interval>
<duration>2332800</duration>
<start>1663992000</start>
</interval>
<IntervalReading>
<cost>115780000</cost>
<timePeriod>
<duration>2332800</duration>
<start>1663992000</start>
</timePeriod>
<value>1330890000</value>
</IntervalReading>
</IntervalBlock>
</content>
</entry>
Loading