Skip to content

feat(apps): emit typed error envelopes across the apps domain#1288

Merged
liangshuo-1 merged 1 commit into
mainfrom
feat/errs-migrate-apps
Jun 11, 2026
Merged

feat(apps): emit typed error envelopes across the apps domain#1288
liangshuo-1 merged 1 commit into
mainfrom
feat/errs-migrate-apps

Conversation

@evandance

@evandance evandance commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Apps errors previously left several command-facing paths as legacy output.Err* / output.Errorf envelopes or untyped helper errors, so callers had to recover validation, API, file, subprocess, and credential failures from prose. This PR makes every error producer in the apps domain — flag validation, local HTML publish file handling, SDK/API boundaries, Miaoda HTML publish business errors, app init/scaffold subprocess orchestration, the local SQL execution flow, and the git credential helper stack — emit typed errs.* errors with stable type, subtype, param / params, hint, code, log_id, and retryable metadata where available.

What to focus on (reviewer guide)

  1. Consumer-visible error envelope changes: apps validation now carries validation/invalid_argument plus param; missing index.html is validation/failed_precondition; local pack/read failures carry typed validation or internal/file_io classification.
  2. API-boundary migration: standard apps JSON calls use runtime.CallAPITyped; multipart HTML publish keeps raw DoAPI because it posts multipart form data, but classifies responses via runtime.ClassifyAPIResponse and wraps SDK-boundary failures with client.WrapDoAPIError.
  3. HTML publish code-specific recovery: business codes 90001 / 90002 are scoped to this endpoint, so both their classification (90002api/not_found) and their recovery hints live in the apps domain instead of the global API code table — the global table stays reserved for service-namespaced codes. Enrichment never overrides a stronger upstream classification and preserves code / log_id.
  4. Subprocess and environment failures (+init git/npx orchestration, git config helper): a missing git/npx binary classifies as validation/failed_precondition with an install hint; a runtime failure of an external tool (clone, push, npx scaffold) classifies under the new internal/external_tool subtype with the tool output in the message and the cause attached, so agents read the tool output instead of retrying with different flags; malformed envelopes from lark-cli subprocess calls classify as internal/invalid_response.
  5. Local credential/state storage (storage.go, gitcred lock/store): failures classify as internal/storage; the not-logged-in path is a typed auth/token_missing with a login hint.
  6. SQL execution partial failures (+db-execute): a failed statement in DBA mode now reports via OutPartialFailure — per-statement results plus statement_index / error_code / rolled_back / note land on stdout as machine-readable ok:false data, with a non-zero partial-failure exit signal, instead of a legacy ExitError detail blob.
  7. Response-shape tightening: an HTML publish success response that omits the published app url now fails as internal/invalid_response instead of returning an empty url.
  8. Anti-backslide lock: .golangci.yml and lint/errscontract opt shortcuts/apps/ (including the gitcred subpackage) into typed-only, no-bare-wrap, no-legacy-helper, legacy-envelope, and legacy-runtime-helper guards; the full apps tree is lint-clean under those guards.

Changes

  • Replace apps-domain legacy validation, file I/O, subprocess, credential-storage, and final error producers with typed errs.* builders and apps-local helper functions.
  • Switch standard apps API calls from runtime.CallAPI to runtime.CallAPITyped.
  • Keep HTML publish multipart upload on raw DoAPI, while routing response classification through runtime.ClassifyAPIResponse and SDK boundary errors through client.WrapDoAPIError.
  • Classify HTML publish endpoint business codes locally (90002api/not_found) and keep their recovery hints local, since these codes are endpoint-scoped rather than service-global; hints are normalized to English.
  • Reject an HTML publish success response that lacks the published app url as internal/invalid_response.
  • Convert +db-execute statement failures to the partial-failure contract (OutPartialFailure): full per-statement results stay machine-readable on stdout, the exit code stays non-zero.
  • Add an internal/external_tool error subtype for runtime failures of tools the CLI shells out to; classify +init/git-config subprocess runtime failures under it, missing git/npx binaries as validation/failed_precondition with install hints, and subprocess envelope parse failures plus a non-http(s) repository_url as internal/invalid_response.
  • Classify local credential/state storage failures (storage.go, gitcred lock) as internal/storage, and the not-logged-in path as typed auth/token_missing.
  • Drop the legacy *output.ExitError passthrough arm from gitCredentialLocalError (everything the apps domain and shared runtime produce is typed).
  • Add apps (including shortcuts/apps/gitcred/) to golangci and errscontract migrated-path guards to prevent legacy error regressions.
  • Update apps tests to assert errs.ProblemOf metadata and the partial-failure stdout contract instead of legacy output.ExitError details, and add helper-level tests for path, file I/O, API boundary, and HTML publish hint enrichment.
  • Isolate apps runtime tests with a temporary LARKSUITE_CLI_CONFIG_DIR so they do not read or write user config state.

