You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -24,24 +24,23 @@ We will use a CSV format for recipe marketplace data with the following structur
24
24
25
25
### Required Columns
26
26
27
+
-**`ecosystem`**: The package ecosystem (e.g., `maven`, `npm`, `yaml`)
28
+
-**`packageName`**: The package identifier (e.g., `org.openrewrite:rewrite-java`)
27
29
-**`name`**: The fully qualified recipe name (e.g., `org.openrewrite.java.cleanup.UnnecessaryParentheses`)
28
-
-**`category1, category2, ..., categoryN`**: Zero or more category columns, read **left to right** with **left representing the deepest level category**
29
30
30
-
### Optional Recipe Columns
31
+
### Optional Recipe Metadata Columns
31
32
32
-
-**`displayName`**: Human-readable recipe display name
33
-
-**`description`**: Recipe description
33
+
-**`displayName`**: Human-readable recipe display name (defaults to `name` if not specified)
34
+
-**`description`**: Recipe description (defaults to empty string if not specified)
35
+
-**`estimatedEffortPerOccurrence`**: ISO-8601 duration format (e.g., PT5M, PT1H)
36
+
-**`category1, category2, ..., categoryN`**: Zero or more category columns, read **left to right** with **left representing the deepest level category**
34
37
-**`option1Name, option1DisplayName, option1Description`**: First recipe option
35
38
-**`option2Name, option2DisplayName, option2Description`**: Second recipe option
36
39
-**`optionNName, optionNDisplayName, optionNDescription`**: Additional options following the same pattern
37
40
38
41
### Optional Bundle Columns
39
42
40
-
Bundle columns describe where a recipe can be installed from. When absent, the marketplace represents a minimal catalog:
-**`version`**: Package version (optional, allows version-independent recipe catalogs)
45
44
-**`team`**: Optional team identifier for marketplace partitioning
46
45
47
46
### Category Structure
@@ -57,46 +56,43 @@ This creates the hierarchy: `Best Practices > Java > Cleanup > UnnecessaryParent
57
56
58
57
The displayName of a category corresponds to the value in its category column.
59
58
60
-
### Epsilon Root
59
+
### Root Category
61
60
62
-
When a CSV contains multiple top-level categories, a synthetic "epsilon root" (`ε`) is created similar to `moderne-organizations-format`. This root:
61
+
The `RecipeMarketplace` maintains an internal root category that contains all top-level categories. The root is not exposed in the CSV format but allows the marketplace to support multiple top-level category trees.
63
62
64
-
- Uses the epsilon character (`\u03B5`) as its display name
65
-
- Is identified via `RecipeMarketplace.isRoot()`
66
-
- Is never written to CSV output
67
-
- Allows the reader to return a single root when multiple disparate category trees exist
63
+
### Version-Independent Catalogs
68
64
69
-
### Minimal vs. Enriched Marketplaces
70
-
71
-
**Minimal Marketplace**: Contains only recipe metadata (name, categories, options) without bundle information. Useful for describing what recipes exist and their categorization.
72
-
73
-
**Enriched Marketplace**: Includes bundle columns (ecosystem, packageName, version, team). Created when recipes are "installed" into an environment, combining the minimal catalog with actual bundle provenance.
65
+
Since `version` is optional, marketplaces can represent version-independent recipe catalogs that describe what recipes exist and their categorization without tying them to specific package versions. Version information can be added later when recipes are installed or resolved in a specific environment.
74
66
75
67
### Implementation
76
68
69
+
-**Data Model**:
70
+
-`RecipeBundle`: Simple data class containing ecosystem, packageName, version (optional), and team (optional)
71
+
-`RecipeListing`: Represents a recipe with metadata (name, displayName, description, options) and an associated `RecipeBundle`
72
+
-`RecipeMarketplace`: Hierarchical structure with nested `Category` instances and a list of `RecipeBundleResolver` instances
- Dynamically determines required category and option columns
86
-
-Filters epsilon root from output
87
-
- Only includes bundle columns if at least one recipe has bundle information
88
-
89
-
-**Bundle Loaders**: Configurable implementations passed to the reader
90
-
-`MavenRecipeBundleLoader` in `rewrite-maven`: Creates `MavenRecipeBundle` instances, requires `MavenExecutionContextView` and `MavenArtifactDownloader`
91
-
-`NpmRecipeBundleLoader` in `rewrite-javascript`: Creates `NpmRecipeBundle` instances
92
-
-`RecipeBundleLoader` interface allows additional ecosystems to be added without modifying core
93
-
94
-
-**Generator**: `MavenRecipeMarketplaceGenerator` in `rewrite-maven`
95
-
- Generates `RecipeMarketplace` from recipe JARs by scanning classpath and extracting recipe metadata
96
-
- Automatically determines categories from recipe package names and `CategoryDescriptor` annotations
97
-
- Creates bundle information from GAV coordinates
98
-
- Distinguishes between YAML-based declarative recipes and Java class-based recipes
99
-
-Useful for generating initial CSV files from existing recipe JARs
81
+
- Dynamically determines required category and option columns based on marketplace content
82
+
-Always includes `ecosystem` and `packageName` columns
83
+
- Only includes `version` column if at least one recipe has version information
84
+
- Only includes `team` column if at least one recipe has team information
85
+
86
+
-**Bundle Resolution**: Two-phase resolution system
87
+
-`RecipeBundleResolver`: Interface with `getEcosystem()` and `resolve(RecipeBundle)` methods
88
+
- Ecosystem-specific resolvers are registered with the `RecipeMarketplace`
89
+
- Examples: `MavenRecipeBundleResolver` in `rewrite-maven`, `NpmRecipeBundleResolver` in `rewrite-javascript`
90
+
-`RecipeBundleReader`: Interface returned by resolvers with methods:
91
+
-`getBundle()`: Returns the associated `RecipeBundle`
92
+
-`read()`: Reads the bundle and returns a `RecipeMarketplace`
93
+
-`describe(RecipeListing)`: Returns a `RecipeDescriptor` for a listing
94
+
-`prepare(RecipeListing, Map<String, Object>)`: Creates a configured `Recipe` instance
95
+
-`RecipeListing.resolve()`: Convenience method that finds the appropriate resolver and returns a `RecipeBundleReader`
100
96
101
97
-**Validators**: Tools for ensuring marketplace quality and completeness
102
98
-`RecipeMarketplaceContentValidator` in `rewrite-core`: Validates content formatting rules
@@ -118,56 +114,63 @@ When a CSV contains multiple top-level categories, a synthetic "epsilon root" (`
118
114
119
115
1.**Human-editable**: CSV files can be created and modified in spreadsheet tools or text editors
120
116
2.**Flexible schema**: Dynamic column detection accommodates varying numbers of categories and options
121
-
3.**Separation of concerns**: Minimal marketplaces can describe recipes independently of their installation source
117
+
3.**Version-independent catalogs**: Optional version column allows describing recipes independently of specific package versions
122
118
4.**Composable**: Multiple marketplace CSVs can be combined by merging rows
123
119
5.**Round-trip compatible**: Reader and writer preserve all information
7.**Extensible bundle loading**: RecipeBundleLoader interface allows new package ecosystems to be added without modifying core modules
126
-
8.**Automated generation**: `MavenRecipeMarketplaceGenerator` can automatically create CSV files from recipe JARs
127
-
9.**Quality assurance**: Validators ensure content quality (formatting) and completeness (CSV ↔ JAR synchronization)
121
+
7.**Extensible bundle resolution**: Two-phase resolution (RecipeBundleResolver → RecipeBundleReader) allows new package ecosystems to be added without modifying core modules
122
+
8.**Quality assurance**: Validators ensure content quality (formatting) and completeness (CSV ↔ JAR synchronization)
123
+
9.**Lazy resolution**: RecipeListing stores bundle metadata but only resolves to actual Recipe instances when needed via resolve()
128
124
129
125
### Negative
130
126
131
127
1.**CSV limitations**: No native support for nested structures (mitigated by column naming conventions)
132
128
2.**Sparse data**: Recipes with few options result in many empty cells in CSVs with high option counts
133
-
3.**Manual maintenance**: Keeping bundle information synchronized with actual artifact versions requires tooling (though `MavenRecipeMarketplaceGenerator` and validators help address this)
129
+
3.**Always requires bundle metadata**: Unlike earlier designs, ecosystem and packageName are always required, even for basic recipe catalogs
134
130
135
131
### Trade-offs
136
132
137
133
-**Left-to-right category ordering** (left = deepest): This matches the `moderne-organizations-format` convention but may be counterintuitive to some users who expect left-to-right to represent root-to-leaf
138
-
-**Null bundles for minimal marketplaces**: Simplifies the model but means `RecipeOffering.describe()` and `prepare()` throw exceptions until bundles are associated
134
+
-**Two-phase resolution**: Separating RecipeBundleResolver and RecipeBundleReader provides flexibility but adds complexity compared to a single interface
139
135
-**Dynamic columns**: Provides flexibility but means schema varies between files, making generic CSV processing tools less effective
140
-
-**Bundle loader configuration**: Bundle loaders require runtime dependencies (e.g., MavenExecutionContextView) that must be provided when constructing the loader and passed to RecipeMarketplaceReader constructor
136
+
-**Resolver configuration**: RecipeBundleResolvers must be registered with the RecipeMarketplace instance before calling RecipeListing.resolve(), describe(), or prepare()
0 commit comments