Skip to content

Comments

AI-driven Dependabot for OpenAPI Connectors #9

Open
TharaniDJ wants to merge 21 commits intowso2:openapi-ai-dependabotfrom
TharaniDJ:main
Open

AI-driven Dependabot for OpenAPI Connectors #9
TharaniDJ wants to merge 21 commits intowso2:openapi-ai-dependabotfrom
TharaniDJ:main

Conversation

@TharaniDJ
Copy link

@TharaniDJ TharaniDJ commented Jan 18, 2026

Purpose

Detect changes in Github published and verified OpenAPI specs

Fixes:

Summary by CodeRabbit

  • New Features

    • Introduced automated OpenAPI specification monitoring that detects updates for tracked APIs.
    • Automatic pull request creation with detailed update summaries when new API versions are discovered.
    • Added initial support for monitoring Stripe and Twilio API specifications.
  • Chores

    • Added GitHub Actions workflow for daily automated dependency checks.
    • Added environment configuration files for automation setup.

✏️ Tip: You can customize this high-level summary in your review settings.

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.
Copilot AI review requested due to automatic review settings January 18, 2026 11:39
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link

coderabbitai bot commented Jan 18, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Introduces 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

Cohort / File(s) Summary
GitHub Actions Workflow
.github/workflows/openapi-dependabot.yml
New workflow scheduled daily at 2:00 UTC with manual trigger capability. Orchestrates Ballerina dependabot execution, detects updates via UPDATE_SUMMARY.txt, stages spec/config changes, creates timestamped branch, initializes GitHub labels, and opens PR with summary body if updates found.
Configuration & Dependencies
.gitignore, dependabot/Ballerina.toml, dependabot/Dependencies.toml
Added .env to gitignore; configured Ballerina package openapi_dependabot (v0.1.0, dist 2201.10.0) with observability disabled; auto-generated comprehensive dependency manifest listing auth, crypto, http, jwt, oauth2, and other packages with nested module mappings.
Core Implementation
dependabot/main.bal
Implements Dependabot-like OpenAPI monitor in Ballerina. Defines Repository and UpdateResult record types. Core functions: hasVersionChanged() for version detection, extractApiVersion() via regex parsing, downloadSpec() with GitHub asset/raw fallback, saveSpec() for spec persistence, createMetadataFile() for tracking, createPullRequest() for PR automation. Main orchestrates GitHub API interactions, monitors repos.json list, detects changes, writes UPDATE_SUMMARY.txt, and triggers PR creation.
Repository Configuration
repos.json
Initial configuration listing two APIs (Stripe and Twilio) with vendor, owner, repo, release details, spec paths, baseUrl, documentation URL, and descriptive metadata. Serves as input data source for monitor iterations.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 Hops with glee through API specs so fine,
Daily checks for updates, oh how divine!
Downloads, extracts, branches sprouting new,
Pull requests auto-born—what a clever brew! ✨🔄

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description is incomplete. It provides only a basic purpose statement but lacks details about the implementation approach, files added, or how the feature works. Expand the description to explain the workflow, key components (Ballerina script, GitHub Actions), and the overall approach to detecting and managing OpenAPI updates.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: introducing an automated system to detect and manage OpenAPI specification updates, which is what the PR implements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines 66 to 74
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;
}
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +371 to +373
time:Utc currentTime = time:utcNow();
string timestamp = string `${time:utcToString(currentTime).substring(0, 10)}-${currentTime[0]}`;
string branchName = string `openapi-update-${timestamp}`;
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.

// Create PR title and body
time:Civil civil = time:utcToCivil(currentTime);
string prTitle = string `Update OpenAPI Specifications - ${civil.year}-${civil.month}-${civil.day}`;
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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}`;

Copilot uses AI. Check for mistakes.
Comment on lines 55 to 64
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
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +63
if !line.startsWith(" ") && !line.startsWith("\t") && trimmedLine != "" && !trimmedLine.startsWith("#") {
break;
}
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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.

Comment on lines 40 to 79
// 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");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, explore the repository structure
git ls-files | head -20

Repository: wso2/api-specs

Length of output: 656


🏁 Script executed:

# Look at the dependabot/main.bal file to understand the context
wc -l dependabot/main.bal

Repository: wso2/api-specs

Length of output: 81


🏁 Script executed:

# Read the entire main.bal file to understand the context
cat -n dependabot/main.bal

Repository: 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 5

Repository: wso2/api-specs

Length of output: 1637


🏁 Script executed:

# Look for JSON parsing functions in the codebase
rg "json|JSON|parse" -t bal

Repository: 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" -i

Repository: wso2/api-specs

Length of output: 50374


🏁 Script executed:

# Look at repos.json to understand the JSON spec configuration
cat repos.json

Repository: 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 -20

Repository: wso2/api-specs

Length of output: 226


🏁 Script executed:

# Look for example JSON OpenAPI specs
find openapi -type f -name "*.json" | head -5

Repository: 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 f

Repository: 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 -20

Repository: wso2/api-specs

Length of output: 528


🏁 Script executed:

# Check the Twilio metadata file
cat openapi/twilio/twilio/*/.metadata.json

Repository: 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")
EOF

Repository: 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.

@lnash94 lnash94 changed the base branch from main to openapi-ai-dependabot January 19, 2026 08:31
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants