Agent Skillsgrowilabs/growi › app-commands

app-commands

GitHub

提供GROWI主应用apps/app的专用命令与脚本参考,涵盖质量检查、数据库迁移、OpenAPI生成及版本管理等开发辅助功能。

apps/app/.claude/skills/app-commands/SKILL.md growilabs/growi

触发场景

需要运行应用特定构建或测试命令时 执行数据库迁移或管理版本时

安装

npx skills add growilabs/growi --skill app-commands -g -y
更多选项

非标准路径

npx skills add https://github.com/growilabs/growi/tree/master/apps/app/.claude/skills/app-commands -g -y

不安装直接使用

npx skills use growilabs/growi@app-commands

指定 Agent (Claude Code)

npx skills add growilabs/growi --skill app-commands -a claude-code -g -y

安装 repo 全部 skill

npx skills add growilabs/growi --all -g -y

预览 repo 内 skill

npx skills add growilabs/growi --list

SKILL.md

Frontmatter
{
    "name": "app-commands",
    "description": "GROWI main application (apps\/app) specific commands and scripts. Auto-invoked when working in apps\/app.",
    "user-invocable": false
}

App Commands (apps/app)

Commands specific to the main GROWI application. For global commands (turbo, pnpm), see the global tech-stack skill.

Quality Check Commands

IMPORTANT: Distinguish between Turborepo tasks and package-specific scripts.

Turbo Tasks vs Package Scripts

Task Turborepo (turbo.json) Package Script (package.json)
lint ✅ Yes ✅ Yes (runs all lint:*)
test ✅ Yes ✅ Yes
build ✅ Yes ✅ Yes
lint:typecheck ❌ No ✅ Yes
lint:biome ❌ No ✅ Yes
lint:styles ❌ No ✅ Yes

Recommended Commands

# Run ALL quality checks (uses Turborepo caching)
turbo run lint --filter @growi/app
turbo run test --filter @growi/app
turbo run build --filter @growi/app

# Run INDIVIDUAL lint checks (package-specific scripts, from apps/app directory)
pnpm run lint:typecheck   # TypeScript only
pnpm run lint:biome       # Biome only
pnpm run lint:styles      # Stylelint only

Running individual test files: See the testing rule (.claude/rules/testing.md).

Quick Reference

Task Command
Migration pnpm run dev:migrate
OpenAPI generate pnpm run openapi:generate-spec:apiv3
REPL console pnpm run console
Visual regression pnpm run reg:run
Version bump pnpm run version:patch

Database Migration

# Run pending migrations
pnpm run dev:migrate

# Check migration status
pnpm run dev:migrate:status

# Apply migrations
pnpm run dev:migrate:up

# Rollback last migration
pnpm run dev:migrate:down

# Production migration
pnpm run migrate

Note: Migrations use migrate-mongo. Files are in config/migrate-mongo/.

Creating a New Migration

# Create migration file manually in config/migrate-mongo/
# Format: YYYYMMDDHHMMSS-migration-name.js

# Test migration cycle
pnpm run dev:migrate:up
pnpm run dev:migrate:down
pnpm run dev:migrate:up

OpenAPI Commands

# Generate OpenAPI spec for API v3
pnpm run openapi:generate-spec:apiv3

# Validate API v3 spec
pnpm run lint:openapi:apiv3

# Generate operation IDs
pnpm run openapi:build:generate-operation-ids

Generated specs output to tmp/openapi-spec-apiv3.json.

Style Pre-build (Vite)

# Development mode
pnpm run dev:pre:styles-commons
pnpm run dev:pre:styles-components

# Production mode
pnpm run pre:styles-commons
pnpm run pre:styles-commons-components

Pre-builds SCSS styles into CSS bundles using Vite.

Debug & Utility

REPL Console

pnpm run console
# or
pnpm run repl

Interactive Node.js REPL with Mongoose models loaded. Useful for debugging database queries.

Visual Regression Testing

pnpm run reg:run

Version Commands

# Bump patch version (e.g., 7.4.3 → 7.4.4)
pnpm run version:patch

# Create prerelease (e.g., 7.4.4 → 7.4.5-RC.0)
pnpm run version:prerelease

# Create preminor (e.g., 7.4.4 → 7.5.0-RC.0)
pnpm run version:preminor

Build Measurement

# Measure module count KPI (cleans .next, starts next dev, triggers compilation)
./bin/measure-chunk-stats.sh           # default port 3099
./bin/measure-chunk-stats.sh 3001      # custom port

Output: [ChunkModuleStats] initial: N, async-only: N, total: N

For details on module optimization and baselines, see the build-optimization skill.

Production

# Start server (after build)
pnpm run server

# Start for CI environments
pnpm run server:ci

Note: preserver hook automatically runs migrations before starting.

CI/CD

# Launch dev server for CI
pnpm run launch-dev:ci

# Start production server for CI
pnpm run server:ci

Environment Variables

