AI-driven Dependabot for OpenAPI Connectors #9
AI-driven Dependabot for OpenAPI Connectors #9TharaniDJ wants to merge 21 commits intowso2:openapi-ai-dependabotfrom
Conversation
The main logic is inside the main.bal file. The openapi-dependabot workflow will run daily and check whether the OpenAPI specs included in repos.json have updated.
|
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughIntroduces an automated OpenAPI dependency monitoring system comprising a daily GitHub Actions workflow, Ballerina-based dependabot monitor, and repository configuration. The system detects API updates, downloads modified specs, and creates pull requests with changes. Changes
Sequence DiagramsequenceDiagram
participant GHA as GitHub Actions
participant Bal as Ballerina Monitor
participant GH as GitHub API
participant FS as File System
GHA->>Bal: Trigger daily (bal run)
Bal->>Bal: Load repos.json
Bal->>GH: Fetch latest release per repo
GH-->>Bal: Release tag & asset info
Bal->>Bal: Compare versions
alt Update Detected
Bal->>GH: Download spec asset/raw
GH-->>Bal: OpenAPI spec content
Bal->>FS: Save spec to openapi/*/
Bal->>FS: Create .metadata.json
Bal->>FS: Write UPDATE_SUMMARY.txt
Bal->>FS: Update repos.json
Bal->>GH: Create branch & push
Bal->>GH: Create pull request
GH-->>Bal: PR URL
Bal->>GHA: Exit (updates found)
else No Updates
Bal->>GHA: Exit (all up-to-date)
end
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR introduces an AI-driven Dependabot system for automatically detecting and updating OpenAPI specifications from GitHub repositories. The system monitors configured vendor repositories for new releases and creates pull requests with updated specifications.
Changes:
- Added configuration file (repos.json) defining Stripe and Twilio APIs to monitor
- Implemented Ballerina-based dependabot that checks for OpenAPI spec updates, downloads new versions, and can create PRs
- Added GitHub Actions workflow for daily automated execution of the dependabot
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| repos.json | Configuration defining API repositories to monitor with metadata |
| dependabot/main.bal | Core dependabot logic for checking releases, downloading specs, and creating PRs |
| dependabot/Dependencies.toml | Auto-generated Ballerina dependency management file |
| dependabot/Ballerina.toml | Ballerina project configuration |
| .gitignore | Added .env to ignore list for local environment variables |
| .github/workflows/openapi-dependabot.yml | GitHub Actions workflow for daily automated monitoring |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if trimmedLine.startsWith("version:") { | ||
| // Extract version value | ||
| string[] parts = regexp:split(re `:`, trimmedLine); | ||
| if parts.length() >= 2 { | ||
| string versionValue = parts[1].trim(); | ||
| // Remove quotes if present | ||
| versionValue = removeQuotes(versionValue); | ||
| return versionValue; | ||
| } |
There was a problem hiding this comment.
The version extraction logic splits on ":" but doesn't handle YAML values that may contain colons (e.g., URLs or timestamps). If the version field contains a colon, this will incorrectly split it. Consider using a more robust YAML parsing approach or at least handle cases where multiple colons exist in the line.
| time:Utc currentTime = time:utcNow(); | ||
| string timestamp = string `${time:utcToString(currentTime).substring(0, 10)}-${currentTime[0]}`; | ||
| string branchName = string `openapi-update-${timestamp}`; |
There was a problem hiding this comment.
The timestamp generation combines a date string with a Unix timestamp, which creates confusing and potentially very long values. The expression currentTime[0] gets the Unix timestamp in seconds. For example, this could produce "2026-01-18-1737244800". Consider using a simpler format like ${civil.year}${civil.month}${civil.day}-${civil.hour}${civil.minute}${civil.second} or just using the workflow's timestamp generation approach.
|
|
||
| // Create PR title and body | ||
| time:Civil civil = time:utcToCivil(currentTime); | ||
| string prTitle = string `Update OpenAPI Specifications - ${civil.year}-${civil.month}-${civil.day}`; |
There was a problem hiding this comment.
The month value in the PR title is not zero-padded, which could result in inconsistent date formats like "2026-1-5" instead of "2026-01-05". Consider using proper date formatting to ensure the month and day are always two digits.
| string prTitle = string `Update OpenAPI Specifications - ${civil.year}-${civil.month}-${civil.day}`; | |
| string paddedMonth = civil.month < 10 ? string `0${civil.month}` : civil.month.toString(); | |
| string paddedDay = civil.day < 10 ? string `0${civil.day}` : civil.day.toString(); | |
| string prTitle = string `Update OpenAPI Specifications - ${civil.year}-${paddedMonth}-${paddedDay}`; |
| if git diff --staged --quiet; then | ||
| echo "⚠️ No changes to commit" | ||
| exit 0 | ||
| fi | ||
| UPDATE_SUMMARY=$(cat UPDATE_SUMMARY.txt) | ||
| COMMIT_MSG="chore: Update OpenAPI specifications\n\n${UPDATE_SUMMARY}\n\nAutomated update by OpenAPI Dependabot" | ||
| git commit -m "$COMMIT_MSG" | ||
| git push -u origin "$BRANCH_NAME" | ||
| echo "✅ Branch pushed successfully" | ||
| echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV |
There was a problem hiding this comment.
The step exits with code 0 when no changes are staged (line 57), but the BRANCH_NAME environment variable is set after this exit. This means if there are no changes to commit, BRANCH_NAME won't be set, but the exit happens before that matters. However, this creates an inconsistent state. Consider moving the BRANCH_NAME assignment before the diff check or restructuring the logic.
| if !line.startsWith(" ") && !line.startsWith("\t") && trimmedLine != "" && !trimmedLine.startsWith("#") { | ||
| break; | ||
| } |
There was a problem hiding this comment.
The logic for determining if we've exited the info section is flawed. A line that starts with whitespace but has a trimmed value that doesn't start with whitespace will incorrectly exit the info section. For example, a line like " servers:" would exit because trimmedLine is "servers:" which doesn't start with a space. The condition should check the original line for indentation, not the trimmed line for top-level keys.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In @.github/workflows/openapi-dependabot.yml:
- Around line 59-61: The COMMIT_MSG variable and git commit invocation use
literal "\n" sequences so the commit body won't get real newlines; change how
COMMIT_MSG is passed to git commit (replace the single git commit -m
"$COMMIT_MSG" usage) by either invoking git commit with multiple -m flags (one
for the title and one for the body using UPDATE_SUMMARY), or construct
COMMIT_MSG with an ANSI-quoted string (e.g., using $'...' or printf) so the "\n"
sequences are expanded before calling git commit; update the references to
UPDATE_SUMMARY, COMMIT_MSG and the git commit -m call accordingly.
In `@dependabot/Ballerina.toml`:
- Around line 1-5: Update the Ballerina distribution to match the auto-generated
lock: change the distribution value under the [package] section in
Ballerina.toml from 2201.10.0 to 2201.13.1, update the workflow step that
installs Ballerina in .github/workflows/openapi-dependabot.yml to install
2201.13.1, and then regenerate dependabot/Dependencies.toml so the lock file and
Ballerina.toml are consistent (verify the [package] distribution and any lock
entries now all report 2201.13.1).
In `@dependabot/main.bal`:
- Around line 370-423: The code calls createPullRequest with branchName but
never creates, commits, or pushes that branch; add steps to create and push the
branch before calling createPullRequest: use branchName to create a new git
branch, stage and commit the updated files (the files updated in updates and
summaryContent), then push the branch to the remote (ensuring the remote ref
matches owner/repoName) and only then invoke createPullRequest; update the flow
around branchName and the createPullRequest call to fail early with a clear
error if git branch/commit/push fails.
- Around line 261-330: The loop uses "foreach Repository repo in repos" which
iterates by value so assigning repo.lastVersion = tagName does not persist;
change to an index-based loop (e.g., for int i = 0; i < repos.length(); i++) and
read the element into a local (repo = repos[i]), then after successful update
assign back to the array (repos[i].lastVersion = tagName) before writing
repos.json; update the code paths around
createMetadataFile/saveSpec/updates.push to use the indexed repo assignment so
the persisted repos array reflects the new version.
- Around line 40-79: The extractApiVersion function only parses YAML-like lines
and misses JSON OpenAPI specs; update extractApiVersion to detect JSON vs YAML
(e.g., check first non-whitespace char '{' or '['), and when JSON is detected
parse the content and return info.version (use the JSON parser available in the
runtime), otherwise parse YAML properly (or use a YAML parser) to read
info.version; specifically modify extractApiVersion to (1) branch on format
detection, (2) for JSON call the JSON decode routine to access
root["info"]["version"], and (3) keep or replace the line-based YAML logic with
a proper YAML parse fallback so both JSON and YAML specs return the true spec
version instead of falling back to the release tag.
dependabot/main.bal
Outdated
| // Extract version from OpenAPI spec content | ||
| function extractApiVersion(string content) returns string|error { | ||
| // Try to find "version:" under "info:" section | ||
| // This is a simple regex-based extraction | ||
|
|
||
| // Split content by lines | ||
| string[] lines = regexp:split(re `\n`, content); | ||
| boolean inInfoSection = false; | ||
|
|
||
| foreach string line in lines { | ||
| string trimmedLine = line.trim(); | ||
|
|
||
| // Check if we're entering info section | ||
| if trimmedLine == "info:" { | ||
| inInfoSection = true; | ||
| continue; | ||
| } | ||
|
|
||
| // If we're in info section, look for version | ||
| if inInfoSection { | ||
| // Exit info section if we hit another top-level key | ||
| if !line.startsWith(" ") && !line.startsWith("\t") && trimmedLine != "" && !trimmedLine.startsWith("#") { | ||
| break; | ||
| } | ||
|
|
||
| // Look for version field | ||
| if trimmedLine.startsWith("version:") { | ||
| // Extract version value | ||
| string[] parts = regexp:split(re `:`, trimmedLine); | ||
| if parts.length() >= 2 { | ||
| string versionValue = parts[1].trim(); | ||
| // Remove quotes if present | ||
| versionValue = removeQuotes(versionValue); | ||
| return versionValue; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return error("Could not extract API version from spec"); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, explore the repository structure
git ls-files | head -20Repository: wso2/api-specs
Length of output: 656
🏁 Script executed:
# Look at the dependabot/main.bal file to understand the context
wc -l dependabot/main.balRepository: wso2/api-specs
Length of output: 81
🏁 Script executed:
# Read the entire main.bal file to understand the context
cat -n dependabot/main.balRepository: wso2/api-specs
Length of output: 18950
🏁 Script executed:
# Search for usages of extractApiVersion to understand how it's called
rg "extractApiVersion" -A 5 -B 5Repository: wso2/api-specs
Length of output: 1637
🏁 Script executed:
# Look for JSON parsing functions in the codebase
rg "json|JSON|parse" -t balRepository: wso2/api-specs
Length of output: 85
🏁 Script executed:
# Search for references to OpenAPI or specs to understand what format is expected
rg "openapi|OpenAPI|spec|json|yaml" -iRepository: wso2/api-specs
Length of output: 50374
🏁 Script executed:
# Look at repos.json to understand the JSON spec configuration
cat repos.jsonRepository: wso2/api-specs
Length of output: 1015
🏁 Script executed:
# Check if Twilio or other specs are actually JSON format
fd -e json -path "*/spec/*" | head -20Repository: wso2/api-specs
Length of output: 226
🏁 Script executed:
# Look for example JSON OpenAPI specs
find openapi -type f -name "*.json" | head -5Repository: wso2/api-specs
Length of output: 277
🏁 Script executed:
# Examine the Twilio spec path to see if it actually exists
ls -la openapi/twilio/ 2>/dev/null || echo "Twilio directory not found in openapi/"Repository: wso2/api-specs
Length of output: 224
🏁 Script executed:
# Check what's in the Twilio directory
find openapi/twilio -type fRepository: wso2/api-specs
Length of output: 174
🏁 Script executed:
# Look at the actual Twilio metadata to see what version is being used
cat openapi/twilio/twilio/*/openapi.yaml | head -20Repository: wso2/api-specs
Length of output: 528
🏁 Script executed:
# Check the Twilio metadata file
cat openapi/twilio/twilio/*/.metadata.jsonRepository: wso2/api-specs
Length of output: 774
🏁 Script executed:
# Now let's examine how extractApiVersion would actually fail on JSON
# Create a sample JSON OpenAPI spec to test the logic
python3 << 'EOF'
import json
# Sample JSON OpenAPI spec like Twilio
sample_json = {
"openapi": "3.0.0",
"info": {
"version": "1.51.0",
"title": "Twilio API"
}
}
json_string = json.dumps(sample_json, indent=2)
print("Sample JSON content:")
print(json_string)
print("\n--- Testing regex extraction logic ---")
# Simulate the Ballerina extractApiVersion logic
lines = json_string.split('\n')
in_info_section = False
for line in lines:
trimmed = line.strip()
print(f"Line: {repr(line)} | Trimmed: {repr(trimmed)}")
if trimmed == "info:":
in_info_section = True
print(" -> Would enter info section (YAML check)")
continue
if in_info_section:
if trimmed.startswith("version:"):
print(" -> Would find version (YAML check)")
if trimmed.startswith('"info"'):
print(" -> Found JSON info key (but code doesn't check for this)")
if trimmed.startswith('"version"'):
print(" -> Found JSON version key (but code doesn't check for this)")
print("\nConclusion: Function would NOT find version in JSON because it only checks for YAML syntax")
EOFRepository: wso2/api-specs
Length of output: 743
JSON OpenAPI specs will fall back to release tag version; verify version consistency or add JSON parsing.
The extractApiVersion function only recognizes YAML syntax (version: with unquoted keys), so JSON specs like Twilio's will fail extraction and use the release tag as a fallback. If a spec's info.version ever diverges from its release tag, the directory structure and metadata will record the tag version instead of the spec version, causing a mismatch. Either parse JSON/YAML formats properly or confirm all tracked repositories maintain tag == spec.version.
🤖 Prompt for AI Agents
In `@dependabot/main.bal` around lines 40 - 79, The extractApiVersion function
only parses YAML-like lines and misses JSON OpenAPI specs; update
extractApiVersion to detect JSON vs YAML (e.g., check first non-whitespace char
'{' or '['), and when JSON is detected parse the content and return info.version
(use the JSON parser available in the runtime), otherwise parse YAML properly
(or use a YAML parser) to read info.version; specifically modify
extractApiVersion to (1) branch on format detection, (2) for JSON call the JSON
decode routine to access root["info"]["version"], and (3) keep or replace the
line-based YAML logic with a proper YAML parse fallback so both JSON and YAML
specs return the true spec version instead of falling back to the release tag.
No major changes in the functionality
Removed emojis
Added license header. Updated the pull request creation part.
No major functionality changes
Changed the code but no major change in logic
Removed the part which takes ballerina version as user input
Auto merge pull request to the main branch and call the respective connector repository.
Also updated main.bal to detect the changes of these newly added openapi specs.
Also solved some formatting issues.
There was an issue with downloading a spec.
Purpose
Detect changes in Github published and verified OpenAPI specs
Fixes:
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.