Skip to content

Commit 498925d

Browse files
Merge branch 'main' into issue-35-strands-integration
2 parents 27b5c47 + 29f52c1 commit 498925d

File tree

196 files changed

+16636
-2656
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

196 files changed

+16636
-2656
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ docs/sdk/go @mattsp1290
1111

1212
sdks/community/dart @mattsp1290
1313
docs/sdk/dart @mattsp1290
14+
15+
integrations/adk-middleware @contextablemark
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
name: Auto-approve community PRs
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
7+
permissions:
8+
pull-requests: write
9+
contents: read
10+
11+
jobs:
12+
auto-approve:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout code
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Fetch PR branch
21+
run: |
22+
git fetch origin ${{ github.event.pull_request.head.ref }}:${{ github.event.pull_request.head.ref }}
23+
24+
- name: Set up Node.js
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: "22"
28+
29+
- name: Auto-approve based on CODEOWNERS
30+
env:
31+
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
32+
PR_NUMBER: ${{ github.event.pull_request.number }}
33+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34+
BASE_REF: ${{ github.event.pull_request.base.ref }}
35+
HEAD_REF: ${{ github.event.pull_request.head.ref }}
36+
run: |
37+
node << 'EOF'
38+
const { execSync } = require('child_process');
39+
const fs = require('fs');
40+
const path = require('path');
41+
42+
const prAuthor = process.env.PR_AUTHOR;
43+
const prNumber = process.env.PR_NUMBER;
44+
45+
// Get changed files
46+
const changedFiles = execSync(
47+
`git diff --name-only origin/${process.env.BASE_REF}...origin/${process.env.HEAD_REF}`,
48+
{ encoding: 'utf-8' }
49+
)
50+
.trim()
51+
.split('\n')
52+
.filter(f => f.trim());
53+
54+
console.log(`Changed files (${changedFiles.length}):`);
55+
changedFiles.forEach(f => console.log(` - ${f}`));
56+
57+
// Parse CODEOWNERS file
58+
const codeownersPath = '.github/CODEOWNERS';
59+
const codeownersContent = fs.readFileSync(codeownersPath, 'utf-8');
60+
const lines = codeownersContent.split('\n');
61+
62+
// Map of path patterns to owners (excluding root * rule)
63+
const codeownersRules = [];
64+
65+
for (const line of lines) {
66+
const trimmed = line.trim();
67+
// Skip empty lines and comments
68+
if (!trimmed || trimmed.startsWith('#')) {
69+
continue;
70+
}
71+
72+
// Skip root * line
73+
if (trimmed.startsWith('* ')) {
74+
console.log('Skipping root * rule');
75+
continue;
76+
}
77+
78+
// Parse pattern and owners
79+
const parts = trimmed.split(/\s+/);
80+
if (parts.length < 2) {
81+
continue;
82+
}
83+
84+
const pattern = parts[0];
85+
const owners = parts.slice(1).map(o => o.replace('@', ''));
86+
87+
codeownersRules.push({ pattern, owners });
88+
}
89+
90+
console.log('\nCODEOWNERS rules (excluding root):');
91+
codeownersRules.forEach(rule => {
92+
console.log(` ${rule.pattern} -> ${rule.owners.join(', ')}`);
93+
});
94+
95+
// Function to check if a file matches a CODEOWNERS pattern
96+
// CODEOWNERS patterns match:
97+
// - Exact file/directory path
98+
// - pattern/ matches everything in that directory
99+
// - pattern/** matches everything recursively in that directory
100+
function matchesPattern(file, pattern) {
101+
// Normalize paths (handle both / and \ separators)
102+
const normalizePath = (p) => p.replace(/\\/g, '/');
103+
const normalizedFile = normalizePath(file);
104+
const normalizedPattern = normalizePath(pattern);
105+
106+
// Exact match
107+
if (normalizedFile === normalizedPattern) {
108+
return true;
109+
}
110+
111+
// Pattern ends with /**: matches recursively in directory
112+
if (normalizedPattern.endsWith('/**')) {
113+
const dirPrefix = normalizedPattern.slice(0, -3);
114+
return normalizedFile.startsWith(dirPrefix + '/');
115+
}
116+
117+
// Pattern ends with /: matches everything in directory
118+
if (normalizedPattern.endsWith('/')) {
119+
const dirPrefix = normalizedPattern.slice(0, -1);
120+
return normalizedFile.startsWith(dirPrefix + '/');
121+
}
122+
123+
// Pattern is a directory prefix (matches subdirectories)
124+
if (normalizedFile.startsWith(normalizedPattern + '/')) {
125+
return true;
126+
}
127+
128+
return false;
129+
}
130+
131+
// Check each changed file
132+
// CODEOWNERS rules are evaluated top-to-bottom, first match wins
133+
const unapprovedFiles = [];
134+
135+
for (const file of changedFiles) {
136+
let matched = false;
137+
let owned = false;
138+
139+
// Find the first matching rule (CODEOWNERS uses first match semantics)
140+
for (const rule of codeownersRules) {
141+
if (matchesPattern(file, rule.pattern)) {
142+
matched = true;
143+
// First match wins in CODEOWNERS, so check ownership here
144+
owned = rule.owners.includes(prAuthor);
145+
break; // Stop at first match
146+
}
147+
}
148+
149+
// File must be matched by a non-root CODEOWNERS rule AND author must own it
150+
if (!matched || !owned) {
151+
unapprovedFiles.push(file);
152+
}
153+
}
154+
155+
// Decision
156+
if (unapprovedFiles.length === 0) {
157+
console.log(`\n✅ All changed files are owned by ${prAuthor} according to CODEOWNERS`);
158+
159+
// Check if already approved by this workflow
160+
try {
161+
const reviews = JSON.parse(
162+
execSync(`gh pr view ${prNumber} --json reviews`, { encoding: 'utf-8' })
163+
);
164+
165+
// Check if there's already an approval from GitHub Actions bot
166+
// (look for approval with the auto-approve message)
167+
const hasAutoApproval = reviews.reviews.some(
168+
review => review.state === 'APPROVED' &&
169+
review.body &&
170+
review.body.includes('Auto-approved: PR author has CODEOWNERS access')
171+
);
172+
173+
if (hasAutoApproval) {
174+
console.log('PR already auto-approved by this workflow');
175+
} else {
176+
// Approve the PR using GitHub Actions bot account
177+
execSync(
178+
`gh pr review ${prNumber} --approve --body "Auto-approved: PR author ${prAuthor} has CODEOWNERS access to all changed files (excluding root rule)"`,
179+
{ stdio: 'inherit' }
180+
);
181+
console.log(`PR approved automatically for ${prAuthor}`);
182+
}
183+
} catch (error) {
184+
console.error('Error checking/approving PR:', error.message);
185+
// Don't fail the workflow if approval fails (might already be approved, etc.)
186+
console.log('Continuing despite approval error...');
187+
}
188+
} else {
189+
console.log(`\n❌ Not auto-approved: Some files are not owned by ${prAuthor}`);
190+
console.log('Unauthorized files:');
191+
unapprovedFiles.forEach(f => console.log(` - ${f}`));
192+
}
193+
EOF

