agentme 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/agents/speckit.analyze.agent.md +184 -0
- package/.github/agents/speckit.checklist.agent.md +295 -0
- package/.github/agents/speckit.clarify.agent.md +181 -0
- package/.github/agents/speckit.constitution.agent.md +84 -0
- package/.github/agents/speckit.implement.agent.md +198 -0
- package/.github/agents/speckit.plan.agent.md +90 -0
- package/.github/agents/speckit.specify.agent.md +237 -0
- package/.github/agents/speckit.tasks.agent.md +200 -0
- package/.github/agents/speckit.taskstoissues.agent.md +30 -0
- package/.github/prompts/speckit.analyze.prompt.md +3 -0
- package/.github/prompts/speckit.checklist.prompt.md +3 -0
- package/.github/prompts/speckit.clarify.prompt.md +3 -0
- package/.github/prompts/speckit.constitution.prompt.md +3 -0
- package/.github/prompts/speckit.implement.prompt.md +3 -0
- package/.github/prompts/speckit.plan.prompt.md +3 -0
- package/.github/prompts/speckit.specify.prompt.md +3 -0
- package/.github/prompts/speckit.tasks.prompt.md +3 -0
- package/.github/prompts/speckit.taskstoissues.prompt.md +3 -0
- package/.specify/memory/constitution.md +119 -0
- package/.specify/scripts/bash/check-prerequisites.sh +190 -0
- package/.specify/scripts/bash/common.sh +253 -0
- package/.specify/scripts/bash/create-new-feature.sh +333 -0
- package/.specify/scripts/bash/setup-plan.sh +73 -0
- package/.specify/scripts/bash/update-agent-context.sh +808 -0
- package/.specify/templates/agent-file-template.md +28 -0
- package/.specify/templates/checklist-template.md +40 -0
- package/.specify/templates/constitution-template.md +50 -0
- package/.specify/templates/plan-template.md +110 -0
- package/.specify/templates/spec-template.md +115 -0
- package/.specify/templates/tasks-template.md +251 -0
- package/.vscode/settings.json +14 -0
- package/.xdrs/agentme/edrs/application/003-javascript-project-tooling.md +89 -0
- package/.xdrs/agentme/edrs/application/010-golang-project-tooling.md +141 -0
- package/.xdrs/agentme/edrs/application/skills/001-create-javascript-project/SKILL.md +360 -0
- package/.xdrs/agentme/edrs/application/skills/003-create-golang-project/SKILL.md +311 -0
- package/.xdrs/agentme/edrs/devops/005-monorepo-structure.md +104 -0
- package/.xdrs/agentme/edrs/devops/006-github-pipelines.md +170 -0
- package/.xdrs/agentme/edrs/devops/008-common-targets.md +207 -0
- package/.xdrs/agentme/edrs/devops/skills/002-monorepo-setup/SKILL.md +270 -0
- package/.xdrs/agentme/edrs/index.md +41 -0
- package/.xdrs/agentme/edrs/observability/011-service-health-check-endpoint.md +78 -0
- package/.xdrs/agentme/edrs/principles/002-coding-best-practices.md +110 -0
- package/.xdrs/agentme/edrs/principles/004-unit-test-requirements.md +97 -0
- package/.xdrs/agentme/edrs/principles/007-project-quality-standards.md +156 -0
- package/.xdrs/agentme/edrs/principles/009-error-handling.md +327 -0
- package/.xdrs/index.md +32 -0
- package/README.md +119 -0
- package/bin/npmdata.js +3 -0
- package/package.json +102 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 003-create-golang-project
|
|
3
|
+
description: >
|
|
4
|
+
Scaffolds the initial boilerplate structure for a Go (Golang) CLI or library project following
|
|
5
|
+
the standard tooling and layout defined in agentme-edr-010. Activate this skill when the user
|
|
6
|
+
asks to create, scaffold, or initialize a new Go project, CLI binary, or Go module.
|
|
7
|
+
metadata:
|
|
8
|
+
author: flaviostutz
|
|
9
|
+
version: "1.0"
|
|
10
|
+
compatibility: Go 1.21+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
Creates a complete Go CLI project from scratch, following the layout from [agentme-edr-010](../../010-golang-project-tooling.md). Business logic lives in named feature packages; CLI wiring lives in `cli/<feature>/`; `main.go` is a thin dispatcher. The project builds cross-platform static binaries via a Makefile.
|
|
16
|
+
|
|
17
|
+
Related EDR: [agentme-edr-010](../../010-golang-project-tooling.md)
|
|
18
|
+
|
|
19
|
+
## Instructions
|
|
20
|
+
|
|
21
|
+
### Phase 1: Gather information
|
|
22
|
+
|
|
23
|
+
Ask for (or infer from context):
|
|
24
|
+
|
|
25
|
+
- **Module name** — Go module path, e.g. `github.com/<owner>/<project>` (in a monorepo this is typically the workspace module path plus the project subdirectory)
|
|
26
|
+
- **Binary name** — name of the produced CLI binary (default: project name)
|
|
27
|
+
- **Short description** — one sentence
|
|
28
|
+
- **Author** name or GitHub username
|
|
29
|
+
- **Go version** — default `1.24`
|
|
30
|
+
- **First feature package name** — the first domain package to scaffold (e.g. `analyze`, `parse`, `report`)
|
|
31
|
+
- **First CLI subcommand name** — typically matches the feature package (e.g. `analyze`)
|
|
32
|
+
- **Confirm target directory** — default: current workspace root
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
### Phase 2: Create root files
|
|
37
|
+
|
|
38
|
+
**`go.mod`** (replace `[module]`, `[go-version]`):
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
module [module]
|
|
42
|
+
|
|
43
|
+
go [go-version]
|
|
44
|
+
|
|
45
|
+
require (
|
|
46
|
+
github.com/sirupsen/logrus v1.9.3
|
|
47
|
+
github.com/stretchr/testify v1.9.0
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**`main.go`** (replace `[module]`, `[subcommand]`, `[feature]`):
|
|
52
|
+
|
|
53
|
+
```go
|
|
54
|
+
package main
|
|
55
|
+
|
|
56
|
+
import (
|
|
57
|
+
"fmt"
|
|
58
|
+
"os"
|
|
59
|
+
|
|
60
|
+
cli[Feature] "[module]/cli/[feature]"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
func main() {
|
|
64
|
+
if len(os.Args) < 2 {
|
|
65
|
+
fmt.Println("Usage: [binary] [[feature]]")
|
|
66
|
+
os.Exit(1)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
switch os.Args[1] {
|
|
70
|
+
case "[subcommand]":
|
|
71
|
+
cli[Feature].Run(os.Args)
|
|
72
|
+
default:
|
|
73
|
+
fmt.Printf("Unknown command: %s\n", os.Args[1])
|
|
74
|
+
fmt.Println("Usage: [binary] [[feature]]")
|
|
75
|
+
os.Exit(1)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**`Makefile`** (replace `[binary]`):
|
|
81
|
+
|
|
82
|
+
```makefile
|
|
83
|
+
SHELL := /bin/bash
|
|
84
|
+
|
|
85
|
+
BINARY := [binary]
|
|
86
|
+
|
|
87
|
+
all: build lint test
|
|
88
|
+
|
|
89
|
+
build: install
|
|
90
|
+
@mkdir -p dist
|
|
91
|
+
go build -o dist/$(BINARY) .
|
|
92
|
+
|
|
93
|
+
build-all: build-arch-os-darwin-amd64 build-arch-os-darwin-arm64 build-arch-os-linux-amd64 build-arch-os-linux-arm64 build-arch-os-windows-amd64
|
|
94
|
+
@echo "All platform builds complete"
|
|
95
|
+
|
|
96
|
+
build-arch-os-darwin-amd64:
|
|
97
|
+
$(MAKE) build-arch-os OS=darwin ARCH=amd64
|
|
98
|
+
|
|
99
|
+
build-arch-os-darwin-arm64:
|
|
100
|
+
$(MAKE) build-arch-os OS=darwin ARCH=arm64
|
|
101
|
+
|
|
102
|
+
build-arch-os-linux-amd64:
|
|
103
|
+
$(MAKE) build-arch-os OS=linux ARCH=amd64
|
|
104
|
+
|
|
105
|
+
build-arch-os-linux-arm64:
|
|
106
|
+
$(MAKE) build-arch-os OS=linux ARCH=arm64
|
|
107
|
+
|
|
108
|
+
build-arch-os-windows-amd64:
|
|
109
|
+
$(MAKE) build-arch-os OS=windows ARCH=amd64
|
|
110
|
+
|
|
111
|
+
build-arch-os:
|
|
112
|
+
@if [ "${OS}" == "" ]; then echo "ENV OS is required"; exit 1; fi
|
|
113
|
+
@if [ "${ARCH}" == "" ]; then echo "ENV ARCH is required"; exit 1; fi
|
|
114
|
+
@echo "Compiling $(BINARY) for ${OS}-${ARCH}..."
|
|
115
|
+
@mkdir -p dist/${OS}-${ARCH}
|
|
116
|
+
go mod download
|
|
117
|
+
GOOS=${OS} GOARCH=${ARCH} CGO_ENABLED=0 go build -a -o dist/${OS}-${ARCH}/$(BINARY) .
|
|
118
|
+
@echo "Done"
|
|
119
|
+
|
|
120
|
+
install:
|
|
121
|
+
go mod download
|
|
122
|
+
|
|
123
|
+
lint:
|
|
124
|
+
golangci-lint run ./...
|
|
125
|
+
|
|
126
|
+
lint-fix:
|
|
127
|
+
golangci-lint run --fix ./...
|
|
128
|
+
|
|
129
|
+
test:
|
|
130
|
+
go test -cover ./...
|
|
131
|
+
|
|
132
|
+
test-coverage:
|
|
133
|
+
go test -coverprofile=coverage.out ./...
|
|
134
|
+
go tool cover -func coverage.out
|
|
135
|
+
|
|
136
|
+
benchmark:
|
|
137
|
+
go test -bench . -benchmem -count 5 ./...
|
|
138
|
+
|
|
139
|
+
clean:
|
|
140
|
+
rm -rf dist
|
|
141
|
+
rm -f coverage.out
|
|
142
|
+
|
|
143
|
+
start:
|
|
144
|
+
go run ./ [subcommand]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**`.golangci.yml`**:
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
linters:
|
|
151
|
+
enable:
|
|
152
|
+
- errcheck
|
|
153
|
+
- govet
|
|
154
|
+
- staticcheck
|
|
155
|
+
- unused
|
|
156
|
+
- gosimple
|
|
157
|
+
- ineffassign
|
|
158
|
+
- typecheck
|
|
159
|
+
run:
|
|
160
|
+
timeout: 5m
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**`.gitignore`**:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
dist/
|
|
167
|
+
coverage.out
|
|
168
|
+
*.pprof
|
|
169
|
+
.DS_Store
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**`README.md`** (replace `[binary]`, `[description]`, `[owner]`, `[repo]`):
|
|
173
|
+
|
|
174
|
+
```markdown
|
|
175
|
+
# [binary]
|
|
176
|
+
|
|
177
|
+
[description]
|
|
178
|
+
|
|
179
|
+
## Usage
|
|
180
|
+
|
|
181
|
+
[binary] [subcommand] --help
|
|
182
|
+
|
|
183
|
+
## Development
|
|
184
|
+
|
|
185
|
+
make build # compile binary to dist/
|
|
186
|
+
make lint # run golangci-lint
|
|
187
|
+
make test # run tests with coverage
|
|
188
|
+
make start # run locally with default args
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### Phase 3: Create the feature package
|
|
194
|
+
|
|
195
|
+
**`[feature]/[feature].go`** (replace `[feature]`, `[Feature]`):
|
|
196
|
+
|
|
197
|
+
```go
|
|
198
|
+
package [feature]
|
|
199
|
+
|
|
200
|
+
import "github.com/sirupsen/logrus"
|
|
201
|
+
|
|
202
|
+
// Options holds the parameters for [Feature] analysis.
|
|
203
|
+
type Options struct {
|
|
204
|
+
Verbose bool
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Result holds the output of a [Feature] run.
|
|
208
|
+
type Result struct {
|
|
209
|
+
Summary string
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Run executes the [Feature] logic with the given options.
|
|
213
|
+
func Run(opts Options) (Result, error) {
|
|
214
|
+
if opts.Verbose {
|
|
215
|
+
logrus.SetLevel(logrus.DebugLevel)
|
|
216
|
+
}
|
|
217
|
+
logrus.Debug("[Feature] started")
|
|
218
|
+
|
|
219
|
+
// TODO: implement business logic here
|
|
220
|
+
|
|
221
|
+
return Result{Summary: "ok"}, nil
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**`[feature]/[feature]_test.go`** (replace `[feature]`, `[Feature]`):
|
|
226
|
+
|
|
227
|
+
```go
|
|
228
|
+
package [feature]
|
|
229
|
+
|
|
230
|
+
import (
|
|
231
|
+
"testing"
|
|
232
|
+
|
|
233
|
+
"github.com/stretchr/testify/assert"
|
|
234
|
+
"github.com/stretchr/testify/require"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
func Test[Feature]Run(t *testing.T) {
|
|
238
|
+
result, err := Run(Options{Verbose: false})
|
|
239
|
+
require.NoError(t, err)
|
|
240
|
+
assert.Equal(t, "ok", result.Summary)
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
### Phase 4: Create the CLI package
|
|
247
|
+
|
|
248
|
+
**`cli/[feature]/[feature].go`** (replace `[module]`, `[feature]`, `[Feature]`, `[subcommand]`):
|
|
249
|
+
|
|
250
|
+
```go
|
|
251
|
+
package cli[Feature]
|
|
252
|
+
|
|
253
|
+
import (
|
|
254
|
+
"flag"
|
|
255
|
+
"fmt"
|
|
256
|
+
"os"
|
|
257
|
+
|
|
258
|
+
"github.com/sirupsen/logrus"
|
|
259
|
+
"[module]/[feature]"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
// Run parses CLI flags for the [subcommand] command and calls the domain package.
|
|
263
|
+
func Run(args []string) {
|
|
264
|
+
fs := flag.NewFlagSet("[subcommand]", flag.ExitOnError)
|
|
265
|
+
verbose := fs.Bool("verbose", false, "Show verbose logs during processing")
|
|
266
|
+
|
|
267
|
+
if err := fs.Parse(args[2:]); err != nil {
|
|
268
|
+
fmt.Fprintf(os.Stderr, "error parsing flags: %v\n", err)
|
|
269
|
+
os.Exit(1)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if *verbose {
|
|
273
|
+
logrus.SetLevel(logrus.DebugLevel)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
result, err := [feature].Run([feature].Options{
|
|
277
|
+
Verbose: *verbose,
|
|
278
|
+
})
|
|
279
|
+
if err != nil {
|
|
280
|
+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
|
281
|
+
os.Exit(1)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
fmt.Println(result.Summary)
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
### Phase 5: Verify and run
|
|
291
|
+
|
|
292
|
+
After creating all files, run the following in the project root:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
go mod tidy
|
|
296
|
+
make all
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Fix any compile or lint errors before finishing.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Conventions and reminders
|
|
304
|
+
|
|
305
|
+
- `main.go` dispatches only — no logic.
|
|
306
|
+
- Business logic only in `[feature]/` packages — no flag parsing, no `fmt.Println` for diagnostics.
|
|
307
|
+
- `cli/[feature]/` owns flags, output, and calls domain. No logic here beyond reading flags and printing results.
|
|
308
|
+
- All tests co-located (`*_test.go` next to the file under test).
|
|
309
|
+
- Log with `logrus`; never use `fmt.Println` for diagnostic/debug output.
|
|
310
|
+
- All development tasks go through `make` targets — never run `go` directly for routine ops.
|
|
311
|
+
- Do not create an `internal/` package unless explicitly justified (importability is preferred).
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# agentme-edr-005: Monorepo structure
|
|
2
|
+
|
|
3
|
+
## Context and Problem Statement
|
|
4
|
+
|
|
5
|
+
Without a defined monorepo layout, teams independently organize projects in ways that are inconsistent, hard to navigate, and difficult to build uniformly. Shared code gets duplicated, tooling varies per project, and onboarding new contributors is slow because there is no standard entry point or build convention.
|
|
6
|
+
|
|
7
|
+
What monorepo structure, naming conventions, tooling, and build standards should be followed to keep multiple projects cohesive, discoverable, and easy to build?
|
|
8
|
+
|
|
9
|
+
## Decision Outcome
|
|
10
|
+
|
|
11
|
+
**Adopt a standardized monorepo layout with top-level application folders, a shared library area, Mise-managed tooling, and Makefiles at every level so any contributor can build, lint, and test any part of the monorepo with a single, predictable command.**
|
|
12
|
+
|
|
13
|
+
For step-by-step scaffolding instructions see [skill 002-monorepo-setup](skills/002-monorepo-setup/SKILL.md).
|
|
14
|
+
|
|
15
|
+
### Policies
|
|
16
|
+
|
|
17
|
+
#### 1. Top-level directory layout
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
/
|
|
21
|
+
├── shared/ # Resources shared across ALL applications
|
|
22
|
+
│ ├── libs/ # Reusable libraries consumed by applications
|
|
23
|
+
│ └── scripts/ # Build/CI/dev scripts used across applications
|
|
24
|
+
│
|
|
25
|
+
├── <application>/ # One folder per application or project
|
|
26
|
+
│ ├── README.md # REQUIRED
|
|
27
|
+
│ ├── <module>/ # One folder per compilable module
|
|
28
|
+
│ └── shared/ # Resources shared by modules within THIS application
|
|
29
|
+
│
|
|
30
|
+
├── Makefile # Root Makefile coordinating all areas
|
|
31
|
+
├── README.md # REQUIRED — onboarding and quickstart guide
|
|
32
|
+
└── .mise.toml # Mise tool version configuration
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### 2. Application folders
|
|
36
|
+
|
|
37
|
+
- Represent a cohesive unit with its own lifecycle (e.g., `mymobileapp`, `graph-visualizer`).
|
|
38
|
+
- **MUST** depend only on resources in `/shared/`. Direct cross-application dependencies are forbidden; use published artifacts (container images, published libraries) instead.
|
|
39
|
+
- **MUST** contain a `README.md` with: purpose, architecture overview, how to build, and how to run.
|
|
40
|
+
|
|
41
|
+
*Why:* Isolating applications prevents implicit coupling and makes the `shared/` boundary explicit and intentional.
|
|
42
|
+
|
|
43
|
+
#### 3. Module folders
|
|
44
|
+
|
|
45
|
+
- A module is a subfolder inside an application that is independently compilable and produces a build artifact.
|
|
46
|
+
- May depend on sibling modules within the same application or on `/shared/` resources.
|
|
47
|
+
- **MUST NOT** depend on modules from other applications.
|
|
48
|
+
|
|
49
|
+
#### 4. Naming conventions
|
|
50
|
+
|
|
51
|
+
- All folder and file names **MUST** be **lowercase**.
|
|
52
|
+
- Use hyphens (`-`) to separate words (e.g., `data-loader`, `graph-visualizer`).
|
|
53
|
+
- Avoid abbreviations unless universally understood in the domain (e.g., `cli`, `api`).
|
|
54
|
+
|
|
55
|
+
#### 5. Makefiles at every level
|
|
56
|
+
|
|
57
|
+
A `Makefile` **MUST** be present at the repository root, in every application folder, and in every module folder.
|
|
58
|
+
|
|
59
|
+
Each Makefile **MUST** define at minimum: `build`, `lint`, and `test` targets.
|
|
60
|
+
|
|
61
|
+
The root `Makefile` **MUST** also define a `setup` target that guides a new contributor to prepare their machine.
|
|
62
|
+
|
|
63
|
+
*Why:* Makefiles provide a universal, stack-agnostic entry point regardless of programming language.
|
|
64
|
+
|
|
65
|
+
#### 6. Mise for tooling management
|
|
66
|
+
|
|
67
|
+
- [Mise](https://mise.jdx.dev/) **MUST** be used to pin all tool versions (compilers, runtimes, CLI tools).
|
|
68
|
+
- A `.mise.toml` **MUST** exist at the repository root.
|
|
69
|
+
- Every language runtime or CLI referenced by any module `Makefile`, CI workflow, or README command **MUST** be pinned in `.mise.toml`.
|
|
70
|
+
- Contributors run `mise install` once after cloning.
|
|
71
|
+
- Agents and contributors **MUST** check `.mise.toml` before using a system-installed compiler, runtime, or CLI.
|
|
72
|
+
- When `.mise.toml` exists, all build, test, lint, and code-generation commands **MUST** run inside the Mise-managed environment, preferably via `mise exec -- <command>` or an activated Mise shell.
|
|
73
|
+
- If a required tool is missing, the first remediation step **MUST** be to update `.mise.toml` or run `mise install`, not to install ad-hoc global tools with language-specific installers such as `go install`, `npm install -g`, `pip install --user`, or `cargo install`.
|
|
74
|
+
- Root and module `Makefile` targets **SHOULD** work correctly when invoked through `mise exec -- make <target>`.
|
|
75
|
+
|
|
76
|
+
*Why:* Eliminates "works on my machine" build failures by ensuring identical tool versions across all environments.
|
|
77
|
+
|
|
78
|
+
#### 7. Root README
|
|
79
|
+
|
|
80
|
+
The root `README.md` **MUST** include: overview, machine setup, quickstart, and a repository map.
|
|
81
|
+
|
|
82
|
+
#### 8. Git tagging and artifact versioning
|
|
83
|
+
|
|
84
|
+
All releases **MUST** be tagged using the format `<module-name>/<semver>` (e.g., `graphvisualizer/renderer/1.0.0`, `shared/libs/mylib/2.1.0`).
|
|
85
|
+
|
|
86
|
+
`<module-name>` is preferably the path-like identifier of the module being released. A custom name is allowed but the folder name is strongly preferred.
|
|
87
|
+
|
|
88
|
+
*Why:* Namespacing tags by module prevents collisions and makes it easy to filter release history when multiple modules release independently.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
#### 11. Summary of requirements
|
|
93
|
+
|
|
94
|
+
| Requirement | Scope | Mandatory |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| Lowercase folder/file names | All | Yes |
|
|
97
|
+
| `README.md` per application | Application folders | Yes |
|
|
98
|
+
| `Makefile` with `build`, `lint`, `test` | All modules and applications | Yes |
|
|
99
|
+
| Root `Makefile` with `setup` target | Repository root | Yes |
|
|
100
|
+
| Root `README.md` with setup + quickstart | Repository root | Yes |
|
|
101
|
+
| Mise `.mise.toml` at root | Repository root | Yes |
|
|
102
|
+
| Applications depend only on `/shared/` | Application folders | Yes |
|
|
103
|
+
| Modules depend only on siblings or `/shared/` | Module folders | Yes |
|
|
104
|
+
| Git tags follow `<module-name>/<semver>` format | All modules | Yes |
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# agentme-edr-006: GitHub CI/CD pipelines
|
|
2
|
+
|
|
3
|
+
## Context and Problem Statement
|
|
4
|
+
|
|
5
|
+
Without a defined GitHub Actions pipeline structure, projects end up with inconsistent workflows — some combine CI and publishing in a single file, others trigger builds on wrong events, and release tagging is done ad-hoc. This leads to accidental publishes, broken release histories, and non-reproducible builds.
|
|
6
|
+
|
|
7
|
+
What GitHub Actions workflows should every project follow to ensure a safe, predictable, and automated CI/CD lifecycle?
|
|
8
|
+
|
|
9
|
+
## Decision Outcome
|
|
10
|
+
|
|
11
|
+
**Use three separate, purpose-scoped GitHub Actions workflows: `ci.yml` for build verification on PRs and main, `release.yml` for calculating and creating a version tag with monotag, and `publish.yml` for publishing artifacts when a version tag is pushed.**
|
|
12
|
+
|
|
13
|
+
Separating these concerns eliminates accidental publishes from CI runs, ensures monotag has access to the full git history, and makes each workflow independently auditable and re-runnable.
|
|
14
|
+
|
|
15
|
+
### Implementation Details
|
|
16
|
+
|
|
17
|
+
#### Workflow overview
|
|
18
|
+
|
|
19
|
+
| Workflow | Trigger | Purpose |
|
|
20
|
+
|----------|---------|---------|
|
|
21
|
+
| `ci.yml` | `pull_request` → `main`, `push` → `main` | Build, lint, and test the codebase |
|
|
22
|
+
| `release.yml` | `workflow_dispatch` | Tag the next version using monotag |
|
|
23
|
+
| `publish.yml` | `push` of tags matching `*` | Publish artifacts for the tagged version |
|
|
24
|
+
|
|
25
|
+
All workflows run on `ubuntu-latest`. Tool versions MUST be managed by Mise via `jdx/mise-action`. Projects should have a .mise.toml file to configure it
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
#### 1. CI workflow — `.github/workflows/ci.yml`
|
|
30
|
+
|
|
31
|
+
Triggered on every PR targeting `main` and every push to `main`. Runs the standard `build`, `lint`, and `test` targets from the root Makefile and fails the workflow if any step exits non-zero.
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
name: ci
|
|
35
|
+
|
|
36
|
+
on:
|
|
37
|
+
push:
|
|
38
|
+
branches: [main]
|
|
39
|
+
pull_request:
|
|
40
|
+
branches: [main]
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
ci:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: jdx/mise-action@v3
|
|
48
|
+
- run: make build
|
|
49
|
+
- run: make lint
|
|
50
|
+
- run: make test
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
*Why separate steps:* Separate steps surface exactly which phase failed (build, lint, or test) without requiring log inspection.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
#### 2. Release workflow — `.github/workflows/release.yml`
|
|
58
|
+
|
|
59
|
+
Manually dispatched (`workflow_dispatch`). Calculates the next semantic version tag using **monotag** and pushes that tag to the repository. Pushing the tag then automatically triggers the publish workflow.
|
|
60
|
+
|
|
61
|
+
The checkout step **must** use `fetch-depth: 0` so monotag can traverse the full commit history to determine the correct next version.
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
name: release
|
|
65
|
+
|
|
66
|
+
on:
|
|
67
|
+
workflow_dispatch:
|
|
68
|
+
inputs:
|
|
69
|
+
prerelease:
|
|
70
|
+
description: 'Pre-release'
|
|
71
|
+
type: boolean
|
|
72
|
+
default: true
|
|
73
|
+
|
|
74
|
+
jobs:
|
|
75
|
+
create-tag:
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
permissions:
|
|
78
|
+
contents: write
|
|
79
|
+
steps:
|
|
80
|
+
- uses: actions/checkout@v3
|
|
81
|
+
with:
|
|
82
|
+
fetch-depth: 0
|
|
83
|
+
# this is needed if you want the tag push to trigger another workflow
|
|
84
|
+
# create a personal token and set a secret with name GH_PERSONAL_TOKEN
|
|
85
|
+
# https://github.com/orgs/community/discussions/27028
|
|
86
|
+
token: ${{ secrets.GH_PERSONAL_TOKEN }}
|
|
87
|
+
- name: Create tag and push to repo
|
|
88
|
+
run: |
|
|
89
|
+
git config --global user.email "noreply@github.com"
|
|
90
|
+
git config --global user.name "Github Wokflow"
|
|
91
|
+
npx -y monotag@latest tag-push ${{ inputs.prerelease == true && '--pre-release' || '' }}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
*Why `workflow_dispatch`:* Manual triggering gives developers explicit control over when a new release tag is created, preventing unintended releases from routine merges.
|
|
95
|
+
|
|
96
|
+
*Why `contents: write`:* Required to allow the workflow to push the new tag back to the repository.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
#### 3. Publish workflow — `.github/workflows/publish.yml`
|
|
101
|
+
|
|
102
|
+
Triggered exclusively when a tag matching `v*.*.*` is pushed to the repository. This ensures only explicitly tagged commits produce published artifacts. Runs `make publish` against the tagged commit.
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
name: publish
|
|
106
|
+
|
|
107
|
+
on:
|
|
108
|
+
push:
|
|
109
|
+
tags:
|
|
110
|
+
- '*'
|
|
111
|
+
|
|
112
|
+
permissions:
|
|
113
|
+
contents: read
|
|
114
|
+
id-token: write
|
|
115
|
+
|
|
116
|
+
jobs:
|
|
117
|
+
publish:
|
|
118
|
+
runs-on: ubuntu-latest
|
|
119
|
+
steps:
|
|
120
|
+
- uses: actions/checkout@v4
|
|
121
|
+
- uses: jdx/mise-action@v3
|
|
122
|
+
- run: make build
|
|
123
|
+
- run: make lint
|
|
124
|
+
- run: make test
|
|
125
|
+
- run: git reset --hard HEAD
|
|
126
|
+
- run: make publish
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
*Why rebuild on publish:* The checkout is done from the exact tag commit. Rebuilding ensures the published artifact matches exactly what is tagged, rather than relying on a prior CI artifact.
|
|
131
|
+
|
|
132
|
+
*Why `id-token: write`:* Required for npm provenance attestation via `npm publish --provenance`, as specified in [agentme-edr-003](003-javascript-project-tooling.md).
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
#### Required secrets and permissions
|
|
137
|
+
|
|
138
|
+
| Secret / Permission | Used in | Purpose |
|
|
139
|
+
|--------------------|---------|---------|
|
|
140
|
+
| `GITHUB_TOKEN` (built-in) | `release.yml` | Push newly calculated tag back to the repository |
|
|
141
|
+
| `id-token: write` | `publish.yml` | Generate OIDC token for npm provenance |
|
|
142
|
+
| `contents: write` | `release.yml` | Allow the workflow job to push tags |
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
#### Relationship between workflows
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
PR opened / push to main
|
|
150
|
+
│
|
|
151
|
+
▼
|
|
152
|
+
ci.yml runs
|
|
153
|
+
(build + lint + test)
|
|
154
|
+
│
|
|
155
|
+
▼ (merge to main)
|
|
156
|
+
developer dispatches
|
|
157
|
+
release.yml manually
|
|
158
|
+
│
|
|
159
|
+
▼
|
|
160
|
+
monotag calculates
|
|
161
|
+
next semver tag
|
|
162
|
+
│
|
|
163
|
+
▼
|
|
164
|
+
tag pushed → v1.2.3
|
|
165
|
+
│
|
|
166
|
+
▼
|
|
167
|
+
publish.yml triggered
|
|
168
|
+
(build + publish artifacts)
|
|
169
|
+
```
|
|
170
|
+
|