From b06ab81273159ca0a52135ce6012f59d04fcd120 Mon Sep 17 00:00:00 2001
From: Claude
Date: Mon, 15 Dec 2025 09:20:34 +0000
Subject: [PATCH 1/3] feat: Adding working_directory support for commands in
app.json
Users can now specify a custom working_directory for each command
in their app.json configuration. This allows commands to run in
different directories than the app's default path.
Changes:
- Added WorkingDirectory field to CommandSpec struct
- Updated pr.yaml template to use command-specific working_directory
- Regenerated JSON schema to include new field
- Added tests for working_directory field unmarshalling
- Updated playground workflows with regenerated templates
Example usage:
{
"commands": {
"test": {
"command": "pnpm test",
"working_directory": "packages/core"
}
}
}
Backwards compatible: commands without working_directory continue
to use the app's path as before.
---
internal/appdef/commands.go | 11 +--
internal/appdef/commands_test.go | 8 ++
internal/playground/.github/dependabot.yaml | 39 +++++----
.../playground/.github/workflows/backup.yaml | 3 -
.../playground/.github/workflows/release.yaml | 9 +-
internal/playground/.webkit/manifest.json | 24 +++---
internal/playground/.webkit/schema.json | 83 +++++++++++--------
internal/playground/README.md | 39 +++++----
.../templates/.github/workflows/pr.yaml.tmpl | 4 +-
internal/templates/schema.json | 8 ++
schema.json | 8 ++
11 files changed, 146 insertions(+), 90 deletions(-)
diff --git a/internal/appdef/commands.go b/internal/appdef/commands.go
index 60eaf0a6..434fe042 100644
--- a/internal/appdef/commands.go
+++ b/internal/appdef/commands.go
@@ -11,11 +11,12 @@ import (
// Commands can be specified as a boolean (enable/disable), a string (command override),
// or an object (full configuration with timeout and CI settings).
type CommandSpec struct {
- Name string `json:"-"`
- Cmd string `json:"command,omitempty" description:"The shell command to execute (e.g., 'pnpm build')"`
- SkipCI bool `json:"skip_ci,omitempty" description:"Whether to skip running this command in CI/CD pipelines"`
- Timeout string `json:"timeout,omitempty" description:"Maximum execution time for the command (e.g., '5m', '1h')"`
- Disabled bool `json:"-"` // Set during unmarshal
+ Name string `json:"-"`
+ Cmd string `json:"command,omitempty" description:"The shell command to execute (e.g., 'pnpm build')"`
+ SkipCI bool `json:"skip_ci,omitempty" description:"Whether to skip running this command in CI/CD pipelines"`
+ Timeout string `json:"timeout,omitempty" description:"Maximum execution time for the command (e.g., '5m', '1h')"`
+ WorkingDirectory string `json:"working_directory,omitempty" description:"Working directory for the command (defaults to app path if not specified)"`
+ Disabled bool `json:"-"` // Set during unmarshal
}
// Ensure CommandSpec implements jsonschema.OneOfExposer
diff --git a/internal/appdef/commands_test.go b/internal/appdef/commands_test.go
index fb660200..593cfe73 100644
--- a/internal/appdef/commands_test.go
+++ b/internal/appdef/commands_test.go
@@ -30,6 +30,14 @@ func TestCommandSpecUnmarshalJSON(t *testing.T) {
input: []byte(`{"command":"run tests","skip_ci":true,"timeout":"5m"}`),
want: CommandSpec{Cmd: "run tests", SkipCI: true, Timeout: "5m"},
},
+ "Full Object with WorkingDirectory": {
+ input: []byte(`{"command":"run tests","skip_ci":true,"timeout":"5m","working_directory":"./subdir"}`),
+ want: CommandSpec{Cmd: "run tests", SkipCI: true, Timeout: "5m", WorkingDirectory: "./subdir"},
+ },
+ "Object with only WorkingDirectory": {
+ input: []byte(`{"command":"pnpm build","working_directory":"./packages/core"}`),
+ want: CommandSpec{Cmd: "pnpm build", WorkingDirectory: "./packages/core"},
+ },
"Invalid JSON": {
input: []byte(`{"invalid":`),
wantErr: true,
diff --git a/internal/playground/.github/dependabot.yaml b/internal/playground/.github/dependabot.yaml
index 46091740..10228352 100755
--- a/internal/playground/.github/dependabot.yaml
+++ b/internal/playground/.github/dependabot.yaml
@@ -10,26 +10,34 @@ updates:
time: "04:00"
open-pull-requests-limit: 30
ignore:
+ # Ignore patch updates to reduce PR volume
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
+ # Payload ecosystem is managed via 'webkit payload bump' command
+ # These dependencies are automatically updated from Payload's template package.json
+ - dependency-name: "payload"
+ - dependency-name: "@payloadcms/*"
+ - dependency-name: "@ainsleydev/payload-helper"
+ - dependency-name: "cross-env"
+ - dependency-name: "dotenv"
+ - dependency-name: "graphql"
+ - dependency-name: "next"
+ - dependency-name: "react"
+ - dependency-name: "react-dom"
+ - dependency-name: "sharp"
groups:
- payload:
+ svelte:
patterns:
- - "@payloadcms/*"
- - "payload"
- - "@ainsleydev/payload-helper"
+ - "svelte"
+ - "@sveltejs/*"
update-types:
- "minor"
- "patch"
- core:
+ react:
patterns:
- "next"
- "react"
- "react-dom"
- - "graphql"
- - "sharp"
- - "cross-env"
- - "dotenv"
update-types:
- "minor"
- "patch"
@@ -37,16 +45,19 @@ updates:
patterns:
- "*"
exclude-patterns:
- - "@payloadcms/*"
- - "payload"
- - "@ainsleydev/payload-helper"
+ - "svelte"
+ - "@sveltejs/*"
- "next"
- "react"
- "react-dom"
- - "graphql"
- - "sharp"
+ # Exclude all Payload template dependencies (managed by 'webkit payload bump')
+ - "payload"
+ - "@payloadcms/*"
+ - "@ainsleydev/payload-helper"
- "cross-env"
- "dotenv"
+ - "graphql"
+ - "sharp"
- package-ecosystem: "gomod"
directory: "/api"
schedule:
diff --git a/internal/playground/.github/workflows/backup.yaml b/internal/playground/.github/workflows/backup.yaml
index be09067d..ec5d5132 100755
--- a/internal/playground/.github/workflows/backup.yaml
+++ b/internal/playground/.github/workflows/backup.yaml
@@ -82,7 +82,6 @@ jobs:
run: |
# Delete backups older than 30 days
rclone delete b2_backup:${BUCKET_NAME}${BACKUP_PATH} --min-age 30d
-
- name: Ping Peekaping Heartbeat Monitor
if: success()
uses: ./.github/actions/peekaping-ping
@@ -147,7 +146,6 @@ jobs:
- name: Verify Backup
run: |
echo "Backup completed successfully!"
-
- name: Ping Peekaping Heartbeat Monitor
if: success()
uses: ./.github/actions/peekaping-ping
@@ -210,7 +208,6 @@ jobs:
- name: Verify Backup
run: |
echo "Codebase backup completed successfully!"
-
- name: Ping Peekaping Heartbeat Monitor
if: success()
uses: ./.github/actions/peekaping-ping
diff --git a/internal/playground/.github/workflows/release.yaml b/internal/playground/.github/workflows/release.yaml
index 34af9d53..6d22a9e8 100755
--- a/internal/playground/.github/workflows/release.yaml
+++ b/internal/playground/.github/workflows/release.yaml
@@ -60,13 +60,13 @@ jobs:
matrix:
service:
- name: cms
- context: ./cms
+ context: cms
dockerfile: ./cms/Dockerfile
- name: web
- context: ./web
+ context: web
dockerfile: ./web/Dockerfile
- name: api
- context: ./api
+ context: api
dockerfile: ./api/Dockerfile
steps:
- name: Checkout Repository
@@ -373,6 +373,7 @@ jobs:
-e docker_image=${{ github.event.repository.name }}-cms
-e docker_image_tag=sha-${{ steps.determine_sha.outputs.sha }}
-e docker_port=3000
+ -e health_check_path=/
-e enable_https=true
-e admin_email=hello@ainsley.dev
-e env_file_source_path=/tmp/cms.env
@@ -408,7 +409,7 @@ jobs:
# Notify Slack on release failure
notify-failure:
runs-on: ubuntu-latest
- needs: [build-and-push,deploy-vm-cms,deploy-app-web]
+ needs: [build-and-push, deploy-vm-cms, deploy-app-web]
if: failure()
steps:
- name: Checkout Repository
diff --git a/internal/playground/.webkit/manifest.json b/internal/playground/.webkit/manifest.json
index 87bb5d51..5ac2cb44 100644
--- a/internal/playground/.webkit/manifest.json
+++ b/internal/playground/.webkit/manifest.json
@@ -1,6 +1,6 @@
{
- "version": "v0.7.4",
- "generated_at": "2025-11-24T19:50:05.385737Z",
+ "version": "v0.9.8",
+ "generated_at": "2025-12-15T09:17:57.472267068Z",
"files": {
"./turbo.json": {
"path": "./turbo.json",
@@ -86,9 +86,9 @@
"path": ".github/dependabot.yaml",
"generator": "files:GitSettings",
"source": "project",
- "hash": "f1ce4b0dcad7973e83ce319052e08282eb6c9ee839deb93ccf2e8f07d1f72df1",
+ "hash": "dd5045db25b3ea4c4c94e073479c2d7f00d4d380ddddba25ab47cae2c26315ca",
"scaffolded": false,
- "generated_at": "2025-11-24T19:50:05.376785Z"
+ "generated_at": "2025-12-15T09:16:05.705711066Z"
},
".github/settings.yml": {
"path": ".github/settings.yml",
@@ -102,25 +102,25 @@
"path": ".github/workflows/backup.yaml",
"generator": "cicd:BackupWorkflow",
"source": "resource:store",
- "hash": "25dabcd4231f662b2267b92e631700d9c0286b9088f59b05102cc99f55a84de6",
+ "hash": "1014a62481cb9cbc4bcbe0aa67777ba953c0157670269ca6793b2fe51ad21914",
"scaffolded": false,
- "generated_at": "2025-11-24T19:50:05.381915Z"
+ "generated_at": "2025-12-15T09:16:05.711894006Z"
},
".github/workflows/pr.yaml": {
"path": ".github/workflows/pr.yaml",
"generator": "cicd:PR",
"source": "project",
- "hash": "7206cb778668a8df1bc8b330ba887bacc5874109f72f231b1e72f96e7b17e1fd",
+ "hash": "3d498e017141afd45db9159a9b82d229ccc2130ad4f46893ccc745a9d7188b96",
"scaffolded": false,
- "generated_at": "2025-11-24T19:50:05.380818Z"
+ "generated_at": "2025-12-15T09:17:57.461325727Z"
},
".github/workflows/release.yaml": {
"path": ".github/workflows/release.yaml",
"generator": "cicd:ReleaseWorkflow",
"source": "app:api",
- "hash": "8095399f3678fb40b987cc3bd542f09adad52eb00e93ba44a7d41af52532213d",
+ "hash": "a75be6c0a208ec49fcae66e2a2763908ed4f95f9c6cfdeb2340a0e7eb85bb07d",
"scaffolded": false,
- "generated_at": "2025-11-20T13:42:31.968943Z"
+ "generated_at": "2025-12-15T09:16:05.710992465Z"
},
".github/workflows/server-maintenance.yaml": {
"path": ".github/workflows/server-maintenance.yaml",
@@ -166,9 +166,9 @@
"path": "README.md",
"generator": "docs:Readme",
"source": "project",
- "hash": "f1c694886a54e9aca154614baa293b0740092ea8220b2c396a21777b54846955",
+ "hash": "e8f101a3b06b3221567b3a49f44544c66a3efa26c53df7f749d7336c1e81160a",
"scaffolded": false,
- "generated_at": "2025-11-24T19:50:05.383946Z"
+ "generated_at": "2025-12-15T09:16:05.717099432Z"
},
"api/.env": {
"path": "api/.env",
diff --git a/internal/playground/.webkit/schema.json b/internal/playground/.webkit/schema.json
index b736fff8..1d800958 100755
--- a/internal/playground/.webkit/schema.json
+++ b/internal/playground/.webkit/schema.json
@@ -29,6 +29,10 @@
"timeout": {
"description": "Maximum execution time for the command (e.g., '5m', '1h')",
"type": "string"
+ },
+ "working_directory": {
+ "description": "Working directory for the command (defaults to app path if not specified)",
+ "type": "string"
}
},
"type": "object"
@@ -46,6 +50,10 @@
"timeout": {
"description": "Maximum execution time for the command (e.g., '5m', '1h')",
"type": "string"
+ },
+ "working_directory": {
+ "description": "Working directory for the command (defaults to app path if not specified)",
+ "type": "string"
}
},
"type": "object"
@@ -77,7 +85,10 @@
},
"monitoring": {
"description": "Whether to enable uptime monitoring for this app (defaults to true)",
- "type": "boolean"
+ "type": [
+ "null",
+ "boolean"
+ ]
},
"name": {
"description": "Unique identifier for the app (lowercase, hyphenated)",
@@ -142,8 +153,16 @@
},
"AppdefBuild": {
"properties": {
+ "context": {
+ "description": "Docker build context path relative to project root (defaults to app path)",
+ "type": "string"
+ },
"dockerfile": {
- "description": "Path to the Dockerfile relative to the app directory",
+ "description": "Path to the Dockerfile relative to project root",
+ "type": "string"
+ },
+ "health_check_path": {
+ "description": "Path for health check endpoint (defaults to /)",
"type": "string"
},
"port": {
@@ -160,6 +179,10 @@
},
"type": "object"
},
+ "AppdefConfig": {
+ "additionalProperties": {},
+ "type": "object"
+ },
"AppdefDomain": {
"properties": {
"name": {
@@ -239,12 +262,8 @@
"AppdefInfra": {
"properties": {
"config": {
- "additionalProperties": {},
- "description": "Provider-specific infrastructure configuration options",
- "type": [
- "object",
- "null"
- ]
+ "$ref": "#/definitions/AppdefConfig",
+ "description": "Provider-specific infrastructure configuration options"
},
"provider": {
"description": "Cloud infrastructure provider (digitalocean, backblaze)",
@@ -259,9 +278,9 @@
},
"AppdefMonitor": {
"properties": {
- "domain": {
- "description": "Domain name for DNS monitors",
- "type": "string"
+ "config": {
+ "$ref": "#/definitions/AppdefConfig",
+ "description": "Type-specific monitor configuration (e.g., url, method, keyword, domain)"
},
"identifier": {
"description": "Machine-readable identifier for variable naming (e.g., 'db' for database). Used by VariableName() method.",
@@ -271,27 +290,12 @@
"description": "Interval in seconds between checks (defaults based on monitor type if not specified)",
"type": "integer"
},
- "maxRedirects": {
- "description": "Maximum redirects to follow for HTTP monitors (default 0). Set to 1 for alias domains that redirect.",
- "type": [
- "null",
- "integer"
- ]
- },
- "method": {
- "description": "HTTP method for HTTP monitors (e.g., GET, POST)",
- "type": "string"
- },
"name": {
"description": "Unique monitor name",
"type": "string"
},
"type": {
- "description": "Monitor type (http, dns, postgres, push)",
- "type": "string"
- },
- "url": {
- "description": "URL for HTTP monitors or database connection string for postgres monitors",
+ "description": "Monitor type (http, http-keyword, dns, postgres, push)",
"type": "string"
}
},
@@ -306,6 +310,13 @@
},
"type": "array"
},
+ "enabled": {
+ "description": "Enable or disable all monitoring globally (defaults to true)",
+ "type": [
+ "null",
+ "boolean"
+ ]
+ },
"statusPage": {
"$ref": "#/definitions/AppdefStatusPage",
"description": "Public status page configuration"
@@ -345,12 +356,8 @@
"description": "Backup configuration for the resource"
},
"config": {
- "additionalProperties": {},
- "description": "Provider-specific resource configuration (e.g., size, region, version)",
- "type": [
- "object",
- "null"
- ]
+ "$ref": "#/definitions/AppdefConfig",
+ "description": "Provider-specific resource configuration (e.g., size, region, version)"
},
"description": {
"description": "Brief description of the resource's purpose and functionality",
@@ -358,7 +365,10 @@
},
"monitoring": {
"description": "Whether to enable uptime monitoring for this resource (defaults to true)",
- "type": "boolean"
+ "type": [
+ "null",
+ "boolean"
+ ]
},
"name": {
"description": "Unique identifier for the resource (used in environment variable references)",
@@ -396,7 +406,10 @@
"properties": {
"enabled": {
"description": "Whether to enable automated backups for this resource",
- "type": "boolean"
+ "type": [
+ "null",
+ "boolean"
+ ]
}
},
"type": "object"
diff --git a/internal/playground/README.md b/internal/playground/README.md
index d8208526..66e50305 100755
--- a/internal/playground/README.md
+++ b/internal/playground/README.md
@@ -1,6 +1,6 @@
-
+
My Website
@@ -14,9 +14,8 @@
-
+
[](https://github.com/ainsleydev//actions/workflows/backup.yaml)
-[](https://github.com/ainsleydev//actions/workflows/pr.yaml)
[](https://github.com/ainsleydev//actions/workflows/release.yaml)
[](https://ainsley.dev)
[](https://twitter.com/ainsleydev)
@@ -26,10 +25,18 @@
## My Website
+This is custom README content for the playground project.
+
+## Features
+
+- Feature 1
+- Feature 2
+- Feature 3
+
My website is a bespoke sales platform for developers and designers.
-Built with [WebKit v0.7.4](https://github.com/ainsleydev/webkit).
+Built with [WebKit v0.9.8](https://github.com/ainsleydev/webkit).
This repository contains **3 applications** and **2 resources**.
@@ -55,9 +62,9 @@ Payload CMS for managing content.
**Infrastructure:**
- **Provider:** digitalocean
- **Type:** vm
-- **domain:** cms.my-website.com
-- **region:** ams3
-- **size:** s-2vcpu-4gb
+- **Domain:** cms.my-website.com
+- **Region:** ams3
+- **Size:** s-2vcpu-4gb
### Web
@@ -79,10 +86,10 @@ Payload CMS for managing content.
**Infrastructure:**
- **Provider:** digitalocean
- **Type:** container
-- **domain:** www.my-website.com
-- **env_from_shared:** true
-- **instance_count:** 2
-- **region:** fra1
+- **Domain:** www.my-website.com
+- **Env From Shared:** true
+- **Instance Count:** 2
+- **Region:** fra1
### API
@@ -117,9 +124,9 @@ GoLang API
**Configuration:**
| Setting | Value |
|---------|-------|
-| engine_version | 17 |
-| region | ams3 |
-| size | db-s-1vcpu-1gb |
+| Engine Version | 17 |
+| Region | ams3 |
+| Size | db-s-1vcpu-1gb |
**Available Outputs:**
- `connection_url` - Full PostgreSQL connection string
@@ -139,8 +146,8 @@ GoLang API
**Configuration:**
| Setting | Value |
|---------|-------|
-| acl | public-read |
-| region | ams3 |
+| Acl | public-read |
+| Region | ams3 |
**Available Outputs:**
- `bucket_name` - S3 bucket identifier
diff --git a/internal/templates/.github/workflows/pr.yaml.tmpl b/internal/templates/.github/workflows/pr.yaml.tmpl
index d3db9920..deabd86d 100644
--- a/internal/templates/.github/workflows/pr.yaml.tmpl
+++ b/internal/templates/.github/workflows/pr.yaml.tmpl
@@ -532,7 +532,9 @@ jobs:
{{- if not $spec.Disabled }}
- name: {{ $spec.Name | title }}
-{{- if $app.Path }}
+{{- if $spec.WorkingDirectory }}
+ working-directory: {{ $spec.WorkingDirectory }}
+{{- else if $app.Path }}
working-directory: {{ $app.Path }}
{{- end }}
run: {{ $spec.Cmd }}
diff --git a/internal/templates/schema.json b/internal/templates/schema.json
index 6be79785..1d800958 100644
--- a/internal/templates/schema.json
+++ b/internal/templates/schema.json
@@ -29,6 +29,10 @@
"timeout": {
"description": "Maximum execution time for the command (e.g., '5m', '1h')",
"type": "string"
+ },
+ "working_directory": {
+ "description": "Working directory for the command (defaults to app path if not specified)",
+ "type": "string"
}
},
"type": "object"
@@ -46,6 +50,10 @@
"timeout": {
"description": "Maximum execution time for the command (e.g., '5m', '1h')",
"type": "string"
+ },
+ "working_directory": {
+ "description": "Working directory for the command (defaults to app path if not specified)",
+ "type": "string"
}
},
"type": "object"
diff --git a/schema.json b/schema.json
index 6be79785..1d800958 100644
--- a/schema.json
+++ b/schema.json
@@ -29,6 +29,10 @@
"timeout": {
"description": "Maximum execution time for the command (e.g., '5m', '1h')",
"type": "string"
+ },
+ "working_directory": {
+ "description": "Working directory for the command (defaults to app path if not specified)",
+ "type": "string"
}
},
"type": "object"
@@ -46,6 +50,10 @@
"timeout": {
"description": "Maximum execution time for the command (e.g., '5m', '1h')",
"type": "string"
+ },
+ "working_directory": {
+ "description": "Working directory for the command (defaults to app path if not specified)",
+ "type": "string"
}
},
"type": "object"
From 1ba62b9460ac6487d5d425999ce78eaee0146fa4 Mon Sep 17 00:00:00 2001
From: Claude
Date: Mon, 15 Dec 2025 09:32:42 +0000
Subject: [PATCH 2/3] fix: Adding nolint for unused param in skipped test
The setup function in cmd_test.go has parameters that appear unused
because the test is skipped with t.Skip(). Added nolint:unparam to
suppress the linter warning until the test is properly implemented.
---
internal/cmd/infra/cmd_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internal/cmd/infra/cmd_test.go b/internal/cmd/infra/cmd_test.go
index bf591190..53bb51e1 100644
--- a/internal/cmd/infra/cmd_test.go
+++ b/internal/cmd/infra/cmd_test.go
@@ -20,7 +20,7 @@ import (
var mtx sync.Mutex
-func setup(t *testing.T, appDef *appdef.Definition, mock *mockinfra.MockManager, initErr bool) (cmdtools.CommandInput, func()) {
+func setup(t *testing.T, appDef *appdef.Definition, mock *mockinfra.MockManager, initErr bool) (cmdtools.CommandInput, func()) { //nolint:unparam
t.Helper()
t.Skip("Need better DI")
From 3742752cda20fdf758c17d3c2994dd8e9c0fdbc0 Mon Sep 17 00:00:00 2001
From: Claude
Date: Mon, 15 Dec 2025 09:39:13 +0000
Subject: [PATCH 3/3] docs: Updating commands documentation with
working_directory field
Updated the Commands section in the apps documentation to reflect:
- The three command formats (boolean, string, object)
- Correct field names (command instead of run)
- New working_directory field with examples
- Other fields like skip_ci and timeout
- Removed outdated 'enabled' field from examples
This aligns the documentation with the actual implementation.
---
docs/manifest/apps.md | 83 +++++++++++++++++++++++++++++++++----------
1 file changed, 64 insertions(+), 19 deletions(-)
diff --git a/docs/manifest/apps.md b/docs/manifest/apps.md
index 53e07635..fe73845b 100644
--- a/docs/manifest/apps.md
+++ b/docs/manifest/apps.md
@@ -206,7 +206,37 @@ See [Environment variables](/manifest/environment-variables) for detailed docume
## Commands
-Override default commands for build, test, lint, and format:
+Customise commands for build, test, lint, and format. Commands can be specified in three formats:
+
+### Boolean format
+
+Enable or disable a command:
+
+```json
+{
+ "commands": {
+ "test": false,
+ "build": true
+ }
+}
+```
+
+### String format
+
+Override the command with a simple string:
+
+```json
+{
+ "commands": {
+ "build": "go build -o bin/api ./cmd/api",
+ "test": "go test -race ./..."
+ }
+}
+```
+
+### Object format
+
+Full control with additional options:
```json
{
@@ -216,20 +246,16 @@ Override default commands for build, test, lint, and format:
"type": "golang",
"commands": {
"build": {
- "run": "go build -o bin/api ./cmd/api",
- "enabled": true
+ "command": "go build -o bin/api ./cmd/api",
+ "working_directory": "./cmd/api",
+ "timeout": "10m"
},
"test": {
- "run": "go test -race ./...",
- "enabled": true
+ "command": "go test -race ./...",
+ "skip_ci": false
},
"lint": {
- "run": "golangci-lint run",
- "enabled": true
- },
- "format": {
- "run": "gofmt -w .",
- "enabled": true
+ "command": "golangci-lint run"
}
}
}
@@ -237,6 +263,31 @@ Override default commands for build, test, lint, and format:
}
```
+| Field | Description | Default |
+|-------|-------------|---------|
+| `command` | Shell command to execute | Required |
+| `working_directory` | Directory to run the command in | App's `path` |
+| `skip_ci` | Skip this command in CI/CD workflows | `false` |
+| `timeout` | Maximum execution time (e.g., `5m`, `1h`) | None |
+
+### Working directory
+
+By default, commands run in the app's `path` directory. Use `working_directory` to run commands in a different location:
+
+```json
+{
+ "name": "web",
+ "path": "apps/web",
+ "commands": {
+ "build": "pnpm build",
+ "test": {
+ "command": "pnpm test",
+ "working_directory": "apps/web/src"
+ }
+ }
+}
+```
+
Commands are used in generated CI/CD workflows.
## Monitoring
@@ -348,14 +399,8 @@ A fully configured app:
}
},
"commands": {
- "build": {
- "run": "pnpm build",
- "enabled": true
- },
- "lint": {
- "run": "pnpm lint",
- "enabled": true
- }
+ "build": "pnpm build",
+ "lint": "pnpm lint"
},
"monitoring": {
"http": true,