Skip to content

Align project config failures with validate json#7103

Closed
dmerand wants to merge 1 commit intodlm-app-validate-error-layerfrom
dlm-app-validate-project-errors
Closed

Align project config failures with validate json#7103
dmerand wants to merge 1 commit intodlm-app-validate-error-layerfrom
dlm-app-validate-project-errors

Conversation

@dmerand
Copy link
Contributor

@dmerand dmerand commented Mar 25, 2026

What

Align shopify app config validate --json with Project-layer app discovery and app-config parse failures.

This makes JSON mode return the same {valid, issues} shape for local failures that occur before the loader can produce a normal validation result.

Why

The current JSON contract is reliable once validation reaches the loader / app-validation pipeline, but some local failures now happen earlier through Project.load().

That left cases like missing app roots and malformed selected app configs on the normal human-readable CLI error path instead of the machine-readable validation contract.

How

  • preserve malformed app config parse failures during Project discovery
  • surface those failures when selecting the active config
  • convert missing app-root / missing selected-config / malformed selected-config cases into AppConfigurationAbortError
  • keep the public JSON contract as {valid, issues}
  • leave malformed extension TOMLs unchanged for now

Testing

Manual checks:

  • run shopify app config validate --json --path=<missing path outside any app> and confirm it returns a root issue in JSON
  • break shopify.app.toml and run shopify app config validate --json to confirm the parse failure returns a root issue in JSON
  • add a malformed named config and run shopify app config validate --json --config=<name> to confirm the selected-config parse failure returns a root issue in JSON

Copy link
Contributor Author

dmerand commented Mar 25, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Contributor

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/public/node/toml/toml-file.d.ts
 import { JsonMapType } from './codec.js';
 /**
- * Thrown when a TOML file does not exist at the expected path.
- */
-export declare class TomlFileNotFoundError extends Error {
-    readonly path: string;
-    constructor(path: string);
-}
-/**
  * Thrown when a TOML file cannot be parsed. Includes the file path for context.
  */
 export declare class TomlParseError extends Error {
     readonly path: string;
     constructor(path: string, cause: Error);
 }
 /**
  * General-purpose TOML file abstraction.
  *
  * Provides a unified interface for reading, patching, removing keys from, and replacing
  * the content of TOML files on disk.
  *
  * - `read` populates content from disk
  * - `patch` does surgical WASM-based edits (preserves comments and formatting)
  * - `remove` deletes a key by dotted path (preserves comments and formatting)
  * - `replace` does a full re-serialization (comments and formatting are NOT preserved).
  * - `transformRaw` applies a function to the raw TOML string on disk.
  */
 export declare class TomlFile {
     /**
      * Read and parse a TOML file from disk. Throws if the file doesn't exist or contains invalid TOML.
      * Parse errors are wrapped in {@link TomlParseError} with the file path for context.
      *
      * @param path - Absolute path to the TOML file.
      * @returns A TomlFile instance with parsed content.
      */
     static read(path: string): Promise<TomlFile>;
     readonly path: string;
     content: JsonMapType;
     constructor(path: string, content: JsonMapType);
     /**
      * Surgically patch values in the TOML file, preserving comments and formatting.
      *
      * Accepts a nested object whose leaf values are set in the TOML. Intermediate tables are
      * created automatically. Setting a leaf to `undefined` removes it (use `remove()` for a
      * clearer API when deleting keys).
      *
      * @example
      * ```ts
      * await file.patch({build: {dev_store_url: 'my-store.myshopify.com'}})
      * await file.patch({application_url: 'https://example.com', auth: {redirect_urls: ['...']}})
      * ```
      */
     patch(changes: {
         [key: string]: unknown;
     }): Promise<void>;
     /**
      * Remove a key from the TOML file by dotted path, preserving comments and formatting.
      *
      * @param keyPath - Dotted key path to remove (e.g. 'build.include_config_on_deploy').
      * @example
      * ```ts
      * await file.remove('build.include_config_on_deploy')
      * ```
      */
     remove(keyPath: string): Promise<void>;
     /**
      * Replace the entire file content. The file is fully re-serialized — comments and formatting
      * are NOT preserved.
      *
      * @param content - The new content to write.
      * @example
      * ```ts
      * await file.replace({client_id: 'abc', name: 'My App'})
      * ```
      */
     replace(content: JsonMapType): Promise<void>;
     /**
      * Transform the raw TOML string on disk. Reads the file, applies the transform function
      * to the raw text, writes back, and re-parses to keep `content` in sync.
      *
      * Use this for text-level operations that can't be expressed as structured edits —
      * e.g. Injecting comments or positional insertion of keys in arrays-of-tables.
      * Subsequent `patch()` calls will preserve any comments added this way.
      *
      * @param transform - A function that receives the raw TOML string and returns the modified string.
      * @example
      * ```ts
      * await file.transformRaw((raw) => `# Header comment\n${raw}`)
      * ```
      */
     transformRaw(transform: (raw: string) => string): Promise<void>;
     private decode;
 }

@github-actions
Copy link
Contributor

github-actions bot commented Mar 25, 2026

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 82.31% 15122/18373
🟡 Branches 74.9% 7480/9986
🟢 Functions 81.39% 3822/4696
🟢 Lines 82.69% 14290/17281

Test suite run success

4001 tests passing in 1529 suites.

Report generated by 🧪jest coverage report action from 4534a8a

@dmerand dmerand force-pushed the dlm-app-validate-error-layer branch from b82d4c6 to a1bb168 Compare March 26, 2026 01:11
@dmerand dmerand force-pushed the dlm-app-validate-project-errors branch from 07f8cb4 to 4534a8a Compare March 26, 2026 01:11
@dmerand
Copy link
Contributor Author

dmerand commented Mar 26, 2026

Superseded by #7106 after collapsing the stack. #7106 now carries both the Project-layer app discovery/app-config parse work and the malformed discovered config reporting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant