mirror of
https://github.com/penpot/penpot.git
synced 2026-08-06 12:58:55 +00:00
♻️ Consolidate dev tooling into scripts/ and reorganize docs
Move all development tools from tools/ to scripts/ for consistency. Rename lint/fmt/check-fmt to lint-clj/fmt-clj/check-fmt-clj to clarify they target Clojure specifically. Remove unused scripts (attach-opencode, start-opencode, start-opencode-server) and the backport-commit skill. Update all internal references across .serena/, AGENTS.md, and CONTRIBUTING.md to point to the new script locations. Simplify CONTRIBUTING.md by delegating module-specific fmt/lint instructions to the respective serena memories. AI-assisted-by: deepseek-v4-flash
This commit is contained in:
parent
73bfc0dc15
commit
f3bf24b4f6
13
.github/workflows/tests-backend.yml
vendored
13
.github/workflows/tests-backend.yml
vendored
@ -62,14 +62,15 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Fmt
|
||||||
|
working-directory: ./backend
|
||||||
|
run: |
|
||||||
|
cljfmt check --parallel=true src/ test/
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
working-directory: ./backend
|
working-directory: ./backend
|
||||||
run: |
|
run: |
|
||||||
corepack enable;
|
clj-kondo --parallel --lint ../common/src/ src/
|
||||||
corepack install;
|
|
||||||
pnpm install;
|
|
||||||
pnpm run check-fmt
|
|
||||||
pnpm run lint
|
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
working-directory: ./backend
|
working-directory: ./backend
|
||||||
@ -81,4 +82,4 @@ jobs:
|
|||||||
|
|
||||||
run: |
|
run: |
|
||||||
mkdir -p /tmp/penpot;
|
mkdir -p /tmp/penpot;
|
||||||
clojure -M:dev:test --reporter kaocha.report/documentation
|
clojure -M:dev:test
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -9,6 +9,7 @@
|
|||||||
.clj-kondo
|
.clj-kondo
|
||||||
.cpcache
|
.cpcache
|
||||||
.lsp
|
.lsp
|
||||||
|
.env
|
||||||
.nrepl-port
|
.nrepl-port
|
||||||
.nyc_output
|
.nyc_output
|
||||||
.rebel_readline_history
|
.rebel_readline_history
|
||||||
@ -73,8 +74,6 @@ opencode.json
|
|||||||
/frontend/target/
|
/frontend/target/
|
||||||
/frontend/test-results/
|
/frontend/test-results/
|
||||||
/frontend/.shadow-cljs
|
/frontend/.shadow-cljs
|
||||||
/other/
|
|
||||||
/scripts/
|
|
||||||
/nexus/
|
/nexus/
|
||||||
/tmp/
|
/tmp/
|
||||||
/vendor/**/target
|
/vendor/**/target
|
||||||
|
|||||||
299
.opencode/plugins/penpot.js
Normal file
299
.opencode/plugins/penpot.js
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
import path from "path"
|
||||||
|
import { spawn } from "child_process"
|
||||||
|
|
||||||
|
const penpotPsqlTool = tool({
|
||||||
|
description:
|
||||||
|
"Execute a SQL command against the Penpot database. Uses the defaults from scripts/psql.",
|
||||||
|
|
||||||
|
args: {
|
||||||
|
sql: tool.schema
|
||||||
|
.string()
|
||||||
|
.describe("SQL command to execute"),
|
||||||
|
|
||||||
|
test: tool.schema
|
||||||
|
.boolean()
|
||||||
|
.describe("Use the penpot_test database")
|
||||||
|
.optional(),
|
||||||
|
},
|
||||||
|
|
||||||
|
async execute(args, context) {
|
||||||
|
const host = process.env.PENPOT_DB_HOST || "postgres"
|
||||||
|
const user = process.env.PENPOT_DB_USER || "penpot"
|
||||||
|
const db = args.test
|
||||||
|
? "penpot_test"
|
||||||
|
: process.env.PENPOT_DB_NAME || "penpot"
|
||||||
|
const password = process.env.PENPOT_DB_PASSWORD || "penpot"
|
||||||
|
|
||||||
|
const psqlArgs = ["-h", host, "-U", user, "-d", db, "-c", args.sql]
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let stdout = ""
|
||||||
|
let stderr = ""
|
||||||
|
|
||||||
|
const proc = spawn("psql", psqlArgs, {
|
||||||
|
cwd: context.worktree,
|
||||||
|
env: { ...process.env, PGPASSWORD: password },
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.stdout.on("data", (data) => {
|
||||||
|
stdout += data.toString()
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.stderr.on("data", (data) => {
|
||||||
|
stderr += data.toString()
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.on("error", (error) => {
|
||||||
|
resolve(`Error: ${error.message}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.on("close", (exitCode) => {
|
||||||
|
const output =
|
||||||
|
exitCode === 0
|
||||||
|
? stdout.trim() || "Query executed successfully"
|
||||||
|
: `Error (exit ${exitCode}): ${
|
||||||
|
(stderr || stdout).trim() || "No error output"
|
||||||
|
}`
|
||||||
|
resolve(output)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const parenRepairTool = tool({
|
||||||
|
description:
|
||||||
|
"Fix mismatched parentheses/braces in Clojure files (.clj, .cljs, .cljc) then reformat with cljfmt.",
|
||||||
|
|
||||||
|
args: {
|
||||||
|
// A string is used instead of an array so OpenCode displays it
|
||||||
|
// in the generic tool invocation.
|
||||||
|
files: tool.schema
|
||||||
|
.string()
|
||||||
|
.describe(
|
||||||
|
"Comma-separated file paths to fix, for example: frontend/src/app/config.cljs, backend/src/core.clj",
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
|
||||||
|
code: tool.schema
|
||||||
|
.string()
|
||||||
|
.describe("Code string to fix via stdin")
|
||||||
|
.optional(),
|
||||||
|
},
|
||||||
|
|
||||||
|
async execute(args, context) {
|
||||||
|
const script = path.join(context.worktree, "scripts/paren-repair")
|
||||||
|
|
||||||
|
const files = args.files
|
||||||
|
? args.files
|
||||||
|
.split(",")
|
||||||
|
.map((file) => file.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
: []
|
||||||
|
|
||||||
|
const paramInfo =
|
||||||
|
files.length > 0
|
||||||
|
? `files=[${files.join(", ")}]`
|
||||||
|
: args.code !== undefined
|
||||||
|
? `code=(${args.code.length} chars)`
|
||||||
|
: "none"
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const childArgs =
|
||||||
|
files.length > 0
|
||||||
|
? [script, ...files]
|
||||||
|
: [script]
|
||||||
|
|
||||||
|
const proc = spawn("bb", childArgs, {
|
||||||
|
cwd: context.worktree,
|
||||||
|
})
|
||||||
|
|
||||||
|
let stdout = ""
|
||||||
|
let stderr = ""
|
||||||
|
|
||||||
|
proc.stdout.on("data", (data) => {
|
||||||
|
stdout += data.toString()
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.stderr.on("data", (data) => {
|
||||||
|
stderr += data.toString()
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.on("error", (error) => {
|
||||||
|
resolve(`Error: ${error.message}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
proc.on("close", (exitCode) => {
|
||||||
|
const output =
|
||||||
|
exitCode === 0
|
||||||
|
? stdout.trim() || "No changes needed"
|
||||||
|
: `Error (exit ${exitCode}): ${
|
||||||
|
(stderr || stdout).trim() || "No error output"
|
||||||
|
}`
|
||||||
|
|
||||||
|
resolve(output)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Close stdin in all cases so the process cannot wait indefinitely.
|
||||||
|
if (args.code !== undefined) {
|
||||||
|
proc.stdin.end(args.code)
|
||||||
|
} else {
|
||||||
|
proc.stdin.end()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default async function plugin() {
|
||||||
|
return {
|
||||||
|
tool: {
|
||||||
|
"paren-repair": parenRepairTool,
|
||||||
|
"penpot-psql": penpotPsqlTool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// import { tool } from "@opencode-ai/plugin"
|
||||||
|
// import path from "path"
|
||||||
|
// import { spawn } from "child_process"
|
||||||
|
|
||||||
|
// function formatFiles(files) {
|
||||||
|
// if (files.length === 0) return "stdin"
|
||||||
|
|
||||||
|
// // Keep the visible tool title reasonably short.
|
||||||
|
// if (files.length <= 3) return files.join(", ")
|
||||||
|
|
||||||
|
// return `${files.slice(0, 3).join(", ")} (+${files.length - 3} more)`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const parenRepairTool = tool({
|
||||||
|
// description:
|
||||||
|
// "Fix mismatched parentheses/braces in Clojure files, then reformat with cljfmt.",
|
||||||
|
|
||||||
|
// args: {
|
||||||
|
// files: tool.schema
|
||||||
|
// .array(tool.schema.string())
|
||||||
|
// .describe("Array of file paths to fix")
|
||||||
|
// .optional(),
|
||||||
|
|
||||||
|
// code: tool.schema
|
||||||
|
// .string()
|
||||||
|
// .describe("Code string to fix via stdin")
|
||||||
|
// .optional(),
|
||||||
|
// },
|
||||||
|
|
||||||
|
// async execute(args, context) {
|
||||||
|
// const script = path.join(context.worktree, "scripts/paren-repair")
|
||||||
|
|
||||||
|
// const files = (args.files ?? []).map((file) => {
|
||||||
|
// const absolute = path.isAbsolute(file)
|
||||||
|
// ? file
|
||||||
|
// : path.resolve(context.worktree, file)
|
||||||
|
|
||||||
|
// return path.relative(context.worktree, absolute)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// const targetSummary =
|
||||||
|
// files.length > 0
|
||||||
|
// ? formatFiles(files)
|
||||||
|
// : args.code !== undefined
|
||||||
|
// ? `stdin (${args.code.length} chars)`
|
||||||
|
// : "no input"
|
||||||
|
|
||||||
|
// // This updates the tool-call title immediately, while it is running.
|
||||||
|
// await context.metadata({
|
||||||
|
// title: `Paren repair: ${targetSummary}`,
|
||||||
|
// metadata: {
|
||||||
|
// files,
|
||||||
|
// codeChars: args.code?.length,
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
|
||||||
|
// const childArgs =
|
||||||
|
// args.files && args.files.length > 0
|
||||||
|
// ? [script, ...args.files]
|
||||||
|
// : [script]
|
||||||
|
|
||||||
|
// return new Promise((resolve) => {
|
||||||
|
// const proc = spawn("bb", childArgs, {
|
||||||
|
// cwd: context.worktree,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// let stdout = ""
|
||||||
|
// let stderr = ""
|
||||||
|
|
||||||
|
// if (args.code !== undefined) {
|
||||||
|
// proc.stdin.end(args.code)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// proc.stdout.on("data", (data) => {
|
||||||
|
// stdout += data.toString()
|
||||||
|
// })
|
||||||
|
|
||||||
|
// proc.stderr.on("data", (data) => {
|
||||||
|
// stderr += data.toString()
|
||||||
|
// })
|
||||||
|
|
||||||
|
// proc.on("close", (exitCode) => {
|
||||||
|
// const successful = exitCode === 0
|
||||||
|
|
||||||
|
// const commandOutput = successful
|
||||||
|
// ? stdout.trim() || "No changes needed"
|
||||||
|
// : `Error (exit ${exitCode}): ${(stderr || stdout).trim()}`
|
||||||
|
|
||||||
|
// const parameterOutput =
|
||||||
|
// files.length > 0
|
||||||
|
// ? `Files passed:\n${files.map((file) => `- ${file}`).join("\n")}`
|
||||||
|
// : args.code !== undefined
|
||||||
|
// ? `Input passed through stdin: ${args.code.length} characters`
|
||||||
|
// : "No files or stdin input were passed"
|
||||||
|
|
||||||
|
// resolve({
|
||||||
|
// title: `Paren repair: ${targetSummary}`,
|
||||||
|
// output: `${parameterOutput}\n\n${commandOutput}`,
|
||||||
|
// metadata: {
|
||||||
|
// files,
|
||||||
|
// codeChars: args.code?.length,
|
||||||
|
// exitCode,
|
||||||
|
// successful,
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
|
||||||
|
// proc.on("error", (error) => {
|
||||||
|
// resolve({
|
||||||
|
// title: `Paren repair failed: ${targetSummary}`,
|
||||||
|
// output: [
|
||||||
|
// files.length > 0
|
||||||
|
// ? `Files passed:\n${files.map((file) => `- ${file}`).join("\n")}`
|
||||||
|
// : `Input: ${targetSummary}`,
|
||||||
|
// `Failed to start bb: ${error.message}`,
|
||||||
|
// ].join("\n\n"),
|
||||||
|
// metadata: {
|
||||||
|
// files,
|
||||||
|
// codeChars: args.code?.length,
|
||||||
|
// successful: false,
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
|
||||||
|
// export default async function plugin() {
|
||||||
|
// return {
|
||||||
|
// tool: {
|
||||||
|
// "paren-repair": parenRepairTool,
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@ -1,85 +0,0 @@
|
|||||||
---
|
|
||||||
name: backport-commit
|
|
||||||
description: Port changes from a specific Git commit to the current branch by manually applying the diff, avoiding cherry-pick when it would introduce complex conflicts.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Backport Commit
|
|
||||||
|
|
||||||
Port changes from a specific Git commit to the current branch by manually
|
|
||||||
applying the diff, avoiding `git cherry-pick` when it would introduce
|
|
||||||
complex conflicts.
|
|
||||||
|
|
||||||
## When to Use
|
|
||||||
|
|
||||||
Use this skill whenever the user asks to backport a commit, especially when:
|
|
||||||
|
|
||||||
- The commit touches multiple modules or files with significant divergence
|
|
||||||
- `git cherry-pick` is explicitly ruled out ("do not use cherry-pick")
|
|
||||||
- The target commit is old enough that conflicts are likely
|
|
||||||
- The commit introduces both source changes AND new files (tests, etc.)
|
|
||||||
- You need full control over how each hunk is applied
|
|
||||||
|
|
||||||
## Workflow
|
|
||||||
|
|
||||||
### 1. Identify the target commit
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Verify the commit exists and understand what it does
|
|
||||||
git log --oneline -1 <commit-sha>
|
|
||||||
|
|
||||||
# Get the full diff (including new/deleted files)
|
|
||||||
git show <commit-sha>
|
|
||||||
|
|
||||||
# Capture the original commit message for later reuse
|
|
||||||
git log --format='%B' -1 <commit-sha>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Identify affected modules
|
|
||||||
|
|
||||||
From the file paths in the diff, determine which Penpot modules are affected
|
|
||||||
(frontend, backend, common, render-wasm, etc.) and read their `AGENTS.md`
|
|
||||||
files **before** making any changes. If a module has no `AGENTS.md`, skip
|
|
||||||
that step — verify with `ls <module>/AGENTS.md` first.
|
|
||||||
|
|
||||||
### 3. Read the current state of each affected file
|
|
||||||
|
|
||||||
For every file the diff touches, read the current version on disk to understand
|
|
||||||
context and ensure correct placement before editing.
|
|
||||||
|
|
||||||
### 4. Apply changes manually (the core of this approach)
|
|
||||||
|
|
||||||
Process every hunk in the diff using the appropriate tool:
|
|
||||||
|
|
||||||
| Diff action | Tool to use |
|
|
||||||
|-------------|-------------|
|
|
||||||
| Modify existing file | `edit` — use enough surrounding context in `oldString` to uniquely match the location |
|
|
||||||
| Add new file | `write` — include proper license header and namespace conventions matching project style |
|
|
||||||
| Delete file | `bash rm <path>` |
|
|
||||||
| Rename/move file | `bash mv <old> <new>`, then apply any content changes with `edit` |
|
|
||||||
|
|
||||||
> **Tip:** Group nearby hunks from the same file into a single `edit` call.
|
|
||||||
> Use separate calls when hunks are far apart to keep `oldString` short and
|
|
||||||
> unambiguous.
|
|
||||||
|
|
||||||
Repeat until **all** hunks in the diff are ported.
|
|
||||||
|
|
||||||
### 5. Validate
|
|
||||||
|
|
||||||
Run **lint**, **check-fmt**, and **tests** for every affected module (see each
|
|
||||||
module's `AGENTS.md` for the exact commands). If the formatter auto-fixes
|
|
||||||
indentation, verify the logic is still semantically correct. All checks must
|
|
||||||
pass before moving on.
|
|
||||||
|
|
||||||
### 6. Commit
|
|
||||||
|
|
||||||
Ask the `commiter` sub-agent to create a commit. Stage all relevant files
|
|
||||||
(exclude unrelated untracked files) and provide the original commit message as
|
|
||||||
a reference, adapting it as needed for the target branch context.
|
|
||||||
|
|
||||||
## Key Principles
|
|
||||||
|
|
||||||
- **Context matters** — always read files before editing; never guess
|
|
||||||
indentation or surrounding code
|
|
||||||
- **Lint + format + test** — never skip validation before committing
|
|
||||||
- **Preserve intent** — keep the original commit message meaning; the
|
|
||||||
`commiter` agent handles formatting
|
|
||||||
@ -1,19 +1,19 @@
|
|||||||
---
|
---
|
||||||
name: nrepl-eval
|
name: nrepl-eval
|
||||||
description: Evaluate Clojure code via nREPL using the standalone tools/nrepl-eval.mjs CLI tool.
|
description: Evaluate Clojure code via nREPL using the standalone scripts/nrepl-eval.mjs CLI tool.
|
||||||
---
|
---
|
||||||
|
|
||||||
# nREPL Eval
|
# nREPL Eval
|
||||||
|
|
||||||
Evaluate Clojure (or ClojureScript) code via a running nREPL server using
|
Evaluate Clojure (or ClojureScript) code via a running nREPL server using
|
||||||
`tools/nrepl-eval.mjs`.
|
`scripts/nrepl-eval.mjs`.
|
||||||
|
|
||||||
Full documentation: `mem:tools/nrepl-eval` (file: `.serena/memories/tools/nrepl-eval.md`)
|
Full documentation: `mem:scripts/nrepl-eval` (file: `.serena/memories/scripts/nrepl-eval.md`)
|
||||||
|
|
||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs [options] [<code>]
|
./scripts/nrepl-eval.mjs [options] [<code>]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Flag | Description | Default |
|
| Flag | Description | Default |
|
||||||
@ -30,8 +30,8 @@ Full documentation: `mem:tools/nrepl-eval` (file: `.serena/memories/tools/nrepl-
|
|||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs '(+ 1 2 3)'
|
./scripts/nrepl-eval.mjs '(+ 1 2 3)'
|
||||||
./tools/nrepl-eval.mjs --backend '(+ 1 2 3)'
|
./scripts/nrepl-eval.mjs --backend '(+ 1 2 3)'
|
||||||
./tools/nrepl-eval.mjs --frontend '(js/alert "hi")'
|
./scripts/nrepl-eval.mjs --frontend '(js/alert "hi")'
|
||||||
./tools/nrepl-eval.mjs -e
|
./scripts/nrepl-eval.mjs -e
|
||||||
```
|
```
|
||||||
|
|||||||
@ -13,7 +13,7 @@ Fetch information from Taiga public API for the **Penpot** project
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- `python3` — the `tools/taiga.py` CLI script is self-contained (stdlib only)
|
- `python3` — the `scripts/taiga.py` CLI script is self-contained (stdlib only)
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@ -21,17 +21,17 @@ The easiest way is to use the bundled Python script:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Pass a Taiga URL directly
|
# Pass a Taiga URL directly
|
||||||
python3 tools/taiga.py https://tree.taiga.io/project/penpot/issue/13714
|
python3 scripts/taiga.py https://tree.taiga.io/project/penpot/issue/13714
|
||||||
|
|
||||||
# Or use "<type> <ref>" syntax
|
# Or use "<type> <ref>" syntax
|
||||||
python3 tools/taiga.py us 14128
|
python3 scripts/taiga.py us 14128
|
||||||
python3 tools/taiga.py task 13648
|
python3 scripts/taiga.py task 13648
|
||||||
|
|
||||||
# Add --json for raw output
|
# Add --json for raw output
|
||||||
python3 tools/taiga.py --json issue 13714
|
python3 scripts/taiga.py --json issue 13714
|
||||||
|
|
||||||
# See full usage
|
# See full usage
|
||||||
python3 tools/taiga.py --help
|
python3 scripts/taiga.py --help
|
||||||
```
|
```
|
||||||
|
|
||||||
## URL Pattern Reference
|
## URL Pattern Reference
|
||||||
@ -51,30 +51,30 @@ To extract the **type** and **ref** from a URL:
|
|||||||
|
|
||||||
## Python Script Reference
|
## Python Script Reference
|
||||||
|
|
||||||
The `tools/taiga.py` script wraps the Taiga API into a single convenient CLI
|
The `scripts/taiga.py` script wraps the Taiga API into a single convenient CLI
|
||||||
with sensible defaults.
|
with sensible defaults.
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
python3 tools/taiga.py <taiga-url>
|
python3 scripts/taiga.py <taiga-url>
|
||||||
python3 tools/taiga.py <type> <ref>
|
python3 scripts/taiga.py <type> <ref>
|
||||||
python3 tools/taiga.py [--json] <taiga-url>
|
python3 scripts/taiga.py [--json] <taiga-url>
|
||||||
python3 tools/taiga.py [--json] <type> <ref>
|
python3 scripts/taiga.py [--json] <type> <ref>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# By URL (recommended — no need to think about type/ref)
|
# By URL (recommended — no need to think about type/ref)
|
||||||
python3 tools/taiga.py https://tree.taiga.io/project/penpot/issue/13714
|
python3 scripts/taiga.py https://tree.taiga.io/project/penpot/issue/13714
|
||||||
|
|
||||||
# By type and ref
|
# By type and ref
|
||||||
python3 tools/taiga.py us 14128
|
python3 scripts/taiga.py us 14128
|
||||||
python3 tools/taiga.py task 13648
|
python3 scripts/taiga.py task 13648
|
||||||
|
|
||||||
# Raw JSON output
|
# Raw JSON output
|
||||||
python3 tools/taiga.py --json issue 13714
|
python3 scripts/taiga.py --json issue 13714
|
||||||
```
|
```
|
||||||
|
|
||||||
### Output
|
### Output
|
||||||
|
|||||||
@ -20,7 +20,7 @@ primary link, with the fix PR inline on the same line.
|
|||||||
|
|
||||||
- `gh` CLI authenticated (`gh auth status`)
|
- `gh` CLI authenticated (`gh auth status`)
|
||||||
- Python 3.8+
|
- Python 3.8+
|
||||||
- `tools/gh.py` helper script available
|
- `scripts/gh.py` helper script available
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
@ -36,13 +36,13 @@ Use the helper script. It uses GraphQL for efficient single-pass fetching
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# All closed issues (default)
|
# All closed issues (default)
|
||||||
python3 tools/gh.py issues "2.16.0"
|
python3 scripts/gh.py issues "2.16.0"
|
||||||
|
|
||||||
# Include open issues too
|
# Include open issues too
|
||||||
python3 tools/gh.py issues "2.16.0" --state all
|
python3 scripts/gh.py issues "2.16.0" --state all
|
||||||
|
|
||||||
# Exclude entries that should not go in the changelog
|
# Exclude entries that should not go in the changelog
|
||||||
python3 tools/gh.py issues "2.16.0" --exclude "release blocker,no changelog"
|
python3 scripts/gh.py issues "2.16.0" --exclude "release blocker,no changelog"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Exclusion rules (issue-level):**
|
**Exclusion rules (issue-level):**
|
||||||
@ -68,7 +68,7 @@ If updating from an existing `CHANGES.md`, find issues in the milestone that
|
|||||||
are NOT yet referenced in the changelog:
|
are NOT yet referenced in the changelog:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 tools/gh.py issues "2.16.0" --exclude "release blocker,no changelog" --compare CHANGES.md
|
python3 scripts/gh.py issues "2.16.0" --exclude "release blocker,no changelog" --compare CHANGES.md
|
||||||
```
|
```
|
||||||
|
|
||||||
This returns a filtered JSON array with only the missing issues.
|
This returns a filtered JSON array with only the missing issues.
|
||||||
@ -85,23 +85,23 @@ community contribution attribution, or to read the PR body for
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# One or more PR numbers
|
# One or more PR numbers
|
||||||
python3 tools/gh.py prs 9179 9204 9311
|
python3 scripts/gh.py prs 9179 9204 9311
|
||||||
|
|
||||||
# From a file
|
# From a file
|
||||||
python3 tools/gh.py prs --file prs.txt
|
python3 scripts/gh.py prs --file prs.txt
|
||||||
|
|
||||||
# From stdin
|
# From stdin
|
||||||
cat prs.txt | python3 tools/gh.py prs --stdin
|
cat prs.txt | python3 scripts/gh.py prs --stdin
|
||||||
```
|
```
|
||||||
|
|
||||||
The `prs` command also supports listing all PRs in a milestone in one call:
|
The `prs` command also supports listing all PRs in a milestone in one call:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# All merged PRs in a milestone (default)
|
# All merged PRs in a milestone (default)
|
||||||
python3 tools/gh.py prs --milestone "2.16.0"
|
python3 scripts/gh.py prs --milestone "2.16.0"
|
||||||
|
|
||||||
# All states (merged, open, closed)
|
# All states (merged, open, closed)
|
||||||
python3 tools/gh.py prs --milestone "2.16.0" --state all
|
python3 scripts/gh.py prs --milestone "2.16.0" --state all
|
||||||
```
|
```
|
||||||
|
|
||||||
The `prs` command returns JSON with `number`, `title`, `body`, `state`,
|
The `prs` command returns JSON with `number`, `title`, `body`, `state`,
|
||||||
@ -113,13 +113,13 @@ You can also list all PRs in a milestone in a single call:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# All merged PRs in a milestone (default)
|
# All merged PRs in a milestone (default)
|
||||||
python3 tools/gh.py prs --milestone "2.16.0"
|
python3 scripts/gh.py prs --milestone "2.16.0"
|
||||||
|
|
||||||
# All states (merged, open, closed)
|
# All states (merged, open, closed)
|
||||||
python3 tools/gh.py prs --milestone "2.16.0" --state all
|
python3 scripts/gh.py prs --milestone "2.16.0" --state all
|
||||||
|
|
||||||
# Open PRs only
|
# Open PRs only
|
||||||
python3 tools/gh.py prs --milestone "2.16.0" --state open
|
python3 scripts/gh.py prs --milestone "2.16.0" --state open
|
||||||
```
|
```
|
||||||
|
|
||||||
The milestone path uses paginated GraphQL on the milestone's `pullRequests`
|
The milestone path uses paginated GraphQL on the milestone's `pullRequests`
|
||||||
@ -155,7 +155,7 @@ The attribution should reference the **PR author**, not the issue author.
|
|||||||
The `prs` subcommand includes the `author` field — use that:
|
The `prs` subcommand includes the `author` field — use that:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 tools/gh.py prs <PR_NUMBER> | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['author'])"
|
python3 scripts/gh.py prs <PR_NUMBER> | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['author'])"
|
||||||
```
|
```
|
||||||
|
|
||||||
Placement in the entry line:
|
Placement in the entry line:
|
||||||
@ -190,7 +190,7 @@ only reference **merged** PRs. Verify before writing:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Collect all PR numbers from the candidate entries and check them
|
# Collect all PR numbers from the candidate entries and check them
|
||||||
python3 tools/gh.py prs <ALL_PR_NUMBERS> | python3 -c "
|
python3 scripts/gh.py prs <ALL_PR_NUMBERS> | python3 -c "
|
||||||
import json, sys
|
import json, sys
|
||||||
for pr in json.load(sys.stdin):
|
for pr in json.load(sys.stdin):
|
||||||
if pr['state'] != 'MERGED':
|
if pr['state'] != 'MERGED':
|
||||||
@ -265,7 +265,7 @@ section) or in the candidate set for the current milestone, check:
|
|||||||
current milestone since the changelog was last updated (e.g., a fix
|
current milestone since the changelog was last updated (e.g., a fix
|
||||||
arrived late and the issue was reassigned to a future milestone)?
|
arrived late and the issue was reassigned to a future milestone)?
|
||||||
- Verify the issue is still in the current milestone via
|
- Verify the issue is still in the current milestone via
|
||||||
`python3 tools/gh.py issues <MILESTONE> --state all`. If it's no
|
`python3 scripts/gh.py issues <MILESTONE> --state all`. If it's no
|
||||||
longer there, remove the entry from the current section. (If the
|
longer there, remove the entry from the current section. (If the
|
||||||
target section doesn't exist yet, the entry is simply dropped.)
|
target section doesn't exist yet, the entry is simply dropped.)
|
||||||
|
|
||||||
@ -337,7 +337,7 @@ cross-reference to catch gaps:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# List all merged PRs in the milestone
|
# List all merged PRs in the milestone
|
||||||
python3 tools/gh.py prs --milestone "<MILESTONE>" --state merged > /tmp/milestone-prs.json
|
python3 scripts/gh.py prs --milestone "<MILESTONE>" --state merged > /tmp/milestone-prs.json
|
||||||
|
|
||||||
# Extract PR numbers from the changelog section
|
# Extract PR numbers from the changelog section
|
||||||
python3 -c "
|
python3 -c "
|
||||||
@ -378,7 +378,7 @@ changelog or is legitimately excluded (check its labels).
|
|||||||
Also verify that no closed-unmerged PRs remain in the changelog:
|
Also verify that no closed-unmerged PRs remain in the changelog:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 tools/gh.py prs --milestone "<MILESTONE>" --state all | python3 -c "
|
python3 scripts/gh.py prs --milestone "<MILESTONE>" --state all | python3 -c "
|
||||||
import json, sys
|
import json, sys
|
||||||
data = json.load(sys.stdin)
|
data = json.load(sys.stdin)
|
||||||
closed = [p for p in data if p['state'] == 'CLOSED']
|
closed = [p for p in data if p['state'] == 'CLOSED']
|
||||||
@ -483,13 +483,13 @@ def fmt_issue_list(nums):
|
|||||||
|
|
||||||
# --- Fetch milestone data ---
|
# --- Fetch milestone data ---
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["python3", "tools/gh.py", "issues", MILESTONE, "--state", "all"],
|
["python3", "scripts/gh.py", "issues", MILESTONE, "--state", "all"],
|
||||||
capture_output=True, text=True)
|
capture_output=True, text=True)
|
||||||
all_issues = json.loads(result.stdout)
|
all_issues = json.loads(result.stdout)
|
||||||
issue_by_num = {i['number']: i for i in all_issues}
|
issue_by_num = {i['number']: i for i in all_issues}
|
||||||
|
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["python3", "tools/gh.py", "prs", "--milestone", MILESTONE, "--state", "all"],
|
["python3", "scripts/gh.py", "prs", "--milestone", MILESTONE, "--state", "all"],
|
||||||
capture_output=True, text=True)
|
capture_output=True, text=True)
|
||||||
all_prs = json.loads(result.stdout)
|
all_prs = json.loads(result.stdout)
|
||||||
pr_by_num = {p['number']: p for p in all_prs}
|
pr_by_num = {p['number']: p for p in all_prs}
|
||||||
@ -728,7 +728,7 @@ self-contained and clickable in any Markdown viewer.
|
|||||||
reference if applicable.
|
reference if applicable.
|
||||||
- **Re-fetch before editing.** Milestones can change — always re-fetch issues
|
- **Re-fetch before editing.** Milestones can change — always re-fetch issues
|
||||||
before making edits, don't rely on cached data.
|
before making edits, don't rely on cached data.
|
||||||
- **Use `tools/gh.py`.** Prefer the helper script over raw `gh api` calls for
|
- **Use `scripts/gh.py`.** Prefer the helper script over raw `gh api` calls for
|
||||||
milestone issue listing and PR detail fetching. It handles GraphQL
|
milestone issue listing and PR detail fetching. It handles GraphQL
|
||||||
pagination, batching, and label filtering automatically.
|
pagination, batching, and label filtering automatically.
|
||||||
- **Verify PR merge status.** Not all closing PRs are merged — community PRs
|
- **Verify PR merge status.** Not all closing PRs are merged — community PRs
|
||||||
@ -739,7 +739,7 @@ self-contained and clickable in any Markdown viewer.
|
|||||||
labels. Check both.
|
labels. Check both.
|
||||||
- **Cross-reference milestone PRs, not just issues.** The `--compare` flag on
|
- **Cross-reference milestone PRs, not just issues.** The `--compare` flag on
|
||||||
the `issues` command only compares issue numbers. Merged PRs not linked to
|
the `issues` command only compares issue numbers. Merged PRs not linked to
|
||||||
any milestone issue can be missed. Use `python3 tools/gh.py prs --milestone`
|
any milestone issue can be missed. Use `python3 scripts/gh.py prs --milestone`
|
||||||
for a full PR cross-reference.
|
for a full PR cross-reference.
|
||||||
- **False-positive PR-to-issue associations.** A PR may claim to close an
|
- **False-positive PR-to-issue associations.** A PR may claim to close an
|
||||||
issue from a different project or context. If the PR title and issue title
|
issue from a different project or context. If the PR title and issue title
|
||||||
|
|||||||
@ -39,8 +39,8 @@ Backend RPC command areas without focused memories include access tokens, binfil
|
|||||||
|
|
||||||
Database migrations live in `backend/src/app/migrations/`; pure SQL migrations are under `backend/src/app/migrations/sql/`. SQL filenames conventionally start with a sequence and verb/table description, e.g. `0026-mod-profile-table-add-is-active-field`. Applied migrations are tracked in the `migrations` table.
|
Database migrations live in `backend/src/app/migrations/`; pure SQL migrations are under `backend/src/app/migrations/sql/`. SQL filenames conventionally start with a sequence and verb/table description, e.g. `0026-mod-profile-table-add-is-active-field`. Applied migrations are tracked in the `migrations` table.
|
||||||
|
|
||||||
For interactive PostgreSQL access with correct dev defaults, use `tools/psql`; to dump
|
For interactive PostgreSQL access with correct dev defaults, use `scripts/psql`; to dump
|
||||||
the current DDL schema, use `tools/db-schema` (see `mem:tools/psql`).
|
the current DDL schema, use `scripts/db-schema` (see `mem:scripts/psql`).
|
||||||
|
|
||||||
For deeper details on transaction semantics, advisory locks, Transit vs JSON helpers, and dev/test DB URLs: `mem:backend/rpc-db-worker-subtleties`.
|
For deeper details on transaction semantics, advisory locks, Transit vs JSON helpers, and dev/test DB URLs: `mem:backend/rpc-db-worker-subtleties`.
|
||||||
|
|
||||||
@ -56,14 +56,14 @@ In devenv, backend nREPL is exposed on port 6064.
|
|||||||
|
|
||||||
### Non-interactive eval (preferred for agents)
|
### Non-interactive eval (preferred for agents)
|
||||||
|
|
||||||
`./tools/nrepl-eval.mjs` connects to an already-running nREPL server and evaluates code. Session state (defs, `in-ns`) persists across invocations via a stored session ID in `/tmp/penpot-nrepl-session-<host>-<port>`.
|
`./scripts/nrepl-eval.mjs` connects to an already-running nREPL server and evaluates code. Session state (defs, `in-ns`) persists across invocations via a stored session ID in `/tmp/penpot-nrepl-session-<host>-<port>`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs '(+ 1 2)' # single expression
|
./scripts/nrepl-eval.mjs '(+ 1 2)' # single expression
|
||||||
./tools/nrepl-eval.mjs "(require '[my.ns :as ns] :reload)" # reload after edits
|
./scripts/nrepl-eval.mjs "(require '[my.ns :as ns] :reload)" # reload after edits
|
||||||
./tools/nrepl-eval.mjs -e # inspect last exception (*e)
|
./scripts/nrepl-eval.mjs -e # inspect last exception (*e)
|
||||||
./tools/nrepl-eval.mjs --reset-session '(def x 0)' # discard session, start fresh
|
./scripts/nrepl-eval.mjs --reset-session '(def x 0)' # discard session, start fresh
|
||||||
./tools/nrepl-eval.mjs <<'EOF' # multi-expression heredoc
|
./scripts/nrepl-eval.mjs <<'EOF' # multi-expression heredoc
|
||||||
(def x 10)
|
(def x 10)
|
||||||
(+ x 20)
|
(+ x 20)
|
||||||
EOF
|
EOF
|
||||||
@ -92,12 +92,12 @@ Fixtures can populate local data for manual testing/perf work. From the backend
|
|||||||
|
|
||||||
IMPORTANT: all CLI commands must be executed from the `backend/` subdirectory.
|
IMPORTANT: all CLI commands must be executed from the `backend/` subdirectory.
|
||||||
|
|
||||||
* **Linting:** `pnpm run lint` from the repository root.
|
* **Linting:** `clj-kondo --lint ../common/src/ src/`.
|
||||||
* **Formatting:** `pnpm run check-fmt`. Use `pnpm run fmt` to fix. Avoid unrelated whitespace diffs.
|
* **Formatting:** `cljfmt check src/ test/` to check, `cljfmt fix src/ test/` to fix. Avoid unrelated whitespace diffs.
|
||||||
|
|
||||||
**Before linting:** if delimiter errors are suspected (after LLM edits), run
|
**Before linting:** if delimiter errors are suspected (after LLM edits), run
|
||||||
`tools/paren-repair.bb` on the affected files first. Delimiter errors produce
|
`scripts/paren-repair` on the affected files first. Delimiter errors produce
|
||||||
misleading linter/compiler output. See `mem:tools/paren-repair`.
|
misleading linter/compiler output. See `mem:scripts/paren-repair`.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
|
|||||||
@ -22,8 +22,8 @@ You are working on the GitHub project `penpot/penpot`, a monorepo.
|
|||||||
- Align `let` binding values: when a `let` form has multiple bindings spanning
|
- Align `let` binding values: when a `let` form has multiple bindings spanning
|
||||||
several lines, align the value forms to the same column with spaces.
|
several lines, align the value forms to the same column with spaces.
|
||||||
- If you introduce delimiter errors (mismatched parens/brackets) in Clojure/CLJS files,
|
- If you introduce delimiter errors (mismatched parens/brackets) in Clojure/CLJS files,
|
||||||
fix them with `tools/paren-repair.bb` BEFORE running lint/format checks.
|
fix them with `scripts/paren-repair` BEFORE running lint/format checks.
|
||||||
See `mem:tools/paren-repair` for usage.
|
See `mem:scripts/paren-repair` for usage.
|
||||||
- Never run anything that destroys data without explicit permission, including `drop-devenv`, `docker compose down -v`, `docker volume rm ...`. The user's real work lives in the volumes of the shared infra.
|
- Never run anything that destroys data without explicit permission, including `drop-devenv`, `docker compose down -v`, `docker volume rm ...`. The user's real work lives in the volumes of the shared infra.
|
||||||
|
|
||||||
# Project modules
|
# Project modules
|
||||||
@ -54,18 +54,21 @@ module. You can read it from `mem:<MODULE>/core`
|
|||||||
|
|
||||||
# Dev tools
|
# Dev tools
|
||||||
|
|
||||||
- `tools/nrepl-eval.mjs` — Evaluate Clojure/ClojureScript code via nREPL.
|
- `scripts/nrepl-eval.mjs` — Evaluate Clojure/ClojureScript code via nREPL.
|
||||||
Supports `--backend` (port 6064) and `--frontend` (port 3447) aliases.
|
Supports `--backend` (port 6064) and `--frontend` (port 3447) aliases.
|
||||||
See `mem:tools/nrepl-eval`.
|
See `mem:scripts/nrepl-eval`.
|
||||||
- `tools/paren-repair.bb` — Fix mismatched delimiters in Clojure/CLJS files
|
- `scripts/paren-repair` — Fix mismatched delimiters in Clojure/CLJS files
|
||||||
and reformat with cljfmt. Run before lint checks when LLM edits break parens.
|
and reformat with cljfmt. Run before lint checks when LLM edits break parens.
|
||||||
See `mem:tools/paren-repair`.
|
See `mem:scripts/paren-repair`.
|
||||||
- `tools/psql` — PostgreSQL client wrapper with devenv defaults.
|
- `scripts/psql` — PostgreSQL client wrapper with devenv defaults.
|
||||||
Companion: `tools/db-schema` for DDL dumps. See `mem:tools/psql`.
|
Companion: `scripts/db-schema` for DDL dumps. See `mem:scripts/psql`.
|
||||||
- `tools/taiga.py` — Fetch public issues, user stories, and tasks from the
|
- `scripts/taiga.py` — Fetch public issues, user stories, and tasks from the
|
||||||
Penpot Taiga project without authentication. See `mem:tools/taiga`.
|
Penpot Taiga project without authentication. See `mem:scripts/taiga`.
|
||||||
- `tools/gh.py` — GitHub operations helper: list milestone issues, fetch PR
|
- `scripts/gh.py` — GitHub operations helper: list milestone issues, fetch PR
|
||||||
details, compare against CHANGES.md. Requires `gh` CLI. See `mem:tools/gh`.
|
details, compare against CHANGES.md. Requires `gh` CLI. See `mem:scripts/gh`.
|
||||||
|
- `scripts/error-reports.mjs` — Query error reports via RPC API with token
|
||||||
|
authentication. Supports list/get operations with filtering and pagination.
|
||||||
|
See `mem:scripts/error-reports`.
|
||||||
|
|
||||||
# Dependency graph
|
# Dependency graph
|
||||||
|
|
||||||
|
|||||||
@ -27,9 +27,9 @@ From `frontend/`:
|
|||||||
- Translation formatting after i18n edits: `pnpm run translations`.
|
- Translation formatting after i18n edits: `pnpm run translations`.
|
||||||
|
|
||||||
**Before linting:** if delimiter errors are suspected (after LLM edits, or
|
**Before linting:** if delimiter errors are suspected (after LLM edits, or
|
||||||
lint/compiler reports syntax errors), run `tools/paren-repair.bb` on the
|
lint/compiler reports syntax errors), run `scripts/paren-repair` on the
|
||||||
affected files first. Delimiter errors produce misleading linter output.
|
affected files first. Delimiter errors produce misleading linter output.
|
||||||
See `mem:tools/paren-repair`.
|
See `mem:scripts/paren-repair`.
|
||||||
|
|
||||||
## Focused memory routing
|
## Focused memory routing
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,10 @@ The latter is needed because syntax errors in parentheses give an uninformative
|
|||||||
tool can often find the exact location of such errors.
|
tool can often find the exact location of such errors.
|
||||||
|
|
||||||
When delimiter errors are detected (typically from lint or compiler output),
|
When delimiter errors are detected (typically from lint or compiler output),
|
||||||
fix the affected files with `tools/paren-repair.bb`. The `clj_check_parentheses`
|
fix the affected files with `scripts/paren-repair`. The `clj_check_parentheses`
|
||||||
MCP tool can also pinpoint the error location when available, but it is not
|
MCP tool can also pinpoint the error location when available, but it is not
|
||||||
required — standard build errors are usually enough.
|
required — standard build errors are usually enough.
|
||||||
See `mem:tools/paren-repair`.
|
See `mem:scripts/paren-repair`.
|
||||||
|
|
||||||
## Runtime patching with `set!`
|
## Runtime patching with `set!`
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# GitHub operations helper
|
# GitHub operations helper
|
||||||
|
|
||||||
`tools/gh.py` is a multi-purpose CLI for querying the penpot/penpot GitHub
|
`scripts/gh.py` is a multi-purpose CLI for querying the penpot/penpot GitHub
|
||||||
repository via GraphQL and REST APIs through the authenticated `gh` CLI.
|
repository via GraphQL and REST APIs through the authenticated `gh` CLI.
|
||||||
|
|
||||||
## When to use
|
## When to use
|
||||||
@ -23,24 +23,24 @@ List issues in a milestone, with filtering by state, labels, and project status.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Closed issues in a milestone (default)
|
# Closed issues in a milestone (default)
|
||||||
python3 tools/gh.py issues "2.16.0"
|
python3 scripts/gh.py issues "2.16.0"
|
||||||
|
|
||||||
# All issues in a milestone
|
# All issues in a milestone
|
||||||
python3 tools/gh.py issues "2.16.0" --state all
|
python3 scripts/gh.py issues "2.16.0" --state all
|
||||||
|
|
||||||
# Issues with no milestone
|
# Issues with no milestone
|
||||||
python3 tools/gh.py issues none
|
python3 scripts/gh.py issues none
|
||||||
python3 tools/gh.py issues none --state open
|
python3 scripts/gh.py issues none --state open
|
||||||
|
|
||||||
# Filter by label (include only)
|
# Filter by label (include only)
|
||||||
python3 tools/gh.py issues "2.16.0" --label "bug"
|
python3 scripts/gh.py issues "2.16.0" --label "bug"
|
||||||
python3 tools/gh.py issues "2.16.0" --label "bug,regression"
|
python3 scripts/gh.py issues "2.16.0" --label "bug,regression"
|
||||||
|
|
||||||
# Exclude by label
|
# Exclude by label
|
||||||
python3 tools/gh.py issues "2.16.0" --exclude "release blocker,no changelog"
|
python3 scripts/gh.py issues "2.16.0" --exclude "release blocker,no changelog"
|
||||||
|
|
||||||
# Show only issues NOT yet in CHANGES.md
|
# Show only issues NOT yet in CHANGES.md
|
||||||
python3 tools/gh.py issues "2.16.0" --compare CHANGES.md
|
python3 scripts/gh.py issues "2.16.0" --compare CHANGES.md
|
||||||
```
|
```
|
||||||
|
|
||||||
**Default filters** (override with flags):
|
**Default filters** (override with flags):
|
||||||
@ -55,19 +55,19 @@ Fetch PR details by number or by milestone.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Fetch specific PRs
|
# Fetch specific PRs
|
||||||
python3 tools/gh.py prs 9179 9204 9311
|
python3 scripts/gh.py prs 9179 9204 9311
|
||||||
|
|
||||||
# Read PR numbers from file
|
# Read PR numbers from file
|
||||||
python3 tools/gh.py prs --file prs.txt
|
python3 scripts/gh.py prs --file prs.txt
|
||||||
|
|
||||||
# Read PR numbers from stdin
|
# Read PR numbers from stdin
|
||||||
cat prs.txt | python3 tools/gh.py prs --stdin
|
cat prs.txt | python3 scripts/gh.py prs --stdin
|
||||||
|
|
||||||
# All PRs in a milestone (default: merged only)
|
# All PRs in a milestone (default: merged only)
|
||||||
python3 tools/gh.py prs --milestone "2.16.0"
|
python3 scripts/gh.py prs --milestone "2.16.0"
|
||||||
|
|
||||||
# All PRs in a milestone (all states)
|
# All PRs in a milestone (all states)
|
||||||
python3 tools/gh.py prs --milestone "2.16.0" --state all
|
python3 scripts/gh.py prs --milestone "2.16.0" --state all
|
||||||
```
|
```
|
||||||
|
|
||||||
**Output**: JSON array to stdout; progress to stderr.
|
**Output**: JSON array to stdout; progress to stderr.
|
||||||
@ -1,7 +1,7 @@
|
|||||||
# nREPL Eval
|
# nREPL Eval
|
||||||
|
|
||||||
Evaluate Clojure (or ClojureScript) code via a running nREPL server using
|
Evaluate Clojure (or ClojureScript) code via a running nREPL server using
|
||||||
`tools/nrepl-eval.mjs` — a standalone CLI application.
|
`scripts/nrepl-eval.mjs` — a standalone CLI application.
|
||||||
|
|
||||||
Session state (defs, in-ns, etc.) persists across invocations via a stored
|
Session state (defs, in-ns, etc.) persists across invocations via a stored
|
||||||
session ID, so you can build up state incrementally.
|
session ID, so you can build up state incrementally.
|
||||||
@ -9,9 +9,9 @@ session ID, so you can build up state incrementally.
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node tools/nrepl-eval.mjs [options] [<code>]
|
node scripts/nrepl-eval.mjs [options] [<code>]
|
||||||
# or
|
# or
|
||||||
./tools/nrepl-eval.mjs [options] [<code>]
|
./scripts/nrepl-eval.mjs [options] [<code>]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
@ -47,37 +47,37 @@ Sessions are persisted to `/tmp/penpot-nrepl-session-<host>-<port>`. State
|
|||||||
carries across calls automatically:
|
carries across calls automatically:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs '(def x 42)'
|
./scripts/nrepl-eval.mjs '(def x 42)'
|
||||||
./tools/nrepl-eval.mjs 'x'
|
./scripts/nrepl-eval.mjs 'x'
|
||||||
# => 42
|
# => 42
|
||||||
```
|
```
|
||||||
|
|
||||||
Reset the session to start fresh:
|
Reset the session to start fresh:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs --reset-session '(def x 0)'
|
./scripts/nrepl-eval.mjs --reset-session '(def x 0)'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Evaluate code
|
### Evaluate code
|
||||||
|
|
||||||
**Single expression (inline) — uses default port 6064:**
|
**Single expression (inline) — uses default port 6064:**
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs '(+ 1 2 3)'
|
./scripts/nrepl-eval.mjs '(+ 1 2 3)'
|
||||||
```
|
```
|
||||||
|
|
||||||
**Backend nREPL (explicit):**
|
**Backend nREPL (explicit):**
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs --backend '(+ 1 2 3)'
|
./scripts/nrepl-eval.mjs --backend '(+ 1 2 3)'
|
||||||
```
|
```
|
||||||
|
|
||||||
**Frontend nREPL:**
|
**Frontend nREPL:**
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs --frontend '(js/alert "hi")'
|
./scripts/nrepl-eval.mjs --frontend '(js/alert "hi")'
|
||||||
```
|
```
|
||||||
|
|
||||||
**Multiple expressions via heredoc (recommended — avoids escaping issues):**
|
**Multiple expressions via heredoc (recommended — avoids escaping issues):**
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs <<'EOF'
|
./scripts/nrepl-eval.mjs <<'EOF'
|
||||||
(def x 10)
|
(def x 10)
|
||||||
(+ x 20)
|
(+ x 20)
|
||||||
EOF
|
EOF
|
||||||
@ -85,7 +85,7 @@ EOF
|
|||||||
|
|
||||||
**Override with a different port:**
|
**Override with a different port:**
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs -p 7888 '(+ 1 2 3)'
|
./scripts/nrepl-eval.mjs -p 7888 '(+ 1 2 3)'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Inspect last exception
|
### Inspect last exception
|
||||||
@ -93,26 +93,48 @@ EOF
|
|||||||
After code throws an error, retrieve the full exception details:
|
After code throws an error, retrieve the full exception details:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs -e
|
./scripts/nrepl-eval.mjs -e
|
||||||
```
|
```
|
||||||
|
|
||||||
## Common Patterns
|
## Common Patterns
|
||||||
|
|
||||||
**Require a namespace with reload:**
|
**Require a namespace with reload:**
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs "(require '[my.namespace :as ns] :reload)"
|
./scripts/nrepl-eval.mjs "(require '[my.namespace :as ns] :reload)"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Test a function:**
|
**Test a function:**
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs "(ns/my-function arg1 arg2)"
|
./scripts/nrepl-eval.mjs "(ns/my-function arg1 arg2)"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Long-running operation with custom timeout:**
|
**Long-running operation with custom timeout:**
|
||||||
```bash
|
```bash
|
||||||
./tools/nrepl-eval.mjs -t 300000 "(long-running-fn)"
|
./scripts/nrepl-eval.mjs -t 300000 "(long-running-fn)"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Accessing Private Functions
|
||||||
|
|
||||||
|
Private functions (declared with `^:private` or `defn-`) cannot be called
|
||||||
|
directly from outside their namespace. Use the var quote syntax `#'` to
|
||||||
|
access the underlying var:
|
||||||
|
|
||||||
|
**This fails:**
|
||||||
|
```bash
|
||||||
|
./scripts/nrepl-eval.mjs "(app.rpc.commands.error-reports/build-list-query {})"
|
||||||
|
# => Syntax error: app.rpc.commands.error-reports/build-list-query is not public
|
||||||
|
```
|
||||||
|
|
||||||
|
**This works:**
|
||||||
|
```bash
|
||||||
|
./scripts/nrepl-eval.mjs "(#'app.rpc.commands.error-reports/build-list-query {})"
|
||||||
|
# => Returns the result
|
||||||
|
```
|
||||||
|
|
||||||
|
The `#'` reader macro resolves to `(var ...)`, giving you direct access to
|
||||||
|
the var regardless of its visibility modifier. The syntax is `#'` followed
|
||||||
|
by the fully qualified symbol.
|
||||||
|
|
||||||
## Key Principles
|
## Key Principles
|
||||||
|
|
||||||
- **Default port is 6064** — just pass code directly, no `-p` needed when
|
- **Default port is 6064** — just pass code directly, no `-p` needed when
|
||||||
43
.serena/memories/scripts/paren-repair.md
Normal file
43
.serena/memories/scripts/paren-repair.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Paren-Repair
|
||||||
|
|
||||||
|
`scripts/paren-repair` fixes mismatched parentheses, brackets, and braces in
|
||||||
|
Clojure/ClojureScript files, then reformats them with cljfmt.
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
|
||||||
|
- After LLM edits introduce broken delimiters — proactively run it on files
|
||||||
|
you just touched.
|
||||||
|
- When lint (clj-kondo), the Clojure compiler, or shadow-cljs report syntax
|
||||||
|
errors mentioning mismatched/unclosed delimiters, reader errors, or
|
||||||
|
unexpected EOF.
|
||||||
|
- Before running lint/format checks — delimiter errors make linter output
|
||||||
|
misleading. Fix them first, then lint.
|
||||||
|
|
||||||
|
## How to use (CLI)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# File mode (in-place fix + format)
|
||||||
|
bb scripts/paren-repair path/to/file.clj
|
||||||
|
|
||||||
|
# Pipe mode (stdin → fixed code to stdout)
|
||||||
|
echo '(def x 1' | bb scripts/paren-repair
|
||||||
|
|
||||||
|
# Help
|
||||||
|
bb scripts/paren-repair --help
|
||||||
|
```
|
||||||
|
`bb` must be invoked from the repo root so the path `scripts/paren-repair` resolves.
|
||||||
|
|
||||||
|
## Native Tool Available (opencode)
|
||||||
|
|
||||||
|
A native opencode tool `paren-repair` is available at `.opencode/tools/paren-repair.ts`.
|
||||||
|
The LLM can call it directly with:
|
||||||
|
- `files`: Array of file paths to fix
|
||||||
|
- `code`: Code string to fix via stdin
|
||||||
|
|
||||||
|
Example usage by the LLM:
|
||||||
|
```
|
||||||
|
paren-repair(files="src/foo.clj, src/bar.cljs")
|
||||||
|
paren-repair(code="(defn foo [x")
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
39
.serena/memories/scripts/psql.md
Normal file
39
.serena/memories/scripts/psql.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Psql
|
||||||
|
|
||||||
|
`scripts/psql` is a wrapper around `psql` that connects to the Penpot PostgreSQL
|
||||||
|
database using environment variables (`PENPOT_DB_HOST`, `PENPOT_DB_USER`,
|
||||||
|
`PENPOT_DB_PASSWORD`, `PENPOT_DB_NAME`) with sensible defaults for local
|
||||||
|
development.
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
|
||||||
|
- Running ad-hoc SQL queries against the Penpot database.
|
||||||
|
- Inspecting schema, migrations, or data during development or debugging.
|
||||||
|
|
||||||
|
## How to use (CLI)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Default connection (penpot db, localhost)
|
||||||
|
scripts/psql -c "SELECT version();"
|
||||||
|
|
||||||
|
# Test database
|
||||||
|
scripts/psql --test -c "SELECT * FROM migrations;"
|
||||||
|
|
||||||
|
# Custom host/user/database
|
||||||
|
scripts/psql --host myhost --user myuser --db mydb
|
||||||
|
```
|
||||||
|
|
||||||
|
`scripts/psql` must be invoked from the repo root so the path resolves.
|
||||||
|
|
||||||
|
## Native Tool Available (opencode)
|
||||||
|
|
||||||
|
A native opencode tool `penpot-psql` is available. The LLM can call it directly
|
||||||
|
with:
|
||||||
|
- `sql`: SQL command string to execute
|
||||||
|
- `test`: Boolean flag to use the `penpot_test` database
|
||||||
|
|
||||||
|
Example usage by the LLM:
|
||||||
|
```
|
||||||
|
penpot-psql(sql="SELECT version();")
|
||||||
|
penpot-psql(sql="SELECT * FROM migrations;", test=true)
|
||||||
|
```
|
||||||
@ -1,6 +1,6 @@
|
|||||||
# Taiga API client
|
# Taiga API client
|
||||||
|
|
||||||
`tools/taiga.py` fetches public issues, user stories, and tasks from the
|
`scripts/taiga.py` fetches public issues, user stories, and tasks from the
|
||||||
Penpot Taiga project (id 345963) without authentication.
|
Penpot Taiga project (id 345963) without authentication.
|
||||||
|
|
||||||
## When to use
|
## When to use
|
||||||
@ -13,16 +13,16 @@ Penpot Taiga project (id 345963) without authentication.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Fetch by full Taiga URL
|
# Fetch by full Taiga URL
|
||||||
python3 tools/taiga.py https://tree.taiga.io/project/penpot/issue/13714
|
python3 scripts/taiga.py https://tree.taiga.io/project/penpot/issue/13714
|
||||||
|
|
||||||
# Fetch by type and ref number
|
# Fetch by type and ref number
|
||||||
python3 tools/taiga.py issue 13714
|
python3 scripts/taiga.py issue 13714
|
||||||
python3 tools/taiga.py us 14128
|
python3 scripts/taiga.py us 14128
|
||||||
python3 tools/taiga.py task 13648
|
python3 scripts/taiga.py task 13648
|
||||||
|
|
||||||
# Output raw JSON instead of formatted summary
|
# Output raw JSON instead of formatted summary
|
||||||
python3 tools/taiga.py --json issue 13714
|
python3 scripts/taiga.py --json issue 13714
|
||||||
python3 tools/taiga.py --json https://tree.taiga.io/project/penpot/us/14128
|
python3 scripts/taiga.py --json https://tree.taiga.io/project/penpot/us/14128
|
||||||
```
|
```
|
||||||
|
|
||||||
## Supported types
|
## Supported types
|
||||||
@ -1,29 +0,0 @@
|
|||||||
# Paren-Repair
|
|
||||||
|
|
||||||
`tools/paren-repair.bb` fixes mismatched parentheses, brackets, and braces in
|
|
||||||
Clojure/ClojureScript files, then reformats them with cljfmt.
|
|
||||||
|
|
||||||
## When to use
|
|
||||||
|
|
||||||
- After LLM edits introduce broken delimiters — proactively run it on files
|
|
||||||
you just touched.
|
|
||||||
- When lint (clj-kondo), the Clojure compiler, or shadow-cljs report syntax
|
|
||||||
errors mentioning mismatched/unclosed delimiters, reader errors, or
|
|
||||||
unexpected EOF.
|
|
||||||
- Before running lint/format checks — delimiter errors make linter output
|
|
||||||
misleading. Fix them first, then lint.
|
|
||||||
|
|
||||||
## How to use
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# File mode (in-place fix + format)
|
|
||||||
bb tools/paren-repair.bb path/to/file.clj
|
|
||||||
|
|
||||||
# Pipe mode (stdin → fixed code to stdout)
|
|
||||||
echo '(def x 1' | bb tools/paren-repair.bb
|
|
||||||
|
|
||||||
# Help
|
|
||||||
bb tools/paren-repair.bb --help
|
|
||||||
```
|
|
||||||
|
|
||||||
`bb` must be invoked from the repo root so the path `tools/paren-repair.bb` resolves.
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
# PostgreSQL client wrapper
|
|
||||||
|
|
||||||
`tools/psql` is a wrapper around the `psql` command with defaults preconfigured
|
|
||||||
for the Penpot development environment.
|
|
||||||
|
|
||||||
## When to use
|
|
||||||
|
|
||||||
- Running SQL queries against the dev database (`penpot`) or test database
|
|
||||||
(`penpot_test`).
|
|
||||||
- Inspecting table structures, running migrations manually, or debugging
|
|
||||||
database state.
|
|
||||||
- Any time you need PostgreSQL access and want the correct host/user/password
|
|
||||||
without typing them each time.
|
|
||||||
|
|
||||||
## How to use
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Interactive session (penpot database)
|
|
||||||
tools/psql
|
|
||||||
|
|
||||||
# Interactive session (penpot_test database)
|
|
||||||
tools/psql --test
|
|
||||||
|
|
||||||
# Inline query (penpot)
|
|
||||||
tools/psql -c "SELECT 1"
|
|
||||||
|
|
||||||
# Inline query (penpot_test)
|
|
||||||
tools/psql --test -c "SELECT 1"
|
|
||||||
|
|
||||||
# Override defaults
|
|
||||||
tools/psql -h other-host -U other-user -d other-db
|
|
||||||
|
|
||||||
# Pipe SQL from a file
|
|
||||||
tools/psql -f some-query.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
All standard `psql` flags are passed through after the wrapper's own flags.
|
|
||||||
|
|
||||||
## Defaults
|
|
||||||
|
|
||||||
| Setting | Default | Env override |
|
|
||||||
|----------|-----------|---------------------|
|
|
||||||
| Host | `postgres` | `PENPOT_DB_HOST` |
|
|
||||||
| User | `penpot` | `PENPOT_DB_USER` |
|
|
||||||
| Password | `penpot` | `PENPOT_DB_PASSWORD` |
|
|
||||||
| Database | `penpot` | `PENPOT_DB_NAME` |
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
`tools/db-schema` — a companion script that dumps the current DDL schema
|
|
||||||
using `pg_dump --schema-only`, with the same defaults and `--test` flag.
|
|
||||||
@ -4,10 +4,10 @@ PR only on explicit request. Branch: issue/feature-specific; fallback `<type>/<s
|
|||||||
|
|
||||||
## Target Branch
|
## Target Branch
|
||||||
|
|
||||||
Auto-detect the base branch with `tools/detect-target-branch`:
|
Auto-detect the base branch with `scripts/detect-target-branch`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
TARGET=$(tools/detect-target-branch)
|
TARGET=$(scripts/detect-target-branch)
|
||||||
```
|
```
|
||||||
|
|
||||||
This outputs `staging` or `develop` by walking the local commit graph (pure local, no remote/network). Do not ask the user for the target branch unless the tool fails.
|
This outputs `staging` or `develop` by walking the local commit graph (pure local, no remote/network). Do not ask the user for the target branch unless the tool fails.
|
||||||
@ -83,7 +83,7 @@ cat > /tmp/pr-body.md << 'PR_BODY'
|
|||||||
<body content here>
|
<body content here>
|
||||||
PR_BODY
|
PR_BODY
|
||||||
|
|
||||||
TARGET=$(tools/detect-target-branch)
|
TARGET=$(scripts/detect-target-branch)
|
||||||
|
|
||||||
gh pr create \
|
gh pr create \
|
||||||
--repo penpot/penpot \
|
--repo penpot/penpot \
|
||||||
|
|||||||
20
AGENTS.md
20
AGENTS.md
@ -1,6 +1,6 @@
|
|||||||
# AI AGENT GUIDE
|
# AI AGENT GUIDE
|
||||||
|
|
||||||
## Hard rules (always apply — no exceptions)
|
## HARD RULES (always apply — no exceptions)
|
||||||
|
|
||||||
- **Never `git push`, force-push, or modify `git origin`** (or any other remote).
|
- **Never `git push`, force-push, or modify `git origin`** (or any other remote).
|
||||||
The user pushes from their own shell. If a push is required to surface the
|
The user pushes from their own shell. If a push is required to surface the
|
||||||
@ -92,3 +92,21 @@ precision while maintaining a strong focus on maintainability and performance.
|
|||||||
down into atomic steps.
|
down into atomic steps.
|
||||||
2. Be concise and autonomous.
|
2. Be concise and autonomous.
|
||||||
3. Do **not** touch unrelated modules unless the task explicitly requires it.
|
3. Do **not** touch unrelated modules unless the task explicitly requires it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Available Scripts & Tools
|
||||||
|
|
||||||
|
## Native opencode Tools (callable directly by the LLM)
|
||||||
|
|
||||||
|
- `paren-repair` — Fix mismatched delimiters + reformat Clojure files. Example: `paren-repair(files="src/foo.clj, src/bar.cljs")`
|
||||||
|
- `penpot-psql` — Execute SQL against the Penpot database. Example: `penpot-psql(sql="SELECT version();")`
|
||||||
|
|
||||||
|
## Scripts (from repo root via `scripts/<name>`)
|
||||||
|
|
||||||
|
- `scripts/paren-repair` — Fix mismatched delimiters in Clojure/CLJS files + reformat with cljfmt. See `mem:scripts/paren-repair`.
|
||||||
|
- `scripts/psql` — Connect to the Penpot PostgreSQL database (wraps `psql` with env-var defaults). See `mem:scripts/psql`.
|
||||||
|
- `scripts/nrepl-eval.mjs` — Evaluate Clojure code via nREPL (backend + frontend).
|
||||||
|
- `scripts/check-commit` — Validate commit messages against Penpot's commit guidelines.
|
||||||
|
- `scripts/check-fmt-clj` — Check Clojure formatting without modifying files.
|
||||||
|
|
||||||
|
|||||||
@ -14,9 +14,9 @@ Center](https://help.penpot.app/).
|
|||||||
- [Reporting Bugs](#reporting-bugs)
|
- [Reporting Bugs](#reporting-bugs)
|
||||||
- [Pull Requests](#pull-requests)
|
- [Pull Requests](#pull-requests)
|
||||||
- [Workflow](#workflow)
|
- [Workflow](#workflow)
|
||||||
- [Title format](#title-format)
|
- [Format](#format)
|
||||||
- [Description](#description)
|
- [Title format](#title-format)
|
||||||
- [Branch naming](#branch-naming)
|
- [Description](#description)
|
||||||
- [Review process](#review-process)
|
- [Review process](#review-process)
|
||||||
- [What we won't accept](#what-we-wont-accept)
|
- [What we won't accept](#what-we-wont-accept)
|
||||||
- [Good first issues](#good-first-issues)
|
- [Good first issues](#good-first-issues)
|
||||||
@ -73,35 +73,23 @@ Advisories](https://github.com/penpot/penpot/security/advisories)
|
|||||||
4. **Format and lint** — run the checks described in
|
4. **Format and lint** — run the checks described in
|
||||||
[Formatting and Linting](#formatting-and-linting) before submitting.
|
[Formatting and Linting](#formatting-and-linting) before submitting.
|
||||||
|
|
||||||
### Title format
|
### Format
|
||||||
|
|
||||||
|
#### Title
|
||||||
|
|
||||||
|
> **IMPORTANT:** When a PR is squash-merged, the PR title becomes the
|
||||||
|
> commit message on the main branch. Getting the title right matters.
|
||||||
|
|
||||||
Pull request titles **must** follow the same convention as commit subjects:
|
Pull request titles **must** follow the same convention as commit subjects:
|
||||||
|
|
||||||
```
|
```
|
||||||
:emoji: <subject>
|
:emoji: Subject line (imperative, capitalized, no period, <=70 chars)
|
||||||
```
|
```
|
||||||
|
|
||||||
- Use the **imperative mood** (e.g. "Fix", not "Fixed").
|
Read [Creating Commits](./.serena/memories/workflow/creating-commits.md)
|
||||||
- Capitalize the first letter of the subject.
|
for more concrete information.
|
||||||
- Do not end the subject with a period.
|
|
||||||
- Keep the subject to **70 characters** or fewer.
|
|
||||||
- Use one of the [commit type emojis](#commit-types) listed below.
|
|
||||||
|
|
||||||
When a PR contains multiple unrelated commits, choose the emoji that
|
#### Description
|
||||||
best represents the dominant change.
|
|
||||||
|
|
||||||
**Examples:**
|
|
||||||
|
|
||||||
```
|
|
||||||
:bug: Fix unexpected error on launching modal
|
|
||||||
:sparkles: Enable new modal for profile
|
|
||||||
:zap: Improve performance of dashboard navigation
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note:** When a PR is squash-merged, the PR title becomes the
|
|
||||||
> commit message on the main branch. Getting the title right matters.
|
|
||||||
|
|
||||||
### Description
|
|
||||||
|
|
||||||
Every pull request should include a description that helps reviewers
|
Every pull request should include a description that helps reviewers
|
||||||
understand the change quickly:
|
understand the change quickly:
|
||||||
@ -114,24 +102,8 @@ understand the change quickly:
|
|||||||
5. **Breaking changes** — call out anything that affects existing users
|
5. **Breaking changes** — call out anything that affects existing users
|
||||||
or requires migration steps.
|
or requires migration steps.
|
||||||
|
|
||||||
### Branch naming
|
Read [Creating PRs](./.serena/memories/workflow/creating-prs.md)
|
||||||
|
for more concrete information.
|
||||||
Use a descriptive branch name that reflects the type and scope of the
|
|
||||||
change:
|
|
||||||
|
|
||||||
```
|
|
||||||
<type>/<short-description>
|
|
||||||
```
|
|
||||||
|
|
||||||
Types: `fix`, `feat`, `refactor`, `docs`, `chore`, `perf`.
|
|
||||||
|
|
||||||
Optionally include the issue number:
|
|
||||||
|
|
||||||
```
|
|
||||||
fix/9122-email-blacklisting
|
|
||||||
feat/export-webp
|
|
||||||
refactor/layout-sizing
|
|
||||||
```
|
|
||||||
|
|
||||||
### Review process
|
### Review process
|
||||||
|
|
||||||
@ -151,10 +123,10 @@ refactor/layout-sizing
|
|||||||
To save time on both sides, please avoid submitting PRs that:
|
To save time on both sides, please avoid submitting PRs that:
|
||||||
|
|
||||||
- Introduce new dependencies without prior discussion.
|
- Introduce new dependencies without prior discussion.
|
||||||
- Change the build system or CI configuration without maintainer
|
- Change the build system or CI configuration without maintainer approval.
|
||||||
approval.
|
- Mix unrelated changes in a single PR — keep PRs focused on one concern.
|
||||||
- Mix unrelated changes in a single PR — keep PRs focused on one
|
- Submit AI-generated code without human review.
|
||||||
concern.
|
- Skip local syntax and formatting checks before submitting.
|
||||||
- Skip the [discussion step](#workflow) for non-bug-fix changes.
|
- Skip the [discussion step](#workflow) for non-bug-fix changes.
|
||||||
|
|
||||||
### Good first issues
|
### Good first issues
|
||||||
@ -217,36 +189,8 @@ Commit messages must follow this format:
|
|||||||
|
|
||||||
## Formatting and Linting
|
## Formatting and Linting
|
||||||
|
|
||||||
We use [cljfmt](https://github.com/weavejester/cljfmt) for formatting and
|
Each module has its own linting and formatting commands — see the relevant one on the
|
||||||
[clj-kondo](https://github.com/clj-kondo/clj-kondo) for linting.
|
[Serena Memories](./.serena/memories/)
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check formatting (does not modify files)
|
|
||||||
./scripts/check-fmt
|
|
||||||
|
|
||||||
# Fix formatting (modifies files in place)
|
|
||||||
./scripts/fmt
|
|
||||||
|
|
||||||
# Lint
|
|
||||||
./scripts/lint
|
|
||||||
```
|
|
||||||
|
|
||||||
For frontend SCSS, we use `stylelint` for linting and
|
|
||||||
`Prettier` for formatting:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd frontend
|
|
||||||
|
|
||||||
# Lint SCSS
|
|
||||||
pnpm run lint:scss (does not modify files)
|
|
||||||
|
|
||||||
# Fix SCSS formatting (modifies files in place)
|
|
||||||
pnpm run fmt:scss
|
|
||||||
```
|
|
||||||
|
|
||||||
Ideally, run these as git pre-commit hooks.
|
|
||||||
[Husky](https://typicode.github.io/husky/#/) is a convenient option for
|
|
||||||
setting this up.
|
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "penpot",
|
"name": "penpot",
|
||||||
"version": "1.20.0",
|
"version": "2.17.0",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"author": "Kaleidos INC Sucursal en España SL",
|
"author": "Kaleidos INC Sucursal en España SL",
|
||||||
"private": true,
|
"private": true,
|
||||||
@ -10,11 +10,6 @@
|
|||||||
"url": "https://github.com/penpot/penpot"
|
"url": "https://github.com/penpot/penpot"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
|
||||||
"lint": "./scripts/lint",
|
|
||||||
"check-fmt": "./scripts/check-fmt",
|
|
||||||
"fmt": "./scripts/fmt"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/mcp": "^0.0.76",
|
"@playwright/mcp": "^0.0.76",
|
||||||
"@playwright/test": "^1.61.1",
|
"@playwright/test": "^1.61.1",
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e;
|
|
||||||
|
|
||||||
PORT=14180
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
--port|-p) PORT="$2"; shift 2 ;;
|
|
||||||
*) echo "Unknown option: $1"; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
pnpm exec opencode attach "http://localhost:${PORT}";
|
|
||||||
@ -12,8 +12,8 @@ set -euo pipefail
|
|||||||
# branch shares its tip commit with the target (not yet diverged).
|
# branch shares its tip commit with the target (not yet diverged).
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# tools/detect-target-branch # print branch name
|
# scripts/detect-target-branch # print branch name
|
||||||
# tools/detect-target-branch --verbose # print "branch~N"
|
# scripts/detect-target-branch --verbose # print "branch~N"
|
||||||
#
|
#
|
||||||
# Exit status:
|
# Exit status:
|
||||||
# 0 — target found
|
# 0 — target found
|
||||||
13
scripts/fmt
13
scripts/fmt
@ -1,13 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
cljfmt --parallel=true fix \
|
|
||||||
common/src/ \
|
|
||||||
common/test/ \
|
|
||||||
frontend/src/ \
|
|
||||||
frontend/test/ \
|
|
||||||
backend/src/ \
|
|
||||||
backend/test/ \
|
|
||||||
exporter/src/ \
|
|
||||||
library/src;
|
|
||||||
@ -9,20 +9,20 @@ Subcommands:
|
|||||||
prs Fetch details for one or more PRs (by number or milestone)
|
prs Fetch details for one or more PRs (by number or milestone)
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python3 tools/gh.py issues <milestone-title> (default: state=closed)
|
python3 scripts/gh.py issues <milestone-title> (default: state=closed)
|
||||||
python3 tools/gh.py issues "2.16.0" --state all
|
python3 scripts/gh.py issues "2.16.0" --state all
|
||||||
python3 tools/gh.py issues "2.16.0" --exclude "release blocker,no changelog"
|
python3 scripts/gh.py issues "2.16.0" --exclude "release blocker,no changelog"
|
||||||
python3 tools/gh.py issues "2.16.0" --label "bug" (include only issues with label)
|
python3 scripts/gh.py issues "2.16.0" --label "bug" (include only issues with label)
|
||||||
python3 tools/gh.py issues "2.16.0" --label "bug,regression" --exclude "no changelog"
|
python3 scripts/gh.py issues "2.16.0" --label "bug,regression" --exclude "no changelog"
|
||||||
python3 tools/gh.py issues "2.16.0" --compare CHANGES.md
|
python3 scripts/gh.py issues "2.16.0" --compare CHANGES.md
|
||||||
python3 tools/gh.py issues none (issues with no milestone)
|
python3 scripts/gh.py issues none (issues with no milestone)
|
||||||
python3 tools/gh.py issues none --label "enhancement"
|
python3 scripts/gh.py issues none --label "enhancement"
|
||||||
python3 tools/gh.py issues none --state open
|
python3 scripts/gh.py issues none --state open
|
||||||
python3 tools/gh.py prs 9179 9204 9311
|
python3 scripts/gh.py prs 9179 9204 9311
|
||||||
python3 tools/gh.py prs --file prs.txt
|
python3 scripts/gh.py prs --file prs.txt
|
||||||
cat prs.txt | python3 tools/gh.py prs --stdin
|
cat prs.txt | python3 scripts/gh.py prs --stdin
|
||||||
python3 tools/gh.py prs --milestone "2.16.0" (default: state=merged)
|
python3 scripts/gh.py prs --milestone "2.16.0" (default: state=merged)
|
||||||
python3 tools/gh.py prs --milestone "2.16.0" --state all
|
python3 scripts/gh.py prs --milestone "2.16.0" --state all
|
||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
- gh CLI authenticated (gh auth status)
|
- gh CLI authenticated (gh auth status)
|
||||||
@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
clj-kondo --parallel=true --lint common/src;
|
|
||||||
clj-kondo --parallel=true --lint frontend/src;
|
|
||||||
clj-kondo --parallel=true --lint backend/src;
|
|
||||||
clj-kondo --parallel=true --lint exporter/src/;
|
|
||||||
clj-kondo --parallel=true --lint library/src;
|
|
||||||
@ -323,27 +323,14 @@
|
|||||||
success-count (count successes)
|
success-count (count successes)
|
||||||
failure-count (count failures)]
|
failure-count (count failures)]
|
||||||
|
|
||||||
;; Print results
|
(doseq [{:keys [file-path success message delimiter-fixed formatted]} results]
|
||||||
(println)
|
(let [status (cond
|
||||||
(println "paren-repair Results")
|
(not success) (str "error: " message)
|
||||||
(println "========================")
|
(and delimiter-fixed formatted) "delimiter-fixed, formatted"
|
||||||
(println)
|
delimiter-fixed "delimiter-fixed"
|
||||||
|
formatted "formatted"
|
||||||
(doseq [{:keys [file-path message delimiter-fixed formatted]} results]
|
:else "no-changes")]
|
||||||
(let [tags (when (or delimiter-fixed formatted)
|
(println (str file-path ": " status))))
|
||||||
(str " ["
|
|
||||||
(string/join ", "
|
|
||||||
(filter some?
|
|
||||||
[(when delimiter-fixed "delimiter-fixed")
|
|
||||||
(when formatted "formatted")]))
|
|
||||||
"]"))]
|
|
||||||
(println (str " " file-path ": " message tags))))
|
|
||||||
|
|
||||||
(println)
|
|
||||||
(println "Summary:")
|
|
||||||
(println " Success:" success-count)
|
|
||||||
(println " Failed: " failure-count)
|
|
||||||
(println)
|
|
||||||
|
|
||||||
(if (zero? failure-count)
|
(if (zero? failure-count)
|
||||||
(System/exit 0)
|
(System/exit 0)
|
||||||
@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e;
|
|
||||||
pnpm exec opencode;
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e;
|
|
||||||
pnpm exec opencode serve --hostname 0.0.0.0 --port 14180;
|
|
||||||
@ -4,15 +4,15 @@ Taiga API client — fetch public issues, user stories, and tasks from the
|
|||||||
Penpot project (id 345963) without authentication.
|
Penpot project (id 345963) without authentication.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python3 tools/taiga.py <taiga-url>
|
python3 scripts/taiga.py <taiga-url>
|
||||||
python3 tools/taiga.py <type> <ref>
|
python3 scripts/taiga.py <type> <ref>
|
||||||
python3 tools/taiga.py [--json] <taiga-url>
|
python3 scripts/taiga.py [--json] <taiga-url>
|
||||||
python3 tools/taiga.py [--json] <type> <ref>
|
python3 scripts/taiga.py [--json] <type> <ref>
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
python3 tools/taiga.py https://tree.taiga.io/project/penpot/issue/13714
|
python3 scripts/taiga.py https://tree.taiga.io/project/penpot/issue/13714
|
||||||
python3 tools/taiga.py --json https://tree.taiga.io/project/penpot/us/14128
|
python3 scripts/taiga.py --json https://tree.taiga.io/project/penpot/us/14128
|
||||||
python3 tools/taiga.py task 13648
|
python3 scripts/taiga.py task 13648
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
Loading…
x
Reference in New Issue
Block a user