Development uses dotenv-flow:

  • .env - Default values
  • .env.local - Local overrides (not committed)
  • .env.development - Development-specific
  • .env.production - Production-specific

See .env.example for available variables.

Smoke Testing

The devcontainer always has MongoDB and other services running (see .claude/rules/devcontainer.md). The dev server can and should be started for smoke verification — never claim the runtime environment is unavailable.

Workflow

Step 1 — Override env vars without touching committed files

Create apps/app/.env.development.local (highest dotenv-flow priority; gitignored):

# Example: disable vault feature to test 404 behaviour
cat > apps/app/.env.development.local << 'EOF'
VAULT_ENABLED=false
EOF

dotenv-flow load order (first definition wins):

  1. .env.development.local ← your override
  2. .env.local
  3. .env.development ← committed defaults
  4. .env

Note: nodemon watches *.* but does not reliably pick up dotfile changes (files starting with .). After editing .env.development.local, kill the server process manually so nodemon restarts it with the new env:

kill $(ss -tlnp | grep ':3000' | grep -o 'pid=[0-9]*' | cut -d= -f2)

Step 2 — Start the dev server in background

turbo run dev --filter @growi/app &

Wait for the ready message:

until curl -s http://localhost:3000/ > /dev/null 2>&1; do sleep 1; done
echo "Server ready"

Or watch the log for Express server is listening on port 3000.

Step 3 — Curl the endpoints

# Feature disabled → 404 (no Retry-After)
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/_vault/repo.git/info/refs?service=git-upload-pack

# Push attempt → always 403
curl -s -o /dev/null -w "%{http_code}" -X POST http://localhost:3000/_vault/repo.git/git-receive-pack

# Check response body
curl -s http://localhost:3000/_vault/repo.git/info/refs?service=git-upload-pack

# Check specific headers
curl -sI http://localhost:3000/_vault/repo.git/info/refs?service=git-upload-pack | grep -i retry-after

Step 4 — Switch env and retest

Edit .env.development.local, then kill and wait for nodemon to restart:

echo "VAULT_ENABLED=true" > apps/app/.env.development.local
kill $(ss -tlnp | grep ':3000' | grep -o 'pid=[0-9]*' | cut -d= -f2)
until curl -s http://localhost:3000/ > /dev/null 2>&1; do sleep 1; done

Step 5 — Manipulate MongoDB state if needed

node -e "
const { MongoClient } = require('/workspace/growi-vault/node_modules/.pnpm/mongodb@6.8.0_@aws-sdk+credential-providers@3.600.0_@aws-sdk+client-sso-oidc@3.600.0__socks@2.8.3/node_modules/mongodb');
async function main() {
  const client = new MongoClient('mongodb://mongo:27017/growi?replicaSet=rs0');
  await client.connect();
  // e.g. reset bootstrap state
  await client.db('growi').collection('vault_sync_state').updateOne(
    { _id: 'singleton' },
    { \$set: { bootstrapState: 'pending' } },
    { upsert: true }
  );
  await client.close();
}
main().catch(console.error);
"

Step 6 — Stop the server

kill $(pgrep -f "nodemon|src/server/app.ts") 2>/dev/null

What counts as a passing smoke test

  • The Express server starts without throwing on import (Express server is listening on port 3000 in logs)
  • Feature-flag–gated endpoints return the correct status code for each flag state (404 when disabled, 503 with the right message when bootstrap incomplete, 403 for read-only enforcement)
  • No unhandled exception in server startup logs

Authorization Regression Check

Three capture tools freeze the apiv3 authorization surface so a refactor can be proven not to have moved it. Run them when a change touches middleware order, route registration, the auth chain, or after a large merge — a dropped guard is invisible to build, lint and unit tests. Baselines are committed under tools/authz-matrix/baselines/.

cd apps/app                        # requires MongoDB (devcontainer) and a free port 3000
pnpm run authz:capture-routes      # structural: (method, path, middlewareNames[]) per apiv3 leaf
pnpm run authz:capture-matrix      # black-box: HTTP status per endpoint × 4 personas
pnpm run authz:capture-ws          # WebSocket: /yjs + socket.io, 3 session cases each

Each writes to its default baseline path under tools/authz-matrix/baselines/; pass -- --out=<path> to write elsewhere (authz:capture-matrix also takes -- --in=<path> for the structural snapshot it derives its endpoint list from).

How to use it: capture on the base commit, apply your change, re-capture, and git diff the baseline files. Any difference inside the entries / matrix arrays is a potential authorization change and must be explained; the envelope metadata (capturedAt, git, node) changes on every run and is not signal. Adding -- --verify-determinism re-runs a capture twice and asserts the output is stable — do that before trusting a diff.

