Composer plugin to execute recipes embedded in packages

Installs: 809

Dependents: 1

Suggesters: 0

Security: 0

Stars: 24

Watchers: 2

Forks: 6

Open Issues: 1

Type:composer-plugin

1.3.0 2024-05-02 10:39 UTC

This package is auto-updated.

Last update: 2024-11-02 11:44:25 UTC


README

Baking recipes for any PHP package.

Github Workflow

Introduction

Cook is a Composer plugin that executes recipes embedded in packages, in a similar fashion to Symfony Flex. It can be used alongside with Flex, or in any other PHP project, as long as Composer is installed.

Features

  • Add new entries to arrays or export new arrays, filter how you want to output it
  • Add content to existing files or create them (.env, Makefile, or anything else)
  • Copy entire directories from your repository to the project
  • Keep existing data by default or overwrite it with a CLI command
  • Supports PHP arrays, JSON, YAML, text files
  • Output post install instructions
  • Process only required packages in the root project
  • Uninstall recipe when a package is removed
  • CLI commands to install or uninstall recipes

Installation

composer require williarin/cook

Make sure to allow the plugin to run. If it's not added automatically, add this in your composer.json file:

    "config": {
        "allow-plugins": {
            "williarin/cook": true
        }
    },

Documentation

Creating a recipe

Take a look at williarin/cook-example for a working example of a Cook recipe.

To make your package Cook-compatible, you just have to create a valid cook.yaml or cook.json at the root directory.

The recipe schema must follow this structure:

Files

Files are a described as key-value pairs.

  • Key is the path to the destination file
  • Value is either an array or a string

If a string is given, it must be a path to the source file.

Directories

Directories are a described as key-value pairs.

  • Key is the path to the destination directory that will receive the files
  • Value is the path of the source directory in the package that contains the files

Post install output

Maybe you want to display some text to the user after installation.
You can use colors using Symfony Console syntax.

Mergers

Text

The text merger can be used to extend any text-based file such as:

  • .gitignore
  • .env
  • Makefile

As it's the default merger, you can simply use the destination: source format in the recipe.

Example 1: merge or create a .env file with a given source file

Given yourrepo/recipe/.env with this content:

SOME_ENV_VARIABLE='hello'
ANOTHER_ENV_VARIABLE='world'

With this recipe:

files:
    .env: recipe/.env

The created .env file will look like this:

###> yourname/yourrepo ###
SOME_ENV_VARIABLE='hello'
ANOTHER_ENV_VARIABLE='world'
###< yourname/yourrepo ###

The ###> yourname/yourrepo ### opening comment and ###< yourname/yourrepo ### closing comment are used by Cook to identify the recipe in the file. If you're familiar with Symfony Flex, the syntax is the same.

Example 2: merge or create a .env file with a string input

Alternatively, you can use content instead of source, to avoid creating a file in your repository.

files:
    .env:
        content: |-
            SOME_ENV_VARIABLE='hello'
            ANOTHER_ENV_VARIABLE='world'

PHP array

The PHP array merger adds new entries to existing arrays or creates a file if it doesn't exist.

Example 1: without filters

This recipe will create or merge the file config/bundles.php in the project.

files:
    config/bundles.php:
        type: php_array
        entries:
            Williarin\CookExample\CookExampleBundle:
                dev: true
                test: true

The output will look like this:

<?php

return [
    'Williarin\CookExample\CookExampleBundle' => [
        'dev' => true,
        'test' => true,
    ],
];

Example 2: with filters

Let's add some filters to our entries.

files:
    config/bundles.php:
        # ...
        filters:
            keys: [class_constant]
            values: [single_line_array]

The output will look like this:

<?php

return [
    Williarin\CookExample\CookExampleBundle::class => ['dev' => true, 'test' => true],
];

JSON

The JSON merger adds new entries to an existing JSON file or creates a file if needed.

Note: Only top-level keys are merged.

Example:

This recipe will add a script in the composer.json file of the project.

files:
    composer.json:
        type: json
        entries:
            scripts:
                post-create-project-cmd: php -r "copy('config/local-example.php', 'config/local.php');"

The output will look like this:

{
    // ... existing config
    "scripts": {
        // ... other scripts
        "post-create-project-cmd": "php -r \"copy('config/local-example.php', 'config/local.php');\""
    }
}

YAML

The YAML merger adds new parameters to top-level parameters in an existing file or creates a file if needed.

Although a YAML file represents arrays like JSON or PHP arrays, the specificity of this merger is to allow YAML comments. Therefore, instead of using entries which restricts content as key-value pairs, you need to describe the content to merge as a string, or a YAML file.

Example 1: default config

Given this existing file in config/services.yaml:

parameters:
    database_url: postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=14&charset=utf8

services:
    _defaults:
        autowire: true
        autoconfigure: true

With this recipe:

files:
    config/services.yaml:
        type: yaml
        content: |
            parameters:
                locale: fr

            services:
                Some\Service: ~

The output will look like this:

parameters:
###> williarin/cook-example ###
    locale: fr
###< williarin/cook-example ###
    database_url: postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=14&charset=utf8

services:
###> williarin/cook-example ###
    Some\Service: ~
###< williarin/cook-example ###
    _defaults:
        autowire: true
        autoconfigure: true

Example 2: with blank lines

To make things a bit prettier, let's add a blank line below our services merge:

files:
    config/services.yaml:
        # ...
        blank_line_after: [services]

The output will look like this:

parameters:
###> williarin/cook-example ###
    locale: fr
###< williarin/cook-example ###
    database_url: postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=14&charset=utf8

services:
###> williarin/cook-example ###
    Some\Service: ~
###< williarin/cook-example ###
    
    _defaults:
        autowire: true
        autoconfigure: true

Note: the YAML merger is only able to prepend existing content, not append.

Uninstalling YAML recipe

When uninstalling a recipe, the YAML merger will not remove the entire section if it's empty, unless you set the uninstall_empty_sections parameter to true.

files:
    config/routes.yaml:
        type: yaml
        source: |
            other_routes:
                resource: .
                type: other_routes_loader
        uninstall_empty_sections: true

In this example, if the other_routes section is empty, it will be removed when uninstalling the recipe.

Docker Compose

The Docker Compose merger is similar to the YAML merger with only specific sections that would be merged.

Only services, volumes, configs, secrets and networks top-level sections will be merged.

Placeholders

You can use several placeholders in your destination and source paths:

  • %BIN_DIR%: defaults to bin
  • %CONFIG_DIR%: defaults to config
  • %SRC_DIR%: defaults to src
  • %VAR_DIR%: defaults to var
  • %PUBLIC_DIR%: defaults to public
  • %ROOT_DIR%: defaults to . or, if defined, to extra.symfony.root-dir defined in composer.json

You can override any of these placeholders by defining them in your composer.json file.

    "extra": {
        "bin-dir": "bin",
        "config-dir": "config",
        "src-dir": "src",
        "var-dir": "var",
        "public-dir": "public"
    }

Any other variable defined in extra is available with %YOUR_VARIABLE% in your recipe.

    "extra": {
        "your-variable": "..."
    }

CLI

You may want to execute your recipes after installation. Cook provides you this command to execute all available recipes:

composer cook

It won't overwrite your configuration if it already exists. To overwrite everything, run:

composer cook --overwrite

Additionally, you can uninstall a recipe with this command:

composer cook:uninstall <package> [--all]

Use either <package> for individual package uninstallation or --all for all packages.

License

MIT

Copyright (c) 2023, William Arin