feat(04-01): generate jobs and admin controllers, reject model imports
Scaffold pact.Job and pact.AdminController stubs with Winter YAML assets, and fail summer build when models imports a sibling package. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -24,6 +24,9 @@ type artifactData struct {
|
||||
Ident string
|
||||
Table string
|
||||
Func string
|
||||
Worker string
|
||||
Kind string
|
||||
ConfigDir string
|
||||
MigrationID string
|
||||
UpSQL string
|
||||
DownSQL string
|
||||
@@ -187,6 +190,124 @@ func MakeCommand(ctx context.Context, startDir, pluginID, name string) (Artifact
|
||||
return ArtifactResult{PluginDir: plugin.Dir, Files: []string{path}, Hint: plugin.Hint}, nil
|
||||
}
|
||||
|
||||
// MakeJob writes a pact.Job stub without importing River.
|
||||
func MakeJob(ctx context.Context, startDir, pluginID, name string) (ArtifactResult, error) {
|
||||
plugin, err := resolvePlugin(startDir, pluginID)
|
||||
if err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
if err := validateExportedIdent(name); err != nil {
|
||||
return ArtifactResult{}, fmt.Errorf("build: job: %w", err)
|
||||
}
|
||||
fn := name + "Job"
|
||||
if err := rejectDuplicateIdent(plugin.Dir, "jobs", fn); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
rel := filepath.Join("jobs", toSnake(name)+".go")
|
||||
if err := rejectDuplicateFile(plugin.Dir, rel); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
src, err := renderStub("job.go", artifactData{
|
||||
Ident: name,
|
||||
Func: fn,
|
||||
Worker: unexportedIdent(name) + "Job",
|
||||
Kind: plugin.ID + "." + toSnake(name),
|
||||
})
|
||||
if err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
path := filepath.Join(plugin.Dir, rel)
|
||||
if err := writeFile(path, src); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
if err := finishArtifact(ctx, plugin); err != nil {
|
||||
os.Remove(path)
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
return ArtifactResult{PluginDir: plugin.Dir, Files: []string{path}, Hint: plugin.Hint}, nil
|
||||
}
|
||||
|
||||
// MakeAdminController writes a pact.AdminController and Winter-shaped YAML.
|
||||
func MakeAdminController(ctx context.Context, startDir, pluginID, name string) (ArtifactResult, error) {
|
||||
plugin, err := resolvePlugin(startDir, pluginID)
|
||||
if err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
if err := validateExportedIdent(name); err != nil {
|
||||
return ArtifactResult{}, fmt.Errorf("build: admin-controller: %w", err)
|
||||
}
|
||||
fn := name + "Controller"
|
||||
if err := rejectDuplicateIdent(plugin.Dir, "controllers", fn); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
snake := toSnake(name)
|
||||
rel := filepath.Join("controllers", snake+".go")
|
||||
if err := rejectDuplicateFile(plugin.Dir, rel); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
configDir := filepath.ToSlash(filepath.Join("controllers", snake))
|
||||
fieldsRel := filepath.Join("controllers", snake, "fields.yaml")
|
||||
columnsRel := filepath.Join("controllers", snake, "columns.yaml")
|
||||
for _, yamlRel := range []string{fieldsRel, columnsRel} {
|
||||
if err := rejectDuplicateFile(plugin.Dir, yamlRel); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
}
|
||||
src, err := renderStub("admin_controller.go", artifactData{
|
||||
Ident: name,
|
||||
Func: fn,
|
||||
Worker: unexportedIdent(name) + "Admin",
|
||||
Kind: plugin.ID + "." + snake,
|
||||
ConfigDir: configDir,
|
||||
})
|
||||
if err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
fieldsSrc, err := renderStub("fields.yaml", nil)
|
||||
if err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
columnsSrc, err := renderStub("columns.yaml", nil)
|
||||
if err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
|
||||
var created []string
|
||||
rollback := true
|
||||
defer func() {
|
||||
if rollback {
|
||||
for _, path := range created {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
}()
|
||||
goPath := filepath.Join(plugin.Dir, rel)
|
||||
if err := writeFile(goPath, src); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
created = append(created, goPath)
|
||||
fieldsPath := filepath.Join(plugin.Dir, fieldsRel)
|
||||
if err := writeFile(fieldsPath, fieldsSrc); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
created = append(created, fieldsPath)
|
||||
columnsPath := filepath.Join(plugin.Dir, columnsRel)
|
||||
if err := writeFile(columnsPath, columnsSrc); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
created = append(created, columnsPath)
|
||||
if err := finishArtifact(ctx, plugin); err != nil {
|
||||
return ArtifactResult{}, err
|
||||
}
|
||||
rollback = false
|
||||
return ArtifactResult{PluginDir: plugin.Dir, Files: created, Hint: plugin.Hint}, nil
|
||||
}
|
||||
|
||||
func unexportedIdent(name string) string {
|
||||
r, size := utf8.DecodeRuneInString(name)
|
||||
return string(unicode.ToLower(r)) + name[size:]
|
||||
}
|
||||
|
||||
type pluginRef struct {
|
||||
Dir string
|
||||
ID string
|
||||
|
||||
Reference in New Issue
Block a user