Contributing a Plugin
DevRail plugins extend the dev-toolchain image with new languages or tool integrations without a fork or PR against the core repos. The plugin loader (shipped in v1.10.0+) reads plugins: from a consumer’s .devrail.yml, resolves each entry to an immutable git ref, builds a project-local extended image (devrail-local:<hash>), and dispatches plugin-defined targets inside the existing make check recipes.
If you have a tool you want every DevRail-managed project to use, you can ship it as a plugin instead of opening a PR against dev-toolchain. This page is the high-level overview; the canonical authoring guide with full templates is in the devrail-standards repo.
Canonical Reference
The authoritative, detailed plugin authoring guide lives atstandards/contributing.md § Contributing a Plugin. This page provides the overview; the canonical guide provides field-by-field schema, container integration patterns, and a publishing checklist.What is a plugin?
A plugin is a git repository containing a plugin.devrail.yml manifest at the repo root. The manifest declares:
- Identity —
name,version,schema_version,devrail_min_version - Container fragment —
apt_packages,copy_from_builder,env,install_script(layered onto the core dev-toolchain image) - Targets —
lint,format_check,format_fix,fix,test,securitycommands - Gates — per-target paths that must exist for the target to run
When a consumer declares your plugin in their .devrail.yml, make check automatically:
- Fetches the plugin’s manifest at the pinned
rev:and validates it against the schema - Generates a
Dockerfile.devrailextending the core image with your container fragment - Builds
devrail-local:<hash>(cached by content hash — unchanged plugin sets reuse the image) - Runs your targets alongside core-language targets, aggregating results into the same JSON envelope
Quick start
Create the repo. Convention is
devrail-plugin-<name>(the trailing path component is not enforced — the manifest’snamefield is authoritative — but encouraged for discoverability).Add
plugin.devrail.yml:schema_version: 1 name: elixir version: 1.0.0 devrail_min_version: 1.10.0 container: base_image: elixir:1.17-slim install_script: install.sh copy_from_builder: - /usr/local/bin/elixir - /usr/local/bin/mix - /usr/local/lib/elixir env: MIX_ENV: prod targets: lint: cmd: "mix credo --strict {paths}" paths_var: ELIXIR_PATHS paths_default: "lib test" test: cmd: "mix test" gates: lint: ["mix.exs"] test: ["mix.exs", "test/"]Test locally against a consumer workspace using a
file://URL:# In the consumer's .devrail.yml languages: - elixir plugins: - source: file:///home/you/devrail-plugin-elixir rev: v1.0.0 languages: [elixir]make plugins-update # resolver fetches the file:// fixture make check # full pipeline runs your pluginTag and publish an annotated semver tag (
git tag -a v1.0.0). Consumers pin viarev: v1.0.0(or a full SHA) — branch refs are rejected.
Versioning
schema_versionis the manifest format. Pinned at1for the v1.10.x line. The loader rejects unknown majors.versionis your plugin’s own semver. Bump on each release.devrail_min_versionis the oldest dev-toolchain version your plugin supports. Set to1.10.0for plugins targeting the first stable plugin-loader release.- The consumer’s
.devrail.lockrecords the resolved SHA + content hash. Re-tagging an existing tag onto different code is detected via content_hash mismatch.
Override surface
Consumers can override your manifest defaults from their .devrail.yml:
elixir:
linter: dialyxir # replaces targets.lint.cmd
test: "mix test --cover" # replaces targets.test.cmd
Override key map: lint→linter, format_check/format_fix→formatter, fix→fixer, test→test, security→security.
What’s NOT in scope for v1.10
These are deferred to later phases:
- Plugin signing — content_hash detects tampering, but not authenticity. Coming in a later release.
- Sidecar / volume-mounted plugins — extended image (Option A) is the only execution mode in v1.
- Parallel plugin execution — sequential per design; needs shared-state semantics first.
Next steps
- Read the canonical plugin authoring guide for field-by-field details, container integration patterns, and the pre-publish checklist.
- Read the plugin architecture design doc for the full rationale and lifecycle.
- See the
plugins:schema documentation for the consumer-side declaration shape.