Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pnpm-lock.yaml -merge linguist-vendored
453 changes: 339 additions & 114 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions .github/workflows/dependency-age-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Dependency Age Check

on:
push:
branches: [main]
paths:
- .github/workflows/dependency-age-check.yml
- pnpm-workspace.yaml
- pnpm-lock.yaml
pull_request:
branches: [main]
# labeled/unlabeled needed so the bypass-age-gate label re-triggers the gate
types: [opened, synchronize, reopened, labeled, unlabeled]

concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.run_id || github.ref }}

jobs:
dependency-check:
name: Check dependency age
runs-on: ubuntu-slim

steps:
- name: Check out Git repository
uses: runloopai/checkout@main

- name: Check dependency age (supply-chain gate)
uses: runloopai/lisan-al-gaib-action@main
with:
ecosystems: npm,actions
min-age-days: '7'
warn-age-days: '14'
bypass-keyword: 'bypass-age-gate'
Comment on lines +20 to +33
6 changes: 3 additions & 3 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ jobs:
runs-on: ubuntu-slim
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: runloopai/checkout@main

- name: Setup pnpm
uses: pnpm/action-setup@v5
uses: runloopai/pnpm-action@master

- name: Setup Node.js
uses: actions/setup-node@v6
uses: runloopai/setup-node@main
with:
node-version: "20"
cache: "pnpm"
Expand Down
109 changes: 36 additions & 73 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,83 +3,46 @@ name: PR Title Check
on:
pull_request:
types: [opened, edited, synchronize, reopened]
workflow_dispatch:

permissions:
pull-requests: read

jobs:
pr-title-check:
runs-on: ubuntu-slim
steps:
- name: Validate PR title
uses: actions/github-script@v8
uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
script: |
// Fetch current PR to always get latest title, even on workflow re-runs
const prNumber = context.issue.number || context.payload.pull_request?.number;
if (!prNumber) {
core.setFailed('Unable to determine PR number');
return;
}

const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});

const title = pr.title;
console.log(`Validating PR title: "${title}"`);

// Define allowed types
const types = [
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'build', 'ci', 'chore', 'revert'
];

// Define allowed scopes (optional)
const scopes = [
'cli', 'mcp', 'devbox', 'benchmark', 'secret',
'blueprint', 'storage-object', 'network-policy',
'main', 'snapshot', 'config', 'auth', 'deps'
];

// Regex pattern: type(scope)?: description
// type is required, scope is optional, description must start with lowercase
const pattern = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9-]+\))?:\s*[a-z].+$/;

if (!pattern.test(title)) {
const errorMsg = [
'❌ PR title does not follow the conventional commit format.',
'',
`Found: "${title}"`,
'',
'Expected format: type(scope)?: description',
'',
'Where:',
`- type: one of ${types.join(', ')}`,
`- scope (optional): one of ${scopes.join(', ')}`,
'- description: starts with a lowercase letter',
'',
'Examples:',
'- feat: add new devbox command',
'- fix(cli): resolve argument parsing issue',
'- docs: update README with usage examples',
'- feat(network-policy): add support for gateway flags'
].join('\n');

core.setFailed(errorMsg);
return;
}

// Extract and validate scope if present
const scopeMatch = title.match(/\(([^)]+)\)/);
if (scopeMatch) {
const scope = scopeMatch[1];
if (!scopes.includes(scope)) {
core.setFailed(
`❌ Invalid scope "${scope}".\n\nAllowed scopes: ${scopes.join(', ')}`
);
return;
}
}

console.log('✅ PR title is valid!');
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
scopes: |
cli
mcp
devbox
benchmark
secret
blueprint
storage-object
network-policy
main
snapshot
config
auth
deps
subjectPattern: '^[a-z].+$'
subjectPatternError: |
The subject "{subject}" found in the pull request title "{title}"
must start with a lowercase letter.
21 changes: 13 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ on:
push:
branches: [main]

permissions:
contents: write
pull-requests: write

