\documentclass{article}
\usepackage{ulem}
\pagestyle{headings}
\begin{document}
\part{ The .horde.yml File Format}
This document describes the .horde.yml file format used by Horde Framework 6 components. The .horde.yml file serves as the canonical source of component metadata and is used by the horde-components tool to generate composer.json files and manage releases. For older versions of this format, see Doc/Dev/HordeYmlFormat\footnote{http://example.com/index.php?page=Doc/Dev/HordeYmlFormat}

\section{ Overview}
The .horde.yml format has evolved from Horde Framework 5.2 through the git split and into Horde 6. \newline
It provides a unified way to declare component metadata, dependencies, autoloading rules and other configuration that can be transformed into various package management formats.

Key characteristics:

\begin{itemize}
\item{YAML 1.2 format}
\item{Single source of truth for component metadata}
\item{Generates composer.json automatically via horde-components tool}
\item{Supports both composer-native and legacy PEAR dependencies}
\item{Provides autoloading configuration for PSR-0, PSR-4 and classmap strategies}
\end{itemize}
\section{ Meta Data Section}
These fields appear at the top level of the .horde.yml file and define the component's identity and basic properties.

\subsection{ Required Fields}
\underline{id} (string, mandatory)

\begin{itemize}
\item{Component identifier}
\item{For libraries: uppercase without Horde\_ prefix (e.g., Autoloader, Exception, Browser)}
\item{For applications: lowercase (e.g., horde, turba, ansel)}
\end{itemize}
\underline{name} (string, mandatory)

\begin{itemize}
\item{Display name with proper capitalization (e.g., Horde, Turba, Autoloader, Exception)}
\end{itemize}
\underline{full} (string, mandatory)

\begin{itemize}
\item{One-line description of the component}
\item{Should be concise and descriptive}
\end{itemize}
\underline{description} (string, mandatory)

\begin{itemize}
\item{Multi-line description of the component}
\item{Can use YAML multi-line string syntax (>- for folded strings)}
\item{Provides detailed information about component functionality}
\end{itemize}
\underline{list} (string, mandatory)

\begin{itemize}
\item{Mailing list appropriate for the component}
\item{Common values: dev, horde, or application-specific list names}
\item{May be empty string if no specific list applies}
\end{itemize}
\underline{type} (string, mandatory)

\begin{itemize}
\item{Component type indicator}
\item{Classic values: application, library - these work for regular composer packages.}
\item{Modern Horde 6 values: horde-application, horde-library, horde-theme, component - these trigger special handling by the horde/horde-composer-plugin installer for components which need it.}
\item{Used to determine composer package type}
\end{itemize}
\underline{authors} (list, mandatory)

\begin{itemize}
\item{List of contributor information}
\item{Each entry has the following attributes:}
\end{itemize}
\textbf{ \underline{name} (string): Full name of the contributor\newline
} \underline{user} (string): Horde username or identifier\newline
\textbf{ \underline{email} (string): Email address\newline
} \underline{active} (boolean): true if currently active, false if inactive\newline
** \underline{role} (string): Contributor role (lead, developer, contributor)

\underline{version} (object, mandatory)

\begin{itemize}
\item{Contains two sub-keys:}
\end{itemize}
\textbf{ \underline{release} (string): Current release version in semantic versioning format (e.g., 3.0.0-alpha10)\newline
} \underline{api} (string): Current API version (e.g., 3.0.0alpha1)

\begin{itemize}
\item{Version numbers follow semantic versioning with optional stability suffixes}
\item{Pre-release versions use formats like: 1.0.0-alpha1, 2.0.0-beta3}
\end{itemize}
\underline{state} (object, mandatory)

\begin{itemize}
\item{Contains two sub-keys:}
\end{itemize}
\textbf{ \underline{release} (string): Release stability (stable, beta, alpha)\newline
} \underline{api} (string): API stability (stable, beta, alpha)

\begin{itemize}
\item{API stability can differ from release stability}
\end{itemize}
\underline{license} (object, mandatory)

\begin{itemize}
\item{Contains two sub-keys:}
\end{itemize}
\textbf{ \underline{identifier} (string): SPDX license identifier (e.g., LGPL-2.1-only, BSD-2-Clause, GPL-2.0-only)\newline
} \underline{uri} (string): Link to full license text (e.g., http://www.horde.org/licenses/lgpl21\footnote{http://www.horde.org/licenses/lgpl21})

\subsection{ Optional Fields}
\underline{homepage} (string)

\begin{itemize}
\item{URL to component's homepage or documentation}
\item{Typically https://www.horde.org/libraries/Horde\_ComponentName\footnote{https://www.horde.org/libraries/Horde\_ComponentName} or https://www.horde.org/apps/appname\footnote{https://www.horde.org/apps/appname}}
\end{itemize}
\underline{vendor} (string)

\begin{itemize}
\item{Composer vendor name}
\item{Defaults to "horde" if not specified}
\item{Used to generate composer package name (vendor/id)}
\end{itemize}
!++ Example Meta Data

\^\newline
---\newline
id: Browser\newline
name: Browser\newline
full: Browser detection library\newline
description: >-\newline
  A library for getting information about the current user's browser and its\newline
  capabilities.\newline
list: dev\newline
type: library\newline
homepage: https://www.horde.org/libraries/Horde\_Browser\footnote{https://www.horde.org/libraries/Horde\_Browser}\newline
authors:\newline
  -\newline
    name: Chuck Hagenbuch\newline
    user: chuck\newline
    email: chuck@horde.org\newline
    active: false\newline
    role: lead\newline
  -\newline
    name: Michael Slusarz\newline
    user: slusarz\newline
    email: slusarz@horde.org\newline
    active: false\newline
    role: developer\newline
version:\newline
  release: 3.0.0-alpha10\newline
  api: 3.0.0alpha1\newline
state:\newline
  release: alpha\newline
  api: alpha\newline
license:\newline
  identifier: LGPL-2.1-only\newline
  uri: http://www.horde.org/licenses/lgpl21\footnote{http://www.horde.org/licenses/lgpl21}\newline
vendor: horde\newline
\^

\section{ Dependencies Section}
The dependencies section declares required, optional and development dependencies. It supports PHP version constraints, PHP extensions, composer packages and legacy PEAR packages.

Don't use PEAR packages anymore. It's strictly for Backwards Compatibility.

!++ Structure

\^\newline
dependencies:\newline
  uses: readonly-parameters           \# Optional: special feature flags\newline
  required:\newline
    php: version-constraint           \# PHP version requirement\newline
    ext:                              \# PHP binary extensions\newline
      extension-name: version-constraint\newline
    composer:                         \# Composer packages\newline
      vendor/package: version-constraint\newline
    pear:                             \# Legacy PEAR packages\newline
      channel/package: version-constraint\newline
  optional:\newline
    composer:                         \# Optional composer packages\newline
      vendor/package: version-constraint\newline
    ext:                              \# Optional PHP extensions\newline
      extension-name: version-constraint\newline
    pear:                             \# Optional PEAR packages\newline
      channel/package: version-constraint\newline
  dev:\newline
    composer:                         \# Development-only composer packages\newline
      vendor/package: version-constraint\newline
\^

!++ Required Dependencies

\underline{dependencies: required: php} (string)

\begin{itemize}
\item{PHP version constraint using composer syntax}
\item{Examples: \^7.4 || \^8, \^8, \^8.2}
\item{Determines minimum PHP version}
\end{itemize}
\underline{dependencies: required: ext} (object)

\begin{itemize}
\item{Map of PHP extension names to version constraints}
\item{Version constraint can be '*' for any version or a specific version}
\item{Common extensions: hash, pdo, gettext, gd, imagick}
\end{itemize}
\underline{dependencies: required: composer} (object)

\begin{itemize}
\item{Map of composer package names to version constraints}
\item{Package names in vendor/package format}
\item{For Horde packages: horde/package-name (lowercase)}
\item{Version constraints use composer syntax: \^3, \^3.0, $\sim$2.0.0, >=1.0}
\end{itemize}
\underline{dependencies: required: pear} (object)

\begin{itemize}
\item{Map of PEAR package names to version constraints}
\item{Package names in channel/package format (e.g., pear.horde.org/Horde\_Date)}
\item{Primarily for legacy compatibility}
\item{Horde 6 components prefer composer dependencies}
\end{itemize}
!++ Optional Dependencies

\underline{dependencies: optional} (object)

\begin{itemize}
\item{Same structure as required section}
\item{Declares suggested packages that enhance functionality}
\item{Can contain composer, ext and pear sub-keys}
\item{Used for feature-specific dependencies}
\end{itemize}
!++ Development Dependencies

\underline{dependencies: dev: composer} (object)

\begin{itemize}
\item{Development-only composer dependencies}
\item{Typically includes testing tools and code quality tools}
\item{Examples: horde/test, phpunit/phpunit, phpstan/phpstan}
\item{Generates composer.json require-dev section}
\end{itemize}
!++ Special Features

\underline{dependencies: uses} (string)

\begin{itemize}
\item{Declares special language or framework features in use}
\item{Currently known value: readonly-parameters}
\item{Used for compatibility checking and code analysis}
\end{itemize}
!++ Example Dependencies

\^\newline
dependencies:\newline
  uses: readonly-parameters\newline
  required:\newline
    php: \^8.2\newline
    ext:\newline
      hash: '*'\newline
      gettext: '*'\newline
    composer:\newline
      horde/exception: \^3\newline
      horde/translation: \^3\newline
      horde/util: \^3\newline
  optional:\newline
    ext:\newline
      gd: '*'\newline
      imagick: '*'\newline
    composer:\newline
      horde/cache: \^3\newline
      horde/log: \^3\newline
  dev:\newline
    composer:\newline
      horde/test: \^3\newline
      phpunit/phpunit: \^12 || \^11\newline
      phpstan/phpstan: \^2\newline
\^

\section{ Autoload Section}
The autoload section defines how PHP classes should be found within the component. It supports PSR-0, PSR-4 and classmap strategies.

!++ Structure

\^\newline
autoload:\newline
  psr-0:                              \# PSR-0 autoloading\newline
    Class\_Prefix: lib/\newline
  psr-4:                              \# PSR-4 autoloading\newline
    Vendor\\Namespace\\: src/\newline
  classmap:                           \# Classmap autoloading\newline
    - lib/\newline
    - other-dir/\newline
\^

The horde-components tool auto-senses some common autoloading patterns and sets up defaults if no autoload section is present.

!++ Autoload Strategies

\underline{autoload: psr-0} (object)

\begin{itemize}
\item{Map of class name prefixes to directories}
\item{Legacy autoloading standard using underscores as directory separators}
\item{Example: Horde\_Http in lib/ directory}
\item{Directory is relative to component root}
\item{Using PSR-0 autoloader with "" namespace should be avoided. Use the classmap instead.}
\end{itemize}
\underline{autoload: psr-4} (object)

\begin{itemize}
\item{Map of namespaces to directories}
\item{Modern autoloading standard using namespace separators}
\item{Namespace must end with backslash}
\item{Example: Horde\\Browser\\ maps to src/}
\item{Directory is relative to component root}
\end{itemize}
\underline{autoload: classmap} (array)

\begin{itemize}
\item{List of directories to recursively scan for PHP files}
\item{Each PHP file is scanned for class definitions}
\item{Useful for legacy code without consistent naming}
\item{Example: ['lib/'] scans all PHP files in lib/ directory}
\end{itemize}
!++ Autoload-Dev Section

\underline{autoload-dev} (object)

\begin{itemize}
\item{Same structure as autoload section}
\item{Applied only in development context}
\item{Typically used for test namespaces}
\item{If test directory exists, PSR-4 rule is auto-generated for Vendor\\Package\\Test\\ namespace}
\end{itemize}
!++ Default Behavior

If no autoload section is present, horde-components will auto-detect:

\begin{itemize}
\item{If lib/ directory exists: generates PSR-0 rule from package name}
\item{If src/ directory exists: generates PSR-4 rule from package name}
\item{If test/ directory exists: generates PSR-4 autoload-dev rule for Test namespace}
\end{itemize}
!++ Example Autoload

\^\newline
autoload:\newline
  psr-0:\newline
    Horde\_Browser: lib/\newline
  psr-4:\newline
    Horde\\Browser\\: src/\newline
  classmap:\newline
    - lib/legacy/\newline
\^

\^\newline
autoload:\newline
  classmap:\newline
    - lib/\newline
  psr-4:\newline
    Horde\\Ansel\\: src/\newline
\^

\section{ Composer Integration}
Several additional sections support composer-specific features.

!++ Commands Section

\underline{commands} (array, optional)

\begin{itemize}
\item{List of paths relative to package root offered as vendor binaries}
\item{If provided, no automatic search in bin/ directory occurs}
\item{Each path should point to an executable file}
\item{These become composer bin entries}
\end{itemize}
\underline{nocommands} (array, optional)

\begin{itemize}
\item{List of paths relative to package root excluded from vendor binaries}
\item{Takes precedence over commands list or implicitly found bin/ executables}
\item{Used to exclude non-binary files from bin/ directory}
\end{itemize}
Example:\newline
\^nocommands:\newline
  - bin/horde-bootstrap\newline
  - bin/horde-components-prototype-transpile\newline
\^

!++ Conflicts Section

\underline{conflicts} (object, optional)

\begin{itemize}
\item{Declares package conflicts in composer format}
\item{Directly transformed into composer.json conflict section}
\item{No composer or pear sub-keys (flat structure)}
\item{Used to prevent incompatible versions from coexisting}
\end{itemize}
Example:\newline
\^conflicts:\newline
  horde/base: <= 5.9.9\newline
  horde/horde: <= 5.9.9\newline
\^

Use conflicts when already-released packages have overly liberal constraints that allow problematic combinations.

!++ Provides Section

\underline{provides} (object, optional)

\begin{itemize}
\item{Declares virtual packages or capabilities provided}
\item{Directly transformed into composer.json provide section}
\item{No composer or pear sub-keys (flat structure)}
\item{Used for interface compliance or successor relationships}
\end{itemize}
Example:\newline
\^provides:\newline
  horde/base: 6.1.1\newline
  horde/user-management-api-implementation: 1.0\newline
\^

Use provides when:

\begin{itemize}
\item{Multiple packages can satisfy the same capability}
\item{Package is successor to another package (e.g., ingo succeeds forwards and vacation modules)}
\item{Package provides an interface that others can depend on}
\end{itemize}
!++ Allow-Plugins Section

\underline{allow-plugins} (mixed, optional)

\begin{itemize}
\item{Controls which composer plugins are allowed to run}
\item{Can be tilde ($\sim$) to allow all plugins}
\item{Can be object mapping plugin names to boolean values}
\item{Prevents composer from prompting about plugin permissions}
\end{itemize}
Example:\newline
\^allow-plugins: $\sim$\newline
\^

Example:\newline
\^allow-plugins:\newline
  horde/horde-installer-plugin: true\newline
  composer/installers: true\newline
\^

\section{ Quality Section}
The quality section configures code quality tools.

!++ Structure

\^\newline
quality:\newline
  phpstan:\newline
    level: 9\newline
  php-cs-fixer:\newline
    enabled: true\newline
\^

!++ PHPStan Configuration

\underline{quality: phpstan: level} (integer)

\begin{itemize}
\item{Sets PHPStan analysis level (0-9)}
\item{Higher levels are more strict}
\item{Level 9 is the maximum strictness}
\end{itemize}
This setting instructs the horde-components qc and ci to FAIL if a previous water mark was no longer achieved.

\section{ Complete Examples}
!++ Modern Horde 6 Library

\^\newline
---\newline
id: Browser\newline
name: Browser\newline
full: Browser detection library\newline
description: >-\newline
  A library for getting information about the current user's browser and its\newline
  capabilities.\newline
list: dev

\renewcommand{\labelenumi}{\arabic{enumi}}
\begin{enumerate}
\item{use horde-library if you need integration with the horde/horde-installer-plugin, i.e. javascript files which need to be exposed to web/ directory or horde database migrations in the migration/ directory. Otherwise use "library"}
\end{enumerate}
type: library\newline
homepage: https://www.horde.org/libraries/Horde\_Browser\footnote{https://www.horde.org/libraries/Horde\_Browser}\newline
authors:\newline
  -\newline
    name: Chuck Hagenbuch\newline
    user: chuck\newline
    email: chuck@horde.org\newline
    active: false\newline
    role: lead\newline
version:\newline
  release: 3.0.0-alpha10\newline
  api: 3.0.0alpha1\newline
state:\newline
  release: alpha\newline
  api: alpha\newline
license:\newline
  identifier: LGPL-2.1-only\newline
  uri: http://www.horde.org/licenses/lgpl21\footnote{http://www.horde.org/licenses/lgpl21}\newline
dependencies:\newline
  required:\newline
    php: \^7.4 || \^8\newline
    composer:\newline
      horde/exception: \^3\newline
      horde/translation: \^3\newline
      horde/util: \^3\newline
vendor: horde\newline
\^

!++ Application with Mixed Dependencies

\^\newline
---\newline
id: ansel\newline
name: Ansel\newline
full: Photo gallery application\newline
description: Ansel is a full featured photo gallery application.\newline
list: ansel\newline
type: application\newline
homepage: https://www.horde.org/apps/ansel\footnote{https://www.horde.org/apps/ansel}\newline
authors:\newline
  -\newline
    name: Michael J Rubinsky\newline
    user: mrubinsk\newline
    email: mrubinsk@horde.org\newline
    active: true\newline
    role: lead\newline
version:\newline
  release: 4.0.0-alpha6\newline
  api: 4.0.0alpha1\newline
state:\newline
  release: alpha\newline
  api: beta\newline
license:\newline
  identifier: GPL-2.0-only\newline
  uri: http://www.horde.org/licenses/gpl\footnote{http://www.horde.org/licenses/gpl}\newline
dependencies:\newline
  required:\newline
    php: \^7.4 || \^8\newline
    composer:\newline
      horde/horde: \^6\newline
      horde/core: \^3\newline
      horde/form: \^3\newline
      horde/image: \^3\newline
    ext:\newline
      gettext: '*'\newline
      hash: '*'\newline
  optional:\newline
    composer:\newline
      horde/service\_twitter: \^3\newline
      horde/service\_facebook: \^3\newline
    ext:\newline
      gd: '*'\newline
      imagick: '*'\newline
autoload:\newline
  classmap:\newline
    - lib/\newline
  psr-4:\newline
    Horde\\Ansel\\: src/\newline
vendor: horde\newline
\^

!++ Component with Quality Tools

\^\newline
---\newline
id: components\newline
name: Components\newline
full: Developer tool for managing Horde components\newline
description: >-\newline
  The package provides utility methods required when preparing a new component\newline
  release for Horde. It also includes quality control checks.\newline
list: horde\newline
type: component\newline
authors:\newline
  -\newline
    name: Jan Schneider\newline
    user: jan\newline
    email: jan@horde.org\newline
    active: true\newline
    role: lead\newline
version:\newline
  release: 1.0.0-alpha39\newline
  api: 1.0.0alpha1\newline
state:\newline
  release: alpha\newline
  api: alpha\newline
license:\newline
  identifier: LGPL-2.1-only\newline
  uri: http://www.horde.org/licenses/lgpl21\footnote{http://www.horde.org/licenses/lgpl21}\newline
dependencies:\newline
  required:\newline
    php: \^8.2\newline
    composer:\newline
      horde/cli: \^3\newline
      horde/argv: '*'\newline
      horde/util: '*'\newline
  optional:\newline
    composer:\newline
      horde/test: \^3\newline
      phpunit/phpunit: \^9\newline
  dev:\newline
    composer:\newline
      horde/test: \^3\newline
      phpunit/phpunit: \^12 || \^11 || \^10 || \^9\newline
nocommands:\newline
  - bin/horde-bootstrap\newline
  - bin/horde-components-prototype-transpile\newline
allow-plugins: $\sim$\newline
quality:\newline
  phpstan:\newline
    level: 9\newline
vendor: horde\newline
\^

\section{ Related Tools}
The .horde.yml file is primarily managed by the horde-components\footnote{http://example.com/index.php?page=horde-components} tool:

\begin{itemize}
\item{Validates .horde.yml syntax and completeness}
\item{Generates composer.json from .horde.yml}
\item{Updates version numbers during releases}
\item{Transforms PEAR dependencies to composer equivalents}
\item{Manages release workflow}
\end{itemize}
For more information on release management, see Doc/Dev/ReleaseProcess\footnote{http://example.com/index.php?page=Doc/Dev/ReleaseProcess}.

\section{ Migration from Horde 5}
When migrating from Horde 5 to Horde 6:

\begin{itemize}
\item{Update type from "library" or "application" to "horde-library" or "horde-application" if applicable - if default composer behaviour is sufficient, keep the default types.}
\item{Convert pear dependencies under dependencies:required:pear to composer dependencies}
\item{Add vendor: horde if not present (automatic update by some horde-components operations)}
\item{Update PHP version constraints. Horde 6.0 targets php 8.1-8.5 but if you really support older versions, say so. }
\item{Review and update license identifiers to use most recent edition of SPDX format}
\item{Only add autoload section if auto-sensing doesn't get it right.}
\item{Consider adding quality section for static analysis (can be done by horde-components qc --fix command)}
\end{itemize}
\section{ See Also}
\begin{itemize}
\item{Doc/Dev/ReleaseProcess\footnote{http://example.com/index.php?page=Doc/Dev/ReleaseProcess} - Component release workflow}
\item{Doc/Dev/ComposerIntegration\footnote{http://example.com/index.php?page=Doc/Dev/ComposerIntegration} - Composer integration details}
\item{horde-components\footnote{http://example.com/index.php?page=horde-components} - Component management tool}
\end{itemize}
\end{document}
