Skip to content

Commit bca478d

Browse files
committed
[minor] feat: adopt standard compose SDK
1 parent df896d8 commit bca478d

13 files changed

Lines changed: 386 additions & 414 deletions

File tree

.github/workflows/goreleaser.yaml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,11 @@ permissions:
1212

1313
jobs:
1414
goreleaser:
15-
uses: libops/terraform-linux-packages/.github/workflows/reusable-goreleaser.yaml@4a8eafe5a90d5f811e389d2d55015287896c2a66 # main
15+
uses: libops/.github/.github/workflows/sitectl-plugin-goreleaser.yaml@main
1616
permissions:
1717
contents: write
1818
id-token: write
1919
secrets: inherit
2020
with:
2121
go-version: ">=1.26.1"
22-
publish-package-repo: true
2322
package-name: sitectl-drupal
24-
package-repo-prefix: sitectl
25-
package-repo-label: sitectl
26-
package-public-key-name: sitectl-archive-keyring
27-
gcp-project: ${{ vars.LIBOPS_PACKAGES_GCLOUD_PROJECT }}
28-
workload-identity-provider: ${{ vars.LIBOPS_PACKAGES_GCLOUD_OIDC_POOL }}
29-
service-account: ${{ vars.LIBOPS_PACKAGES_GSA }}
30-
gcs-bucket: ${{ vars.LIBOPS_PACKAGES_GCS_BUCKET }}
31-
aptly-gpg-key-id: ${{ vars.LIBOPS_PACKAGES_APTLY_GPG_KEY_ID }}
32-
aptly-gpg-private-key-secret: ${{ vars.LIBOPS_PACKAGES_APTLY_GPG_PRIVATE_KEY_SECRET }}
33-
aptly-gpg-passphrase-secret: ${{ vars.LIBOPS_PACKAGES_APTLY_GPG_PASSPHRASE_SECRET }}