jobs:
release:
runs-on: ubuntu-slim
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
client-id: ${{ secrets.DEPLOY_APP_CLIENT_ID }}
private-key: ${{ secrets.DEPLOY_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write

- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
id: release
with:
token: ${{ steps.app-token.outputs.token }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

Expand All @@ -30,13 +35,13 @@ jobs:
id-token: write # Required for OIDC
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: runloopai/checkout@main

- name: Setup pnpm
uses: pnpm/action-setup@v5
uses: runloopai/pnpm-action@master

- name: Setup Node.js
uses: actions/setup-node@v6
uses: runloopai/setup-node@main
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ coverage
*.log
package-lock.json
src/mcp/index.js
pnpm-lock.yaml
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default {

// Transform ignore patterns for node_modules
transformIgnorePatterns: [
'node_modules/(?!(conf|@runloop|ink|react|ink-big-text|ink-gradient|ink-spinner|ink-text-input|ink-select-input|ink-box|ink-text|ink-testing-library|figures|is-unicode-supported)/)'
'node_modules/(?!(conf|@runloop|ink|react|ink-gradient|ink-spinner|ink-text-input|ink-select-input|ink-box|ink-text|ink-testing-library|figures|is-unicode-supported)/)'
],

// Treat these extensions as ESM
Expand Down
44 changes: 7 additions & 37 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@
"test:e2e": "NODE_OPTIONS='--experimental-vm-modules' jest --config jest.e2e.config.js --forceExit",
"test:router": "NODE_OPTIONS='--experimental-vm-modules' jest --config jest.router.config.js --forceExit",
"docs:commands": "pnpm run build && node scripts/generate-command-docs.js",
"prepare": "husky"
"prepare": "husky",
"actions:update": "actions-up --min-age 14 --exclude 'runloopai/.+'"
},
"packageManager": "pnpm@9.15.4",
"packageManager": "pnpm@10.33.0",
"keywords": [
"runloop",
"cli",
Expand Down Expand Up @@ -83,58 +84,28 @@
"figures": "6.1.0",
"gradient-string": "3.0.0",
"ink": "6.6.0",
"ink-big-text": "2.0.0",
"ink-gradient": "3.0.0",
"ink-link": "5.0.0",
"ink-spinner": "5.0.0",
"ink-text-input": "6.0.0",
"react": "19.2.0",
"ws": "^8.20.1",
"tar-stream": "3.1.7",
"ws": "^8.20.1",
"yaml": "2.8.3",
"zustand": "5.0.10"
},
"pnpm": {
"onlyBuiltDependencies": [
"esbuild"
],
"overrides": {
"tmp": "^0.2.6",
"qs": "^6.15.2",
"ws": "^8.20.1",
"hono": "4.12.24",
"@hono/node-server": "^1.19.14",
"@modelcontextprotocol/sdk>ajv": "^8.18.0",
"express-rate-limit": "^8.3.2",
"fast-uri": "3.1.2",
"ip-address": "10.1.1",
"tar": "^7.5.13",
"flatted": "^3.4.2",
"handlebars": "^4.7.9",
"eslint>minimatch": "^3.1.3",
"eslint-plugin-react>minimatch": "^3.1.3",
"glob>minimatch": "^3.1.3",
"test-exclude>minimatch": "^3.1.3",
"@typescript-eslint/typescript-estree>minimatch": "^9.0.9",
"jest-util>picomatch": "^2.3.2",
"anymatch>picomatch": "^2.3.2",
"eslint>ajv": "^6.14.0",
"minimatch>brace-expansion": "^1.1.13",
"node-forge": "^1.4.0",
"micromatch>picomatch": "^2.3.2",
"tinyglobby>picomatch": "^4.0.4",
"path-to-regexp": "^8.4.2"
}
},
"devDependencies": {
"@anthropic-ai/mcpb": "2.1.2",
"@eslint/js": "9.39.2",
"@types/adm-zip": "0.5.7",
"@types/jest": "29.5.14",
"@types/node": "22.19.7",
"@types/react": "19.2.10",
"@types/tar-stream": "3.1.4",
"@types/ws": "^8.5.0",
"@typescript-eslint/eslint-plugin": "8.54.0",
"@typescript-eslint/parser": "8.54.0",
"actions-up": "^1.14.2",
"esbuild": "0.27.2",
"eslint": "9.39.2",
"eslint-plugin-react": "7.37.5",
Expand All @@ -146,7 +117,6 @@
"prettier": "3.8.1",
"ts-jest": "29.4.6",
"ts-node": "10.9.2",
"@types/ws": "^8.5.0",
"typescript": "5.9.3"
}
}
Loading
Loading