Tasks
This document defines the task system for Morphir, modeled after mise tasks.
Overview
Morphir provides built-in tasks for common operations and allows user-defined tasks for custom workflows. Tasks are run via morphir run <task>.
Built-in Tasks
Morphir and its extensions provide intrinsic tasks that work out of the box:
| Task | Description | Equivalent Command |
|---|---|---|
build | Compile project to IR | morphir build |
test | Run tests | morphir test |
check | Lint and validate | morphir check |
codegen | Generate code for targets | morphir codegen |
clean | Remove build artifacts | morphir clean |
pack | Create distributable package | morphir pack |
publish | Publish to registry | morphir publish |
# These all work without any configuration
morphir run build
morphir run test
morphir run codegen
User-Defined Tasks
Users can define custom tasks or override built-in tasks:
# morphir.toml
[tasks]
# Custom task
integration = "./scripts/integration-tests.sh"
# Override built-in task
test = "morphir test && ./scripts/integration-tests.sh"
morphir run integration # Custom task
morphir run test # Runs overridden test task
Future extension-provided tasks
This section explores a future integration. The current MEP capability model does not let extensions register tasks.
# Tasks provided by a TypeScript codegen extension
morphir run codegen:typescript
# Tasks provided by a Scala codegen extension
morphir run codegen:scala
See the extension overview for the current runtime and protocol boundary.
Task Definition
Inline Tasks
The simplest form is an inline command string:
[tasks]
lint = "morphir check --strict"
clean = "rm -rf .morphir dist/"
Detailed Tasks
For more control, use the detailed table syntax:
[tasks.build]
description = "Build the project and generate TypeScript"
run = "morphir build && morphir codegen typescript"
[tasks.test]
description = "Run all tests"
run = [
"morphir build",
"morphir test",
"./scripts/integration-tests.sh"
]
depends = ["lint"]
Task Options
| Option | Type | Description |
|---|---|---|
run | string or string[] | Command(s) to execute |
description | string | Description shown in morphir run --list |
depends | string[] | Tasks to run before this task |
env | table | Environment variables for the task |
dir | string | Working directory (default: project root) |
sources | string[] | File patterns that trigger rebuild |
outputs | string[] | Expected output files |
hide | bool | Hide from task listings |
Task Dependencies
Tasks can depend on other tasks:
[tasks.lint]
run = "morphir check"
[tasks.test]
run = "morphir test"
depends = ["lint"]
[tasks.ci]
description = "Run full CI pipeline"
depends = ["lint", "test", "build"]
run = "echo 'CI complete'"
Dependencies run in order. If multiple dependencies have no interdependencies, they may run in parallel.
Environment Variables
Set environment variables for tasks:
[tasks.test]
run = "morphir test"
env = { MORPHIR_LOG_LEVEL = "debug", CI = "true" }
[tasks.deploy]
run = "./scripts/deploy.sh"
env = { ENVIRONMENT = "production" }
File-Based Tasks
For complex tasks, use external scripts in a tasks/ or .morphir/tasks/ directory:
my-project/
├── morphir.toml
├── tasks/
│ ├── build.sh
│ ├── deploy.sh
│ └── test.py
File-based tasks are automatically discovered and can include metadata:
#!/usr/bin/env bash
#MISE description="Build and package for release"
#MISE depends=["lint", "test"]
set -euo pipefail
morphir build --release
morphir pack
Reference file tasks in configuration:
[tasks.build]
file = "tasks/build.sh"
[tasks.deploy]
file = "tasks/deploy.sh"
env = { ENVIRONMENT = "production" }
Pre/Post Hooks
Conventional pre: and post: task prefixes allow extending built-in Morphir commands:
[tasks."pre:build"]
description = "Run before morphir build"
run = "echo 'Starting build...'"
[tasks."post:build"]
description = "Run after morphir build"
run = [
"cp .morphir-dist/morphir-ir.json dist/",
"echo 'Build complete'"
]
[tasks."pre:test"]
run = "morphir build"
[tasks."post:codegen"]
run = "prettier --write generated/"
Hook Execution Order
When running morphir build:
pre:buildtask (if defined)- Built-in
morphir buildcommand post:buildtask (if defined)
Available Hooks
| Hook | Triggered By |
|---|---|
pre:build / post:build | morphir build |
pre:test / post:test | morphir test |
pre:codegen / post:codegen | morphir codegen |
pre:pack / post:pack | morphir pack |
pre:publish / post:publish | morphir publish |
pre:clean / post:clean | morphir clean |
Disabling Hooks
# Skip hooks for a single command
morphir build --no-hooks
# Skip specific hook
morphir build --skip-hook=pre:build
Workspace Tasks
In workspaces, tasks can be defined at workspace or project level:
# workspace/morphir.toml
[workspace]
members = ["packages/*"]
[tasks]
# Workspace-level tasks available from anywhere
build-all = "morphir workspace build"
test-all = "morphir workspace test"
# workspace/packages/core/morphir.toml
[project]
name = "my-org/core"
[tasks]
# Project-specific tasks
benchmark = "./scripts/benchmark.sh"
Running Workspace Tasks
# From workspace root
morphir run build-all
# From project directory (runs project task)
cd packages/core
morphir run benchmark
# Run workspace task from project directory
morphir run --workspace build-all
Built-in Variables
Tasks have access to these environment variables:
| Variable | Description |
|---|---|
MORPHIR_PROJECT_ROOT | Project root directory |
MORPHIR_WORKSPACE_ROOT | Workspace root (if in workspace) |
MORPHIR_PROJECT_NAME | Current project name |
MORPHIR_TASK_NAME | Name of the current task |
MORPHIR_CONFIG_DIR | Directory containing morphir.toml |
[tasks.info]
run = "echo Building $MORPHIR_PROJECT_NAME from $MORPHIR_PROJECT_ROOT"
Incremental Tasks
Use sources and outputs for incremental execution:
[tasks.codegen]
description = "Generate TypeScript (incremental)"
run = "morphir codegen typescript --output generated/"
sources = ["src/**/*.morphir", ".morphir-dist/**/*.json"]
outputs = ["generated/**/*.ts"]
The task only runs if sources are newer than outputs.
CLI Reference
# List available tasks
morphir run --list
# Run a task
morphir run <task>
# Run with arguments (passed to task)
morphir run test -- --verbose
# Run multiple tasks
morphir run lint test build
# Dry run (show what would execute)
morphir run --dry-run build
# Force run (ignore incremental check)
morphir run --force codegen
Examples
CI Pipeline with Hooks
Since build, test, and check are built-in tasks, you only need to configure custom behavior:
# Use pre/post hooks to extend built-in tasks
[tasks."pre:test"]
description = "Ensure build is fresh before testing"
run = "morphir build"
[tasks."post:test"]
description = "Generate coverage report"
run = "./scripts/coverage-report.sh"
# Custom CI task that chains built-in tasks
[tasks.ci]
description = "Full CI pipeline"
depends = ["check", "test", "build", "pack"]
run = "echo 'CI passed'"
# Built-in tasks work immediately
morphir run build # Runs pre:build -> build -> post:build
morphir run test # Runs pre:test -> test -> post:test
morphir run ci # Runs the full pipeline
Development Workflow
[tasks.dev]
description = "Start development with watch mode"
run = "morphir workspace watch"
[tasks."post:build"]
description = "Auto-format generated code"
run = "prettier --write .morphir-dist/"
[tasks."post:codegen"]
description = "Format generated TypeScript"
run = "prettier --write generated/"
[tasks.release]
description = "Create a release"
depends = ["test", "build", "pack"]
run = "morphir publish --backend github"
env = { MORPHIR_RELEASE = "true" }
Monorepo Tasks
# workspace/morphir.toml
[tasks.bootstrap]
description = "Initialize workspace after clone"
run = [
"morphir deps resolve",
"morphir workspace build"
]
[tasks.release-all]
description = "Release all packages"
depends = ["test"] # Built-in test runs for all projects
run = "./scripts/release-all.sh"
Custom Integration Tests
# Add integration tests after the built-in test task
[tasks."post:test"]
run = "./scripts/integration-tests.sh"
# Or define a separate task
[tasks.integration]
description = "Run integration tests"
depends = ["build"]
run = "./scripts/integration-tests.sh"
Migration from Toolchain Config
If you previously used [toolchain] configuration, migrate to tasks:
# Before (deprecated)
[toolchain.morphir-elm]
enabled = true
[toolchain.morphir-elm.tasks.make]
exec = "morphir-elm"
args = ["make"]
# After (tasks)
[tasks.elm-make]
description = "Build with morphir-elm"
run = "morphir-elm make"
Design Notes
- Simplicity: Tasks are just shell commands, no special DSL
- Composability: Dependencies allow building complex workflows from simple tasks
- Convention: Pre/post hooks follow predictable naming
- Compatibility: File-based tasks work with any scripting language
- Incremental: Source/output tracking avoids unnecessary work
For current frontend, backend, validator, and transform extension points, see the extension overview. The archived Component Model page is not the current ABI.
Related
Morphir Rust Design Documents
- Morphir Extensions - Extension system overview
- Historical WASM Component Model - Superseded WIT design rationale
- Morphir Daemon - Workspace and build management
Main Morphir Documentation
- Morphir Documentation - Main Morphir documentation site
- Morphir LLMs.txt - Machine-readable documentation index
- Morphir IR v4 Design - IR v4 design documents
- Morphir IR Specification - Complete IR specification