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, 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/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") 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 @@
-
+