Skip to content

feat: add OpenRPC JSON updater tool (#1837) #3810

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
fae7e1f
feat: add OpenRPC JSON updater tool (#1837)
mwb-al Jun 3, 2025
8bd6212
chore: update OpenRPC paths, enhance README, and pin GitHub Actions v…
mwb-al Jun 3, 2025
2a66691
chore: refactor utilities with new helper classes (#1837)
mwb-al Jun 3, 2025
1f0eb3b
chore: clean up code formatting and reorder imports in OpenRPC JSON u…
mwb-al Jun 4, 2025
b686d83
chore: clean up code formatting and reorder imports in OpenRPC JSON u…
mwb-al Jun 4, 2025
6d0cd1b
chore: add SPDX license identifiers and fix minor formatting issues i…
mwb-al Jun 4, 2025
c102620
chore: pin peter-evans/create-pull-request action to v7.0.8 in OpenRP…
mwb-al Jun 4, 2025
d9f620c
chore: remove npm cache configuration from OpenRPC JSON updater workf…
mwb-al Jun 4, 2025
b64ee28
chore: enhance OpenRPC JSON updater with configuration updates, impro…
mwb-al Jun 6, 2025
eabc938
chore: update OpenRPC JSON updater README with file creation details …
mwb-al Jun 6, 2025
91eb85b
chore: clean up formatting (#1837)
mwb-al Jun 6, 2025
c428955
chore: restrict OpenRPC updater workflow triggers to `docs/openrpc.js…
mwb-al Jun 9, 2025
56f3a8c
chore: remove `customFields` handling from OpenRPC JSON updater scrip…
mwb-al Jun 13, 2025
567c7dd
chore: update OpenRPC JSON updater config by pruning unused fields an…
mwb-al Jun 13, 2025
1de58c1
chore: enhance OpenRPC JSON updater with refined method handling and …
mwb-al Jun 13, 2025
487d0b0
Merge branch 'main' into 1837_openrpc-updater
mwb-al Jun 16, 2025
5937d64
Merge branch 'main' into 1837_openrpc-updater
mwb-al Jun 17, 2025
2a7a15d
chore: update OpenRPC JSON updater config to include `eth_getStorageA…
mwb-al Jun 17, 2025
d83ba75
Merge branch 'main' into 1837_openrpc-updater
Ferparishuertas Jun 24, 2025
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
133 changes: 133 additions & 0 deletions .github/workflows/openrpc-updater.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@

name: OpenRPC JSON Updater

on:
push:
branches:
- main

jobs:
clone-and-build-execution-apis:
runs-on: ubuntu-latest

steps:
- name: Checkout execution-apis repo
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
ref: main
repository: 'ethereum/execution-apis'
path: 'execution-apis'

- name: Use Node.js TLS 20
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20

- name: Install dependencies
run: npm install
working-directory: ./execution-apis

- name: Build project
run: npm run build
working-directory: ./execution-apis

- name: Upload openrpc.json as an artifact
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: openrpc
path: ./execution-apis/refs-openrpc.json

update-openrpc:
runs-on: ubuntu-latest
needs: clone-and-build-execution-apis
steps:
- name: Checkout repository
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
ref: 'main'
token: ${{ secrets.PAT_TOKEN }}

- name: Download openrpc.json artifact
uses: actions/download-artifact@v4
with:
name: openrpc
path: ./downloaded-artifacts/

- name: Copy generated openrpc.json to scripts directory
run: |
mkdir -p scripts/openrpc-json-updater
cp ./downloaded-artifacts/refs-openrpc.json scripts/openrpc-json-updater/original-openrpc.json

- name: Setup Node.js
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '22'

- name: Install dependencies
run: |
cd scripts/openrpc-json-updater
npm install

- name: Generate comparison report
id: generate-report
run: |
cd scripts/openrpc-json-updater
REPORT_OUTPUT=$(node cli.js)
echo "REPORT_OUTPUT<<EOF" >> $GITHUB_ENV
echo "$REPORT_OUTPUT" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV

- name: Perform merge
id: merge
run: |
cd scripts/openrpc-json-updater
MERGE_OUTPUT=$(node cli.js --merge)
MERGE_EXIT_CODE=$?
echo "$MERGE_OUTPUT"

if [ $MERGE_EXIT_CODE -eq 0 ]; then
if [[ "$MERGE_OUTPUT" =~ No\ differences\ found\ after\ merge ]]; then
echo "No differences found. Skipping PR creation."
echo "SKIP_PR=true" >> $GITHUB_ENV
exit 0
elif [[ "$MERGE_OUTPUT" == *"Merge completed"* ]]; then
echo "Successfully updated openrpc.json"
echo "SKIP_PR=false" >> $GITHUB_ENV
else
echo "Unexpected output. Output was: $MERGE_OUTPUT"
exit 1
fi
else
echo "Failed to update file. Output was: $MERGE_OUTPUT"
exit 1
fi

- name: Generate unique branch name
id: branch-name
run: |
TIMESTAMP=$(date +%Y%m%d%H%M%S)
UNIQUE_BRANCH="update-openrpc-${TIMESTAMP}"
echo "UNIQUE_BRANCH=${UNIQUE_BRANCH}" >> $GITHUB_ENV
echo "Generated unique branch name: ${UNIQUE_BRANCH}"

- name: Create Pull Request
if: env.SKIP_PR != 'true'
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with:
token: ${{ secrets.PAT_TOKEN }}
commit-message: Update OpenRPC JSON
title: 'Update OpenRPC JSON'
body: |
# OpenRPC JSON Update

This PR updates the OpenRPC JSON file with the latest changes.

## Comparison Report
```
${{ env.REPORT_OUTPUT }}
```
branch: ${{ env.UNIQUE_BRANCH }}
base: 'main'
add-paths: |
docs/openrpc.json
delete-branch: false
39 changes: 39 additions & 0 deletions scripts/openrpc-json-updater/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# OpenRPC Diff & Merge CLI

A command-line tool for comparing and merging OpenRPC JSON specifications.

## GitHub Actions Integration

This tool is used with the `openrpc-updater.yml` workflow that automatically:
- Fetches the latest OpenRPC spec from the ethereum/execution-apis repository
- Compares it with our local version
- Creates a PR with the changes if differences are found

## Install

```shell script
npm install
```

## Usage

```shell script
node cli.js [options]

Options:
-g, --merge merge original -> modified (writes a new dated file)
```

### Examples

Full diff report:

```shell script
node cli.js
```

Merge changes:

```shell script
node cli.js --merge
```
56 changes: 56 additions & 0 deletions scripts/openrpc-json-updater/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0

import { mergeDocuments } from './operations/merge.js';
import { compareIgnoringFormatting, prepareDocuments } from './operations/prepare.js';
import { generateReport } from './operations/report.js';
import { readJson, writeJson } from './utils/file.utils.js';

const originalFilePath = './original-openrpc.json';
const modifiedFilePath = '../../docs/openrpc.json';

const { data: originalJson } = readJson(originalFilePath);
const { data: modifiedJson, originalContent: modifiedContent } = readJson(modifiedFilePath);

function parseArgs() {
const argv = process.argv.slice(2);
const result = { mergeFlag: false };

for (let i = 0; i < argv.length; i++) {
switch (argv[i]) {
case '-g':
case '--merge':
result.mergeFlag = true;
break;
}
}
return result;
}

function hasDifferences(original, merged) {
const differences = compareIgnoringFormatting(original, merged);
return differences && differences.length > 0;
}

(async () => {
const { mergeFlag } = parseArgs();

const { normalizedOriginal, normalizedModified } = prepareDocuments(originalJson, modifiedJson);

if (mergeFlag) {
const merged = mergeDocuments(normalizedOriginal, normalizedModified);

if (!hasDifferences(normalizedModified, merged)) {
console.log(`\nNo differences found after merge. No changes needed.\n`);
process.exit(0);
}

writeJson(modifiedFilePath, merged, modifiedContent);
console.log(`\nMerge completed. Updated file: '${modifiedFilePath}'.\n`);
return;
}

await generateReport(normalizedOriginal, normalizedModified).catch((err) => {
console.error('Unexpected error while generating report:', err);
process.exit(1);
});
})();
132 changes: 132 additions & 0 deletions scripts/openrpc-json-updater/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: Apache-2.0

export const SKIPPED_KEYS = [
"examples",
"baseFeePerBlobGas",
"blobGasUsedRatio",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, inconsistent intendations (2 spaces here 4 in 10th line)

];

export const CUSTOM_FIELDS = [
"eth_accounts.description",
"eth_accounts.result.description",
"eth_accounts.result.schema.title",

"eth_call.summary",

"eth_coinbase.summary",
"eth_blobBaseFee.summary",

"eth_feeHistory.summary",
"eth_feeHistory.description",
"eth_feeHistory.params.2.description",
"eth_feeHistory.result.name",
"eth_feeHistory.result.schema.properties.gasUsedRatio.description",
"eth_feeHistory.result.schema.properties.baseFeePerGas.title",
"eth_feeHistory.result.schema.properties.baseFeePerGas.description",
"eth_feeHistory.result.schema.properties.reward.title",

"eth_gasPrice.summary",

"eth_getBalance.result.schema.title",

"eth_getBlockTransactionCountByHash.result.name",
"eth_getBlockTransactionCountByNumber.result.name",

"eth_getLogs.summary",
"eth_getStorageAt.summary",
"eth_getStorageAt.params.1.name",
"eth_getStorageAt.result.name",

"eth_getTransactionCount.summary",
"eth_getTransactionCount.result.name",
"eth_getTransactionCount.result.schema.title",

"eth_maxPriorityFeePerGas.summary",
"eth_maxPriorityFeePerGas.result.schema.description",
"eth_maxPriorityFeePerGas.result.schema.title",

"eth_newBlockFilter.summary",
"eth_newBlockFilter.result.name",
];

export const DISCARDED_METHODS = [
"engine_*",
];

export const NOT_IMPLEMENTED_METHODS = [
"debug_getBadBlocks",
"debug_getRawBlock",
"debug_getRawHeader",
"debug_getRawReceipts",
"debug_getRawTransaction",
];

export const SKIPPED_METHODS = [
...DISCARDED_METHODS,
...NOT_IMPLEMENTED_METHODS,
];

export function shouldSkipMethod(methodName, path) {
if (!methodName) return false;

if (path) {
const fullPath = `${methodName}.${path}`;
if (CUSTOM_FIELDS.includes(fullPath)) return true;
}

for (const pattern of SKIPPED_METHODS) {
if (pattern === methodName) return true;

if (pattern.endsWith('*')) {
const prefix = pattern.slice(0, -1);
if (methodName.startsWith(prefix)) return true;
}
}
return false;
}

export function shouldSkipKey(key) {
if (!key) return false;
for (const pattern of SKIPPED_KEYS) {
if (pattern === key) return true;
if (pattern.endsWith('*')) {
const prefix = pattern.slice(0, -1);
if (key.startsWith(prefix)) return true;
}
}
return false;
}

export function shouldSkipPath(path) {
if (!path) return false;
const parts = path.split('.');
for (const part of parts) {
if (shouldSkipKey(part)) return true;
}
return false;
}

export function getSkippedMethodCategory(methodName) {
if (!methodName) return null;

const matchesPattern = (pattern, method) => {
if (pattern === method) return true;

if (pattern.endsWith('*')) {
const prefix = pattern.slice(0, -1);
return method.startsWith(prefix);
}

return false;
};

if (DISCARDED_METHODS.some(pattern => matchesPattern(pattern, methodName))) {
return 'discarded';
}

if (NOT_IMPLEMENTED_METHODS.some(pattern => matchesPattern(pattern, methodName))) {
return 'not implemented';
}

return null;
}
Loading
Loading