AGENTS.md

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# AGENTS.md
2+
3+
Guidance for AI agents (Claude Code, etc.) working in this repository.
4+
5+
## Project Overview
6+
7+
sitectl-drupal is a [sitectl](https://github.com/libops/sitectl) plugin for Drupal websites. It adds Drupal-specific commands to sitectl via the plugin SDK, including database/files sync between contexts, drush execution, user login links, and component/extension debugging.
8+
9+
## Build, Test, and Lint Commands
10+
11+
```bash
12+
# Install dependencies and build
13+
make build
14+
15+
# Run linter (includes gofmt and golangci-lint)
16+
make lint
17+
18+
# Run all tests with race detection
19+
make test
20+
21+
# Run a specific test
22+
go test -v -race ./pkg/... -run TestSpecificTestName
23+
24+
# Run tests for a single package
25+
go test -v -race ./cmd
26+
27+
# Use Go workspaces to develop against local sitectl repo
28+
make work
29+
```
30+
31+
## Architecture
32+
33+
### Plugin Structure
34+
35+
This binary is a sitectl plugin. It is invoked by sitectl when discovered in `$PATH` as `sitectl-drupal`. The entry point is `main.go`, which calls `cmd.RegisterCommands(sdk)` to register all commands with the plugin SDK.
36+
37+
### Key Commands (`cmd/`)
38+
39+
- **`drush.go`**: Runs drush commands inside the Drupal container
40+
- **`uli.go`**: Generates a one-time login link via drush (`user:login`)
41+
- **`sync.go`**: Syncs database and/or files between contexts (source → target)
42+
- **`extensions.go`**: Lists/debugs enabled Drupal extensions (modules, themes)
43+
- **`helpers.go`**: Shared command helpers (e.g., resolving the Drupal container)
44+
45+
### Key Packages (`pkg/`)
46+
47+
**`pkg/jobs`**: Background job definitions registered with the plugin SDK
48+
- Jobs are long-running operations (sync, import) exposed via `sdk.RegisterJob`
49+
50+
### Plugin SDK Usage
51+
52+
Always use the sitectl SDK functions rather than re-implementing:
53+
54+
- **`docker.ExecCapture()`**: Capture stdout from a container exec — do not write your own wrapper
55+
- **`job.ConfirmDatabaseReplacement()`**: Prompt before destructive DB imports — do not copy locally
56+
- **`job.ResolveRecentArtifact()`**: Resolve or produce a dated artifact (today/yesterday reuse)
57+
- **`job.StageArtifactBetweenContexts()`**: Download from source, upload to target
58+
- **`job.DownloadContextFile()`** / **`job.EnsurePathAbsentOnContext()`** / **`job.EnsureDirOnContext()`**: File transfer helpers
59+
- **`plugin.debugui`**: Use `debugui.RenderPanel`, `debugui.FormatRows` — do not copy locally
60+
- **`helpers.FirstNonEmpty()`**: Returns first non-empty string from a variadic list
61+
- **`helpers.GetContextFromArgs()`**: Extracts `--context` from `DisableFlagParsing` commands
62+
63+
## Go Coding Conventions
64+
65+
### Core Principles
66+
67+
- **Simplicity First:** Favor simple, readable code over clever solutions
68+
- **Idiomatic Go:** Follow standard Go conventions and community practices
69+
- **Standard Library:** Prefer the Go standard library over third-party dependencies
70+
71+
### Code Style
72+
73+
- Follow all conventions outlined in [Effective Go](https://go.dev/doc/effective_go)
74+
- Use `gofmt` to format all code before committing
75+
- Keep functions small and focused on a single responsibility
76+
- Create utility functions for any behavior that repeats more than twice
77+
- Name variables clearly; avoid abbreviations unless universally understood (e.g., `i` for index)
78+
79+
### Naming Conventions
80+
81+
- **Packages:** Short, concise, lowercase, single-word names
82+
- **Interfaces:** Use `-er` suffix for single-method interfaces (e.g., `Reader`, `Writer`)
83+
- **Getters:** Omit `Get` prefix (use `Name()`, not `GetName()`)
84+
- **Acronyms:** Keep consistent case (e.g., `userID`, `HTTPServer`, not `userId`, `HttpServer`)
85+
86+
### Dependency Management
87+
88+
- Default to the standard library; only introduce external dependencies when necessary
89+
- Prefer `net/http` for routing (Go 1.22+ has built-in advanced routing)
90+
- Document why any external dependency is required
91+
92+
### Error Handling
93+
94+
- Always check and handle errors explicitly
95+
- Use `RunE` (not `Run`) for Cobra commands so errors propagate correctly
96+
- Return errors rather than calling `os.Exit` or `log.Fatal` outside of `main`/`Execute`
97+
- Wrap errors with context: `fmt.Errorf("context: %w", err)`
98+
- Don't ignore errors with `_` without a clear reason
99+
- Output to `cmd.OutOrStdout()` / `cmd.ErrOrStderr()`, not `fmt.Println` / `os.Stdout`
100+
101+
```go
102+
// Good
103+
if err := doSomething(); err != nil {
104+
return fmt.Errorf("failed to do something: %w", err)
105+
}
106+
```
107+
108+
### Plugin/SDK Reuse
109+
110+
- Before writing a helper, check `pkg/docker`, `pkg/job`, `pkg/helpers`, and `pkg/plugin/debugui` in sitectl
111+
- Do not copy debug panel styles or render functions locally — use `debugui`
112+
- Do not copy `ExecCapture` — use `docker.ExecCapture`
113+
- Do not copy `ConfirmDatabaseReplacement` — use `job.ConfirmDatabaseReplacement`
114+
- Follow the Go idiom: a little copying is better than a little abstraction, but we already have the dependency
115+
116+
### Go Workspaces
117+
118+
- **Never use `replace` directives** in `go.mod` for local development
119+
- Use `make work` (runs `scripts/use-go-work.sh`) to create a `go.work` file instead
120+
121+
### Concurrency
122+
123+
- Prefer channels for communication between goroutines
124+
- Avoid shared mutable state; use `sync.Mutex` when necessary
125+
- Always run tests with `-race`: `go test -race ./...`
126+
- Use `context.Context` for cancellation and timeout control
127+
128+
### Logging
129+
130+
- Use `log/slog` for all structured logging
131+
- Log levels: Debug (diagnostics), Info (general), Warn (potentially harmful), Error (failures)
132+
- Never log sensitive data (passwords, tokens, secrets)
133+
134+
```go
135+
slog.Info("user authenticated", "user_id", userID, "ip_address", ipAddr)
136+
```
137+
138+
### Testing
139+
140+
- Write unit tests for all new features and bug fixes
141+
- Use table-driven tests for multiple scenarios
142+
- Use `t.Helper()` in test helper functions
143+
- Run with race detection: `go test -race ./...`
144+
145+
```go
146+
func TestCalculate(t *testing.T) {
147+
tests := []struct {
148+
name string
149+
input int
150+
want int
151+
wantErr bool
152+
}{
153+
{"positive number", 5, 25, false},
154+
{"zero", 0, 0, false},
155+
{"negative number", -5, 0, true},
156+
}
157+
for _, tt := range tests {
158+
t.Run(tt.name, func(t *testing.T) {
159+
got, err := Calculate(tt.input)
160+
if (err != nil) != tt.wantErr {
161+
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
162+
return
163+
}
164+
if got != tt.want {
165+
t.Errorf("got %v, want %v", got, tt.want)
166+
}
167+
})
168+
}
169+
}
170+
```
171+
172+
### Documentation
173+
174+
- Every exported function, type, method, and package must have a comment
175+
- Start comments with the name of the element being documented
176+
- Comment the **why**, not the **what** for internal logic
177+
178+
```go
179+
// UserService handles all user-related operations.
180+
type UserService struct{}
181+
182+
// GetUser retrieves a user by their ID.
183+
func (s *UserService) GetUser(id string) (*User, error) {}
184+
```
185+
186+
### Linting
187+
188+
- Use `golangci-lint` for all linting checks
189+
- Fix all linting issues before committing
190+
- Run `make lint` before pushing
191+
192+
## Development Notes
193+
194+
- The plugin binary must be named `sitectl-drupal` and placed in `$PATH` for sitectl to discover it
195+
- Uses the plugin SDK's `RegisterCommands` pattern — all commands registered in `cmd/root.go`
196+
- The `--yolo` flag bypasses `ConfirmDatabaseReplacement` prompts for scripted use
197+
- Logging via `slog` with level controlled by `LOG_LEVEL` env var or `--log-level` flag

Makefile

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
.PHONY: build deps lint test work install
22

33
BINARY_NAME=sitectl-drupal
4-
INSTALL_DIR ?= $(or $(dir $(shell which $(BINARY_NAME) 2>/dev/null)),/usr/local/bin/)
54

65
deps: work
76
go mod tidy
87

98
build:
109
go build -o $(BINARY_NAME) .
1110

12-
install: work build
13-
sudo cp $(BINARY_NAME) $(INSTALL_DIR)$(BINARY_NAME)
11+
install: build
12+
mv $(BINARY_NAME) /usr/local/bin
1413

1514
lint:
1615
go fmt ./...
@@ -28,4 +27,3 @@ test: build
2827

2928
work:
3029
./scripts/use-go-work.sh
31-

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,16 @@ Instead of homebrew, you can download a binary for your system from [the latest
1919

2020
Then put the binary in a directory that is in your `$PATH`
2121

22+
## Commands
23+
24+
- `sitectl drupal build`
25+
- `sitectl drupal init`
26+
- `sitectl drupal up`
27+
- `sitectl drupal down`
28+
- `sitectl drupal status`
29+
- `sitectl drupal logs [SERVICE...]`
30+
- `sitectl drupal rollout`
31+
- `sitectl drupal drush [COMMAND...]`
32+
- `sitectl drupal uli`
33+
- `sitectl drupal sync database`
34+
- `sitectl drupal sync config`

0 commit comments

Comments
 (0)