.github/workflows/dojo-e2e.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ jobs:
2828
dojo:
2929
name: dojo / ${{ matrix.suite }}
3030
runs-on: depot-ubuntu-24.04
31+
timeout-minutes: 20
3132
strategy:
3233
fail-fast: false
3334
matrix:
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
name: Publish Kotlin SDK to Maven Central
2+
3+
on:
4+
# Manual trigger only - no automatic publishing
5+
workflow_dispatch:
6+
inputs:
7+
dry_run:
8+
description: 'Run in dry-run mode (test without uploading)'
9+
required: false
10+
type: boolean
11+
default: false
12+
13+
jobs:
14+
publish:
15+
runs-on: ubuntu-latest
16+
17+
permissions:
18+
contents: read
19+
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Set up JDK 21
25+
uses: actions/setup-java@v4
26+
with:
27+
java-version: "21"
28+
distribution: "temurin"
29+
30+
- name: Setup Gradle
31+
uses: gradle/gradle-build-action@v3
32+
33+
- name: Install Android SDK
34+
uses: android-actions/setup-android@v3
35+
36+
- name: Install Android SDK 36 components
37+
run: |
38+
echo "Installing Android SDK 36 components..."
39+
sdkmanager --install "platforms;android-36"
40+
sdkmanager --install "build-tools;36.0.0"
41+
42+
- name: Accept Android licenses
43+
run: yes | sdkmanager --licenses || true
44+
45+
- name: Verify Android SDK installation
46+
run: |
47+
echo "Checking Android SDK installation..."
48+
sdkmanager --list_installed | grep -E "(platforms;android-36|build-tools;36)"
49+
50+
- name: Run tests
51+
working-directory: sdks/community/kotlin/library
52+
run: ./gradlew allTests --no-daemon --stacktrace
53+
54+
- name: Parse test results
55+
if: always()
56+
working-directory: sdks/community/kotlin/library
57+
run: |
58+
echo "## Kotlin SDK Test Results Summary"
59+
echo ""
60+
61+
total_tests=0
62+
total_failures=0
63+
total_errors=0
64+
65+
for module in core client tools; do
66+
xml_dir="$module/build/test-results/jvmTest"
67+
68+
if [ -d "$xml_dir" ]; then
69+
# Sum up test counts from all XML files in the directory
70+
module_tests=$(find "$xml_dir" -name "*.xml" -exec grep -h '<testsuite' {} \; | grep -o 'tests="[0-9]*"' | sed 's/tests="\([0-9]*\)"/\1/' | awk '{sum += $1} END {print sum}')
71+
module_failures=$(find "$xml_dir" -name "*.xml" -exec grep -h '<testsuite' {} \; | grep -o 'failures="[0-9]*"' | sed 's/failures="\([0-9]*\)"/\1/' | awk '{sum += $1} END {print sum}')
72+
module_errors=$(find "$xml_dir" -name "*.xml" -exec grep -h '<testsuite' {} \; | grep -o 'errors="[0-9]*"' | sed 's/errors="\([0-9]*\)"/\1/' | awk '{sum += $1} END {print sum}')
73+
74+
# Default to 0 if empty
75+
module_tests=${module_tests:-0}
76+
module_failures=${module_failures:-0}
77+
module_errors=${module_errors:-0}
78+
79+
if [ "$module_tests" -gt 0 ]; then
80+
echo "✅ kotlin-$module: $module_tests tests, $module_failures failures, $module_errors errors"
81+
total_tests=$((total_tests + module_tests))
82+
total_failures=$((total_failures + module_failures))
83+
total_errors=$((total_errors + module_errors))
84+
fi
85+
fi
86+
done
87+
88+
echo ""
89+
echo "---"
90+
echo "### Overall Results: $total_tests tests, $total_failures failures, $total_errors errors"
91+
92+
if [ $total_failures -gt 0 ] || [ $total_errors -gt 0 ]; then
93+
echo "❌ Some tests failed - aborting publish"
94+
exit 1
95+
elif [ $total_tests -eq 0 ]; then
96+
echo "⚠️ No tests were found or executed - aborting publish"
97+
exit 1
98+
else
99+
echo "✅ All $total_tests tests passed!"
100+
fi
101+
102+
- name: Publish to Maven Central (dry-run)
103+
if: inputs.dry_run == true
104+
working-directory: sdks/community/kotlin
105+
env:
106+
JRELEASER_MAVENCENTRAL_SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
107+
JRELEASER_MAVENCENTRAL_SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
108+
JRELEASER_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
109+
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.GPG_PUBLIC_KEY }}
110+
JRELEASER_GPG_SECRET_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
111+
run: |
112+
echo "🔍 Running publish script in dry-run mode..."
113+
./publish.sh --dry-run
114+
115+
- name: Publish to Maven Central
116+
if: inputs.dry_run == false
117+
working-directory: sdks/community/kotlin
118+
env:
119+
JRELEASER_MAVENCENTRAL_SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
120+
JRELEASER_MAVENCENTRAL_SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
121+
JRELEASER_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
122+
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.GPG_PUBLIC_KEY }}
123+
JRELEASER_GPG_SECRET_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
124+
run: |
125+
echo "🚀 Publishing to Maven Central..."
126+
./publish.sh
127+
128+
- name: Upload JReleaser logs
129+
if: always()
130+
uses: actions/upload-artifact@v4
131+
with:
132+
name: jreleaser-logs
133+
path: sdks/community/kotlin/library/build/jreleaser/
134+
retention-days: 7
135+
136+
- name: Summary
137+
if: success() && inputs.dry_run == false
138+
working-directory: sdks/community/kotlin/library
139+
run: |
140+
# Extract version from build.gradle.kts
141+
VERSION=$(grep "^version = " build.gradle.kts | sed 's/version = "\(.*\)"/\1/')
142+
143+
echo "## ✅ Publishing Complete!" >> $GITHUB_STEP_SUMMARY
144+
echo "" >> $GITHUB_STEP_SUMMARY
145+
echo "The Kotlin SDK has been published to Maven Central." >> $GITHUB_STEP_SUMMARY
146+
echo "" >> $GITHUB_STEP_SUMMARY
147+
echo "### Published Artifacts" >> $GITHUB_STEP_SUMMARY
148+
echo "- \`com.ag-ui.community:kotlin-core:${VERSION}\` (JVM, Android, iOS)" >> $GITHUB_STEP_SUMMARY
149+
echo "- \`com.ag-ui.community:kotlin-client:${VERSION}\` (JVM, Android, iOS)" >> $GITHUB_STEP_SUMMARY
150+
echo "- \`com.ag-ui.community:kotlin-tools:${VERSION}\` (JVM, Android, iOS)" >> $GITHUB_STEP_SUMMARY
151+
echo "" >> $GITHUB_STEP_SUMMARY
152+
echo "**Note:** All platforms published including iOS artifacts in .klib format." >> $GITHUB_STEP_SUMMARY
153+
echo "" >> $GITHUB_STEP_SUMMARY
154+
echo "### Next Steps" >> $GITHUB_STEP_SUMMARY
155+
echo "1. Check deployment status: https://central.sonatype.com/publishing" >> $GITHUB_STEP_SUMMARY
156+
echo "2. Artifacts will be validated automatically" >> $GITHUB_STEP_SUMMARY
157+
echo "3. Publishing completes in ~10-30 minutes" >> $GITHUB_STEP_SUMMARY
158+
159+
- name: Dry-run Summary
160+
if: success() && inputs.dry_run == true
161+
run: |
162+
echo "## ✅ Dry-run Complete!" >> $GITHUB_STEP_SUMMARY
163+
echo "" >> $GITHUB_STEP_SUMMARY
164+
echo "The dry-run completed successfully. No artifacts were uploaded." >> $GITHUB_STEP_SUMMARY
165+
echo "" >> $GITHUB_STEP_SUMMARY
166+
echo "Run without the dry-run flag to publish to Maven Central." >> $GITHUB_STEP_SUMMARY

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ node_modules
1313
.vscode
1414

1515
**/mastra.db*
16+
17+
.pnpm-store
18+
19+
**/.poetry-cache

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pnpm dev
1919
# Run linting
2020
pnpm lint
2121

22+
2223
# Run type checking
2324
pnpm check-types
2425

0 commit comments

Comments
 (0)