Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 74 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "30 2 * * *"

concurrency:
group: ci-${{ github.ref }}
Expand Down Expand Up @@ -45,12 +47,8 @@ jobs:
- '.github/workflows/**'
- '.github/scripts/**'
docker_ci:
- 'go.mod'
- 'go.sum'
- 'Dockerfile'
- '.dockerignore'
- '.github/workflows/ci.yml'
- '.github/workflows/publish-image.yml'

linux:
name: quality (Linux)
Expand All @@ -65,7 +63,34 @@ jobs:
with:
go-version-file: go.mod
cache: true
cache-dependency-path: go.sum
cache-dependency-path: |
go.sum
examples/orders-api/go.sum
- name: Set up project tools
uses: jdx/mise-action@v4
with:
cache: true
install: true
github_token: ${{ github.token }}
- name: Run check
run: MISE_JOBS=1 mise run check:core

lint:
name: lint (Linux)
needs: changes
if: needs.changes.outputs.quality_ci == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true
cache-dependency-path: |
go.sum
examples/orders-api/go.sum
- name: Set up project tools
uses: jdx/mise-action@v4
with:
Expand All @@ -79,21 +104,51 @@ jobs:
key: golangci-lint-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.mise.toml', '.golangci.yml', 'go.mod', 'go.sum') }}
restore-keys: |
golangci-lint-${{ runner.os }}-${{ runner.arch }}-
- name: Run full check
run: mise run check
- name: Run lint
run: mise run lint

- name: Build macOS binaries
env:
CGO_ENABLED: "0"
GOOS: darwin
run: |
set -euo pipefail
for goarch in amd64 arm64; do
GOARCH="${goarch}" GOWORK=off go build \
-trimpath \
-o "${RUNNER_TEMP}/devctl-darwin-${goarch}" \
./cmd/devctl
done
e2e:
name: e2e (Linux)
needs: changes
if: needs.changes.outputs.quality_ci == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true
cache-dependency-path: |
go.sum
examples/orders-api/go.sum
- name: Set up project tools
uses: jdx/mise-action@v4
with:
cache: true
install: true
github_token: ${{ github.token }}
- name: Run e2e
run: mise run e2e

race:
name: race (Linux)
if: github.event_name == 'schedule' || (github.event_name == 'push' && github.ref == 'refs/heads/main')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true
cache-dependency-path: |
go.sum
examples/orders-api/go.sum
- name: Run race tests
run: GOWORK=off go test -race ./...

docker:
name: docker (linux/amd64)
Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Main CI

on:
push:
branches: [main]

concurrency:
group: main-ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
macos-build:
name: ${{ format('build (macOS/{0})', matrix.goarch) }}
runs-on: ubuntu-latest
strategy:
matrix:
goarch: [amd64, arm64]
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true
cache-dependency-path: |
go.sum
examples/orders-api/go.sum
- name: Build macOS binary
env:
CGO_ENABLED: "0"
GOOS: darwin
GOARCH: ${{ matrix.goarch }}
run: |
set -euo pipefail
GOWORK=off go build \
-trimpath \
-o "${RUNNER_TEMP}/devctl-darwin-${GOARCH}" \
./cmd/devctl
5 changes: 4 additions & 1 deletion .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ run = "GOWORK=off go mod tidy -diff"
[tasks.e2e]
run = "GOWORK=off go test -tags=e2e ./e2e -v"

[tasks."check:core"]
depends = ["fmt:check", "test", "build", "mod:check", "docs:check", "example:orders:check"]

[tasks.check]
depends = ["fmt:check", "lint", "test:race", "build", "mod:check", "docs:check", "example:orders:check", "e2e"]
depends = ["check:core", "lint", "e2e"]

[tasks."docs:generate"]
run = "GOWORK=off go run ./cmd/devctl/internal/docgen"
Expand Down
4 changes: 2 additions & 2 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,5 @@ An optional environment-backed gate for a Capability. It distinguishes
construction of a runtime dependency from starting its background work.

**Migration Target**:
The migration path and migration-only environment belonging to one SQLite,
PostgreSQL, or ClickHouse database Variant.
The migration configuration of one SQLite, PostgreSQL, or ClickHouse database
Variant, identified by its Connection and Variant.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ go install github.com/devctllabs/devctl/cmd/devctl@latest
devctl --version
```

For builds from the current source checkout, see the
[temporary sibling-module requirement](docs/development.md#working-with-sibling-modules).

## Start a Project

```sh
Expand Down Expand Up @@ -72,6 +75,7 @@ does not need `sync`, while a changed external Contract does.

## Documentation

