fix(02): revise plans based on checker feedback
- 02-01 Task 3: Add concrete REPL verification command for PluginManifest.parse - 02-02 Task 1: Add FieldDef case class to placeholder types in PluginRegistration.scala - 02-02 Task 1: Document PluginEnv as placeholder to be expanded in 02-03 - 02-03 Task 3: Remove duplicate Main.scala code, keep only corrected version - 02-03: Add SummerPlugin.scala to files_modified for PluginEnv update
This commit is contained in:
@@ -282,7 +282,25 @@ dependencies:
|
||||
golem15.user: "^1.0.0"
|
||||
```
|
||||
|
||||
Then verify parsing works by checking the code compiles and the types are correct.
|
||||
Then test discovery works by running this command in the Mill REPL:
|
||||
```bash
|
||||
./mill -i summercms.console
|
||||
```
|
||||
|
||||
Once in the REPL, verify parsing:
|
||||
```scala
|
||||
import plugin._
|
||||
val yaml = """
|
||||
vendor: test
|
||||
name: sample
|
||||
version: 1.0.0
|
||||
description: Test plugin
|
||||
"""
|
||||
val result = PluginManifest.parse(yaml)
|
||||
println(result) // Should print Right(PluginManifest(test,sample,1.0.0,...))
|
||||
```
|
||||
|
||||
Exit REPL with `:quit` after verification.
|
||||
</verify>
|
||||
<done>PluginManifest parses YAML manifests, PluginDiscovery scans plugins/ directory, plugins/ directory exists</done>
|
||||
</task>
|
||||
|
||||
@@ -136,7 +136,9 @@ case class PluginRegistration(
|
||||
// Event subscriptions (Phase 2 extension API)
|
||||
events: List[EventSubscription] = List.empty,
|
||||
// Extensions to other plugins (Phase 2 extension API)
|
||||
extensions: List[ExtensionDef] = List.empty
|
||||
extensions: List[ExtensionDef] = List.empty,
|
||||
// Form field definitions (for YAML-driven forms)
|
||||
fields: List[FieldDef] = List.empty
|
||||
)
|
||||
|
||||
object PluginRegistration:
|
||||
@@ -149,6 +151,7 @@ case class NavigationDef(id: String, label: String, url: String, icon: String =
|
||||
case class SettingDef(key: String, label: String, icon: String = "")
|
||||
case class EventSubscription(eventType: String, handler: String)
|
||||
case class ExtensionDef(target: String, extensionClass: String)
|
||||
case class FieldDef(name: String, fieldType: String)
|
||||
```
|
||||
|
||||
**SummerPlugin.scala:**
|
||||
@@ -172,6 +175,9 @@ trait SummerPlugin:
|
||||
* Async boot phase - can perform effects.
|
||||
* Called after all dependencies have booted.
|
||||
* Use for: database setup, event subscriptions, service initialization.
|
||||
*
|
||||
* PluginEnv is a placeholder that will be expanded in 02-03 to include
|
||||
* EventService and ExtensionRegistry when the extension API is implemented.
|
||||
*/
|
||||
def boot: ZIO[PluginEnv, PluginError, Unit] = ZIO.unit
|
||||
|
||||
@@ -181,12 +187,15 @@ trait SummerPlugin:
|
||||
*/
|
||||
def shutdown: ZIO[Any, Nothing, Unit] = ZIO.unit
|
||||
|
||||
/** Environment available to plugins during boot */
|
||||
/**
|
||||
* Environment available to plugins during boot.
|
||||
* Initially just PluginContext; extended in 02-03 to include EventService & ExtensionRegistry.
|
||||
*/
|
||||
type PluginEnv = PluginContext
|
||||
```
|
||||
</action>
|
||||
<verify>Run `./mill summercms.compile` - all types compile without errors</verify>
|
||||
<done>PluginState enum with all lifecycle states, PluginContext, PluginRegistration with placeholder defs, SummerPlugin trait</done>
|
||||
<done>PluginState enum with all lifecycle states, PluginContext, PluginRegistration with placeholder defs including FieldDef, SummerPlugin trait</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
|
||||
@@ -9,6 +9,7 @@ files_modified:
|
||||
- summercms/src/plugin/ExtensionRegistry.scala
|
||||
- summercms/src/plugin/SummerEvent.scala
|
||||
- summercms/src/plugin/package.scala
|
||||
- summercms/src/plugin/SummerPlugin.scala
|
||||
- summercms/src/Main.scala
|
||||
autonomous: true
|
||||
|
||||
@@ -311,7 +312,7 @@ trait BlogExtension:
|
||||
/** Validate post data */
|
||||
def validate(data: Map[String, Any]): Either[String, Unit] = Right(())
|
||||
|
||||
// FieldDef is already defined in PluginRegistration.scala, reuse it
|
||||
// FieldDef is defined in PluginRegistration.scala
|
||||
```
|
||||
</action>
|
||||
<verify>Run `./mill summercms.compile` - ExtensionRegistry compiles. It provides type-safe extension registration keyed by ClassTag.</verify>
|
||||
@@ -319,9 +320,10 @@ trait BlogExtension:
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Create plugin package exports and integrate with Main</name>
|
||||
<name>Task 3: Create plugin package exports, update PluginEnv, and integrate with Main</name>
|
||||
<files>
|
||||
summercms/src/plugin/package.scala
|
||||
summercms/src/plugin/SummerPlugin.scala
|
||||
summercms/src/Main.scala
|
||||
</files>
|
||||
<action>
|
||||
@@ -340,87 +342,47 @@ package object plugin:
|
||||
type PluginBootEnv = PluginContext & EventService & ExtensionRegistry
|
||||
```
|
||||
|
||||
**Update Main.scala** to integrate the plugin system:
|
||||
**Update SummerPlugin.scala** to use the expanded PluginEnv:
|
||||
|
||||
Read the existing file from 02-02 and update ONLY the PluginEnv type alias at the bottom:
|
||||
|
||||
```scala
|
||||
package plugin
|
||||
|
||||
import zio.*
|
||||
import zio.http.*
|
||||
import zio.config.typesafe.TypesafeConfigProvider
|
||||
|
||||
import api.Routes
|
||||
import _root_.config.{AppConfig as SummerConfig}
|
||||
import db.QuillContext
|
||||
import plugin.{PluginManager, PluginDiscovery, pluginLayer}
|
||||
/** Base trait for all SummerCMS plugins */
|
||||
trait SummerPlugin:
|
||||
/** Plugin identifier - must match manifest */
|
||||
def id: PluginId
|
||||
|
||||
object Main extends ZIOAppDefault {
|
||||
/**
|
||||
* Synchronous registration phase - pure data, no effects.
|
||||
* Called during discovery to collect plugin declarations.
|
||||
*/
|
||||
def register(ctx: PluginContext): PluginRegistration = PluginRegistration.empty
|
||||
|
||||
private val banner: String =
|
||||
"""
|
||||
|
|
||||
| | .
|
||||
| `. * | .'
|
||||
| `. ._|_* .' .
|
||||
| . * .' `. *
|
||||
| -------| |-------
|
||||
| . *`.___.' * .
|
||||
| .' |* `. *
|
||||
| .' * | . `.
|
||||
| . |
|
||||
|
|
||||
| S U M M E R C M S
|
||||
|""".stripMargin
|
||||
/**
|
||||
* Async boot phase - can perform effects.
|
||||
* Called after all dependencies have booted.
|
||||
* Use for: database setup, event subscriptions, service initialization.
|
||||
*/
|
||||
def boot: ZIO[PluginEnv, PluginError, Unit] = ZIO.unit
|
||||
|
||||
override val bootstrap: ZLayer[ZIOAppArgs, Any, Any] =
|
||||
Runtime.setConfigProvider(TypesafeConfigProvider.fromResourcePath())
|
||||
/**
|
||||
* Async shutdown phase - cleanup resources.
|
||||
* Called in reverse dependency order during application shutdown.
|
||||
*/
|
||||
def shutdown: ZIO[Any, Nothing, Unit] = ZIO.unit
|
||||
|
||||
override def run: ZIO[Any, Any, Any] =
|
||||
for {
|
||||
cfg <- ZIO.config[SummerConfig](SummerConfig.config)
|
||||
_ <- Console.printLine(banner)
|
||||
_ <- Console.printLine(s"Starting on port ${cfg.server.port}...")
|
||||
_ <- Console.printLine("")
|
||||
// Initialize plugin system
|
||||
_ <- Console.printLine("Loading plugins...")
|
||||
_ <- PluginManager.loadPlugins(PluginDiscovery.defaultPluginsDir)
|
||||
.catchAll(e => Console.printLine(s"Plugin loading warning: ${e.message}").as(()))
|
||||
_ <- PluginManager.bootAll
|
||||
.catchAll(e => Console.printLine(s"Plugin boot warning: ${e.message}").as(()))
|
||||
plugins <- PluginManager.listPlugins
|
||||
_ <- Console.printLine(s"Loaded ${plugins.count(_._2.isActive)} plugin(s)")
|
||||
_ <- Console.printLine("")
|
||||
// Note: Migrations are NOT auto-run. Use CLI to run migrations (Phase 5).
|
||||
_ <- Server.serve(Routes.routes).provide(
|
||||
Server.defaultWithPort(cfg.server.port),
|
||||
QuillContext.dataSourceLayer
|
||||
)
|
||||
} yield ()
|
||||
|
||||
// Provide plugin layers
|
||||
override def run: ZIO[Any, Any, Any] =
|
||||
(for {
|
||||
cfg <- ZIO.config[SummerConfig](SummerConfig.config)
|
||||
_ <- Console.printLine(banner)
|
||||
_ <- Console.printLine(s"Starting on port ${cfg.server.port}...")
|
||||
_ <- Console.printLine("")
|
||||
// Initialize plugin system
|
||||
_ <- Console.printLine("Loading plugins...")
|
||||
_ <- PluginManager.loadPlugins(PluginDiscovery.defaultPluginsDir)
|
||||
.catchAll(e => Console.printLine(s"Plugin loading warning: ${e.message}").as(()))
|
||||
_ <- PluginManager.bootAll
|
||||
.catchAll(e => Console.printLine(s"Plugin boot warning: ${e.message}").as(()))
|
||||
plugins <- PluginManager.listPlugins
|
||||
_ <- Console.printLine(s"Loaded ${plugins.count(_._2.isActive)} plugin(s)")
|
||||
_ <- Console.printLine("")
|
||||
// Note: Migrations are NOT auto-run. Use CLI to run migrations (Phase 5).
|
||||
_ <- Server.serve(Routes.routes).provide(
|
||||
Server.defaultWithPort(cfg.server.port),
|
||||
QuillContext.dataSourceLayer
|
||||
)
|
||||
} yield ()).provide(pluginLayer)
|
||||
/**
|
||||
* Environment available to plugins during boot.
|
||||
* Includes context, events, and extension registry for full plugin capabilities.
|
||||
*/
|
||||
type PluginEnv = PluginContext & EventService & ExtensionRegistry
|
||||
```
|
||||
|
||||
Wait - the above has a duplicate `def run`. Let me provide the correct version:
|
||||
|
||||
**Main.scala (corrected full file):**
|
||||
**Main.scala** (complete replacement):
|
||||
```scala
|
||||
import zio.*
|
||||
import zio.http.*
|
||||
@@ -483,7 +445,7 @@ object Main extends ZIOAppDefault {
|
||||
Run `./mill summercms.compile` - Main.scala compiles with plugin integration.
|
||||
Run `./mill summercms.run` - Server starts, logs "Loading plugins..." and "Loaded 0 plugin(s)" (since no plugins exist yet).
|
||||
</verify>
|
||||
<done>Plugin package exports combined layer, Main.scala loads and boots plugins on startup</done>
|
||||
<done>Plugin package exports combined layer, PluginEnv updated to include EventService and ExtensionRegistry, Main.scala loads and boots plugins on startup</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
Reference in New Issue
Block a user