新建项目:从 goat init 到第一个模型

本指南将带你基于 goatcms-child 启动模板创建项目、配置 .env、通过 herd/dev.goat 启动环境,并添加第一个自定义实体。

要求

  • PATH 中的 Goat CLI(goat),
  • Docker(已启动),
  • Git,
  • Python 3 和 qtc——仅用于 child/*.sh 脚本,
  • Go 1.25+——可选,用于在主机上运行测试。

PostgreSQL、Node.js 和后端的 Go 编译器均在容器中运行。

1. 创建项目

goat init ./my-app \
  --template goatcms-child \
  --name my-app \
  --git-repo https://github.com/acme/my-app.git
标志含义
--template内置模板。goatcms-child 是一个小型应用启动模板:登录、管理面板、页面、示例 note 模型、Angular 前端。默认的 goatcms 是一个包含博客、文档和商店的完整服务。
--name项目名称(字母、数字、.、_、-)。必填。
--git-repo仓库地址。必填;将被设置为 origin。
--default-language、--supported-languages应用语言(默认 en 和 pl,en,de)。
--terminal-prefix终端变量前缀(默认 APP)。

目标目录必须为空或不存在。goat init 会复制模板、执行 git init 并添加 origin——不会构建或下载任何内容。

goatcms-child 启动模板目前保留原始 Go 模块路径(code.pozoga.eu/spozoga/goatcms.com)和二进制文件名 goatcms。项目无需修改即可运行;如果你想更改它们,请在项目变大之前立即在 go.mod、--go-prefix 中的 herd/_model.goat,以及 child/ 中的导入路径中完成修改。

立即创建第一次提交——这样后续每次重新生成时都能在 git diff 中清晰查看。

2. 配置 .env

cd my-app
cp .env.example .env

至少填写 GOAT_JWT_SECRET_KEY,例如使用以下命令生成的值:

openssl rand -hex 32

最重要的变量:

变量示例说明
GOAT_DEV_APP_PORT8091开发环境中的应用端口。
GOAT_HOST:8091监听地址;应与端口保持一致。
GOAT_URL_BASEhttp://localhost:8091公开地址——用于邮件中的链接和 SEO。
GOAT_DOMAINlocalhost环境域名(不含协议和端口)。
GOAT_DB_MAIN_HOST / PORTlocalhost / 55432开发数据库。每个项目使用不同端口。
GOAT_DB_MAIN_NAME / USER / PASSstarter所创建 PostgreSQL 容器的凭据。
GOAT_JWT_SECRET_KEY随机十六进制值必填。更改后会使会话失效。
GOAT_DIR_TMP、GOAT_DIR_DATA、GOAT_DIR_DIST、GOAT_DIR_SHARED_TMP./data 等工作目录;保留默认值。
GOAT_SMTP_*空可选。未配置 SMTP 时不会发送邮件。
GOAT_AI_OPENAI_API_KEY、GOAT_AI_OPENAI_MODEL空、gpt-4o-mini可选——在面板中启用 AI 助手。

由 Goat 脚本执行的提交和推送所需凭据请保存在 .private.env 中(格式:.example.private.env)。

.env 和 .private.env 绝不能提交到仓库。若同时运行多个项目,请一并修改 GOAT_DEV_APP_PORT、GOAT_HOST、GOAT_URL_BASE 和 GOAT_DB_MAIN_PORT。

更多信息:[环境配置](/doc/zh/environment-configuration)。

3. 通过 herd/dev.goat 启动项目

goat run:script --path herd/dev.goat

脚本读取 .env 并启动 child/dev/runtime.goat,其会:

  1. 执行 re —— 从 herd/_modules.goat 获取模块到 .goat/modules/,并生成 goatapp/,
  2. 并行启动三个任务:
  • 数据库 —— 在 GOAT_DB_MAIN_PORT 上运行 PostgreSQL 17,数据存储在 .cache/postgres,
  • 后端 —— 在 Go 容器中构建二进制文件,等待数据库,执行 db:migrate、child:db:migrate,加载 herd/fixture.goat,并启动 serve,
  • 前端 —— 安装依赖并根据 herd/dev/frontends.json 启动 Angular 监视器(管理面板、child-app、foundation 元素)。

首次启动需要几分钟(Docker 镜像、npm 依赖、首次 Angular 构建)。请等待监视器完成首次构建后再打开前端。

启动后:

地址内容
http://localhost:8091/公开页面(SSR),波兰语版本位于 /pl
http://localhost:8091/app/管理面板
http://localhost:8091/child/项目自定义 Angular 应用

fixture 中的开发账户:admin / starter-dev-123 和 user / starter-dev-123。将实例提供给他人之前,请先修改密码。

实用说明:

  • 数据库会在多次启动之间保留;仅执行缺失的迁移。每次后端启动时都会加载 fixture,因此它们必须是幂等的。
  • herd/dev.goat 仅在启动时生成一次代码。修改模型后,请停止脚本(Ctrl+C)并重新运行。
  • 如果希望在模型、模板和后端变更后自动重启,请使用 supervisor:bash child/setup.sh(仅一次),然后执行 bash child/dev.sh。Supervisor 使用自己的 goat-starter-<port> 容器并占用同一端口——不要同时运行两种变体(docker stop goat-starter-55432)。
  • “端口已被占用”错误几乎总是意味着另一个项目或另一个开发变体占用了同一端口。

4. 各部分所在位置

herd/_model.goat        模型:实体、角色、模块、仪表盘
herd/_modules.goat      框架模块(goatcore、goatcms)
herd/fixtures/          由 herd/fixture.goat 加载的示例数据
child/app/              你的 Go 代码:布局、路由、服务、命令、迁移
child/web/              你的 Angular 应用(/child/)
child/static/raw/       你的静态资源(主题 CSS、图片)
goatapp/                生成的代码——绝不要手动编辑

划分原则:由模型决定的内容在 herd/ 中描述;应用特有的内容在 child/ 中编写。goatapp/ 中的所有内容都会在下一次 goat re 时被覆盖。

5. 第一个自定义实体

在 herd/_model.goat 中添加一个分配给用户的任务实体:

entity:add --name=task --label=title --base=base_entity --doc=<<DOCEOF
    A task assigned to a team member.
DOCEOF --properties=<<PROPERTIESEOF
    def --write=admin,manager --read=admin,manager,user
    add --name=title --type=short_text --required --doc="Short task name."
    add --name=description --type=short_text
    add --name=due_at --type=date_time
    add --name=done --type=bool
PROPERTIESEOF --relations=<<RELATIONSEOF
    def --onremove
    add --name=assignee --to=user --doc="User responsible for the task."
RELATIONSEOF --system=<<ENTITYSYSTEMEOF
    module:add --name=crud --property:list:list="title,due_at,done" --property:list:persist="title,description,due_at,done"
    module:add --name=crud_cli --property:list:list="title,done" --property:list:persist="title,description,due_at,done"
    module:add --name=harness --property:list:list="title,done" --property:list:persist="title,description,due_at,done"
ENTITYSYSTEMEOF

在仪表盘导航中添加链接(位于 app:module:add --name=dashboard 的 --navigation 块中):

link:add --label="Tasks" --entity=task

模块提供的功能:

  • crud —— 管理面板 /app/ 中的 API 和表单,
  • crud_cli —— crud:task:* 命令,包括用于 fixture 的 crud:task:persist,
  • harness —— 此实体的 AI 助手工具(如果不希望向 AI 提供数据,请跳过),
  • seo 和 ssr —— 仅用于公开内容(示例:page 实体)。

然后:

goat re                 # regeneracja goatapp/
git diff                # przejrzyj zmiany w herd/ i child/

在现有数据库上生成模型迁移,检查 SQL 并重启 herd/dev.goat:

goat run:script --path=@goatcms/herd/db/migration_generate.goat

迁移会进入 child/app/migrations/sql/0000_goatmigrations/。检查类型转换、删除数据的操作以及表锁。应用的手写 SQL 请放在 0001_childigrations/ 中。

最后添加示例数据,例如 herd/fixtures/tasks/fixture.goat:

crud:task:persist --title="Prepare release notes" --done=false

并在 herd/fixture.goat 中将其挂接:

run:script --path herd/fixtures/tasks/fixture.goat

完整模型语法:[数据模型与代码生成](/doc/zh/data-model-and-code-generation)。

最佳实践

模型

  • 从模型开始。在 herd/_model.goat 中描述实体、字段、关系、权限和列列表,而不是在手写代码中描述。
  • 为每个实体和字段添加 --doc。这些描述会进入代码、API 和 AI 助手。
  • 在 def --write=... --read=... 中显式设置权限。不要依赖默认值。
  • 在适用时使用语义类型(web_slug、seo_description、language、image、block_content)而非通用的 short_text——它们可提供验证和合适的控件。
  • 将公共字段提取到基类(entity:base:add),并通过 entity:extended 继承,例如 content → page。
  • 在模型中声明唯一性(--unique 或 unique:add --fields=lang,slug)——幂等 fixture persist 依赖于此。
  • 以小步骤修改模型:一次修改 → goat re → git diff → 测试 → 提交。

代码

  • 绝不要编辑 goatapp/。如果生成的代码不合适,请修改模型或 herd/gen/ 中的模板。
  • 在 child/ 中编写业务逻辑、自定义路由和服务,并使用完整包路径导入 foundation。
  • 提交 herd/ 和 child/;goatapp/ 是生成的,并被 Git 忽略。

数据库和数据

  • 不要更改已应用迁移的名称或内容——请新增迁移。
  • 仅通过带有唯一键的 crud:*:persist 编写 fixture,以便重复运行时得到相同状态。
  • 开发用 fixture(账户、密码)绝不能进入生产环境。

环境

  • 一个项目——在 .env 中使用一套端口。
  • 密钥仅存放于 .env / .private.env,绝不放入仓库。
  • 提交前运行测试:goat run:script --path=herd/test.goat。

后续步骤

  • [项目架构](/doc/zh/project-architecture)
  • [数据模型与代码生成](/doc/zh/data-model-and-code-generation)
  • [数据库与迁移](/doc/zh/database-and-migrations)
  • [测试数据与 fixture](/doc/zh/test-data-and-fixtures)
  • [harness 模块:AI 助手](/doc/zh/harness-module)