- [Documentation index](docs/README.md)
- [User guide](docs/user-guide/README.md)
- [Command reference](docs/user-guide/reference/commands.md)
- [Manifest reference](docs/user-guide/reference/manifest/README.md)
Expand Down
2 changes: 1 addition & 1 deletion cmd/devctl/internal/add/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func newDBCmd(opts dbCmdOpts, build dbBuilder) *cli.Command {
return &cli.Command{
Name: "db",
Usage: "Add a database variant",
Description: "Add a SQLite, PostgreSQL, or ClickHouse Variant to a named database Connection. A migration target is declared by default; Devctl never writes SQL or applies migrations.",
Description: "Add a SQLite, PostgreSQL, or ClickHouse Variant to a named database Connection. A Migration Target is declared by default; use devctl migrate explicitly to create SQL files or run migrations.",
UsageText: "devctl add db <database-name> --kind <sqlite|postgres|clickhouse>",
UseShortOptionHandling: true,
Arguments: []cli.Argument{&cli.StringArg{
Expand Down
4 changes: 3 additions & 1 deletion cmd/devctl/internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
initcmd "github.com/devctllabs/devctl/cmd/devctl/internal/init"
inspectcmd "github.com/devctllabs/devctl/cmd/devctl/internal/inspect"
lintcmd "github.com/devctllabs/devctl/cmd/devctl/internal/lint"
migratecmd "github.com/devctllabs/devctl/cmd/devctl/internal/migrate"
synccmd "github.com/devctllabs/devctl/cmd/devctl/internal/sync"
validatecmd "github.com/devctllabs/devctl/cmd/devctl/internal/validate"
"github.com/urfave/cli/v3"
Expand All @@ -20,7 +21,7 @@ func New(releaseVersion, releaseCommit string) *cli.Command {
return &cli.Command{
Name: "devctl",
Usage: "Manage Devctl Go projects",
Description: "Devctl defines, validates, and materializes reproducible Go projects from a devctl.yaml manifest. Commands are non-interactive and keep manifest mutation, synchronization, linting, scaffolding, and generation explicit.",
Description: "Devctl defines, validates, and materializes reproducible Go projects from a devctl.yaml manifest. Commands are non-interactive and keep manifest mutation, synchronization, linting, scaffolding, generation, and migration execution explicit.",
Version: buildVersion(releaseVersion, releaseCommit),
Commands: []*cli.Command{
initcmd.NewCmd(),
Expand All @@ -31,6 +32,7 @@ func New(releaseVersion, releaseCommit string) *cli.Command {
synccmd.NewCmd(),
gencmd.NewCmd(),
lintcmd.NewCmd(),
migratecmd.NewCmd(),
},
ExitErrHandler: func(context.Context, *cli.Command, error) {},
OnUsageError: func(_ context.Context, _ *cli.Command, err error, _ bool) error {
Expand Down
17 changes: 17 additions & 0 deletions cmd/devctl/internal/command/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/devctllabs/devctl/internal/domain/contract"
"github.com/devctllabs/devctl/internal/domain/failure"
migrationdomain "github.com/devctllabs/devctl/internal/domain/migration"
projectdomain "github.com/devctllabs/devctl/internal/domain/project"
"github.com/urfave/cli/v3"
"go.uber.org/zap"
Expand Down Expand Up @@ -93,6 +94,22 @@ func classifyError(err error) errorReport {
report.addDetail("reason", string(metadataErr.Reason))
report.addDetail("hint", metadataErr.Hint)
}
var migrationErr *migrationdomain.Error
if errors.As(err, &migrationErr) {
report.addDetail("reason", migrationErr.Reason)
if migrationErr.Target != "" {
report.addDetail("target", migrationErr.Target)
}
if migrationErr.Path != "" {
report.addDetail("path", migrationErr.Path)
}
if migrationErr.EnvKey != "" {
report.addDetail("env", migrationErr.EnvKey)
}
if len(migrationErr.Candidates) > 0 {
report.addDetail("targets", migrationErr.Candidates)
}
}
return report
}

Expand Down
9 changes: 9 additions & 0 deletions cmd/devctl/internal/migrate/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package migrate

import "github.com/urfave/cli/v3"

func newCreateCmd(build migrateBuilder) *cli.Command {
command, cmd := newLeaf("create", "Create a pair of timestamped SQL files", build)
command.Arguments = []cli.Argument{&cli.StringArg{Name: "name", UsageText: "<name>", Destination: &cmd.opts.name}}
return command
}
9 changes: 9 additions & 0 deletions cmd/devctl/internal/migrate/down.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package migrate

import "github.com/urfave/cli/v3"

func newDownCmd(build migrateBuilder) *cli.Command {
command, cmd := newLeaf("down", "Roll back migrations (one step by default)", build)
command.Flags = append(command.Flags, &cli.IntFlag{Name: "steps", Value: 1, Usage: "roll back `N` migrations", Destination: &cmd.opts.steps})
return command
}
Loading
Loading