Test Plan

  • gofmt -l .
  • go vet ./...
  • go test ./shortcuts/apps/... ./internal/errclass ./internal/client
  • go test ./errscontract from the lint module
  • go run -C lint . .. — 0 violations repo-wide
  • golangci-lint v2.1.6 run --new-from-rev=origin/main — 0 issues
  • golangci-lint v2.1.6 run ./shortcuts/apps/... (full-tree, not diff-scoped) — 0 issues
  • make unit-test

Output changes for consumers

  • Apps command errors now expose typed type / subtype plus stable fields such as param, hint, code, log_id, and retryable where the underlying failure provides them.
  • Local HTML publish input failures are classified as validation or file I/O conditions instead of generic prose-only errors.
  • Known HTML publish API failures keep their upstream code / log_id and gain command-specific recovery hints; code 90002 is classified api/not_found.
  • An HTML publish success response without the published app url fails as internal/invalid_response instead of printing an empty url.
  • +db-execute statement failures emit an ok:false envelope on stdout carrying per-statement results, statement_index, error_code, error_message, rolled_back, and a re-run guidance note, with a non-zero exit code.
  • +init failures report typed classifications instead of opaque api_error strings: missing binaries as validation/failed_precondition with install hints, tool runtime failures as internal/external_tool carrying the tool output.

Out of scope / deferred

  • Shared auth, scope, credential, and transport failures remain owned by the shared runtime/client layers; apps preserves typed errors from those layers instead of reclassifying them locally.
  • Per-item structured problems inside batch payloads remain plain strings/maps, consistent with the partial-failure contract used by other domains.

Related Issues

N/A

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Too many files changed? Review this PR in Change Stack to see how the pieces fit before you dive in.

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR migrates the shortcuts/apps package from legacy internal/output error handling to typed errs.Problem errors, updates lint rules and error infrastructure, switches API calls to typed responses, and standardizes error assertions in tests across all apps commands and features.

Changes

Apps Domain Typed Error Migration

Layer / File(s) Summary
Lint configuration and error infrastructure
.golangci.yml, lint/errscontract/rule_no_legacy_common_helper_call.go, lint/errscontract/rule_no_legacy_envelope_literal.go, shortcuts/apps/apps_errors.go
forbidigo rule exclusions and lint contract paths extended to include shortcuts/apps/. New error constructors for validation, precondition, file-IO, and API-boundary failures with cause-chain support.
Simple commands: validation and typed API calls
shortcuts/apps/apps_access_scope_get.go, shortcuts/apps/apps_create.go, shortcuts/apps/apps_list.go, shortcuts/apps/apps_update.go, shortcuts/apps/apps_create_test.go
Validation errors migrated from output.ErrValidation to appsValidationParamError. Internal/output imports removed. API calls switched from CallAPI to CallAPITyped. Test helpers added to assert *errs.Problem category.
Access-scope-set: comprehensive flag validation
shortcuts/apps/apps_access_scope_set.go
Validation refactored to return appsValidationParamError for parameter failures, JSON unmarshal errors wrapped via WithCause, and per-flag invalid markers via WithParams. API call switched to CallAPITyped.
HTML publish validation and error handling
shortcuts/apps/apps_html_publish.go
Required parameter validation, sensitive credential blocking, index.html precondition, and size-limit checks refactored to use appsValidationParamError and appsFailedPreconditionParamError with hints. Publisher/API errors wrapped via client.WrapDoAPIError.
HTML publish client response handling
shortcuts/apps/html_publish_client.go
ClassifyAPIResponse replaces JSON envelope parsing. Missing/empty url returns internal error. enrichHTMLPublishAPIError wraps both untyped and typed API failures and updates failure hints to English.
HTML publish tarball and candidate-walker errors
shortcuts/apps/html_publish_tarball.go, shortcuts/apps/walk_html_publish_candidates.go
Tarball packing and walk errors switched from fmt.Errorf/errors.New to appsValidationParamError, appsFileIOError, and appsInputPathEntryError with preserved context.
Test updates: error assertion standardization
shortcuts/apps/apps_errors_test.go, shortcuts/apps/apps_html_publish_test.go, shortcuts/apps/html_publish_client_test.go
New tests for apps error classification and enrichment; HTML publish tests standardized on requireAppsValidationProblem/requireAppsAPIProblem and add app-not-found classification test.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • larksuite/cli#1242: Extends lint migrated-path logic for shortcuts domains similar to this PR's addition of shortcuts/apps/.
  • larksuite/cli#1072: Previously modified apps_html_publish.go credential scanning; this PR refactors validation/error handling in the same file.
  • larksuite/cli#1002: Introduced or modified apps commands that this PR migrates to typed errors and typed API calls.