Properties worth knowing:

  • The structural walker fails if any middleware layer is anonymous, because an unnamed handler makes every slot look identical and destroys the diff. Fix the source (name the function the middleware factory returns); do not weaken the tool. The terminal route-body slot is exempt (~260 inline arrow handlers are pinned to their (path, method) slot), so "no anonymous" means no anonymous chain middleware slot.
  • The black-box matrix records the observed status code, not business-logic validity — a 400 from a missing request body after the auth gate passed is fine and deterministic.
  • Persona injection is mounted where passport.session() sits, so the matrix exercises the route-level chain (accessTokenParserloginRequiredadminRequired → handler) but not passport's own cookie parsing. Cover that with E2E.
  • WebSocket endpoints never appear in app._router.stack, which is why the third tool exists: the structural snapshot structurally cannot see /yjs/<pageId> or the socket.io namespace middleware.

External Plugin Install Smoke

GROWI installs third-party plugins as prebuilt assets (download → validate the growiPlugin directive → serve dist/ statically, or scan templates server-side). None of that path runs during build, server:ci, or the usual E2E, so it must be smoke-tested by hand whenever a change touches the plugin install route factory, /static/plugins serving, the _document script/stylesheet injection, the Vite manifest reader, or the published @growi/pluginkit format.

Reference plugins — one per type, and the two manifest formats the reader supports:

Type Repository (growilabs/…) Manifest
script growi-plugin-datatables Vite 4 (dist/manifest.json)
theme growi-plugin-theme-vivid-internet Vite 5 (dist/.vite/manifest.json)
template growi-plugin-templates-for-marketing — (scanned server-side)

Procedure: boot the production artifact, issue an admin access token with read:admin:plugin / write:admin:plugin, then POST /_api/v3/plugins with { pluginInstallerForm: { url, ghBranch: 'main' } } for each. It passes when:

  1. GET /_api/v3/plugins is 200 with a token and 403 without one (the route factory and its auth chain are alive in the production output).
  2. growiplugins documents are created with the right meta.types; the theme grows themes[] metadata and the template grows templateSummaries[].
  3. retrieveAllPluginResourceEntries() returns the script's JS/CSS entries and the SSR HTML of a real page contains the matching <script type="module"> / <link rel="stylesheet">.
  4. Those asset URLs return HTTP 200 from /static/plugins/… with the right content type.

Clean up afterwards: delete the smoke access token, and the growiplugins documents plus tmp/plugins/growilabs/* if you do not want the installs to persist.

Troubleshooting

Boot Crash Diagnosis

  • pino swallows the last log line. The async transport can lose a logger.error(err) written immediately before process.exit(1), so a boot crash exits silently. Temporarily add console.error(err) to the main() catch in src/server/app.ts to see the stack.
  • nodemon keeps running after "app crashed" and restarts on the next file edit, so a stale dev server can answer 200 on :3000 and fake a passing smoke. Confirm the port is free before starting, and attribute every response to the process you actually launched.

Migration Issues

pnpm run dev:migrate:status   # Check status
pnpm run dev:migrate:down     # Rollback
pnpm run dev:migrate:up       # Re-apply

Build Issues

pnpm run clean                # Clear artifacts
pnpm run build                # Rebuild

版本历史

  • 0dc62d2 当前 2026-08-20 19:46

同 Skill 集合

.claude/skills/detect-flaky-ci/SKILL.md
.claude/skills/essential-test-design/SKILL.md
.claude/skills/essential-test-patterns/SKILL.md
.claude/skills/investigate-flaky-test/SKILL.md
.claude/skills/kiro-debug/SKILL.md
.claude/skills/kiro-discovery/SKILL.md
.claude/skills/kiro-impl/SKILL.md
.claude/skills/kiro-review/SKILL.md
.claude/skills/kiro-spec-batch/SKILL.md
.claude/skills/kiro-spec-cleanup/SKILL.md
.claude/skills/kiro-spec-design/SKILL.md
.claude/skills/kiro-spec-init/SKILL.md
.claude/skills/kiro-spec-quick/SKILL.md
.claude/skills/kiro-spec-requirements/SKILL.md
.claude/skills/kiro-spec-tasks/SKILL.md
.claude/skills/kiro-steering-custom/SKILL.md
.claude/skills/kiro-steering/SKILL.md
.claude/skills/kiro-validate-design/SKILL.md
.claude/skills/kiro-validate-gap/SKILL.md
.claude/skills/kiro-validate-impl/SKILL.md
.claude/skills/kiro-verify-completion/SKILL.md
.claude/skills/mongoose-to-prisma/SKILL.md
apps/app/.claude/skills/app-architecture/SKILL.md
apps/app/.claude/skills/app-specific-patterns/SKILL.md
apps/app/.claude/skills/build-optimization/SKILL.md
apps/app/.claude/skills/next-express-route-consistency/SKILL.md
apps/app/.claude/skills/vendor-styles-components/SKILL.md
.claude/skills/kiro-spec-status/SKILL.md
.claude/skills/suggest-path-evaluator/SKILL.md
apps/app/.claude/skills/esm-merge-coverage/SKILL.md

元信息

文件数
0
版本
b2fbe5b
Hash
7e122c9d
收录时间
2026-08-20 19:46

首页 - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-21 16:50
浙ICP备14020137号-1