fix(apktool): name host requirements in configuration refusals (#1946)
A missing apktool launcher projected the generic "unavailable for the current target" message and remediation, leaving the actual reason only in details.reason. A relative REA_APKTOOL_COMMAND escaped as UNKNOWN because command selection ran before the try that maps configuration failures. Resolve the selection inside that try for both execution and availability, and pass the refusal reason as userMessage, as JADX and the adapters fixed in #1224 do. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0ae38e1e31
commit
7cdb7981c3
@@ -153,12 +153,13 @@ const runApktool = async (
|
||||
invocation = apktoolInvocation(selection, arguments_, hostEnvironment);
|
||||
} catch (cause) {
|
||||
if (!(cause instanceof WindowsBatchCommandError)) throw cause;
|
||||
const reason = `The apktool batch launcher cannot receive REA's decode paths through cmd.exe: ${cause.message}`;
|
||||
return err(
|
||||
new AnalysisCapabilityUnavailableError(
|
||||
APKTOOL_PROVIDER_IDENTITY.id,
|
||||
operation,
|
||||
`The apktool batch launcher cannot receive REA's decode paths through cmd.exe: ${cause.message}`,
|
||||
{ cause },
|
||||
reason,
|
||||
{ cause, userMessage: reason },
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -345,8 +346,8 @@ export class ApktoolProvider {
|
||||
async #inspectAvailability(
|
||||
signal: AbortSignal,
|
||||
): Promise<ProviderAvailability> {
|
||||
const selection = this.selection();
|
||||
try {
|
||||
const selection = this.selection();
|
||||
await inspectApktoolClient(selection, signal, this.environment);
|
||||
throwIfCancelled("inspect_apktool_client", signal);
|
||||
return {
|
||||
@@ -397,9 +398,10 @@ export class ApktoolProvider {
|
||||
): Promise<Result<AnalysisExecution, AnalysisError>> {
|
||||
if (signal?.aborted === true)
|
||||
return err(new AnalysisCancelledError(request.operation));
|
||||
const selection = this.selection();
|
||||
let selection: ApktoolCommandSelection;
|
||||
let identity: ApktoolClientIdentity;
|
||||
try {
|
||||
selection = this.selection();
|
||||
identity = await inspectApktoolClient(
|
||||
selection,
|
||||
signal,
|
||||
@@ -407,17 +409,21 @@ export class ApktoolProvider {
|
||||
);
|
||||
} catch (cause) {
|
||||
if (cause instanceof AnalysisCancelledError) return err(cause);
|
||||
if (cause instanceof ApktoolConfigurationFailure)
|
||||
if (cause instanceof ApktoolConfigurationFailure) {
|
||||
// A host configuration refusal; the target is not the problem.
|
||||
const reason =
|
||||
typeof cause.diagnostics.remediation === "string"
|
||||
? `${cause.message}. ${cause.diagnostics.remediation}.`
|
||||
: `${cause.message}.`;
|
||||
return err(
|
||||
new AnalysisCapabilityUnavailableError(
|
||||
APKTOOL_PROVIDER_IDENTITY.id,
|
||||
request.operation,
|
||||
typeof cause.diagnostics.remediation === "string"
|
||||
? `${cause.message} ${cause.diagnostics.remediation}`
|
||||
: cause.message,
|
||||
{ cause },
|
||||
reason,
|
||||
{ cause, userMessage: reason },
|
||||
),
|
||||
);
|
||||
}
|
||||
return err(
|
||||
new AnalysisProtocolError(
|
||||
`apktool version probe failed for ${request.operation}`,
|
||||
|
||||
@@ -12,13 +12,37 @@ it.each(["inspect-apktool-client", "decode-android-resources"])(
|
||||
},
|
||||
);
|
||||
|
||||
it("reports a missing apktool launcher with recovery guidance", async () => {
|
||||
const result = await serve(["inspect-apktool-client", "--json"], {
|
||||
REA_APKTOOL_COMMAND: "/nonexistent/apktool",
|
||||
});
|
||||
expect(result.stdout).toContain("capability_unavailable");
|
||||
expect(result.stdout).toContain("REA_APKTOOL_COMMAND");
|
||||
});
|
||||
it.each([
|
||||
[
|
||||
["inspect-apktool-client", "--json"],
|
||||
"/nonexistent/apktool",
|
||||
"The selected apktool launcher was not found: /nonexistent/apktool. Set REA_APKTOOL_COMMAND",
|
||||
],
|
||||
[
|
||||
["decode-android-resources", "/targets/app.apk", "--json"],
|
||||
"/nonexistent/apktool",
|
||||
"The selected apktool launcher was not found: /nonexistent/apktool. Set REA_APKTOOL_COMMAND",
|
||||
],
|
||||
[
|
||||
["decode-android-resources", "/targets/app.apk", "--json"],
|
||||
"relative/apktool",
|
||||
"REA_APKTOOL_COMMAND must be an absolute path to the apktool launcher.",
|
||||
],
|
||||
])(
|
||||
"%j with REA_APKTOOL_COMMAND=%s names the host requirement",
|
||||
async (argv, command, reason) => {
|
||||
const result = await serve(argv, { REA_APKTOOL_COMMAND: command });
|
||||
const output = JSON.parse(result.stdout) as {
|
||||
code?: unknown;
|
||||
message?: unknown;
|
||||
remediation?: { action?: unknown };
|
||||
};
|
||||
expect(output.code).toBe("capability_unavailable");
|
||||
// The target is not the problem; the refusal must say what to configure.
|
||||
expect(output.message).toEqual(expect.stringContaining(reason));
|
||||
expect(output.remediation?.action).toEqual(expect.stringContaining(reason));
|
||||
},
|
||||
);
|
||||
|
||||
it("validates the locale option before launching apktool", async () => {
|
||||
const result = await serve([
|
||||
|
||||
Reference in New Issue
Block a user