diff --git a/.fleet/goals/example.md b/.fleet/goals/example.md new file mode 100644 index 0000000..a473a00 --- /dev/null +++ b/.fleet/goals/example.md @@ -0,0 +1,24 @@ +--- +milestone: "1" +--- + +# Example Fleet Goal + +Analyze the codebase for potential improvements and create +issues for the engineering team. + +## Tools +- Test Coverage: `npx vitest --coverage --json` + +## Assessment Hints +- Focus on missing error handling in API routes +- Look for hardcoded configuration values + +## Insight Hints +- Report on overall test coverage metrics +- Note any unusually complex functions (cyclomatic complexity) + +## Constraints +- Do NOT propose changes already covered by open issues +- Do NOT propose changes rejected in recently closed issues +- Keep tasks small and isolated — one logical change per issue diff --git a/.github/workflows/fleet-analyze.yml b/.github/workflows/fleet-analyze.yml new file mode 100644 index 0000000..73efc52 --- /dev/null +++ b/.github/workflows/fleet-analyze.yml @@ -0,0 +1,42 @@ +# Generated by @google/jules-fleet init +# https://github.com/google-labs-code/jules-sdk + +name: Fleet Analyze + +on: + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + inputs: + goal: + description: 'Path to goal file (or blank for all)' + type: string + default: '' + milestone: + description: 'Milestone ID override' + type: string + default: '' + +concurrency: + group: fleet-analyze + cancel-in-progress: false + +jobs: + analyze: + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + pull-requests: read + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + - run: npx -y --package=@google/jules-fleet jules-fleet analyze --goal "${{ inputs.goal }}" --milestone "${{ inputs.milestone }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JULES_API_KEY: ${{ secrets.JULES_API_KEY }} + FLEET_APP_ID: ${{ secrets.FLEET_APP_ID }} + FLEET_APP_PRIVATE_KEY_BASE64: ${{ secrets.FLEET_APP_PRIVATE_KEY_BASE64 }} + FLEET_APP_INSTALLATION_ID: ${{ secrets.FLEET_APP_INSTALLATION_ID }} diff --git a/.github/workflows/fleet-dispatch.yml b/.github/workflows/fleet-dispatch.yml new file mode 100644 index 0000000..1895f56 --- /dev/null +++ b/.github/workflows/fleet-dispatch.yml @@ -0,0 +1,59 @@ +# Generated by @google/jules-fleet init +# https://github.com/google-labs-code/jules-sdk + +name: Fleet Dispatch + +on: + schedule: + - cron: '0 3-23/6 * * *' + workflow_dispatch: + inputs: + milestone: + description: 'Milestone ID to dispatch (leave empty to dispatch all)' + type: string + required: false + +concurrency: + group: fleet-dispatch + cancel-in-progress: false + +jobs: + discover: + runs-on: ubuntu-latest + outputs: + milestones: ${{ steps.list.outputs.milestones }} + steps: + - name: Resolve milestones + id: list + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + INPUT_MILESTONE: ${{ inputs.milestone }} + run: | + if [ -n "$INPUT_MILESTONE" ]; then + echo "milestones=[\"$INPUT_MILESTONE\"]" >> "$GITHUB_OUTPUT" + else + milestones=$(gh api repos/${{ github.repository }}/milestones --jq '[.[].number | tostring]') + echo "milestones=$milestones" >> "$GITHUB_OUTPUT" + fi + + dispatch: + needs: discover + runs-on: ubuntu-latest + strategy: + matrix: + milestone: ${{ fromJSON(needs.discover.outputs.milestones) }} + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + - run: npx -y --package=@google/jules-fleet jules-fleet dispatch --milestone ${{ matrix.milestone }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JULES_API_KEY: ${{ secrets.JULES_API_KEY }} + FLEET_APP_ID: ${{ secrets.FLEET_APP_ID }} + FLEET_APP_PRIVATE_KEY_BASE64: ${{ secrets.FLEET_APP_PRIVATE_KEY_BASE64 }} + FLEET_APP_INSTALLATION_ID: ${{ secrets.FLEET_APP_INSTALLATION_ID }} diff --git a/.github/workflows/fleet-label.yml b/.github/workflows/fleet-label.yml new file mode 100644 index 0000000..6796972 --- /dev/null +++ b/.github/workflows/fleet-label.yml @@ -0,0 +1,45 @@ +name: Fleet Label PR +on: + pull_request: + types: [opened, edited, synchronize] + +permissions: + pull-requests: write + issues: read + +jobs: + label_pr: + runs-on: ubuntu-latest + steps: + - name: Check linked issue and apply label/milestone + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_URL: ${{ github.event.pull_request.html_url }} + REPO: ${{ github.repository }} + run: | + # Use GitHub's own closing keyword resolution to find linked issues + ISSUE_NUMBER=$(gh pr view "$PR_URL" --json closingIssuesReferences --jq '.closingIssuesReferences[0].number // empty') + + if [ -z "$ISSUE_NUMBER" ]; then + echo "No closing issue reference found on this PR. Exiting." + exit 0 + fi + + echo "Found linked issue: #$ISSUE_NUMBER" + + # Check if the linked issue has the 'fleet' label + HAS_FLEET=$(gh issue view "$ISSUE_NUMBER" --repo "$REPO" --json labels --jq '[.labels[].name] | any(. == "fleet")') + + if [ "$HAS_FLEET" = "true" ]; then + echo "Linked issue has 'fleet' label. Applying 'fleet-merge-ready' to PR." + gh pr edit "$PR_URL" --add-label "fleet-merge-ready" + + # Check if linked issue has a milestone and copy it + MILESTONE_TITLE=$(gh issue view "$ISSUE_NUMBER" --repo "$REPO" --json milestone --jq '.milestone.title // empty') + if [ -n "$MILESTONE_TITLE" ]; then + echo "Applying milestone '$MILESTONE_TITLE' to PR." + gh pr edit "$PR_URL" --milestone "$MILESTONE_TITLE" + fi + else + echo "Linked issue does not have 'fleet' label. Ignoring." + fi diff --git a/.github/workflows/fleet-merge.yml b/.github/workflows/fleet-merge.yml new file mode 100644 index 0000000..87ac367 --- /dev/null +++ b/.github/workflows/fleet-merge.yml @@ -0,0 +1,54 @@ +# Generated by @google/jules-fleet init +# https://github.com/google-labs-code/jules-sdk + +name: Fleet Merge + +on: + schedule: + - cron: '0 */3 * * *' + workflow_dispatch: + inputs: + mode: + description: 'PR selection mode' + type: choice + options: + - label + - fleet-run + default: 'label' + fleet_run_id: + description: 'Fleet run ID (required for fleet-run mode)' + type: string + default: '' + redispatch: + description: 'Enable smart conflict resolution' + type: boolean + default: true + +concurrency: + group: fleet-merge + cancel-in-progress: true + +jobs: + merge: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + - run: | + REDISPATCH_FLAG="--redispatch" + if [ "${{ inputs.redispatch }}" = "false" ]; then + REDISPATCH_FLAG="" + fi + npx -y --package=@google/jules-fleet jules-fleet merge --mode ${{ inputs.mode || 'label' }} --run-id "${{ inputs.fleet_run_id }}" $REDISPATCH_FLAG + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JULES_API_KEY: ${{ secrets.JULES_API_KEY }} + FLEET_APP_ID: ${{ secrets.FLEET_APP_ID }} + FLEET_APP_PRIVATE_KEY_BASE64: ${{ secrets.FLEET_APP_PRIVATE_KEY_BASE64 }} + FLEET_APP_INSTALLATION_ID: ${{ secrets.FLEET_APP_INSTALLATION_ID }} diff --git a/.github/workflows/jules-merge-conflicts.yml b/.github/workflows/jules-merge-conflicts.yml new file mode 100644 index 0000000..6e85f3d --- /dev/null +++ b/.github/workflows/jules-merge-conflicts.yml @@ -0,0 +1,23 @@ +# Generated by @google/jules-fleet init +# This workflow scans PRs for overlapping file changes. +name: Conflict Detection + +on: + pull_request: + branches: [main] + +permissions: + contents: read + pull-requests: read + +jobs: + scan-overlaps: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Scan for overlapping changes + run: | + npx -y --package=@google/jules-merge jules-merge scan --json '{"prs":[${{ github.event.pull_request.number }}],"repo":"${{ github.repository }}","base":"${{ github.event.pull_request.base.ref }}"}'