Compare commits
287 Commits
fixbot
...
persistent
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ad741c0d2 | ||
|
|
69570bc65f | ||
|
|
b719615aa6 | ||
|
|
afcc49f6f6 | ||
|
|
d12184f4a0 | ||
|
|
bde8a43aeb | ||
|
|
76f8cb6ce3 | ||
|
|
31840a1a31 | ||
|
|
a5968cdf24 | ||
|
|
5bcdea9b51 | ||
|
|
8eafca6c74 | ||
|
|
6adf7f2ef8 | ||
|
|
2ea7960d5c | ||
|
|
66af12b9cd | ||
|
|
8171f24196 | ||
|
|
a86dffa344 | ||
|
|
db09e70823 | ||
|
|
407f3bc2df | ||
|
|
e9b56f49b5 | ||
|
|
307b2906b7 | ||
|
|
5b1181eba5 | ||
|
|
1574a28b4f | ||
|
|
b8d3ac0869 | ||
|
|
ad4dfb41b1 | ||
|
|
ace1e034a2 | ||
|
|
9142569d91 | ||
|
|
38e28e0ec3 | ||
|
|
30c6275dc5 | ||
|
|
0f2e7ea906 | ||
|
|
cff57cbbb5 | ||
|
|
655a3f6ac7 | ||
|
|
62c13bb028 | ||
|
|
3c968befff | ||
|
|
8f7064e87f | ||
|
|
334ed1fe42 | ||
|
|
8e79105b9b | ||
|
|
92cd3f6cbf | ||
|
|
7089932b89 | ||
|
|
27e5f298d8 | ||
|
|
c4a27f18d3 | ||
|
|
811e4466b2 | ||
|
|
48c41ec6c8 | ||
|
|
b93319ab57 | ||
|
|
31905eaba4 | ||
|
|
a0a14a42d3 | ||
|
|
839784b50c | ||
|
|
e9bc4ac447 | ||
|
|
7180c6ae4b | ||
|
|
a57a1c0ab8 | ||
|
|
bee30e6423 | ||
|
|
281aa0dc12 | ||
|
|
14bd0853f6 | ||
|
|
8be374db14 | ||
|
|
c6dff735a9 | ||
|
|
b7e1d1eff8 | ||
|
|
3dd4c46871 | ||
|
|
e7ae8072ca | ||
|
|
483f2086dd | ||
|
|
cb9c042404 | ||
|
|
1b26236cf1 | ||
|
|
14ff22021e | ||
|
|
55df67b632 | ||
|
|
53b678bcd6 | ||
|
|
daaca9ef62 | ||
|
|
6a15fee1c5 | ||
|
|
92c07ddea1 | ||
|
|
3dd99b6494 | ||
|
|
edb5eaad63 | ||
|
|
ad9b980fef | ||
|
|
96a308a64e | ||
|
|
77afdaafc0 | ||
|
|
56f04b92ca | ||
|
|
8020b65546 | ||
|
|
0cc35ac876 | ||
|
|
617ca0f5f0 | ||
|
|
607fb9ba58 | ||
|
|
3c24edbf47 | ||
|
|
74c8811981 | ||
|
|
ce15b2d9f0 | ||
|
|
e5db491df4 | ||
|
|
fdffe7dc82 | ||
|
|
3bd3bf5b6a | ||
|
|
c940bb3935 | ||
|
|
366c74c02e | ||
|
|
0303419907 | ||
|
|
1511899a7e | ||
|
|
b1a2673e1a | ||
|
|
7074e0236a | ||
|
|
ddba65c368 | ||
|
|
93b375bbe2 | ||
|
|
d8bb291939 | ||
|
|
ca3a2ea136 | ||
|
|
1132bfd402 | ||
|
|
e9f6c93013 | ||
|
|
37f3a01838 | ||
|
|
87dc660191 | ||
|
|
40691f70d8 | ||
|
|
f4b0e5946b | ||
|
|
f3a2cfab7f | ||
|
|
817eb1d787 | ||
|
|
89786605c1 | ||
|
|
8b5224136b | ||
|
|
051c1a6b25 | ||
|
|
c3c7feeddf | ||
|
|
274b7de269 | ||
|
|
a0bea40dab | ||
|
|
2576505465 | ||
|
|
9f5298fa9a | ||
|
|
07f25c0cc7 | ||
|
|
cfb3ab406c | ||
|
|
a4db64b01b | ||
|
|
efcefcad96 | ||
|
|
38b071e39d | ||
|
|
46902911c8 | ||
|
|
23f686e698 | ||
|
|
311b735ae5 | ||
|
|
e2f1a1a315 | ||
|
|
1f7e4b12f7 | ||
|
|
178ecbaeef | ||
|
|
5ac346612f | ||
|
|
2ace0b69c1 | ||
|
|
6b8d7846de | ||
|
|
82f5dea54d | ||
|
|
d34d1dc9ff | ||
|
|
95f85256c2 | ||
|
|
33661d4b6a | ||
|
|
d39d8c5800 | ||
|
|
60da7517d1 | ||
|
|
ea43a75160 | ||
|
|
77f28324a0 | ||
|
|
778dcf54a1 | ||
|
|
161ebe81d5 | ||
|
|
1dbfa65885 | ||
|
|
8cb8e93394 | ||
|
|
afc8eb428e | ||
|
|
1fbe726c08 | ||
|
|
2aec3aa887 | ||
|
|
446de63944 | ||
|
|
08717ff883 | ||
|
|
ccef0576f9 | ||
|
|
1b12502ec3 | ||
|
|
b42c2f6605 | ||
|
|
cfa96f3fe0 | ||
|
|
3f55d1b9f4 | ||
|
|
acbf9bcc88 | ||
|
|
442cf4abb9 | ||
|
|
aee64c6047 | ||
|
|
41ca038f67 | ||
|
|
a0896d1faf | ||
|
|
ad62aed71a | ||
|
|
3ab76adf8d | ||
|
|
1f019c1e10 | ||
|
|
3e4335a798 | ||
|
|
70967125aa | ||
|
|
5921542894 | ||
|
|
c8cb9c2f04 | ||
|
|
b8c90cdfbe | ||
|
|
0ab5a0f603 | ||
|
|
8f9282dca1 | ||
|
|
8c572f77f4 | ||
|
|
c8560d8251 | ||
|
|
7e5bb21c3d | ||
|
|
74a9d5baeb | ||
|
|
fb428f307e | ||
|
|
e50748827a | ||
|
|
9ec99afb69 | ||
|
|
c2ad36cccb | ||
|
|
76b01b772b | ||
|
|
733176c925 | ||
|
|
88a87db2b8 | ||
|
|
45380db283 | ||
|
|
84b79b9126 | ||
|
|
2c8c0e30aa | ||
|
|
d6d3bbb89a | ||
|
|
1bb4b60d60 | ||
|
|
1827c0c37d | ||
|
|
5dd6f68c46 | ||
|
|
c85d47365d | ||
|
|
17bca77f06 | ||
|
|
4c4c049ac8 | ||
|
|
38fa86db3f | ||
|
|
ac3a38209f | ||
|
|
c568186ebc | ||
|
|
a49e97237f | ||
|
|
f1c36719aa | ||
|
|
292a667717 | ||
|
|
361549c5af | ||
|
|
c009e25907 | ||
|
|
e13fd47144 | ||
|
|
2812c4496d | ||
|
|
28093ce803 | ||
|
|
a649b08171 | ||
|
|
7a7585be68 | ||
|
|
6b241a18fd | ||
|
|
b9573fc138 | ||
|
|
8d41c7d068 | ||
|
|
e0365e3297 | ||
|
|
6b6ea19ab7 | ||
|
|
5a142f6eb1 | ||
|
|
cfacacd556 | ||
|
|
a9b9ad9d85 | ||
|
|
68ec84ae4d | ||
|
|
6724b20a7b | ||
|
|
dd34d56120 | ||
|
|
3559ec98b0 | ||
|
|
bc80f935a2 | ||
|
|
d0ac061fee | ||
|
|
0cdcd2232c | ||
|
|
8de615a69d | ||
|
|
24968aa972 | ||
|
|
a3ae3ba1f4 | ||
|
|
22b6962656 | ||
|
|
94be4bfae3 | ||
|
|
39bc014f14 | ||
|
|
4864f0ae24 | ||
|
|
cd7eb82bdb | ||
|
|
536d6bf22f | ||
|
|
ac092618ac | ||
|
|
03a09e1832 | ||
|
|
4ca03e17ef | ||
|
|
ec2b0f38ae | ||
|
|
2bad85564e | ||
|
|
d1252c7e8e | ||
|
|
4a7059cac7 | ||
|
|
9bd22bf4b1 | ||
|
|
0462cde56d | ||
|
|
e99312fe64 | ||
|
|
d8a7cd3501 | ||
|
|
2d2c4b35a5 | ||
|
|
9e717a6056 | ||
|
|
9ed603e6ce | ||
|
|
8a5545ad32 | ||
|
|
82b89e31fb | ||
|
|
607985e054 | ||
|
|
a2e43e1996 | ||
|
|
3b96cd4619 | ||
|
|
31790b6052 | ||
|
|
846f38d443 | ||
|
|
e7a9e8c100 | ||
|
|
e1b7b8e912 | ||
|
|
2480ce058b | ||
|
|
cf04ba48c2 | ||
|
|
8a853116bf | ||
|
|
c54c9ded12 | ||
|
|
0fc1a4b8c9 | ||
|
|
6ca5625902 | ||
|
|
a4fdcdef64 | ||
|
|
b1fe71ca2f | ||
|
|
4daf2cb8a2 | ||
|
|
4a3d739318 | ||
|
|
ba85c0513c | ||
|
|
b8c26e3c0c | ||
|
|
21b5b3e93a | ||
|
|
0a1a13b48b | ||
|
|
a18f7a94e2 | ||
|
|
067626c71d | ||
|
|
3bc10ee2e1 | ||
|
|
b9fba6ab7a | ||
|
|
20e5611810 | ||
|
|
37d68bca75 | ||
|
|
e58e0b7f80 | ||
|
|
81aab7b8ca | ||
|
|
bcf8a422f6 | ||
|
|
ac434a50aa | ||
|
|
4f050fba0a | ||
|
|
c36a72146e | ||
|
|
d82ec362ba | ||
|
|
c2172311f5 | ||
|
|
5a7bcfab93 | ||
|
|
decbc82b1a | ||
|
|
bd0f5b3b69 | ||
|
|
7f98271a59 | ||
|
|
8cc219994d | ||
|
|
84168dbf05 | ||
|
|
d6da219a70 | ||
|
|
65426fe653 | ||
|
|
6403d80fea | ||
|
|
bbcba1500c | ||
|
|
fcf4dbb6e8 | ||
|
|
3d75754ad8 | ||
|
|
af72309d8e | ||
|
|
646b4c4548 | ||
|
|
92ada9186c | ||
|
|
e67ce4582e | ||
|
|
25e3afcdbb | ||
|
|
b31a9a09c5 | ||
|
|
50614c15aa |
@@ -1,12 +0,0 @@
|
||||
List all fixbot worktree sessions with status information.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Run the mage task to list sessions:
|
||||
```
|
||||
./bin/mage -fixbot-list
|
||||
```
|
||||
|
||||
2. Present the output to the user.
|
||||
|
||||
3. Remind the user that they can run `./bin/mage -fixbot-dashboard` in a separate terminal for a live TUI dashboard showing all active agents.
|
||||
@@ -1,12 +0,0 @@
|
||||
Pause a fixbot worktree session — stops Docker containers and dev servers, closes tmux window, but preserves the worktree and branch for later resuming. Pass `all` to pause all running fixbot sessions.
|
||||
|
||||
The user provided: `$ARGUMENTS`
|
||||
|
||||
## Steps
|
||||
|
||||
1. Run the mage task to pause the session:
|
||||
```
|
||||
./bin/mage -fixbot-pause $ARGUMENTS
|
||||
```
|
||||
|
||||
2. Report the result to the user.
|
||||
@@ -1,5 +0,0 @@
|
||||
Tear down and remove a fixbot worktree session.
|
||||
|
||||
Run: `./bin/mage -fixbot-quit $ARGUMENTS`
|
||||
|
||||
Show the user the output.
|
||||
@@ -1,53 +0,0 @@
|
||||
Resume a paused fixbot session, or create a new session from an existing branch or PR.
|
||||
|
||||
The user provided: `$ARGUMENTS`
|
||||
|
||||
This can be an issue ID (e.g., `MB-12345`), a branch name, or a GitHub PR URL (e.g., `https://github.com/metabase/metabase/pull/12345`).
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Try resuming via mage task
|
||||
|
||||
Run:
|
||||
```
|
||||
./bin/mage -fixbot-resume $ARGUMENTS
|
||||
```
|
||||
|
||||
If this succeeds, the session is resumed — report success and stop.
|
||||
|
||||
### 2. If no existing session found (mage task exits non-zero), create from branch/PR
|
||||
|
||||
1. **Parse the argument:**
|
||||
- If it looks like a GitHub PR URL → extract the PR number, then run:
|
||||
```
|
||||
gh pr view <NUM> --json headRefName --jq .headRefName
|
||||
```
|
||||
to get the branch name. Save the PR number for later.
|
||||
- If it looks like an issue ID (e.g., `MB-12345`) → use as-is for matching
|
||||
- Otherwise → treat as a branch name
|
||||
|
||||
2. **Determine the issue ID** from the branch name by extracting the `MB-XXXXX` pattern (case-insensitive). If no issue ID can be found, ask the user for it.
|
||||
|
||||
3. **Fetch the issue from Linear:**
|
||||
```
|
||||
./bin/mage -fixbot-fetch-issue <ISSUE_ID>
|
||||
```
|
||||
|
||||
4. **Determine the app database** — analyze the issue description and comments:
|
||||
- If it mentions MySQL problems or MySQL-specific syntax → use `mysql`
|
||||
- If it mentions MariaDB specifically → use `mariadb`
|
||||
- Otherwise → use `postgres` (default)
|
||||
|
||||
5. **If a PR URL was given**, also fetch the PR title and body for additional context:
|
||||
```
|
||||
gh pr view <NUM> --json title,body
|
||||
```
|
||||
|
||||
6. **Write the agent prompt** — read the reference template at `.claude/fixbot/fixbot-agent.md`, then write a completed prompt to `.fixbot/metabase-fixbot-<ISSUE_ID>-prompt.md`. Include all the same content as `/fixbot` step 5 (issue details, comments, database info, credentials, workflow instructions, TDD requirements, etc.).
|
||||
|
||||
7. **Launch the session:**
|
||||
```
|
||||
./bin/mage fixbot-go <ISSUE_ID> --app-db <DB> --prompt-file .fixbot/metabase-fixbot-<ISSUE_ID>-prompt.md --branch '<BRANCH>'
|
||||
```
|
||||
|
||||
8. Tell the user the session was created from the existing branch.
|
||||
@@ -1,105 +0,0 @@
|
||||
You are the orchestrator for the fixbot workflow. Your job is to fetch a Linear issue, analyze it, determine the right database, write an agent prompt, and launch the workmux session.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Preflight checks
|
||||
|
||||
Run:
|
||||
```
|
||||
./bin/mage -fixbot-preflight
|
||||
```
|
||||
|
||||
If it fails, show the error to the user and stop. Do not attempt to recover or work around failures.
|
||||
|
||||
### 2. Resolve the issue ID
|
||||
|
||||
The user provided: `$ARGUMENTS`
|
||||
|
||||
This can be one of three formats:
|
||||
- **Linear issue ID** (e.g., `MB-12345`, `UXW-3155`) — use directly
|
||||
- **GitHub issue number** (e.g., `12345`) — resolve to Linear first
|
||||
- **GitHub issue URL** (e.g., `https://github.com/metabase/metabase/issues/12345`) — extract the number, then resolve to Linear
|
||||
|
||||
**If the input is a GitHub issue number or URL:**
|
||||
1. Fetch the GitHub issue with `gh issue view <NUMBER> --repo metabase/metabase --json body,comments,title`
|
||||
2. Search the issue body and comments for a Linear issue link (pattern: `https://linear.app/metabase/issue/[A-Z]+-[0-9]+`). Extract the Linear issue ID from the URL.
|
||||
3. If no Linear link is found in the GitHub issue, search Linear directly: run `./bin/mage -fixbot-fetch-issue` with a search term derived from the GitHub issue title. If that doesn't find a match, tell the user you couldn't find a corresponding Linear issue and stop.
|
||||
4. Tell the user which Linear issue you resolved to, so they can verify it's correct.
|
||||
|
||||
**Validation:** After resolving, confirm the issue ID looks like a Linear identifier (e.g., `MB-12345`). If not, tell the user the expected format and stop.
|
||||
|
||||
Run `./bin/mage -fixbot-list` and check if a session already exists for this issue (look for the issue ID in the branch name or session name). If one exists:
|
||||
- Tell the user the session already exists
|
||||
- Show them how to connect: `tmux attach -t <session-name>`
|
||||
- Show them how to stop it: `/fixbot-quit <issue-id>`
|
||||
- **STOP** — do not proceed with the remaining steps. Do not attempt to shut it down or clean it up.
|
||||
|
||||
### 3. Fetch the issue from Linear
|
||||
|
||||
Run:
|
||||
```
|
||||
./bin/mage -fixbot-fetch-issue <ISSUE_ID>
|
||||
```
|
||||
|
||||
Read the output carefully. Extract:
|
||||
- Issue title
|
||||
- Issue description
|
||||
- All comments (author, timestamp, body)
|
||||
- Issue URL
|
||||
- Branch name (the suggested branch name from Linear)
|
||||
|
||||
If the issue is not found, tell the user and stop.
|
||||
|
||||
### 4. Determine the app database
|
||||
|
||||
Analyze the issue description and comments to determine which database is needed:
|
||||
|
||||
- If the issue mentions **MySQL** problems, MySQL-specific SQL syntax, or MySQL error messages → use `mysql`
|
||||
- If the issue mentions **MariaDB** specifically → use `mariadb`
|
||||
- If the issue mentions **Postgres/PostgreSQL** specifically, or doesn't mention any database → use `postgres` (the default)
|
||||
- If unclear, default to `postgres`
|
||||
|
||||
### 5. Write the agent prompt
|
||||
|
||||
Read the reference template at `.claude/fixbot/fixbot-agent.md` to understand the required structure.
|
||||
|
||||
**Always rewrite** the prompt file at `.fixbot/metabase-fixbot-<ISSUE_ID>-prompt.md` — even if it already exists from a previous session. The template or prompt logic may have changed since it was first written. Use the `Read` tool on the file first (it may not exist, and that's fine), then use the `Write` tool. Do NOT use Bash `cat`/`echo` to create this file.
|
||||
|
||||
The prompt should include:
|
||||
|
||||
- The issue ID, title, and Linear URL
|
||||
- The branch name from Linear
|
||||
- The full issue description
|
||||
- All comments formatted with author and timestamp
|
||||
- The correct database type and appropriate port information
|
||||
- Pre-configured instance: users and API keys are auto-created via config file (admin: `admin@example.com` / `admin123`, regular: `regular@example.com` / `regular123`, API keys: `mb_admin_apikey` and `mb_regular_apikey`)
|
||||
- User context: the user is NOT a developer — the agent must work autonomously on all code/technical decisions, but should consult the user on product behavior questions and acceptance testing (they are an expert Metabase user)
|
||||
- All the workflow instructions from the reference template (Phase 1-5, important rules)
|
||||
- Red/green TDD requirement for both backend (Clojure with `./bin/test-agent`) and frontend (Jest/Cypress) changes
|
||||
- Self-review requirement: after fixing, use `/clojure-review` and `/typescript-review` to review changes, fix findings, and re-review if significant changes were made — repeat until clean before asking user to verify
|
||||
- **Backend reload reminder:** After finishing code changes and before asking the user to verify, use `clj-nrepl-eval` to reload any modified backend namespaces. This is critical — the user interacts with the running server through their browser, and they won't see changes until the backend is reloaded.
|
||||
- Tell the agent that when the user is ready to ship, it should commit, push, create the PR with `gh pr create`, and run `/fixbot-ci` to monitor CI
|
||||
- **Browser automation:** The agent has `playwright-cli` available for interacting with the running Metabase instance in a real browser. It should use this to reproduce UI issues (Phase 1), self-validate fixes before asking the user to verify (Phase 4), and troubleshoot rendering/interaction problems. Include the usage patterns from the reference template.
|
||||
- **Troubleshooting with backend logs:** When the user reports something isn't working, or when API calls are failing or behaving unexpectedly, check the backend logs — they are often the best source of error messages, warnings, and info-level diagnostics. If the logs aren't revealing enough, increase the logging level to debug for the relevant namespace(s) to get more detail.
|
||||
- Enterprise Edition: the dev environment always runs EE. If the fix requires OSS-only behavior, the agent should stop and tell the user.
|
||||
|
||||
Use the port computation: ports are based on a deterministic slot derived from the worktree name. Since you don't know the exact worktree name yet, use placeholder descriptions like "the port shown in your environment" and note that the dev environment will be configured automatically.
|
||||
|
||||
### 6. Launch the workmux session
|
||||
|
||||
Run:
|
||||
```
|
||||
./bin/mage fixbot-go <ISSUE_ID> --app-db <DB> --prompt-file .fixbot/metabase-fixbot-<ISSUE_ID>-prompt.md --branch '<BRANCH_NAME>'
|
||||
```
|
||||
|
||||
Where:
|
||||
- `<DB>` is the database you determined in step 3 (postgres, mysql, or mariadb)
|
||||
- `<BRANCH_NAME>` is the branch name from Linear (from step 2)
|
||||
|
||||
### 7. Report
|
||||
|
||||
Tell the user:
|
||||
- Which issue you're fixing
|
||||
- Which database you chose and why
|
||||
- That the workmux session has been launched
|
||||
- That they can run `./bin/mage -fixbot-dashboard` in a separate terminal for a live TUI dashboard showing all active agents
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Initialize beads in a git worktree by creating the database in a temp repo
|
||||
# and moving it into the current directory.
|
||||
#
|
||||
# Usage: bd-init-worktree.sh [worktree-name]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ -d ".beads" ]; then
|
||||
echo "beads already initialized, skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
name="${1:-$(basename "$PWD")}"
|
||||
scratch=".fixbot/bd-scratch"
|
||||
|
||||
mkdir -p "$scratch"
|
||||
git init -q "$scratch/repo"
|
||||
(cd "$scratch/repo" && echo "n" | bd init --stealth --quiet --skip-hooks -p "$name")
|
||||
mv "$scratch/repo/.beads" .beads
|
||||
rm -rf "$scratch"
|
||||
@@ -1,76 +0,0 @@
|
||||
Review your conversation transcript from this session and generate a structured improvement report. Save the report to `FIXBOT-SUGGESTIONS.md` in the current worktree root.
|
||||
|
||||
## What to analyze
|
||||
|
||||
Scroll back through the full conversation history from this session. Look at every tool call, every error, every retry, every place you got stuck or went in circles. Categorize what you find.
|
||||
|
||||
## Categories to evaluate
|
||||
|
||||
### 1. Tool issues
|
||||
- Tools that failed, returned unexpected results, or required workarounds
|
||||
- Tools you tried to use but couldn't (missing permissions, wrong arguments, etc.)
|
||||
- Tools you used inefficiently (e.g., multiple calls where one would suffice)
|
||||
|
||||
### 2. Prompt gaps
|
||||
- Steps that were missing from your initial prompt that you had to figure out on your own
|
||||
- Instructions that were ambiguous or led you down the wrong path
|
||||
- Steps that were in the wrong order or had missing prerequisites
|
||||
- Situations where you had to ask the user for clarification that should have been covered in the prompt
|
||||
|
||||
### 3. Knowledge gaps
|
||||
- Facts about the codebase, architecture, or conventions you had to discover through trial and error
|
||||
- Things you assumed incorrectly about how the code works
|
||||
- Database schemas, API endpoints, config options, or other reference info that would have saved time if documented upfront
|
||||
|
||||
### 4. Memory-worthy patterns
|
||||
- Stable lessons learned that would benefit future fixbot sessions (not one-off fixes)
|
||||
- Common failure modes and their solutions
|
||||
- Patterns about how this codebase works that aren't obvious from reading the code
|
||||
|
||||
### 5. Workflow inefficiencies
|
||||
- Places where you repeated work unnecessarily
|
||||
- Times you went down a dead end — what signal should have told you earlier?
|
||||
- Test/verify cycles that could have been shorter
|
||||
|
||||
## Report format
|
||||
|
||||
Write `FIXBOT-SUGGESTIONS.md` with this structure:
|
||||
|
||||
```markdown
|
||||
# Fixbot Auto-Improvement Report
|
||||
|
||||
**Session:** <issue ID>
|
||||
**Date:** <today's date>
|
||||
**Outcome:** <succeeded / partially succeeded / failed>
|
||||
|
||||
## Summary
|
||||
|
||||
<2-3 sentence summary of the session and the main improvement themes>
|
||||
|
||||
## Suggestions
|
||||
|
||||
### <Category name>
|
||||
|
||||
#### <Short title for the suggestion>
|
||||
|
||||
**What happened:** <Describe the specific problem you encountered>
|
||||
**Where to fix:** <Exact file path and section/line range>
|
||||
**Proposed change:** <The actual text to add, remove, or modify — be specific enough that someone can apply it directly>
|
||||
**Impact:** <What this would have saved — time, retries, user interruptions, etc.>
|
||||
|
||||
(Repeat for each suggestion, grouped by category)
|
||||
|
||||
## Quick wins
|
||||
|
||||
<Bulleted list of the 3-5 highest-impact, lowest-effort changes>
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- **Be specific** — include exact file paths, section names, line ranges, and actual proposed text changes. Vague suggestions like "improve error handling" are useless.
|
||||
- **Be actionable** — every suggestion must propose a concrete fix that can be applied. If you can't propose a specific change, it's not ready to be a suggestion.
|
||||
- **Be honest** — if you made a mistake due to your own reasoning (not a prompt gap), say so. Not everything is the prompt's fault.
|
||||
- **Prioritize by impact** — order suggestions within each category by how much time/frustration they would save in future sessions.
|
||||
- **Include prompt text diffs** — for prompt gap suggestions, show the exact text you'd add to the prompt file (e.g., `.fixbot/metabase-fixbot-<ID>-prompt.md` template or `.claude/fixbot/fixbot-agent.md`) as a before/after or insertion.
|
||||
- **Cover the fixbot orchestrator too** — if the issue was in how the session was set up (database choice, prompt generation, environment config), suggest changes to `.claude/commands/fixbot.md` as well.
|
||||
- **Don't suggest things that are already documented** — if the answer was in the prompt and you just missed it, that's a different problem than a missing instruction. Note which it was.
|
||||
@@ -1,55 +0,0 @@
|
||||
Monitor CI for this branch's PR, fix failures you caused, and re-run flaky tests.
|
||||
|
||||
## Step 1: Find the PR
|
||||
|
||||
Run `gh pr view --json number,url,headRefName` to get the PR for the current branch. If no PR exists, tell the user and stop.
|
||||
|
||||
## Step 2: Monitor CI
|
||||
|
||||
Check CI status every 10 minutes until all checks pass or you've exhausted retries:
|
||||
|
||||
1. Run `gh pr checks` to see the current status of all checks
|
||||
2. If all checks pass, tell the user "CI is green" and stop
|
||||
3. If checks are still running, wait 10 minutes and check again
|
||||
4. If checks have failed, proceed to Step 3
|
||||
5. **Ignore** the "Decide whether to backport or not" check — that's a label added by the user/reviewer
|
||||
|
||||
## Step 3: Analyze failures
|
||||
|
||||
For each failed check:
|
||||
|
||||
1. Run `./bin/mage ci-report` to get a detailed failure report
|
||||
2. Categorize each failure:
|
||||
- **Caused by your changes**: The test failure is in code you modified, or the error clearly relates to your changes
|
||||
- **Flaky test / pre-existing**: The failure is in unrelated code, is a known flaky test, or is an infrastructure issue (timeout, OOM, network)
|
||||
|
||||
**Flaky tests are common in this repo.** If a test failure looks unrelated to your changes, it's likely a flake.
|
||||
|
||||
## Step 4: Handle failures
|
||||
|
||||
**For failures caused by your changes:**
|
||||
1. Fix the issue in your code
|
||||
2. Run the failing test locally with `./bin/test-agent` to confirm the fix
|
||||
3. Commit and push the fix — **you don't need to wait for the current CI run to finish.** Pushing new changes automatically cancels the old run and starts a new one.
|
||||
4. Go back to Step 2 to monitor the new CI run
|
||||
|
||||
**For flaky / pre-existing failures:**
|
||||
1. Re-run all failed jobs: `gh run rerun --failed`
|
||||
2. Go back to Step 2 to monitor the re-run
|
||||
3. **If the same test fails again on the 3rd run**, investigate more closely:
|
||||
- Read the test and the failure output carefully
|
||||
- Determine whether your changes could have caused it
|
||||
- If it's genuinely unrelated, re-run one more time
|
||||
- If your changes may be the cause, treat it as "caused by your changes" and fix it
|
||||
|
||||
**Hard limits:**
|
||||
- **Never re-run more than 5 times without making code changes.** If tests keep failing after 5 re-runs, something is wrong — investigate or stop.
|
||||
- **If you are not making progress** on getting CI to pass (e.g., stuck in a loop of re-runs, or fixes aren't helping), **STOP and tell the user.** They can work with the Metabase developers from there.
|
||||
|
||||
## Step 5: Report
|
||||
|
||||
After CI passes (or after exhausting retries), tell the user:
|
||||
- CI status (green / still failing)
|
||||
- What failures you fixed
|
||||
- What failures you re-ran as flakes
|
||||
- If anything still needs attention
|
||||
@@ -1,310 +0,0 @@
|
||||
# Fixbot Agent Prompt — Reference Template
|
||||
|
||||
This file is a reference template for the fixbot orchestrator. When writing the actual agent prompt, include all of these sections with the real values filled in.
|
||||
|
||||
## CRITICAL: 20-Minute Time Limit
|
||||
|
||||
**YOU MUST NOT SPEND MORE THAN 20 MINUTES FIXING.** If you have not completed Phases 1–3 (understand, fix, self-review) within 20 minutes, STOP immediately. Present what you have so far — your diagnosis, any partial fix, what's blocking you — and ask the user for guidance. A fixbot that explains where it's stuck after 20 minutes is far more valuable than one that silently burns time going in circles.
|
||||
|
||||
## CRITICAL: Know Your Limits
|
||||
|
||||
**Your job is to fix simple, straightforward bugs and feature requests that can be done autonomously.** You are NOT a substitute for human judgment on complex decisions.
|
||||
|
||||
**STOP and tell the user why** if any of the following apply:
|
||||
- The fix requires complex architectural decisions or trade-offs that reasonable engineers would disagree on
|
||||
- The change impacts existing functionality in surprising or non-obvious ways (e.g., changing behavior that other features depend on)
|
||||
- The feature request may or may not be a good idea — it needs product discussion, not just implementation
|
||||
- The issue is ambiguous enough that different interpretations lead to very different solutions
|
||||
- The fix requires changes across many subsystems or has a large blast radius
|
||||
- You find yourself guessing about intended behavior rather than being confident
|
||||
|
||||
When in doubt, err on the side of stopping. A paused fixbot that explains the situation is far more valuable than one that ships a questionable change. Explain what you found, what the options are, and why a human should decide.
|
||||
|
||||
### Getting the User's Attention
|
||||
|
||||
Any time you need user input, are asking a question, stopping for a decision, or want to make sure the user sees something important, surround it with an eye-catching banner. The user may not be watching closely, so make it impossible to miss. For example:
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ 🛑 FIXBOT NEEDS YOUR INPUT ║
|
||||
╠══════════════════════════════════════════════════════════════╣
|
||||
║ ║
|
||||
║ <your message here> ║
|
||||
║ ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
```
|
||||
|
||||
Use different headers to match the situation (e.g., "READY FOR TESTING", "QUESTION", "STOPPING — HUMAN DECISION NEEDED", "PR OPENED"). Be creative with the banners — vary them so they stay noticeable.
|
||||
|
||||
## Required Sections
|
||||
|
||||
### Header
|
||||
Include the issue ID, title, Linear URL, and branch name.
|
||||
|
||||
### Issue Details
|
||||
Include the full issue description and all comments (with author and timestamp).
|
||||
|
||||
### Environment
|
||||
Tell the agent about its dev environment:
|
||||
- Ports are dynamically assigned per worktree. Read `mise.local.toml` to discover them:
|
||||
- `MB_JETTY_PORT` — backend URL is `http://localhost:$MB_JETTY_PORT`
|
||||
- `MB_FRONTEND_DEV_PORT` — frontend dev server is `http://localhost:$MB_FRONTEND_DEV_PORT`
|
||||
- `NREPL_PORT` — nREPL server port
|
||||
- The app database port is in the JDBC URL (`MB_DB_CONNECTION_URI`)
|
||||
- The agent MUST read `mise.local.toml` at startup to discover its ports — do not hardcode or assume any port numbers
|
||||
- App database type
|
||||
- **IMPORTANT**: The dev environment always runs the **Enterprise Edition (EE)**. Even if the issue mentions the OSS version, develop and test against EE. If the fix specifically requires running the OSS edition (e.g., testing OSS-only behavior that differs from EE), STOP and tell the user — do not attempt an OSS-only fix.
|
||||
|
||||
### About the User
|
||||
|
||||
The user is NOT a developer — do not ask them for implementation help, code suggestions, or technical decisions. Work autonomously on all code, debugging, and architecture choices. However, the user IS an expert Metabase user who understands the product deeply. Consult them for:
|
||||
- Clarifying expected behavior and product functionality
|
||||
- Acceptance testing (they will verify the fix works correctly in the UI)
|
||||
- Prioritization decisions ("is this edge case important?")
|
||||
|
||||
### Instance Setup
|
||||
|
||||
The dev environment is pre-configured with users and API keys via `MB_CONFIG_FILE_PATH`. No manual setup or API calls are needed. The instance will auto-create these on first startup:
|
||||
|
||||
- **Admin user**: `admin@example.com` / `admin123` (superuser)
|
||||
- **Regular user**: `regular@example.com` / `regular123`
|
||||
- **Admin API key**: `mb_AdminApiKey` (admin permissions)
|
||||
- **Regular API key**: `mb_RegularApiKey` (regular permissions)
|
||||
|
||||
Use these credentials to log in via the UI or make API calls. Do NOT call `/api/setup` — it is already handled.
|
||||
|
||||
### Instructions
|
||||
|
||||
The agent should follow this workflow. **CRITICAL: Execute all phases (0 through 4) in a single turn without stopping.** Do not end your turn after self-review — immediately continue to Phase 4 (browser verification and user testing instructions). Only stop and wait for user input after presenting the "READY FOR TESTING" banner in Phase 4.
|
||||
|
||||
#### Phase 0: Startup
|
||||
Read `.fixbot/llm-status.txt` and `mise.local.toml` using the `Read` tool before doing anything else. You need the ports from `mise.local.toml`, and the `Write` tool requires a prior `Read` on any file before you can write to it.
|
||||
|
||||
#### Phase 1: Understand
|
||||
1. Read and analyze the issue description and comments
|
||||
2. **CRITICAL: Before writing any code**, read `CLAUDE.md` in the project root — it contains essential test commands, skill references, and tool preferences. CLAUDE.md may not be auto-loaded in your session, so you MUST explicitly read it. Follow the skill references to find detailed guides (e.g., `.claude/skills/_shared/typescript-commands.md` for frontend test commands).
|
||||
3. Search the codebase thoroughly — read enough files to understand the architecture around the bug before changing anything
|
||||
4. Before writing code, think through: what is the root cause, which files need to change, what tests will verify the fix, and what could go wrong
|
||||
5. If the issue involves UI behavior, use the Playwright MCP tools (`mcp__playwright__browser_navigate`, `browser_snapshot`, etc.) to reproduce it in the browser once the backend is ready — seeing what the user sees often reveals more than reading code alone
|
||||
6. Only ask the user if the expected *product behavior* is genuinely ambiguous — they know Metabase well but don't want to hear about implementation details
|
||||
7. Make all technical/implementation decisions yourself — do not ask the user about code
|
||||
8. **Do not wait for servers to start.** The backend takes several minutes to boot. Start coding and writing tests immediately. Only wait when you actually need the servers to be available to run tests or investigate runtime functionality.
|
||||
9. **Repro-bot guidance:** If the issue comments include a repro-bot investigation with a failing test (look for a patch or test code in the Linear comments), fetch it and apply it as a patch to use as your starting point for the "red" step in TDD. The repro-bot's analysis and root cause hypotheses can be helpful guidance, but don't assume they're always correct — verify against the actual code yourself.
|
||||
|
||||
#### Phase 2: Fix
|
||||
1. ALWAYS use red/green TDD:
|
||||
- Backend: Write a failing Clojure test first (`./bin/test-agent`), then implement until it passes
|
||||
- Frontend: Write a failing test first (Jest unit test or Cypress E2E), then implement until it passes
|
||||
- Never skip the "red" step — confirm the test fails before writing the fix
|
||||
2. Run all relevant tests:
|
||||
- **Backend:** `./bin/test-agent`
|
||||
- **Frontend unit tests:**
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ bun run test-unit-keep-cljs path/to/file.unit.spec.ts │
|
||||
│ bun run test-unit-keep-cljs -t "pattern" │
|
||||
│ │
|
||||
│ Do NOT use bare `npx jest` — wrong config │
|
||||
│ Do NOT use `bun run test-unit` — build step fails sandbox │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
- If you need to run a specific test file, always use the full command above — do not guess at jest config paths or flags
|
||||
3. Report progress at each milestone with a clear status update
|
||||
|
||||
#### Phase 3: Self-Review
|
||||
Before asking the user to test, review your own changes thoroughly:
|
||||
1. Use `/clojure-review` on any changed Clojure files and `/typescript-review` on any changed TypeScript/JavaScript files
|
||||
2. Address all findings — fix issues, not just acknowledge them
|
||||
3. Re-run tests after making review-driven changes
|
||||
4. **If the review led to significant changes, re-review those changes.** Repeat until the review is clean.
|
||||
5. Only proceed to Phase 4 when the review is clean and all tests pass
|
||||
6. **Do not stop here** — immediately continue to Phase 4
|
||||
|
||||
#### Phase 4: Verify
|
||||
0. **Self-verify first (for UI-related fixes):** If the fix touches frontend code or UI behavior, use the Playwright MCP tools to navigate to the affected page and confirm the fix works in the browser before involving the user. Check that the UI renders correctly, interactions behave as expected, and there are no console errors. If self-verification fails, go back to Phase 2. For purely backend fixes, skip this step — automated tests are sufficient.
|
||||
- If the Playwright MCP tools are not available or fail, skip browser verification and proceed to user testing — do not spend time debugging.
|
||||
1. Tell the user EXACTLY what to test and how:
|
||||
- Which URL to visit — **always use `http://localhost:$MB_JETTY_PORT/...`** (the backend port), never the frontend dev server port
|
||||
- What steps to reproduce
|
||||
- What the expected behavior should be now
|
||||
- Remind them of login credentials: admin (`admin@example.com` / `admin123`) and regular user (`regular@example.com` / `regular123`), and API keys (`mb_AdminApiKey`, `mb_RegularApiKey`)
|
||||
2. WAIT for the user to test and provide feedback
|
||||
3. If they report issues, iterate (go back to Phase 2, then re-review in Phase 3 before asking the user again)
|
||||
|
||||
#### Phase 5: Open PR
|
||||
When the user says they're happy (e.g., "looks good", "ship it", "done", "open the pr", "commit it"):
|
||||
1. Stage and commit all fix-related changes:
|
||||
- **NEVER commit changes under `.claude/`** — the worktree setup copies fixbot commands there, and those must not be committed
|
||||
- **NEVER commit changes under `.fixbot/` or `.beads/` or `mage/`** — these are copied or generated files
|
||||
- Stage files individually by name (`git add path/to/file.clj`) — do NOT use `git add .` or `git add -A`
|
||||
- Only stage files that are part of the actual fix
|
||||
- Do not include yourself as a co-author in the commit message
|
||||
- **REMEMBER that the commit history is public and NO sensitive information should ever be stored in the git messages**
|
||||
- **REMEMBER that the pull request is public and NO sensitive information should ever be stored in the pull request**
|
||||
2. Push the branch to origin
|
||||
3. Create the PR with `gh pr create`:
|
||||
- Title: concise description of the fix
|
||||
- **NEVER include Linear URLs or Linear issue IDs in the PR title, body, or commits** — Linear is internal
|
||||
- Body should follow this template (do NOT include the backport/contributing sections from the repo's PR template):
|
||||
```
|
||||
### Description
|
||||
|
||||
<Describe the overall approach and the problem being solved>
|
||||
|
||||
### How to verify
|
||||
|
||||
<Step-by-step instructions to verify the fix>
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] Tests have been added/updated to cover changes in this PR
|
||||
```
|
||||
- Do NOT add any labels — that's up to the user
|
||||
4. Tell the user the PR URL and a summary of what was fixed
|
||||
|
||||
#### Phase 6: Monitor PR
|
||||
After submitting the pull request, monitor the pull request until it passes. NOTE: this may take a while and several attempts.
|
||||
1. Run `/fixbot-ci` to monitor CI results and handle failures
|
||||
2. **Ignore** the "Decide whether to backport or not" check failure — that's a label requirement handled by the user/reviewer, not something you can fix
|
||||
3. **Small PRs:** For frontend-only changes (no backend files touched), the relevant CI checks are `frontend-tests/*` and `e2e-tests/*`. If those pass and only unrelated checks (SDK, driver tests, etc.) fail, re-run the failed jobs immediately rather than waiting for the full suite to complete.
|
||||
|
||||
### Status Bar
|
||||
|
||||
A status bar at the bottom of the tmux session shows issue info, service health, and your status message. The issue info and health indicators are managed automatically. You control only the status message by writing to `.fixbot/llm-status.txt`.
|
||||
|
||||
Write to `.fixbot/llm-status.txt` (overwrite the whole file each time) when something important changes — for example:
|
||||
- Current phase of work (e.g., "Phase 1: Analyzing issue", "Phase 2: Writing tests", "Phase 3: Self-review", "Phase 4: Ready for user testing")
|
||||
- A blocking question waiting on user input
|
||||
- URLs the user needs (e.g., the page to test)
|
||||
|
||||
**Rules:**
|
||||
- Keep it to **1-3 lines** — the pane is small and other info is displayed above your status
|
||||
- Only update when the visible state meaningfully changes — don't spam updates
|
||||
- Keep each line short and scannable
|
||||
|
||||
### Task Tracking with Beads
|
||||
|
||||
`bd` (beads) is installed and initialized in stealth mode for structured task tracking. Use it to break down complex work, track dependencies, and maintain context across phases.
|
||||
|
||||
**Key commands:**
|
||||
- `bd create "Task title" -p 0` — create a task (lower priority number = higher priority)
|
||||
- `bd ready` — list tasks that have no blockers and are ready to work on
|
||||
- `bd update <id> --claim` — assign a task to yourself and mark it in-progress
|
||||
- `bd update <id> --close` — mark a task as done
|
||||
- `bd show <id>` — view task details and history
|
||||
- `bd list` — list all tasks
|
||||
|
||||
**When to use:**
|
||||
- When breaking a fix into multiple subtasks
|
||||
- To track what's been done vs what remains
|
||||
- To note blockers or dependencies between tasks
|
||||
|
||||
**Rules:**
|
||||
- Beads is in stealth mode — it will not modify git state
|
||||
- Don't overthink it — simple issues may not need task tracking at all
|
||||
|
||||
### nREPL
|
||||
|
||||
The backend runs an nREPL server. Connect using `localhost` and the nREPL port (read `NREPL_PORT` from `mise.local.toml`):
|
||||
|
||||
```bash
|
||||
clj-nrepl-eval -H localhost -p $NREPL_PORT "(+ 1 2)"
|
||||
```
|
||||
|
||||
Use nREPL for:
|
||||
- Evaluating Clojure expressions against the running backend
|
||||
- Requiring namespaces with `:reload` to pick up code changes
|
||||
- Testing functions interactively
|
||||
- Checking compilation
|
||||
|
||||
**REPL expression rules**: Send one expression per eval call. Multi-expression evals frequently timeout. Split into separate (parallel if independent) calls. Test/dev namespaces (`metabase.test`, `dev`, enterprise test namespaces) are available natively on the classpath.
|
||||
|
||||
### Browser Automation with Playwright MCP
|
||||
|
||||
A Playwright MCP server is configured in `.mcp.json` and provides browser automation tools directly as MCP tool calls (prefixed with `mcp__playwright__`). The browser runs headless outside the sandbox — no bash commands needed.
|
||||
|
||||
**This is optional.** For purely backend fixes where the issue and verification are API-level or logic-level, you don't need the browser at all — automated tests and nREPL are sufficient. Use the Playwright MCP tools when the issue involves UI behavior, when you need to see what the user sees, or when the user reports something that doesn't match what you'd expect from the code.
|
||||
|
||||
**Core workflow:**
|
||||
1. `mcp__playwright__browser_navigate` — navigate to `http://localhost:$MB_JETTY_PORT`
|
||||
2. `mcp__playwright__browser_snapshot` — capture page state with element refs (you MUST do this before any interaction)
|
||||
3. Interact: `browser_click`, `browser_fill`, `browser_type`, `browser_select_option`, `browser_hover`, etc.
|
||||
4. `mcp__playwright__browser_snapshot` again — verify the result
|
||||
5. `mcp__playwright__browser_close` — close the browser when done
|
||||
|
||||
**Key MCP tools:**
|
||||
- `browser_navigate` — navigate to a URL (`url` param)
|
||||
- `browser_snapshot` — capture accessibility snapshot with element refs — **always snapshot before interacting**
|
||||
- `browser_click` — click an element (`element` param: description from snapshot, `ref` param: element ref from snapshot)
|
||||
- `browser_fill` — fill text into an input (`element`, `ref`, `value` params)
|
||||
- `browser_type` — type text with keyboard events (`text` param, optional `element`/`ref`)
|
||||
- `browser_select_option` — select a dropdown option (`element`, `ref`, `values` params)
|
||||
- `browser_hover` — hover over an element (`element`, `ref` params)
|
||||
- `browser_press_key` — press a keyboard key (`key` param)
|
||||
- `browser_evaluate` — execute JavaScript (`script` param)
|
||||
- `browser_console_messages` — get browser console logs
|
||||
- `browser_network_requests` — list network activity
|
||||
- `browser_take_screenshot` — capture visual screenshot (`raw` param for base64)
|
||||
- `browser_close` — close the browser
|
||||
- `browser_resize` — resize viewport (`width`, `height` params)
|
||||
- `browser_navigate_back` / `browser_navigate_forward` — history navigation
|
||||
- `browser_wait_for` — wait for a condition
|
||||
|
||||
**Login and navigate:**
|
||||
1. Navigate to `http://localhost:$MB_JETTY_PORT/auth/login`
|
||||
2. Use `browser_snapshot` to get element refs
|
||||
3. Use `browser_fill` on the email and password fields
|
||||
4. Use `browser_click` on the sign-in button
|
||||
|
||||
Alternatively, get a session token via API and use `browser_evaluate` to set a cookie:
|
||||
```bash
|
||||
curl -s -X POST http://localhost:$MB_JETTY_PORT/api/session \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"username":"admin@example.com","password":"admin123"}' | jq -r '.id'
|
||||
```
|
||||
Then use `browser_evaluate` with script: `document.cookie = 'metabase.SESSION=<token>;path=/'`
|
||||
|
||||
**"Start exploring" modal:** Metabase shows this modal on first native question view. Use `browser_snapshot` to find the button, then `browser_click` to dismiss it.
|
||||
|
||||
**When to use:**
|
||||
- **Phase 1 (Understand):** If the issue involves UI behavior, reproduce it in the browser to see exactly what the user sees
|
||||
- **Phase 4 (Verify):** Before asking the user to test, self-verify the fix by navigating to the affected page and confirming it works. Still ask the user for final sign-off — your browser check supplements but does not replace user acceptance testing.
|
||||
- **Troubleshooting:** When API responses look correct but the user reports UI problems, use the browser to see what's actually rendering
|
||||
|
||||
**Rules:**
|
||||
- If the Playwright MCP tools are not available or fail on first use, skip browser verification — do not spend time debugging MCP server issues.
|
||||
- Always `browser_snapshot` before interacting — you need element refs to click/fill
|
||||
- Always use `http://localhost:$MB_JETTY_PORT` (the backend port), never the frontend dev server port
|
||||
- Close the browser when you're done to free resources
|
||||
- If a page takes time to load, `browser_snapshot` again after a few seconds to get the updated state
|
||||
|
||||
### Server Logs
|
||||
|
||||
Read server output from log files in `.fixbot/`:
|
||||
- **Backend**: `ls -t .fixbot/backend-*.log | head -1` to find the latest, then `tail -200 <file>`
|
||||
- **Frontend**: `ls -t .fixbot/frontend-*.log | head -1` to find the latest, then `tail -200 <file>`
|
||||
|
||||
Check these when:
|
||||
- Debugging startup failures (check if backend/frontend started successfully)
|
||||
- Looking for error messages or stack traces
|
||||
- Verifying that your code changes were picked up by hot reload
|
||||
|
||||
### GitHub API Workaround
|
||||
|
||||
If `gh` commands fail with `tls: failed to verify certificate: x509` errors, switch to using `curl` with `gh auth token` for the rest of the session. For example, to create a PR:
|
||||
```bash
|
||||
curl -s -X POST https://api.github.com/repos/metabase/metabase/pulls \
|
||||
-H "Authorization: token $(gh auth token)" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-d '{ "title": "...", "head": "branch-name", "base": "master", "body": "..." }'
|
||||
```
|
||||
Use the same pattern for any other `gh` operations (e.g., `gh pr view` → `curl -s https://api.github.com/repos/metabase/metabase/pulls/NUMBER ...`).
|
||||
|
||||
### Important Rules
|
||||
- **Python in bash:** Never write Python code inline in bash heredocs — `!` and other characters get mangled by shell expansion. Instead, write Python scripts to a temp file (`$TMPDIR/script.py`) and run them with `python3 $TMPDIR/script.py`.
|
||||
- Focus ONLY on the reported issue — no unrelated changes
|
||||
- Always run tests before telling the user to verify
|
||||
- Check backend readiness: `curl -s http://localhost:$MB_JETTY_PORT/api/health`
|
||||
- Be patient — the backend takes several minutes to start on first launch. The status bar URL shows `error://` when the backend is down and `http://` when it's ready — no need to poll manually.
|
||||
- Work autonomously — do not block on the user for technical questions. Research the codebase, read tests, and make your own decisions.
|
||||
- Only involve the user for product/behavior questions and acceptance testing
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Proxy for xdg-open that works on macOS host.
|
||||
# Used by Claude Code inside the sandbox to open browser for OAuth login.
|
||||
exec open "$@"
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"sandbox": {
|
||||
"enabled": true,
|
||||
"autoAllowBashIfSandboxed": true,
|
||||
"allowUnsandboxedCommands": true,
|
||||
"filesystem": {
|
||||
"allowWrite": [".", "{{pwd}}", "~/.m2", "~/.gitlibs", "~/.deps.clj", "~/.local/state/workmux", "~/.cache/workmux", "{{tmpdir}}", "/tmp", "/private/tmp", "~/Library/Caches/ms-playwright", "~/Library/Application Support/Google/Chrome"],
|
||||
"allowRead": [".", "{{pwd}}", "~/.m2", "~/.gitlibs", "~/.deps.clj", "~/.local/state/workmux", "~/.cache/workmux", "{{tmpdir}}", "/tmp", "/private/tmp", "~/Library/Caches/ms-playwright", "~/Library/Application Support/Google/Chrome"]
|
||||
},
|
||||
"excludedCommands": ["docker"],
|
||||
"network": {
|
||||
"allowLocalBinding": true,
|
||||
"allowAllUnixSockets": true,
|
||||
"allowedDomains": ["github.com", "api.github.com", "*.blob.core.windows.net"]
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Edit",
|
||||
"Write",
|
||||
"Skill",
|
||||
"Grep",
|
||||
"mcp__playwright__*",
|
||||
"WebFetch(domain:linear.app)",
|
||||
"WebFetch(domain:metabase.com)"
|
||||
]
|
||||
},
|
||||
"skipDangerousModePermissionPrompt": true
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
agent: claude
|
||||
|
||||
files:
|
||||
symlink:
|
||||
- node_modules
|
||||
- bin/bb
|
||||
|
||||
post_create:
|
||||
# Copy fixbot tooling from source repo (worktree is based on master, not possibly-different)
|
||||
- cp -r {{SOURCE_REPO}}/.claude/fixbot .claude/fixbot
|
||||
- cp -r {{SOURCE_REPO}}/mage/src/mage/fixbot mage/src/mage/fixbot
|
||||
- cp {{SOURCE_REPO}}/bb.edn bb.edn
|
||||
|
||||
- sed "s|{{pwd}}|$(pwd)|;s|{{tmpdir}}|${TMPDIR%/}|" .claude/fixbot/settings.local-template.json > .claude/settings.local.json
|
||||
- mkdir -p .fixbot/playwright/sessions .fixbot/playwright/sockets
|
||||
- echo '{{ISSUE_ID}} | {{ISSUE_URL}}' > .fixbot/issue.txt
|
||||
- "echo 'Phase 0: Setting up' > .fixbot/llm-status.txt"
|
||||
- rm -f .claude/commands/fixbot*.md
|
||||
- cp .claude/fixbot/commands/*.md .claude/commands/
|
||||
- mkdir -p .github/hooks/workmux-status
|
||||
- echo '{"version":1,"hooks":{"userPromptSubmitted":[{"type":"command","bash":"workmux set-window-status working"}],"postToolUse":[{"type":"command","bash":"workmux set-window-status working"}],"agentStop":[{"type":"command","bash":"workmux set-window-status done"}]}}' > .github/hooks/workmux-status/hooks.json
|
||||
- MB_PREMIUM_EMBEDDING_TOKEN={{MB_PREMIUM_EMBEDDING_TOKEN}} LINEAR_API_KEY={{LINEAR_API_KEY}} ./bin/mage -fixbot-dev-env --app-db {{APP_DB}}
|
||||
- mise trust mise.local.toml
|
||||
- bun install
|
||||
- 'echo ''{"mcpServers":{"playwright":{"command":"npx","args":["-y","@playwright/mcp","--headless","--browser","chrome"]}}}'' > .mcp.json'
|
||||
|
||||
pre_remove:
|
||||
- ./bin/mage -fixbot-dev-env --down
|
||||
|
||||
panes:
|
||||
- command: <agent>
|
||||
focus: true
|
||||
|
||||
- command: mise x -- clj -M:dev:dev-start:drivers:drivers-dev:ee:ee-dev 2>&1 | tee .fixbot/backend-$(date +%Y%m%d-%H%M%S).log
|
||||
split: horizontal
|
||||
percentage: 30
|
||||
|
||||
- command: mise x -- bun run build-hot 2>&1 | tee .fixbot/frontend-$(date +%Y%m%d-%H%M%S).log
|
||||
split: vertical
|
||||
|
||||
- command: mise x -- ./bin/mage -fixbot-status
|
||||
split: vertical
|
||||
size: 7
|
||||
@@ -1,4 +1,4 @@
|
||||
--b-
|
||||
---
|
||||
name: add-tracing
|
||||
description: Add OpenTelemetry tracing spans to Clojure code following Metabase tracing conventions. Use when instrumenting backend code with trace coverage.
|
||||
---
|
||||
|
||||
@@ -176,8 +176,10 @@
|
||||
clojure.core.async/to-chan!
|
||||
clojure.core.async/to-chan!!
|
||||
metabase.channel.core/send!
|
||||
metabase.warehouses.models.database/assert-not-h2!
|
||||
metabase.driver.sql-jdbc.execute/execute-prepared-statement!
|
||||
metabase.notification.models/create-notification!
|
||||
metabase.oauth-server.core/reset-provider!
|
||||
metabase.pulse.send/send-pulse!
|
||||
metabase-enterprise.transforms-python.python-runner-test/execute!
|
||||
;; Workspace isolation functions are safe in parallel tests because they operate on
|
||||
@@ -552,6 +554,7 @@
|
||||
metabase-enterprise.cache.config-test/with-temp-persist-models hooks.common/with-seven-bindings
|
||||
metabase-enterprise.dependencies.api-test/with-dependents-test! hooks.common/with-two-bindings
|
||||
metabase-enterprise.metabot-v3.tools.dependencies-test/with-dependent-transforms! hooks.common/with-two-bindings
|
||||
metabase-enterprise.metabot.tools.dependencies-test/with-dependent-transforms! hooks.common/with-two-bindings
|
||||
metabase-enterprise.semantic-search.db.connection/with-migrate-tx hooks.common/let-one-with-optional-value
|
||||
metabase-enterprise.serialization.api-test/with-serialization-test-data! hooks.common/with-three-bindings
|
||||
metabase-enterprise.serialization.test-util/with-temp-dpc hooks.toucan2.tools.with-temp/with-temp
|
||||
@@ -593,6 +596,9 @@
|
||||
metabase.model-persistence.api-test/with-persistence-setup! hooks.common/with-one-top-level-binding
|
||||
metabase.model-persistence.api-test/with-setup! hooks.common/with-one-top-level-binding
|
||||
metabase.model-persistence.test-util/with-persistence-enabled! hooks.metabase.model-persistence.test-util/with-persistence-enabled!
|
||||
metabase.mq.core/with-queue hooks.common/let-second
|
||||
metabase.mq.core/with-topic hooks.common/let-second
|
||||
metabase.mq.core/def-listener! hooks.metabase.mq.impl/def-listener
|
||||
metabase.models.interface/define-batched-hydration-method hooks.metabase.models.interface/define-hydration-method
|
||||
metabase.models.interface/define-simple-hydration-method hooks.metabase.models.interface/define-hydration-method
|
||||
metabase.permissions.core/allow-changing-all-users-group-members hooks.common/do*
|
||||
|
||||
24
.clj-kondo/src/hooks/metabase/mq/impl.clj
Normal file
@@ -0,0 +1,24 @@
|
||||
(ns hooks.metabase.mq.impl
|
||||
(:require
|
||||
[clj-kondo.hooks-api :as hooks]))
|
||||
|
||||
(defn def-listener
|
||||
"Kondo hook for `mq/def-listener`. Rewrites to a `fn` form so kondo can analyze bindings.
|
||||
|
||||
Forms:
|
||||
(mq/def-listener! :topic/foo [msg] body...)
|
||||
(mq/def-listener! :queue/bar {:config map} [msgs] body...)"
|
||||
[{:keys [node]}]
|
||||
(let [[_ _channel-name & args] (:children node)
|
||||
;; skip optional config map
|
||||
args (if (hooks/map-node? (first args))
|
||||
(rest args)
|
||||
args)
|
||||
[bindings & body] args
|
||||
;; rewrite as: (fn [bindings...] body...)
|
||||
new-node (hooks/list-node
|
||||
(list*
|
||||
(hooks/token-node 'fn)
|
||||
bindings
|
||||
body))]
|
||||
{:node new-node}))
|
||||
@@ -303,7 +303,6 @@
|
||||
metabase.util.malli/with [[:default]]
|
||||
metabase.util.malli/with-api-error-message [[:default]]
|
||||
metabase.util.namespaces/import-fns [[:default]]
|
||||
metabase.util.queue-test/await-test-while [[:block 1]]
|
||||
metabase.util/auto-retry [[:block 1]]
|
||||
metabase.util/case-enum [[:block 1]]
|
||||
metabase.util/for-map [[:block 1]]
|
||||
|
||||
6
.github/team.json
vendored
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"teams": [
|
||||
{
|
||||
"name": "Querying",
|
||||
"label": ".Team/Querying",
|
||||
"name": "Querying Platform",
|
||||
"label": ".Team/QueryingPlatform",
|
||||
"members": [
|
||||
"bshepherdson",
|
||||
"metamben",
|
||||
"rileythomp",
|
||||
"camsual"
|
||||
"camsaul"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
6
.github/workflows/backend.yml
vendored
@@ -37,16 +37,16 @@ jobs:
|
||||
name: Run Eastwood linter
|
||||
|
||||
be-linter-eastwood-test:
|
||||
if: ${{ !inputs.skip }}
|
||||
if: ${{ !inputs.skip && github.ref_name == 'master' }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
timeout-minutes: 30
|
||||
name: Eastwood (test)
|
||||
name: Eastwood (tests only)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
- run: clojure -X:dev:ee:ee-dev:drivers:drivers-dev:test:eastwood/test
|
||||
name: Run Eastwood linter on tests
|
||||
name: Run Eastwood linter on our backend tests
|
||||
|
||||
be-tests:
|
||||
if: ${{ !inputs.skip }}
|
||||
|
||||
360
.github/workflows/claude-code-bug-fix.yml
vendored
@@ -1,360 +0,0 @@
|
||||
name: "Claude Code Bug Fix"
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
pull_request_review:
|
||||
types:
|
||||
- submitted
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: "Issue number to fix"
|
||||
required: true
|
||||
type: string
|
||||
override_employee_check:
|
||||
description: "Override employee check for issue creator"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
auto-fix-bug:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
if: |
|
||||
(github.event_name == 'issues' && github.event.label.name == 'Claude Bug Fix' && github.event.issue.state == 'open') ||
|
||||
(github.event_name == 'workflow_dispatch') ||
|
||||
(github.event_name == 'pull_request_review' && github.event.review.state == 'changes_requested' && startsWith(github.event.pull_request.head.ref, 'claude-fix/'))
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get issue data and check employee status
|
||||
id: get_issue
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { getIssueData } = require('${{ github.workspace }}/.github/scripts/get-issue-data.js');
|
||||
|
||||
try {
|
||||
const result = await getIssueData({
|
||||
context,
|
||||
github,
|
||||
manualIssueNumber: '${{ github.event.inputs.issue_number }}'
|
||||
});
|
||||
|
||||
const { issueNumber, issueData, employeeComments, prReviews, triggerType, isIssueAuthorEmployee } = result;
|
||||
|
||||
// Check employee status and fail if not authorized
|
||||
if (!result.isIssueAuthorEmployee && '${{ github.event.inputs.override_employee_check }}' !== 'true') {
|
||||
core.setFailed(`User ${result.issueData.user.login} is not a Metabase employee. Skipping bug fix automation.`);
|
||||
}
|
||||
|
||||
core.setOutput('issue_number', issueNumber);
|
||||
core.setOutput('issue_data', JSON.stringify(issueData));
|
||||
core.setOutput('issue_comments', JSON.stringify(employeeComments));
|
||||
core.setOutput('pr_reviews', JSON.stringify(prReviews));
|
||||
core.setOutput('trigger_type', triggerType);
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
|
||||
- name: Find linked Linear issue
|
||||
id: find_linear
|
||||
continue-on-error: true
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
|
||||
ISSUE_NUMBER: ${{ steps.get_issue.outputs.issue_number }}
|
||||
with:
|
||||
script: |
|
||||
const { findLinearIssue } = require('${{ github.workspace }}/.github/scripts/find-linear-issue.js');
|
||||
|
||||
const githubIssueUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/issues/${process.env.ISSUE_NUMBER}`;
|
||||
|
||||
const result = await findLinearIssue({
|
||||
githubIssueUrl,
|
||||
linearApiKey: process.env.LINEAR_API_KEY
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
console.log(`Found Linear issue: ${result.issue}`);
|
||||
core.setOutput('linear_issue', result.issue);
|
||||
} else {
|
||||
console.log(`Could not find Linear issue: ${result.message}`);
|
||||
}
|
||||
|
||||
- name: Set up git configuration
|
||||
run: |
|
||||
git config --global user.name "Claude Code[bot]"
|
||||
git config --global user.email "noreply@anthropic.com"
|
||||
|
||||
- name: Prepare issue context and bug fix prompt
|
||||
id: prepare_prompt
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
ISSUE_DATA: ${{ steps.get_issue.outputs.issue_data }}
|
||||
ISSUE_COMMENTS: ${{ steps.get_issue.outputs.issue_comments }}
|
||||
PR_REVIEWS: ${{ steps.get_issue.outputs.pr_reviews }}
|
||||
TRIGGER_TYPE: ${{ steps.get_issue.outputs.trigger_type }}
|
||||
with:
|
||||
script: |
|
||||
const issueData = JSON.parse(process.env.ISSUE_DATA);
|
||||
const issueComments = JSON.parse(process.env.ISSUE_COMMENTS);
|
||||
const prReviews = JSON.parse(process.env.PR_REVIEWS);
|
||||
const triggerType = process.env.TRIGGER_TYPE;
|
||||
const issueNumber = '${{ steps.get_issue.outputs.issue_number }}';
|
||||
|
||||
// Convert issue title to kebab case and create branch name
|
||||
const issueTitle = issueData.title || 'untitled';
|
||||
const kebabTitle = issueTitle
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.replace(/-+/g, '-')
|
||||
.substring(0, 50);
|
||||
const branchName = `claude-fix/issue-${issueNumber}-${kebabTitle}`;
|
||||
|
||||
// Format data for bash environment
|
||||
const formattedComments = issueComments.length > 0
|
||||
? issueComments.map(comment =>
|
||||
`**Comment by @${comment.user.login} on ${comment.created_at}:**\n${comment.body}`
|
||||
).join('\n\n---\n\n')
|
||||
: '';
|
||||
const formattedReviews = prReviews.length > 0
|
||||
? prReviews.map(review =>
|
||||
`**Review by @${review.user.login} on ${review.submitted_at} (Changes Requested):**\n${review.body || 'No review comment provided'}`
|
||||
).join('\n\n---\n\n')
|
||||
: '';
|
||||
|
||||
core.exportVariable('BRANCH_NAME', branchName);
|
||||
core.exportVariable('ISSUE_NUMBER', issueNumber);
|
||||
core.exportVariable('ISSUE_TITLE', issueData.title);
|
||||
core.exportVariable('ISSUE_AUTHOR', issueData.user.login);
|
||||
core.exportVariable('ISSUE_CREATED', issueData.created_at);
|
||||
core.exportVariable('ISSUE_BODY', issueData.body || 'No description provided');
|
||||
core.exportVariable('ISSUE_COMMENTS_FORMATTED', formattedComments);
|
||||
core.exportVariable('PR_REVIEWS_FORMATTED', formattedReviews);
|
||||
core.exportVariable('TRIGGER_TYPE', triggerType);
|
||||
|
||||
- name: Generate bug fix prompt
|
||||
run: |
|
||||
cat << 'EOF' >> $GITHUB_ENV
|
||||
BUG_FIX_PROMPT<<PROMPT_END
|
||||
You are helping to fix a bug reported in GitHub issue #${{ env.ISSUE_NUMBER }}.
|
||||
|
||||
## Bug Report Details
|
||||
**Title:** ${{ env.ISSUE_TITLE }}
|
||||
**Reporter:** ${{ env.ISSUE_AUTHOR }}
|
||||
**Created:** ${{ env.ISSUE_CREATED }}
|
||||
|
||||
**Description:**
|
||||
${{ env.ISSUE_BODY }}
|
||||
|
||||
**Comments:**
|
||||
${{ env.ISSUE_COMMENTS_FORMATTED }}
|
||||
EOF
|
||||
|
||||
# Add review feedback section only if this is a review trigger
|
||||
if [ "${{ env.TRIGGER_TYPE }}" = "pull_request_review" ]; then
|
||||
cat << 'EOF' >> $GITHUB_ENV
|
||||
|
||||
## Previous Fix Attempt - Review Feedback (MUST ADDRESS)
|
||||
Your previous automated fix attempt received the following feedback that needs to be addressed:
|
||||
|
||||
${{ env.PR_REVIEWS_FORMATTED }}
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Add the rest of the prompt (always the same)
|
||||
cat << 'EOF' >> $GITHUB_ENV
|
||||
|
||||
## Instructions
|
||||
1. **Analyze the issue description and comments** - Understand what the issue is describing
|
||||
a. Pay particular attention to reproduction steps and if there's an agreed upon fix.
|
||||
2. **Search the codebase** - Use Grep and Glob tools to find relevant files
|
||||
3. **Implement a fix** - Use the appropriate write skill based on the file types:
|
||||
- Use clojure-write for Clojure/ClojureScript files
|
||||
- Use typescript-write for TypeScript/JavaScript files
|
||||
- Follow the coding standards and patterns in each skill
|
||||
4. **Test the fix** - Run appropriate tests as specified in the skills
|
||||
5. **Verify the fix** - Ensure tests pass and code follows conventions
|
||||
|
||||
## Important Guidelines
|
||||
- Use the appropriate write skill for the language you're working in
|
||||
- Write failing tests first, then fix them
|
||||
- Work in small, testable increments
|
||||
- Don't commit changes - leave that for the workflow
|
||||
- IMPORTANT: Focus only on fixing the reported bug, don't make unrelated changes
|
||||
|
||||
## Scratchpad Directory
|
||||
A `.claude-scratchpad/` directory has been created for your temporary files:
|
||||
- Use this directory for ALL temporary verification files. However, prefer using production test files rather than adhoc ones.
|
||||
- Only modify actual production code in the main repository directories
|
||||
- The scratchpad will be cleaned up automatically - nothing from it will be committed
|
||||
|
||||
Start by understanding the bug and then systematically work toward a solution.
|
||||
PROMPT_END
|
||||
EOF
|
||||
|
||||
- name: Determine branch strategy
|
||||
run: |
|
||||
BRANCH_NAME="${{ env.BRANCH_NAME }}"
|
||||
if git branch -r --list "origin/${BRANCH_NAME}" | grep -q .; then
|
||||
echo "Branch already exists: $BRANCH_NAME"
|
||||
echo "Will push to existing branch and skip PR creation"
|
||||
echo "BRANCH_EXISTS=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Creating new branch: $BRANCH_NAME"
|
||||
echo "Will create new PR"
|
||||
echo "BRANCH_EXISTS=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
# Export the branch name
|
||||
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Create scratchpad directory for Claude
|
||||
run: |
|
||||
# Create a scratchpad directory for Claude to use for temporary files
|
||||
mkdir -p .claude-scratchpad
|
||||
echo "Scratchpad directory created for Claude's temporary verification files"
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare back-end environment
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Generate GitHub App Token for Claude
|
||||
uses: actions/create-github-app-token@v1
|
||||
id: claude-app-token
|
||||
with:
|
||||
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Generate Bug Fix with Claude Code
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
github_token: ${{ steps.claude-app-token.outputs.token }}
|
||||
prompt: ${{ env.BUG_FIX_PROMPT }}
|
||||
claude_args: |
|
||||
--allowedTools "Bash(bun run test-unit*),Bash(bun run lint-eslint-pure),Bash(./bin/mage*),Bash(clojure*),Bash(find*),Bash(ls*),Bash(grep*),Write,Read,Glob,Grep,Edit,MultiEdit"
|
||||
--disallowedTools "Bash(git commit*),Bash(git push*),Bash(gh*)"
|
||||
|
||||
- name: Clean up scratchpad directory
|
||||
run: |
|
||||
# Remove the scratchpad directory so its contents don't get committed
|
||||
rm -rf .claude-scratchpad/
|
||||
# Remove output file generated by Claude Code action
|
||||
rm -f output.txt
|
||||
echo "Scratchpad directory and action output files cleaned up"
|
||||
|
||||
- name: Check for changes and analysis
|
||||
id: check_changes
|
||||
run: |
|
||||
# Now check for actual code changes (excluding analysis file)
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
echo "Changes detected:"
|
||||
git status --short
|
||||
else
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
echo "No changes made by Claude."
|
||||
fi
|
||||
|
||||
- name: Get issue team labels and prepare reviewers
|
||||
if: steps.check_changes.outputs.has_changes == 'true'
|
||||
id: prepare_reviewers
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
ISSUE_DATA: ${{ steps.get_issue.outputs.issue_data }}
|
||||
with:
|
||||
script: |
|
||||
const issueData = JSON.parse(process.env.ISSUE_DATA);
|
||||
const issueLabels = issueData.labels.map(label => label.name);
|
||||
const teamLabels = issueLabels.filter(label => label.startsWith('.Team/'));
|
||||
const issueCreator = issueData.user.login;
|
||||
core.setOutput('team_labels', teamLabels.join(','));
|
||||
core.setOutput('reviewers', issueCreator);
|
||||
|
||||
- name: Create Pull Request
|
||||
id: create_pr
|
||||
if: steps.check_changes.outputs.has_changes == 'true'
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ env.BRANCH_NAME }}
|
||||
base: master
|
||||
commit-message: |
|
||||
Fix: ${{ env.ISSUE_TITLE }}
|
||||
|
||||
Fixes #${{ env.ISSUE_NUMBER }}
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
committer: "Claude Code[bot] <noreply@anthropic.com>"
|
||||
author: "Claude Code[bot] <noreply@anthropic.com>"
|
||||
title: "Fix: ${{ env.ISSUE_TITLE }}"
|
||||
body: |
|
||||
This PR automatically fixes the bug reported in #${{ env.ISSUE_NUMBER }}.
|
||||
|
||||
${{ steps.find_linear.outputs.linear_issue && format('Closes {0}', steps.find_linear.outputs.linear_issue) || '' }}
|
||||
|
||||
## Bug Report
|
||||
**Original Issue:** #${{ env.ISSUE_NUMBER }}
|
||||
**Reporter:** @${{ env.ISSUE_AUTHOR }}
|
||||
**Issue Title:** ${{ env.ISSUE_TITLE }}
|
||||
|
||||
## Automated Fix
|
||||
🤖 This fix was generated automatically by Claude Code based on the issue description.
|
||||
|
||||
**Please review carefully:**
|
||||
- [ ] The fix addresses the reported issue
|
||||
- [ ] Tests pass and code follows conventions
|
||||
- [ ] No unrelated changes were introduced
|
||||
- [ ] The solution is appropriate and maintainable
|
||||
|
||||
## Next Steps
|
||||
- Review the changes and test thoroughly
|
||||
- If the fix looks good, approve and merge
|
||||
- If issues are found, either fix them or close this PR
|
||||
|
||||
---
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
labels: |
|
||||
${{ steps.prepare_reviewers.outputs.team_labels }}
|
||||
automated-fix
|
||||
ci skip
|
||||
reviewers: ${{ steps.prepare_reviewers.outputs.reviewers }}
|
||||
assignees: ${{ github.actor }}
|
||||
|
||||
- name: Log existing branch update
|
||||
if: steps.check_changes.outputs.has_changes == 'true' && env.BRANCH_EXISTS == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
console.log('🔄 Updated existing branch: ${{ env.BRANCH_NAME }}');
|
||||
console.log('The changes have been pushed to the existing PR for this issue.');
|
||||
|
||||
- name: Log analysis for no changes
|
||||
if: steps.check_changes.outputs.has_changes == 'false'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
console.log('🤖 No code changes were made by Claude.');
|
||||
3
.github/workflows/containerize-uberjar.yml
vendored
@@ -76,7 +76,7 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit || github.event.pull_request.head.sha || github.sha }}
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@0.29.0
|
||||
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1
|
||||
env:
|
||||
TRIVY_OFFLINE_SCAN: true
|
||||
repo: ${{ fromJson(needs.get-container-info.outputs[matrix.edition]).repo }}
|
||||
@@ -85,7 +85,6 @@ jobs:
|
||||
image-ref: docker.io/${{ env.repo }}:${{ env.tag }}
|
||||
format: sarif
|
||||
output: trivy-results.sarif
|
||||
version: "v0.57.1"
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
|
||||
@@ -154,7 +154,6 @@ jobs:
|
||||
services:
|
||||
mariadb:
|
||||
image: metabase/mariadb:10.6 # https://github.com/metabase/metabase-mariadb-docker-image
|
||||
image: ${{ matrix.version.image }}
|
||||
ports:
|
||||
- "3306:3306"
|
||||
env:
|
||||
|
||||
119
.github/workflows/cross-version.yml
vendored
@@ -61,13 +61,38 @@ jobs:
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Run cross-version test
|
||||
env:
|
||||
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
|
||||
RESULT_FILE: ${{ runner.temp }}/cross-version-result-${{ matrix.source }}-${{ matrix.target }}.json
|
||||
run: |
|
||||
cd cross-version
|
||||
./test.sh --source "${{ matrix.source }}" --target "${{ matrix.target }}"
|
||||
|
||||
- name: Upload failure result
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: failure-result-${{ matrix.source }}-${{ matrix.target }}
|
||||
path: ${{ runner.temp }}/cross-version-result-${{ matrix.source }}-${{ matrix.target }}.json
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-${{ matrix.source }}-${{ matrix.target }}
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Show logs on failure
|
||||
if: failure()
|
||||
run: |
|
||||
@@ -75,6 +100,84 @@ jobs:
|
||||
docker compose logs metabase || true
|
||||
docker compose logs postgres || true
|
||||
|
||||
# Notify Slack on nightly failures
|
||||
notify-on-failure:
|
||||
if: always() && github.event_name == 'schedule' && needs.test-matrix.result == 'failure'
|
||||
needs: [test-matrix]
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
release
|
||||
.github
|
||||
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Download failure results
|
||||
uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
pattern: failure-result-*
|
||||
path: failure-results
|
||||
merge-multiple: true
|
||||
|
||||
- name: Notify Slack
|
||||
continue-on-error: true
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { buildFailureResults, sendCrossVersionSlackNotification } = require('${{ github.workspace }}/release/dist/index.cjs');
|
||||
|
||||
// Get all failed jobs from the GitHub API
|
||||
const { owner, repo } = context.repo;
|
||||
const jobInfo = await github.rest.actions.listJobsForWorkflowRun({
|
||||
owner,
|
||||
repo,
|
||||
run_id: context.runId,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const failedJobs = jobInfo.data.jobs
|
||||
.filter(job => job.status === 'completed' && job.conclusion === 'failure' && job.name.startsWith('test-matrix'))
|
||||
.map(job => ({ name: job.name, url: job.html_url }));
|
||||
|
||||
if (failedJobs.length === 0) {
|
||||
console.log('No failed test-matrix jobs found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Load artifact data for enrichment (phase)
|
||||
const resultsDir = 'failure-results';
|
||||
const artifactData = new Map();
|
||||
if (fs.existsSync(resultsDir)) {
|
||||
for (const file of fs.readdirSync(resultsDir)) {
|
||||
if (!file.endsWith('.json')) continue;
|
||||
try {
|
||||
const result = JSON.parse(fs.readFileSync(path.join(resultsDir, file), 'utf8'));
|
||||
artifactData.set(`${result.source}-${result.target}`, result);
|
||||
} catch (e) {
|
||||
console.log(`Skipping malformed result: ${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const failures = buildFailureResults(failedJobs, artifactData);
|
||||
const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${context.runId}`;
|
||||
const slackBotToken = process.env.SLACK_BOT_TOKEN;
|
||||
if (!slackBotToken) {
|
||||
console.log('SLACK_BOT_TOKEN is not set; skipping Slack notification.');
|
||||
return;
|
||||
}
|
||||
await sendCrossVersionSlackNotification(failures, runUrl, slackBotToken);
|
||||
console.log('Slack notification sent');
|
||||
|
||||
# Manual test for workflow_dispatch
|
||||
test-manual:
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
@@ -87,6 +190,12 @@ jobs:
|
||||
- name: Prepare release scripts
|
||||
uses: ./.github/actions/prepare-release-scripts
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Prepare Cypress environment
|
||||
uses: ./.github/actions/prepare-cypress
|
||||
|
||||
- name: Run cross-version test
|
||||
env:
|
||||
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
|
||||
@@ -94,6 +203,16 @@ jobs:
|
||||
cd cross-version
|
||||
./test.sh --source "${{ inputs.source_version }}" --target "${{ inputs.target_version }}"
|
||||
|
||||
- name: Upload Cypress Artifacts upon failure
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-recording-${{ inputs.source_version }}-${{ inputs.target_version }}
|
||||
path: |
|
||||
./cypress
|
||||
./logs/test.log
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Show logs on failure
|
||||
if: failure()
|
||||
run: |
|
||||
|
||||
2
.github/workflows/docs-master-generate.yml
vendored
@@ -2,7 +2,7 @@ name: Auto-update documentation (master)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 1 * * 1-5" # Every Monday through Friday at 1 AM UTC
|
||||
- cron: "0 21 * * 1-5" # Every Monday through Friday at 9 PM UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
2
.github/workflows/docs-release-generate.yml
vendored
@@ -2,7 +2,7 @@ name: Auto-update documentation (release)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 2 * * 1-5" # Every Monday through Friday at 2 AM UTC
|
||||
- cron: "0 22 * * 1-5" # Every Monday through Friday at 10 PM UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_branch:
|
||||
|
||||
@@ -31,6 +31,11 @@ on:
|
||||
type: boolean
|
||||
required: false
|
||||
default: true
|
||||
fail_fast:
|
||||
description: "Stop running remaining tests after the first failure"
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
determine-test-type:
|
||||
@@ -67,6 +72,7 @@ jobs:
|
||||
echo '- `grep`: "${{ inputs.grep }}"' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `qa_db`: "${{ inputs.qa_db }}"' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `enable_network_throttling`: "${{ inputs.enable_network_throttling }}"' >> $GITHUB_STEP_SUMMARY
|
||||
echo '- `fail_fast`: "${{ inputs.fail_fast }}"' >> $GITHUB_STEP_SUMMARY
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo 'triggered by: @${{ github.event.sender.login }}' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -144,6 +150,7 @@ jobs:
|
||||
env:
|
||||
GREP: ${{ github.event.inputs.grep }}
|
||||
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
|
||||
FAIL_FAST: ${{ inputs.fail_fast }}
|
||||
run: |
|
||||
node e2e/runner/run_cypress_ci.js ${{ needs.determine-test-type.outputs.type }} \
|
||||
--spec '${{ github.event.inputs.spec }}' \
|
||||
|
||||
20
.github/workflows/loki-stress-test-flake-fix.yml
vendored
@@ -88,22 +88,30 @@ jobs:
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Generate workflow summary
|
||||
env:
|
||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||
STORY_FILES: ${{ needs.detect-changed-stories.outputs.story_files }}
|
||||
BURN_IN: ${{ inputs.burn_in || 50 }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_ACTOR: ${{ github.event.pull_request.user.login }}
|
||||
ACTOR: ${{ github.actor }}
|
||||
run: |
|
||||
echo '## Loki Stress Test' >> $GITHUB_STEP_SUMMARY
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '**Branch:** `${{ github.head_ref || github.ref_name }}`' >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Branch:** \`${BRANCH_NAME}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '**Story files:**' >> $GITHUB_STEP_SUMMARY
|
||||
echo '${{ needs.detect-changed-stories.outputs.story_files }}' | tr ',' '\n' | while read -r file; do
|
||||
echo "${STORY_FILES}" | tr ',' '\n' | while read -r file; do
|
||||
echo "- \`$file\`" >> $GITHUB_STEP_SUMMARY
|
||||
done
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
echo '**Burn-in:** ${{ inputs.burn_in || 50 }} runs' >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Burn-in:** ${BURN_IN} runs" >> $GITHUB_STEP_SUMMARY
|
||||
echo '' >> $GITHUB_STEP_SUMMARY
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
echo 'Triggered by PR #${{ github.event.pull_request.number }} from @${{ github.event.pull_request.user.login }}' >> $GITHUB_STEP_SUMMARY
|
||||
if [[ "${EVENT_NAME}" == "pull_request" ]]; then
|
||||
echo "Triggered by PR #${PR_NUMBER} from @${PR_ACTOR}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo 'Triggered manually by @${{ github.actor }}' >> $GITHUB_STEP_SUMMARY
|
||||
echo "Triggered manually by @${ACTOR}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
stress-test-loki:
|
||||
|
||||
@@ -20,6 +20,7 @@ jobs:
|
||||
NAMED_MODULES=$(node --input-type=module -e "
|
||||
import { elements } from './frontend/lint/module-boundaries.mjs';
|
||||
const named = elements.filter(e => !['shared/other', 'app/misc', 'other'].includes(e.type));
|
||||
console.log(named.length);
|
||||
")
|
||||
|
||||
NAMED_DIRS=$(node --input-type=module -e "
|
||||
@@ -29,6 +30,7 @@ jobs:
|
||||
const m = el.pattern.match(/^frontend\/src\/metabase\/(\w[\w-]*)\//)
|
||||
if (m) dirs.add(m[1]);
|
||||
}
|
||||
for (const d of dirs) console.log(d);
|
||||
")
|
||||
TOTAL_DIRS=$(ls -d frontend/src/metabase/*/ | xargs -I{} basename {} | wc -l | tr -d ' ')
|
||||
NAMED_COUNT=$(echo "$NAMED_DIRS" | grep -c . || true)
|
||||
|
||||
53
.github/workflows/querying-team-pagerduty.yml
vendored
@@ -13,32 +13,35 @@ jobs:
|
||||
(github.event.label.name == 'Priority:P0' || github.event.label.name == 'Priority:P1' || github.event.label.name == '.Escalation') &&
|
||||
contains(github.event.issue.labels.*.name, '.Team/Querying')
|
||||
steps:
|
||||
- name: Setting title
|
||||
uses: actions/github-script@v7
|
||||
id: vars
|
||||
with:
|
||||
script: |
|
||||
core.setOutput('issue_title', ${{ toJson(github.event.issue.title) }}.replaceAll(/"/g, '\\"'));
|
||||
- name: Trigger PagerDuty Incident
|
||||
env:
|
||||
ROUTING_KEY: ${{ secrets.QUERYING_PAGERDUTY_INTEGRATION_KEY }}
|
||||
ISSUE_TITLE: ${{ github.event.issue.title }}
|
||||
LABEL_NAME: ${{ github.event.label.name }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
ISSUE_URL: ${{ github.event.issue.html_url }}
|
||||
REPO: ${{ github.repository }}
|
||||
ALL_LABELS: ${{ join(github.event.issue.labels.*.name, ', ') }}
|
||||
run: |
|
||||
payload=$(jq -n \
|
||||
--arg rk "$ROUTING_KEY" \
|
||||
--arg dk "github-issue-$ISSUE_NUMBER" \
|
||||
--arg sum "[$LABEL_NAME] $ISSUE_TITLE" \
|
||||
--arg src "GitHub Issues - $REPO" \
|
||||
--arg lbl "$ALL_LABELS" \
|
||||
--arg url "$ISSUE_URL" \
|
||||
'{
|
||||
routing_key: $rk,
|
||||
event_action: "trigger",
|
||||
dedup_key: $dk,
|
||||
payload: {
|
||||
summary: $sum,
|
||||
source: $src,
|
||||
severity: "critical",
|
||||
custom_details: { Labels: $lbl }
|
||||
},
|
||||
links: [{ href: $url, text: "View GitHub Issue" }]
|
||||
}')
|
||||
curl -X POST https://events.pagerduty.com/v2/enqueue \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"routing_key": "${{ secrets.QUERYING_PAGERDUTY_INTEGRATION_KEY }}",
|
||||
"event_action": "trigger",
|
||||
"dedup_key": "github-issue-${{ github.event.issue.number }}",
|
||||
"payload": {
|
||||
"summary": "[${{ github.event.label.name }}] ${{ steps.vars.outputs.issue_title }}",
|
||||
"source": "GitHub Issues - ${{ github.repository }}",
|
||||
"severity": "critical",
|
||||
"custom_details": {
|
||||
"Labels": "${{ join(github.event.issue.labels.*.name, ', ') }}"
|
||||
}
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "${{ github.event.issue.html_url }}",
|
||||
"text": "View GitHub Issue"
|
||||
}
|
||||
]
|
||||
}'
|
||||
-d "$payload"
|
||||
|
||||
68
.github/workflows/release-custom-viz.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: Release Custom Viz Package
|
||||
run-name: Release @metabase/custom-viz v${{ inputs.version }} from ${{ inputs.branch }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: "Branch to release from"
|
||||
type: string
|
||||
required: true
|
||||
default: "custom-viz"
|
||||
version:
|
||||
description: "Version to publish (e.g. 0.1.0)"
|
||||
type: string
|
||||
required: true
|
||||
npm_tag:
|
||||
description: "NPM dist-tag"
|
||||
type: choice
|
||||
required: true
|
||||
default: "latest"
|
||||
options:
|
||||
- latest
|
||||
- next
|
||||
- canary
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
defaults:
|
||||
run:
|
||||
working-directory: frontend/src/custom-viz
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
|
||||
- name: Prepare front-end environment
|
||||
uses: ./.github/actions/prepare-frontend
|
||||
|
||||
- name: Setup npm registry auth
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Set package version
|
||||
run: npm version ${{ inputs.version }} --no-git-tag-version
|
||||
|
||||
- name: Build package
|
||||
run: bun run build
|
||||
|
||||
- name: Publish to NPM
|
||||
run: npm publish --tag ${{ inputs.npm_tag }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_RELEASE_TOKEN }}
|
||||
|
||||
- name: Generate workflow summary
|
||||
run: |
|
||||
tee -a $GITHUB_STEP_SUMMARY << EOF
|
||||
## Published @metabase/custom-viz
|
||||
|
||||
- **Version**: ${{ inputs.version }}
|
||||
- **Tag**: ${{ inputs.npm_tag }}
|
||||
- **Branch**: ${{ inputs.branch }}
|
||||
- **Triggered by**: @${{ github.event.sender.login }}
|
||||
EOF
|
||||
6
.github/workflows/release-patch.yml
vendored
@@ -13,13 +13,13 @@ on:
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
# schedule:
|
||||
# - cron: "0 21 * * 1-5" # every weekday at 4 pm EST/5 pm EDT
|
||||
schedule:
|
||||
- cron: "0 21 * * 1-5" # every weekday at 4 pm EST/5 pm EDT
|
||||
|
||||
jobs:
|
||||
auto-patch-trigger:
|
||||
# don't run this workflow on a cron for forks
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
|
||||
if: ${{ github.event_name != 'schedule' || (github.repository == 'metabase/metabase' && vars.ENABLE_AUTO_PATCH == 'true') }}
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
|
||||
2
.github/workflows/run-tests.yml
vendored
@@ -110,7 +110,7 @@ jobs:
|
||||
|
||||
sdk-tests:
|
||||
needs: [uberjar, embedding-sdk-package, files-changed]
|
||||
if: always()
|
||||
if: always() && !cancelled()
|
||||
uses: ./.github/workflows/embedding-sdk.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
|
||||
36
.github/workflows/tag-for-release.yml
vendored
@@ -192,6 +192,42 @@ jobs:
|
||||
./COMMIT-ID
|
||||
./SHA256.sum
|
||||
|
||||
upload-to-cloud:
|
||||
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
||||
needs: tag-uberjar-for-release
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
|
||||
aws-region: ${{ vars.AWS_REGION }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .github
|
||||
- name: Retrieve release ee uberjar artifact
|
||||
uses: ./.github/actions/fetch-artifact
|
||||
with:
|
||||
commit: ${{ inputs.commit }}
|
||||
edition: ee
|
||||
release: "true"
|
||||
- name: Determine the upload path
|
||||
uses: actions/github-script@v7
|
||||
id: version_path
|
||||
with:
|
||||
result-encoding: string
|
||||
script: | # js
|
||||
const version = '${{ inputs.version }}';
|
||||
// only EE version
|
||||
const version_path = 'release/' + version.replace(/^v0\./, "v1.");
|
||||
|
||||
console.log("sending to", version_path);
|
||||
return version_path;
|
||||
- name: Upload to S3
|
||||
run: |
|
||||
aws s3 cp ./metabase.jar s3://${{ secrets.AWS_S3_CLOUD_DEPLOY_BUCKET }}/${{ steps.version_path.outputs.result }}/metabase.jar
|
||||
|
||||
run-pre-release-tests:
|
||||
name: Run pre-release tests for ${{ inputs.version }}
|
||||
needs: tag-uberjar-for-release
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
id: vars
|
||||
with:
|
||||
script: |
|
||||
core.setOutput('issue_title', ${{ toJson(github.event.issue.title) }}.replaceAll(/"/g, '\\"'));
|
||||
core.setOutput('issue_title', JSON.stringify(${{ toJson(github.event.issue.title) }} + '\n' + ${{ toJson(github.event.issue.html_url) }}));
|
||||
- name: Extracting team name
|
||||
id: team_name
|
||||
run: |
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "${{ steps.vars.outputs.issue_title }}\n${{ github.event.issue.html_url }}"
|
||||
"text": ${{ steps.vars.outputs.issue_title }}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
5
.gitignore
vendored
@@ -13,8 +13,6 @@
|
||||
.\#*
|
||||
.cpcache/
|
||||
.eastwood
|
||||
/.beads/
|
||||
/.fixbot/
|
||||
.idea/
|
||||
.nrepl-port
|
||||
.portal
|
||||
@@ -98,7 +96,7 @@ coverage-summary.json
|
||||
junit.xml
|
||||
pom.xml
|
||||
pom.xml.asc
|
||||
profiles.clj
|
||||
/profiles.clj
|
||||
target/checksum.txt
|
||||
xcshareddata
|
||||
xcuserdata
|
||||
@@ -178,3 +176,4 @@ CLAUDE.local.md
|
||||
/resources/python-sources/sqlglot-*.dist-info/
|
||||
/resources/python-sources/.lock
|
||||
__pycache__/
|
||||
.mcp.json
|
||||
|
||||
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 167 KiB |
@@ -6,7 +6,7 @@ For detailed guidance on writing and reviewing code and documentation, see the s
|
||||
|
||||
## Clojure
|
||||
|
||||
- **[clojure-eval](.claude/skills/clojure-eval/SKILL.md)** - Always use this to evaluate Clojure code, **run tests**, and verify edits/compile. Prefer this over `clojure -X:...` shell commands.
|
||||
- **[clojure-eval](.claude/skills/clojure-eval/SKILL.md)** - Always use this to evaluate Clojure code, **run tests**, and verify edits/compile. Prefer this over shell commands.
|
||||
- **[clojure-write](.claude/skills/clojure-write/SKILL.md)** - Clojure/ClojureScript development with REPL-driven workflow and coding conventions
|
||||
- **[clojure-review](.claude/skills/clojure-review/SKILL.md)** - Clojure/ClojureScript code review guidelines and style enforcement
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ FROM eclipse-temurin:21-jre-alpine AS runner
|
||||
|
||||
ENV FC_LANG=en-US LC_CTYPE=en_US.UTF-8
|
||||
|
||||
# copy certs before the RUN so keytool can import them
|
||||
COPY bin/docker/DigiCertGlobalRootG2.crt.pem /app/certs/DigiCertGlobalRootG2.crt.pem
|
||||
|
||||
# dependencies
|
||||
RUN apk add -U bash fontconfig curl font-noto font-noto-arabic font-noto-hebrew font-noto-cjk java-cacerts && \
|
||||
apk upgrade && \
|
||||
@@ -49,7 +52,6 @@ RUN apk add -U bash fontconfig curl font-noto font-noto-arabic font-noto-hebrew
|
||||
mkdir -p /app/certs && \
|
||||
curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o /app/certs/rds-combined-ca-bundle.pem && \
|
||||
/opt/java/openjdk/bin/keytool -noprompt -import -trustcacerts -alias aws-rds -file /app/certs/rds-combined-ca-bundle.pem -keystore /etc/ssl/certs/java/cacerts -keypass changeit -storepass changeit && \
|
||||
curl https://cacerts.digicert.com/DigiCertGlobalRootG2.crt.pem -o /app/certs/DigiCertGlobalRootG2.crt.pem && \
|
||||
/opt/java/openjdk/bin/keytool -noprompt -import -trustcacerts -alias azure-cert -file /app/certs/DigiCertGlobalRootG2.crt.pem -keystore /etc/ssl/certs/java/cacerts -keypass changeit -storepass changeit && \
|
||||
mkdir -p /plugins && chmod a+rwx /plugins
|
||||
|
||||
|
||||
101
bb.edn
@@ -260,7 +260,7 @@
|
||||
{:doc "Generate XML configuration for Cursive's symbol resolution based on the current clj-kondo lint-as configuration."
|
||||
:examples [["./bin/mage generate-cursive-resolve-as" "Generate resolve-as XML configuration"]
|
||||
["./bin/mage generate-cursive-resolve-as > /Users/bgrabow/Library/Application\\ Support/JetBrains/IntelliJIdea2025.3/options/ClojureResolveSettings.xml"
|
||||
"Generate resolve-as XML configuration and install it in your global editor configuration"]]
|
||||
"Generate resolve-as XML configuration and install it in your global editor configuration"]]
|
||||
:requires [[mage.cursive-resolve-as :as cursive-resolve-as]]
|
||||
:task (task! (cursive-resolve-as/do-it))}
|
||||
|
||||
@@ -303,17 +303,21 @@
|
||||
:task (task! (doctor/doctor!))}
|
||||
|
||||
ci-report
|
||||
{:doc "Generate a CI failure report for a PR"
|
||||
:examples [["./bin/mage ci-report" "Check current branch, find its PR, and generate report"]
|
||||
{:doc "Generate a CI failure report for a PR, branch, or commit"
|
||||
:examples [["./bin/mage ci-report" "Check current branch (PR or branch CI)"]
|
||||
["./bin/mage ci-report 68292" "Generate report for PR #68292"]
|
||||
["./bin/mage ci-report --detailed 68292" "Generate detailed report with full FAIL/ERROR blocks"]
|
||||
["./bin/mage ci-report source-replacement" "Report for a branch by name"]
|
||||
["./bin/mage ci-report abc123def" "Report for a specific commit SHA"]
|
||||
["./bin/mage ci-report --detailed 68292" "Detailed report with full FAIL/ERROR blocks"]
|
||||
["./bin/mage ci-report --detailed > ci_report.md"
|
||||
"Save detailed report to markdown file and print progress."]]
|
||||
:options [["-d" "--detailed" "Include full FAIL/ERROR blocks with expected/actual diffs"]]
|
||||
:usage-fn (fn [_]
|
||||
(str "Fetches CI check status and failed job logs from GitHub Actions.\n"
|
||||
"Parses Trunk test reports to extract test failure details.\n\n"
|
||||
"With no arguments, looks up the PR for your current git branch.\n\n"
|
||||
"Accepts a PR number/URL, branch name, or commit SHA.\n"
|
||||
"With no arguments, uses the current branch's PR if one exists,\n"
|
||||
"otherwise falls back to the current branch's latest CI run.\n\n"
|
||||
"Use --detailed to get full test failure output with expected/actual diffs.\n\n"
|
||||
"Requires GitHub CLI (gh) to be installed and authenticated.\n"
|
||||
"If you see 'gh: command not found' or auth errors, run: gh auth login"))
|
||||
@@ -602,87 +606,6 @@
|
||||
:requires [[mage.bgrabow.ci-test-repl :as ci-test-repl]]
|
||||
:options [["-n" "--dry-run" "Print docker, env, and clojure commands without executing"]]
|
||||
:task (task!
|
||||
(let [wj (first (:arguments parsed))
|
||||
[workflow job] (when wj (clojure.string/split wj (re-pattern "/") 2))]
|
||||
(ci-test-repl/ci-test-repl {:workflow workflow :job job :dry-run? (:dry-run (:options parsed))})))}
|
||||
|
||||
-fixbot-preflight
|
||||
{:doc "Check prerequisites for the fixbot workflow"
|
||||
:examples [["./bin/mage -fixbot-preflight" "Run all preflight checks"]]
|
||||
:requires [[mage.fixbot.preflight :as preflight]]
|
||||
:task (task! (preflight/preflight!))}
|
||||
|
||||
-fixbot-status
|
||||
{:doc "Watch and display fixbot status"
|
||||
:requires [[mage.fixbot.status :as status]]
|
||||
:arg-schema [:? [:string {:name "file-path"}]]
|
||||
:task (task! (status/run! parsed))}
|
||||
|
||||
fixbot-go
|
||||
{:doc "Launch auto-fix workflow for a Linear issue"
|
||||
:examples [["./bin/mage fixbot-go MB-12345 --app-db postgres --prompt-file /tmp/prompt.md --branch 'username/mb-12345-fix-thing'"
|
||||
"Launch workmux session"]]
|
||||
:requires [[mage.fixbot.go :as fixbot-go]]
|
||||
:arg-schema [:tuple [:string {:name "issue-id"}]]
|
||||
:options [[nil "--app-db DB" "App database" :default "postgres"]
|
||||
[nil "--prompt-file FILE" "Path to agent prompt file"]
|
||||
[nil "--branch BRANCH" "Branch name from Linear"]
|
||||
[nil "--base BASE" "Base branch/commit to branch from"]]
|
||||
:task (task! (fixbot-go/run! parsed))}
|
||||
|
||||
-fixbot-dev-env
|
||||
{:doc "Stand up dev environment for auto-fix workflow"
|
||||
:examples [["./bin/mage -fixbot-dev-env --app-db postgres" "Postgres app-db"]
|
||||
["./bin/mage -fixbot-dev-env --down" "Tear down"]]
|
||||
:requires [[mage.fixbot.dev-env :as fixbot-dev-env]]
|
||||
:options [[nil "--app-db DB" "App database: postgres (default), mysql, h2" :default "postgres"]
|
||||
[nil "--down" "Tear down containers"]
|
||||
[nil "--status" "Show status"]
|
||||
[nil "--slot SLOT" "Override port slot" :parse-fn Integer/parseInt]]
|
||||
:task (task! (fixbot-dev-env/dev-env! parsed))}
|
||||
|
||||
-fixbot-fetch-issue
|
||||
{:doc "Fetch a Linear issue by identifier"
|
||||
:examples [["./bin/mage -fixbot-fetch-issue MB-12345" "Fetch and display issue"]]
|
||||
:requires [[mage.fixbot.linear :as linear]]
|
||||
:arg-schema [:tuple [:string {:name "issue-id"}]]
|
||||
:task (task! (linear/print-issue! parsed))}
|
||||
|
||||
-fixbot-pause
|
||||
{:doc "Pause a fixbot session (stop containers, close tmux, keep worktree)"
|
||||
:examples [["./bin/mage -fixbot-pause mb-12345" "Pause session"]]
|
||||
:requires [[mage.fixbot.session :as session]]
|
||||
:arg-schema [:tuple [:string {:name "name-or-id"}]]
|
||||
:task (task! (session/pause! parsed))}
|
||||
|
||||
-fixbot-resume
|
||||
{:doc "Resume a paused fixbot session"
|
||||
:examples [["./bin/mage -fixbot-resume mb-12345" "Resume session"]]
|
||||
:requires [[mage.fixbot.session :as session]]
|
||||
:arg-schema [:tuple [:string {:name "name-or-id"}]]
|
||||
:task (task! (session/resume! parsed))}
|
||||
|
||||
-fixbot-list
|
||||
{:doc "List fixbot sessions with status"
|
||||
:examples [["./bin/mage -fixbot-list" "List all sessions"]]
|
||||
:requires [[mage.fixbot.session :as session]]
|
||||
:task (task! (session/list-sessions! parsed))}
|
||||
|
||||
-fixbot-quit
|
||||
{:doc "Tear down and remove a fixbot worktree session"
|
||||
:examples [["./bin/mage -fixbot-quit mb-12345" "Remove session and worktree"]]
|
||||
:requires [[mage.fixbot.session :as session]]
|
||||
:arg-schema [:tuple [:string {:name "name-or-id"}]]
|
||||
:task (task! (session/quit! parsed))}
|
||||
|
||||
-fixbot-write-sandbox-settings
|
||||
{:doc "Write container-compatible settings.local.json for sandbox"
|
||||
:examples [["./bin/mage -fixbot-write-sandbox-settings" "Write sandbox settings"]]
|
||||
:requires [[mage.fixbot.session :as session]]
|
||||
:task (task! (session/write-sandbox-settings! parsed))}
|
||||
|
||||
-fixbot-dashboard
|
||||
{:doc "Open fixbot TUI dashboard"
|
||||
:examples [["./bin/mage -fixbot-dashboard" "Open dashboard"]]
|
||||
:requires [[mage.fixbot.session :as session]]
|
||||
:task (task! (session/dashboard!))}}}
|
||||
(let [wj (first (:arguments parsed))
|
||||
[workflow job] (when wj (clojure.string/split wj (re-pattern "/") 2))]
|
||||
(ci-test-repl/ci-test-repl {:workflow workflow :job job :dry-run? (:dry-run (:options parsed))})))}}}
|
||||
|
||||
@@ -93,4 +93,4 @@ exec docker run -it --rm \
|
||||
"${ENV_ARGS[@]}" \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
"$IMAGE_NAME" \
|
||||
--dangerously-skip-permissions "$@"
|
||||
--dangerously-skip-permissions
|
||||
|
||||
22
bin/docker/DigiCertGlobalRootG2.crt.pem
Normal file
@@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
|
||||
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
|
||||
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
|
||||
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
|
||||
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
|
||||
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
|
||||
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
|
||||
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
|
||||
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
|
||||
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
|
||||
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
|
||||
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
|
||||
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
|
||||
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
|
||||
MrY=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -5,6 +5,9 @@ ENV FC_LANG=en-US LC_CTYPE=en_US.UTF-8
|
||||
ARG GIT_COMMIT_SHA
|
||||
ENV GIT_COMMIT_SHA=${GIT_COMMIT_SHA}
|
||||
|
||||
# copy cert before RUN so keytool can import it
|
||||
COPY DigiCertGlobalRootG2.crt.pem /app/certs/DigiCertGlobalRootG2.crt.pem
|
||||
|
||||
# dependencies
|
||||
RUN apk add -U bash fontconfig curl font-noto font-noto-arabic font-noto-hebrew font-noto-cjk font-noto-thai java-cacerts && \
|
||||
apk upgrade && \
|
||||
@@ -12,7 +15,6 @@ RUN apk add -U bash fontconfig curl font-noto font-noto-arabic font-noto-hebrew
|
||||
mkdir -p /app/certs && \
|
||||
curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o /app/certs/rds-combined-ca-bundle.pem && \
|
||||
/opt/java/openjdk/bin/keytool -noprompt -import -trustcacerts -alias aws-rds -file /app/certs/rds-combined-ca-bundle.pem -keystore /etc/ssl/certs/java/cacerts -keypass changeit -storepass changeit && \
|
||||
curl https://cacerts.digicert.com/DigiCertGlobalRootG2.crt.pem -o /app/certs/DigiCertGlobalRootG2.crt.pem && \
|
||||
/opt/java/openjdk/bin/keytool -noprompt -import -trustcacerts -alias azure-cert -file /app/certs/DigiCertGlobalRootG2.crt.pem -keystore /etc/ssl/certs/java/cacerts -keypass changeit -storepass changeit && \
|
||||
mkdir -p /plugins && chmod a+rwx /plugins
|
||||
|
||||
|
||||
@@ -5,13 +5,15 @@ ENV FC_LANG=en-US LC_CTYPE=en_US.UTF-8
|
||||
ARG GIT_COMMIT_SHA
|
||||
ENV GIT_COMMIT_SHA=${GIT_COMMIT_SHA}
|
||||
|
||||
# copy cert before RUN so it's available without a network fetch
|
||||
COPY DigiCertGlobalRootG2.crt.pem /usr/local/share/ca-certificates/DigiCertGlobalRootG2.crt.pem
|
||||
|
||||
# Dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get upgrade -y && \
|
||||
apt-get install -y ca-certificates ca-certificates-java fonts-noto && \
|
||||
apt-get clean && \
|
||||
curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o /usr/local/share/ca-certificates/rds-combined-ca-bundle.pem && \
|
||||
curl https://cacerts.digicert.com/DigiCertGlobalRootG2.crt.pem -o /usr/local/share/ca-certificates/DigiCertGlobalRootG2.crt.pem && \
|
||||
update-ca-certificates && \
|
||||
mkdir -p /plugins && chmod a+rwx /plugins && \
|
||||
keytool -list -cacerts
|
||||
|
||||