Suggested labels

domain/ccm, size/L, feature

Suggested reviewers

  • liangshuo-1
  • liuxinyanglxy
  • kongenpei

"🐰 I hopped through code and sorted the fray,
apps errors now speak in Problem way.
Legacy output set aside,
hints and causes kept with pride,
tests assert the typed errors today."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.93% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: migrating the apps domain to emit typed error envelopes, which matches the core objective throughout the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering all required template sections with detailed explanations of changes, test verification, and implementation rationale.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/errs-migrate-apps

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label Jun 5, 2026
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@361b8bb71217fb0ea93d3bb472a81044772179c6

🧩 Skill update

npx skills add larksuite/cli#feat/errs-migrate-apps -y -g

@codecov

codecov Bot commented Jun 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.10219% with 60 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.83%. Comparing base (e64610f) to head (361b8bb).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/apps/apps_init.go 60.52% 15 Missing ⚠️
shortcuts/apps/apps_errors.go 76.74% 6 Missing and 4 partials ⚠️
shortcuts/apps/apps_access_scope_set.go 77.27% 5 Missing ⚠️
shortcuts/apps/apps_env_pull.go 55.55% 4 Missing ⚠️
shortcuts/apps/git_credential.go 88.88% 2 Missing and 2 partials ⚠️
shortcuts/apps/walk_html_publish_candidates.go 20.00% 4 Missing ⚠️
shortcuts/apps/apps_html_publish.go 83.33% 3 Missing ⚠️
shortcuts/apps/apps_release_get.go 0.00% 2 Missing ⚠️
shortcuts/apps/apps_session_stop.go 33.33% 2 Missing ⚠️
shortcuts/apps/html_publish_tarball.go 71.42% 2 Missing ⚠️
... and 9 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1288      +/-   ##
==========================================
+ Coverage   72.75%   72.83%   +0.07%     
==========================================
  Files         730      732       +2     
  Lines       69034    69140     +106     
==========================================
+ Hits        50228    50356     +128     
+ Misses      15034    15003      -31     
- Partials     3772     3781       +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
shortcuts/apps/html_publish_client_test.go (1)

21-31: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add config-dir isolation in the test runtime helper.

newAppsClientRuntime uses cmdutil.TestFactory but does not isolate config state. This can leak state across tests when config-backed paths are touched.

