-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathconfiguration.md
More file actions
470 lines (391 loc) · 15.6 KB
/
Copy pathconfiguration.md
File metadata and controls
470 lines (391 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
# Configuration
This project provides a generic way to create a various set of project
types. For each project type, an appropriate build configuration exists.
The configuration describes how to build a project, e.g. which steps are
necessary and what properties are required when rendering project
templates.
## External template packages
Project templates are distributed through **external Composer packages**.
```{important}
Each Composer package must be of the type `project-builder-template`.
```
```{code-block} json
:linenos:
:caption: composer.json
{
"name": "cpsit/project-builder-template-my-fancy-project",
"type": "project-builder-template",
// ...
}
```
Additionally, the packages must be installable via Composer. There are
three ways to make a template package available to the project builder:
1. Either register it on [Packagist](https://packagist.org/),
2. Use any other Composer registry (e.g. self-hosted [Satis][4] instance),
or
3. Host the project template on a VCS repository (such as GitHub) to make
your package available to the project builder.
Once you use the project builder to create a new project, you can
select the appropriate provider that hosts your template package.
:::{tip}
You can add the `cpsit/project-builder` package as a dependency to
your template package. Composer will correctly resolve the constraint.
This way, you can define the versions of the project builder actually
supported by your package:
```{code-block} json
:linenos:
:caption: composer.json
:emphasize-lines: 4-6
{
"name": "cpsit/project-builder-template-my-fancy-project",
"type": "project-builder-template",
"require": {
"cpsit/project-builder": "^1.0"
}
// ...
}
```
:::
### Exclude packages from listing
If a template package is published on a supported platform such as Packagist,
it is always included in the list of available template packages when generating
new projects.
However, a single template package can also be explicitly excluded from that list.
This may be useful if a package is not meant to be publicly used or if it's just
published for demonstration or testing purposes.
In such cases, template packages may provide the following configuration in
their `composer.json` file:
```{code-block} json
:linenos:
:caption: composer.json
{
"extra": {
"cpsit/project-builder": {
"exclude-from-listing": true
}
}
}
```
## File structure
Within the external Composer template package, the following file
structure must exist:
```
my-fancy-project
├── composer.json
├── config.yml
├── config
│ └── services.yaml
├── src
│ ├── ...
│ └── Twig
│ └── Function
│ └── MyCustomTwigFunction.php
└── templates
├── shared
│ ├── ...
│ └── my-fancy-shared-resource
│ ├── composer.json
│ └── templates
│ └── src
│ ├── ...
│ └── .gitlab-ci.yml.twig
└── src
├── ...
└── composer.json.twig
```
In this example, the project type `my-fancy-project` is configured and
distributed through the package `cpsit/project-builder-template-my-fancy-project`.
It contains the following files and directories:
* **`composer.json`** (optional) defines additional template dependencies.
Those are installed by the build step `installComposerDependencies`.
Read more at [`Processing build steps#Install Composer dependencies`](build-steps.md#install-composer-dependencies).
* **`config.yml`** is the main configuration file. It contains all
instructions on how to build new projects of this project type. Read more
at [`Config file`](#config-file).
* **`config`** (optional) contains additional service configuration files,
e.g. `services.yaml` or `services.php`. Read more at
[`Dependency injection#Extending service configuration`](dependency-injection.md#extending-service-configuration).
* **`src`** (optional) may contain additional PHP classes. Normally, these
require an additional service configuration as described before.
* **`templates`** (optional) contains various project source files. The
following sub-folders are supported:
- **`shared`** (optional) should contain shared source files. Those are
normally created when installing Composer dependencies defined by
`composer.json`. Read more at [`Shared source files`](#shared-source-files).
- **`src`** (optional) contains all project source files. Those can be
either generic files to be copied to the generated project or Twig
template files. Twig files are processed before copying them to the
generated project. Read more at [`Source files`](#source-files).
## Config file
Each project type requires a configuration file. It describes how to build
a new project of this type and is located in the template directory of the
associated project type.
The following filename variants are supported:
1. `config.yml`
2. `config.yaml`
3. `config.json`
```{seealso}
See [`ConfigReader::FILE_VARIANTS`](https://github.com/CPS-IT/project-builder/blob/main/src/Builder/Config/ConfigReader.php)
for an overview of supported filenames.
```
### Structure
Each config file should at least contain the following properties:
* **`name`** is kind of a label for the configured project type. It is mainly
used for communication with the user, keeping the actual project type internal.
* **`steps`** defines a list of necessary build steps. Those steps are processed
once a new project of the associated project type is generated. Read more at
[`Processing build steps`](build-steps.md).
Usually, it is also necessary to collect some more information from the user, e.g.
to be able to prepare template files such as `README.md.twig` or `composer.json.twig`.
For this, a set of `properties` can be defined. Those properties are then used to
collect information in form of build instructions from the user. Read more at
[`Processing build steps#Collect build instructions`](build-steps.md#collect-build-instructions).
Example:
```{code-block} yaml
:linenos:
:caption: config.yaml
name: My fancy project
steps:
- type: installComposerDependencies
- type: collectBuildInstructions
- type: processSourceFiles
options:
fileConditions:
# You can define a Twig template to render...
- path: composer.json.twig
if: 'features["composer"]'
# ... or use static files
- path: example-v3.conf
if: 'features["example"] && example["version"] == "3"'
target: example.conf
- type: processSharedSourceFiles
options:
fileConditions:
# Use Symfony Expression Language to define file conditions
- path: phpunit.xml
if: 'features["phpunit"]'
# Mirror an entire directory
- path: 'source-dir/*'
target: 'target-dir/*'
# Apply Twig expression for custom target directory names
- path: 'source-dir/*'
target: '{{ project.name | slugify }}-target-dir/*'
- type: mirrorProcessedFiles
- type: runCommand
options:
command: 'git init --initial-branch=main'
skipConfirmation: true
allowFailure: true
- type: runCommand
options:
command: 'composer update'
allowFailure: true
required: false
- type: showNextSteps
options:
templateFile: templates/next-steps.html.twig
properties:
# Project
- identifier: project
name: Project
properties:
- identifier: customer_name
name: Customer name
type: staticValue
validators:
- type: notEmpty
- identifier: project_name
name: Project name
type: staticValue
defaultValue: basic
validators:
- type: notEmpty
- type: regex
options:
pattern: '/^[a-zA-Z]+$/'
errorMessage: 'The project name should consist of letters only.'
# Features
- identifier: features
name: Features
properties:
- identifier: composer
name: Enable <comment>Composer</comment> support?
type: question
defaultValue: true
- identifier: phpstan
name: Do you need <comment>PHPStan</comment> support?
type: question
- identifier: phpunit
name: Do you want to run tests with <comment>PHPUnit</comment>?
type: question
# Author
- identifier: author
name: About you
properties:
- identifier: name
name: Your name
type: staticValue
validators:
- type: notEmpty
- identifier: email
name: Your e-mail address
type: staticValue
validators:
- type: notEmpty
- type: email
```
### Twig integration
Some configuration parts may be configured as Twig templates. For example, the
`defaultValue` option of a configured property may contain the processed value
of a previously added property:
```{code-block} yaml
:linenos:
:caption: config.yaml
:emphasize-lines: 18
properties:
- identifier: project
name: Project
properties:
- identifier: name
name: Name
type: staticValue
validators:
- type: notEmpty
- identifier: vendor
name: Vendor
type: staticValue
validators:
- type: notEmpty
- identifier: package_name
name: Package name
type: staticValue
defaultValue: '{{ project.vendor | slugify }}/{{ project.name | slugify }}'
validators:
- type: notEmpty
```
The following configuration options are currently processed by the Twig renderer:
* `steps.*.options.fileConditions.*.target`
* `properties.*.value`
* `properties.*.properties.*.defaultValue`
* `properties.*.properties.*.options.*.value`
### Symfony Expression Language integration
Several configuration options use conditions to determine whether a property or step
should be applied. All used conditions are parsed by the [Symfony Expression Language][5].
Example:
```{code-block} yaml
:linenos:
:caption: config.yaml
:emphasize-lines: 6
steps:
- type: processSharedSourceFiles
options:
fileConditions:
- path: composer.json
if: 'features["composer"] == true'
```
The following configuration options are currently evaluated by the Symfony Expression
Language:
* `steps.*.options.fileConditions.*.if`
* `properties.*.if`
* `properties.*.properties.*.if`
* `properties.*.properties.*.options.*.if`
### Mapping and hydration
Config files are located by the [`ConfigReader`](https://github.com/CPS-IT/project-builder/blob/main/src/Builder/Config/ConfigReader.php)
and parsed by the internal [`ConfigFactory`](https://github.com/CPS-IT/project-builder/blob/main/src/Builder/Config/ConfigFactory.php).
With the help of the fantastic external library [`cuyz/valinor`][1], the parsed
config file is mapped to an object structure of value objects. The final configuration
ends up in an instance of [`Builder\Config\Config`](https://github.com/CPS-IT/project-builder/blob/main/src/Builder/Config/Config.php):
```{code-block} php
:linenos:
$configReader = \CPSIT\ProjectBuilder\Builder\Config\ConfigReader::create();
$config = $configReader->readConfig('my-fancy-project');
echo $config->getIdentifier(); // my-fancy-project
echo $config->getName(); // My fancy project
```
Each configured property in the config file is now accessible from the
`Config` object:
| Property | Accessor | Type |
|--------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| `name` | `$config->getName()` | `string` |
| `steps` | `$config->getSteps()` | [`list<Builder\Config\ValueObject\Step>`](https://github.com/CPS-IT/project-builder/blob/main/src/Builder/Config/ValueObject/Step.php) |
| `properties` | `$config->getProperties()` | [`list<Builder\Config\ValueObject\Property>`](https://github.com/CPS-IT/project-builder/blob/main/src/Builder/Config/ValueObject/Property.php) |
```{seealso}
All hydrated value objects can be found at [`Builder\Config\ValueObject`](https://github.com/CPS-IT/project-builder/tree/main/src/Builder/Config/ValueObject).
```
### Validation
Config files are validated against a JSON schema. The schema file is located
at [`resources/config.schema.json`](https://github.com/CPS-IT/project-builder/blob/main/resources/config.schema.json). Schema
validation is handled by [`ConfigFactory::isValidConfig()`](https://github.com/CPS-IT/project-builder/blob/main/src/Builder/Config/ConfigFactory.php)
with the help of the great external library [`justinrainbow/json-schema`][2].
```{warning}
If a config file does not match the required schema, project generation will fail immediately.
```
## Source files
Each project type may provide several source files. They must be stored in a
`templates/src` folder.
Currently, the following file variants are supported:
* **Generic files** can be any files other than Twig files. They will be copied
as-is to the generated project. Example: `composer.json`
* **Twig template files** are pre-processed by the Twig renderer before they are
copied to the generated project. The configured properties are used as template
variables. Read more at [`Architecture#Template rendering`](architecture/components.md#template-rendering).
Example: `composer.json.twig`
## Shared source files
In case multiple project types share the same source files, it might be useful
to outsource them to an external Composer package. This allows better maintenance
of those shared source files. Per convention, external shared Composer packages
should be of the type `project-builder-shared`.
```{code-block} json
:linenos:
:caption: composer.json
{
"name": "my-vendor/my-fancy-shared-template",
"type": "project-builder-shared",
// ...
}
```
### Integration into the template package
The shared source file packages must be required in the `composer.json` file of
each project type that requires the shared source files. As a consequence, the
package must be installable via Composer.
The project builder expects shared source files to be installed within the
project type's `templates/shared/<package-name>/templates/src` folder. For this,
it is useful to use the Composer package [`oomphinc/composer-installers-extender`][3]
and define the installation paths of each shared source file package.
```{code-block} json
:linenos:
:caption: composer.json
{
"name": "my-vendor/my-fancy-project-template",
"type": "project-builder-template",
"require": {
"my-vendor/my-fancy-shared-template": "^1.0",
"oomphinc/composer-installers-extender": "^2.0"
},
"extra": {
"installer-paths": {
"templates/shared/{$name}/": [
"type:project-builder-shared"
]
},
"installer-types": [
"project-builder-shared"
]
},
// ...
}
```
The shared source file package must then provide the following folder structure:
```
my-fancy-shared-resource
├── composer.json
└── templates
└── src
├── ...
└── some-shared-file.json.twig
```
[1]: https://github.com/CuyZ/Valinor
[2]: https://github.com/justinrainbow/json-schema
[3]: https://packagist.org/packages/oomphinc/composer-installers-extender
[4]: https://github.com/composer/satis
[5]: https://symfony.com/doc/current/components/expression_language.html