Skip to content
Merged
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Happy debugging!

<!-- commands -->

- [`sf apex get compile-status`](#sf-apex-get-compile-status)
- [`sf apex get log`](#sf-apex-get-log)
- [`sf apex get test`](#sf-apex-get-test)
- [`sf apex list log`](#sf-apex-list-log)
Expand All @@ -96,6 +97,39 @@ Happy debugging!
- [`sf logic get test`](#sf-logic-get-test)
- [`sf logic run test`](#sf-logic-run-test)

## `sf apex get compile-status`

Fetches any Apex in your org with compilation status issues.

```
USAGE
$ sf apex get compile-status -o <value> [--json] [--flags-dir <value>] [--api-version <value>]

FLAGS
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
configuration variable is already set.
--api-version=<value> Override the api version used for api requests made by this command

GLOBAL FLAGS
--flags-dir=<value> Import flag values from a directory.
--json Format output as json.

DESCRIPTION
Fetches invalid Apex or Apex with compiler warnings, and reports the current compilation status.


EXAMPLES
Fetches invalid Apex or Apex with compilation warnings in your default org:

$ sf apex get compile-status

Fetch invalid Apex or Apex with compilation warnings in the org with the specified username:

$ sf apex get compile-status --target-org me@my.org
```

_See code: [src/commands/apex/get/compile-status.ts](https://github.com/salesforcecli/plugin-apex/blob/4.3.0/src/commands/apex/get/compile-status.ts)_

## `sf apex get log`

Fetch the specified log or given number of most recent logs from the org.
Expand Down
8 changes: 8 additions & 0 deletions command-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
[
{
"alias": [],
"command": "apex:get:compile-status",
"flagAliases": ["apiversion", "targetusername", "u"],
"flagChars": ["o"],
"flags": ["api-version", "flags-dir", "json", "target-org"],
"plugin": "@salesforce/plugin-apex"
},
{
"alias": ["force:apex:log:get"],
"command": "apex:get:log",
Expand Down
21 changes: 21 additions & 0 deletions messages/get-compile-status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# summary

Fetches the compilation status for Apex classes that are currently invalid or have compilation warnings.

# description

Return compile results for only Apex classes and triggers with validation errors or warnings, instead of recompiling the entire org.

# examples

- Fetch invalid Apex classes or classes that have compilation warnings for a target org

<%= config.bin %> <%= command.id %> --target-org me@my.org

# noResultsFound

No Apex compiler issues found.

# minimumApiVersionNotMet

Apex compile results available in API versions 68.0 and above
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"description": "Use the apex commands to create Apex classes, execute anonymous blocks, view your logs, run Apex tests, and view Apex test results.",
"subtopics": {
"get": {
"description": "Get debug logs or test results."
"description": "Get debug logs, Apex class compile status, or test results."
},
"list": {
"description": "List debug logs."
Expand Down
66 changes: 66 additions & 0 deletions schemas/apex-get-compile__status.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/CompilationResult",
"definitions": {
"CompilationResult": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"results": {
"type": "array",
"items": {
"$ref": "#/definitions/InvalidApexClassResult"
}
}
},
"required": ["status", "results"],
"additionalProperties": false
},
"InvalidApexClassResult": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"namespace": {
"type": "string"
},
"success": {
"type": "boolean"
},
"problems": {
"type": "array",
"items": {
"$ref": "#/definitions/ApexClassProblem"
}
},
"warnings": {
"type": "array",
"items": {
"$ref": "#/definitions/ApexClassProblem"
}
}
},
"required": ["name", "namespace", "success", "problems", "warnings"],
"additionalProperties": false
},
"ApexClassProblem": {
"type": "object",
"properties": {
"line": {
"type": "number"
},
"column": {
"type": "number"
},
"message": {
"type": "string"
}
},
"required": ["line", "column", "message"],
"additionalProperties": false
}
}
}
100 changes: 100 additions & 0 deletions src/commands/apex/get/compile-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
orgApiVersionFlagWithDeprecations,
requiredOrgFlagWithDeprecations,
SfCommand,
} from '@salesforce/sf-plugins-core';
import { Messages, SfError } from '@salesforce/core';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-apex', 'get-compile-status');

export type ApexClassProblem = {
line: number;
column: number;
message: string;
};

export type InvalidApexClassResult = {
name: string;
namespace: string;
success: boolean;
problems: ApexClassProblem[];
warnings: ApexClassProblem[];
};

export type CompilationResult = {
status: string;
results: InvalidApexClassResult[];
};

export default class GetCompileStatus extends SfCommand<CompilationResult> {
public static readonly flags = {
'target-org': requiredOrgFlagWithDeprecations,
'api-version': orgApiVersionFlagWithDeprecations,
};
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');

public async run(): Promise<CompilationResult> {
const { flags } = await this.parse(GetCompileStatus);
const apiVersion = flags['api-version'];
if (apiVersion && Number(apiVersion.replace('.0', '')) < 68) {
throw new SfError(messages.getMessage('minimumApiVersionNotMet'));
}

this.spinner.start('Retrieving Apex compilation results');

const connection = flags['target-org'].getConnection(apiVersion);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const invalidApexResponse = (await connection.tooling.request({
// eslint-disable-next-line no-underscore-dangle
url: `${connection.tooling._baseUrl()}/apexCompileResults`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
})) as CompilationResult;

this.spinner.stop();

// the actual command output is only displayed when --json is passed as a flag
// so we format the results for everyone else
if (!this.jsonEnabled()) {
if (invalidApexResponse.results.length > 0) {
const tableFormatter = (tableItems: ApexClassProblem[]): string =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON.stringify per problem produces output like {"line":10,"column":5,"message":"Unexpected token: }"} in the table cell — not great for human consumption.

Consider a readable format, e.g.:

const tableFormatter = (items: ApexClassProblem[]): string =>
  items.map((p) => `L${p.line}:C${p.column} - ${p.message}`).join('\n');

This would render as L10:C5 - Unexpected token: } which is much easier to scan.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll work a little bit on the output, but I thought the tabled results looked nice as is. Let me play around it with it...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried a few different output formats, but the bare string versions just look weird on my terminal. I don't think they're easier to read, and in many cases the "message" would wrap onto a new line in a way that the stringified versions didn't.

tableItems.map((p) => `L${p.line}:C${p.column} - ${p.message}`).join('\n');
this.table({
columns: ['name', 'namespace', 'success', 'problems', 'warnings'],
data: invalidApexResponse.results.map((result) => ({
...result,
problems: tableFormatter(result.problems),
warnings: tableFormatter(result.warnings),
})),
title: 'Compile Status Results:',
overflow: 'wrap',
});
} else {
this.log(messages.getMessage('noResultsFound'));
}
}

return invalidApexResponse;
}
}
Loading
Loading