💡 Suggested fix
 func newAppsClientRuntime(t *testing.T) (*common.RuntimeContext, *httpmock.Registry) {
 	t.Helper()
+	t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
 	cfg := &core.CliConfig{
 		AppID:      "test-app-" + strings.ToLower(t.Name()),
 		AppSecret:  "test-secret",
 		Brand:      core.BrandFeishu,
 		UserOpenId: "ou_test",
 	}

As per coding guidelines: **/*_test.go must Use t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) to isolate config state in tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/apps/html_publish_client_test.go` around lines 21 - 31, The test
helper newAppsClientRuntime should isolate config state: before calling
cmdutil.TestFactory set the environment var LARKSUITE_CLI_CONFIG_DIR to a temp
dir via t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()), so tests don't share
config-backed paths; update newAppsClientRuntime to call t.Setenv(...) as the
first action in the function (reference newAppsClientRuntime and the call site
cmdutil.TestFactory).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@shortcuts/apps/html_publish_client_test.go`:
- Around line 21-31: The test helper newAppsClientRuntime should isolate config
state: before calling cmdutil.TestFactory set the environment var
LARKSUITE_CLI_CONFIG_DIR to a temp dir via t.Setenv("LARKSUITE_CLI_CONFIG_DIR",
t.TempDir()), so tests don't share config-backed paths; update
newAppsClientRuntime to call t.Setenv(...) as the first action in the function
(reference newAppsClientRuntime and the call site cmdutil.TestFactory).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cad262b6-f9b7-409c-93ee-75177089ac7c

📥 Commits

Reviewing files that changed from the base of the PR and between f3949f0 and deb12eb.

📒 Files selected for processing (17)
  • .golangci.yml
  • internal/errclass/codemeta_apps.go
  • lint/errscontract/rule_no_legacy_common_helper_call.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/apps/apps_access_scope_get.go
  • shortcuts/apps/apps_access_scope_set.go
  • shortcuts/apps/apps_create.go
  • shortcuts/apps/apps_create_test.go
  • shortcuts/apps/apps_errors.go
  • shortcuts/apps/apps_html_publish.go
  • shortcuts/apps/apps_html_publish_test.go
  • shortcuts/apps/apps_list.go
  • shortcuts/apps/apps_update.go
  • shortcuts/apps/html_publish_client.go
  • shortcuts/apps/html_publish_client_test.go
  • shortcuts/apps/html_publish_tarball.go
  • shortcuts/apps/walk_html_publish_candidates.go

@evandance evandance force-pushed the feat/errs-migrate-apps branch from deb12eb to 8920914 Compare June 6, 2026 03:16
@evandance evandance changed the title refactor(apps): migrate errors to typed taxonomy feat(apps): emit typed error envelopes across the apps domain Jun 6, 2026
@github-actions github-actions Bot added size/M Single-domain feat or fix with limited business impact and removed size/L Large or sensitive change across domains or core paths labels Jun 6, 2026
@evandance evandance force-pushed the feat/errs-migrate-apps branch 7 times, most recently from e76456c to 32d9a82 Compare June 11, 2026 07:20
@github-actions github-actions Bot added size/L Large or sensitive change across domains or core paths and removed size/M Single-domain feat or fix with limited business impact labels Jun 11, 2026
@evandance evandance force-pushed the feat/errs-migrate-apps branch 4 times, most recently from c629720 to e16b6d9 Compare June 11, 2026 09:56
Apps command errors previously surfaced as legacy output.Err* envelopes or
untyped helper errors, so agent and script callers had to recover validation,
API, subprocess, credential, and file failures from prose. Every error
producer in the apps domain now emits typed errs.* errors with stable
type/subtype/param/hint/code/log_id/retryable metadata:

- Flag and input validation carries validation/invalid_argument with param;
  state preconditions (missing git/npx binary, login mismatch, foreign git
  helper) carry validation/failed_precondition with recovery hints.
- A new internal/external_tool subtype classifies runtime failures of tools
  the CLI shells out to (git clone/push, npx scaffold, git config): the tool
  output is carried in the message so agents act on it instead of retrying
  with different flags.
- Standard apps API calls use runtime.CallAPITyped; the multipart HTML
  publish and the git-credential issue endpoint classify responses through
  the shared classifier, so generic codes (missing scope) and HTTP 5xx keep
  their canonical category/subtype/retryable classification.
- Local credential/state storage failures classify as internal/storage;
  malformed subprocess and API payloads as internal/invalid_response;
  defense-in-depth invariant guards report internal errors instead of
  blaming user input.
- +db-execute statement failures report via the partial-failure contract:
  per-statement results stay machine-readable on stdout (ok:false) with a
  non-zero exit, in both json and pretty formats.
- shortcuts/apps/ (including gitcred/) is locked into the golangci and
  errscontract migrated-path guards; the full tree is lint-clean.

Validation: gofmt, go vet, go build, go test ./shortcuts/apps/... ./errs and
the lint module, errscontract repo-wide clean, golangci-lint clean both
diff-scoped and full-tree, make unit-test.
@evandance evandance force-pushed the feat/errs-migrate-apps branch from e16b6d9 to 361b8bb Compare June 11, 2026 10:03
@liangshuo-1 liangshuo-1 merged commit 9bc66cc into main Jun 11, 2026
21 checks passed
@liangshuo-1 liangshuo-1 deleted the feat/errs-migrate-apps branch June 11, 2026 11:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants