Skip to content

Organization configuration via URL or local file#950

Merged
dividedmind merged 20 commits into
mainfrom
feat/remote-config
Jun 29, 2026
Merged

Organization configuration via URL or local file#950
dividedmind merged 20 commits into
mainfrom
feat/remote-config

Conversation

@dividedmind

Copy link
Copy Markdown
Contributor

The organization configuration can now be applied from either a URL or local file. The functionality can be accessed through an action, menu item, from the settings dialog or the login screen.

Copilot AI 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.

Pull request overview

This PR adds “Organization Configuration” support to the AppMap JetBrains plugin, allowing enterprise deployment settings to be applied at runtime from either a URL (including APPMAP_CONFIG_URL) or a one-shot local file, and surfaces that functionality via an action, settings UI, and the sign-in webview.

Changes:

  • Introduces an EnterpriseConfigService + non-roaming cache to fetch/apply org config asynchronously and persist the last good config.
  • Adds UI/action entry points (Tools menu, settings page, sign-in screen) to apply/change/clear org config and show status.
  • Updates deployment/telemetry plumbing to support nullable “auto-update tools” and hot-reload telemetry routing when org config changes.

Reviewed changes

Copilot reviewed 3 out of 120 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
README.md Documents runtime org config and manifest URL overrides.
plugin-core/src/test/java/appland/utils/AppMapFileChoosersTest.java Adds tests for background initial-directory resolution for file choosers.
plugin-core/src/test/java/appland/deployment/AppMapDeploymentSettingsTest.java Updates tests for nullable autoUpdateTools semantics.
plugin-core/src/test/java/appland/deployment/AppMapDeploymentSettingsServiceTest.java Updates default auto-update expectation for nullable settings.
plugin-core/src/main/resources/META-INF/appmap-core.xml Registers enterprise config services/startup activity and new action.
plugin-core/src/main/resources/messages/appland.properties Adds UI strings for org-config action/settings UI.
plugin-core/src/main/kotlin/appland/settings/AppMapProjectSettingsPanel.kt Adds org-config status/apply/change/clear UI in settings panel.
plugin-core/src/main/java/appland/utils/AppMapFileChoosers.java Adds async file chooser helpers to avoid slow VFS ops on EDT.
plugin-core/src/main/java/appland/toolwindow/signInView/SignInViewPanel.java Adds org-config init data + reactive applied-state updates to sign-in webview.
plugin-core/src/main/java/appland/telemetry/TelemetryService.java Enables telemetry reporter hot-reload on org-config changes.
plugin-core/src/main/java/appland/settings/DownloadSettings.java Treats nullable deployment auto-update as “enabled unless explicitly false”.
plugin-core/src/main/java/appland/settings/AutoDownloadSettingsListener.java Refreshes downloads when enterprise deployment settings change.
plugin-core/src/main/java/appland/settings/AppMapSettingsReloadProjectListener.java Restarts JSON-RPC server when effective telemetry settings change.
plugin-core/src/main/java/appland/settings/AppMapSettingsListener.java Adds new settings bus events for org config + telemetry changes.
plugin-core/src/main/java/appland/settings/AppMapProjectConfigurable.java Reloads settings UI live when org/deployment settings change.
plugin-core/src/main/java/appland/settings/AppMapApplicationSettings.java Persists org-config URL + applied timestamp; publishes URL changes.
plugin-core/src/main/java/appland/enterpriseConfig/EnterpriseConfigStartupActivity.java Eager background org-config fetch at startup (non-test).
plugin-core/src/main/java/appland/enterpriseConfig/EnterpriseConfigSettingsListener.java Triggers async apply when config URL changes.
plugin-core/src/main/java/appland/enterpriseConfig/EnterpriseConfigService.java Core org-config fetch/apply/clear/cache + override superseding + telemetry reload.
plugin-core/src/main/java/appland/enterpriseConfig/EnterpriseConfigCacheService.java Non-roaming persistence for cached org-config JSON (token may be present).
plugin-core/src/main/java/appland/deployment/AppMapDeploymentSettingsService.java Merges enterprise settings over bundled defaults on read path.
plugin-core/src/main/java/appland/deployment/AppMapDeploymentSettings.java Makes autoUpdateTools nullable to distinguish “unset” vs explicit value.
plugin-core/src/main/java/appland/actions/SetConfigurationUrlAction.java Adds action to apply org config via URL or local file.
plugin-core/src/main/java/appland/actions/PluginStatus.java Includes org-config source + redacted contents in plugin status report.
plugin-core/src/main/java/appland/actions/ChooseAndAddNavieContextFilesAction.java Switches to async chooser helper for pinned files selection.
ORGANIZATION_CONFIGURATION.md New doc explaining schema, application methods, precedence, and failure behavior.
appland-webview/yarn.lock Updates @appland/components dependency lock to ^4.49.0.
appland-webview/signin.js Adds webview event to request org-config apply and react to applied state.
appland-webview/package.json Bumps @appland/components to ^4.49.0.
appland-webview/dist/timeline-definition-faaaa080-FCQT3WAD.js Rebuilt webview dist asset (dependency bump).
appland-webview/dist/stateDiagram-v2-c2b004d7-CUVNE3XY.js Rebuilt webview dist asset (dependency bump).
appland-webview/dist/stateDiagram-1ecb1508-7NKRNZHU.js Rebuilt webview dist asset (dependency bump).
appland-webview/dist/signin.js.map Rebuilt webview dist sourcemap (org-config UI hook).
appland-webview/dist/signin.js Rebuilt webview dist bundle (org-config UI hook).
appland-webview/dist/review.js.map Rebuilt webview dist sourcemap (dependency bump).
appland-webview/dist/review.js Rebuilt webview dist bundle (dependency bump).
appland-webview/dist/pieDiagram-a8764435-YUCDHKSQ.js Rebuilt webview dist asset (dependency bump).
appland-webview/dist/navie.js Rebuilt webview dist bundle (dependency bump).
appland-webview/dist/install-guide.js.map Rebuilt webview dist sourcemap (dependency bump).
appland-webview/dist/install-guide.js Rebuilt webview dist bundle (dependency bump).
appland-webview/dist/infoDiagram-8eee0895-AM7FTXMF.js Rebuilt webview dist asset (dependency bump).
appland-webview/dist/flowDiagram-v2-4f6560a1-UTQCF3NU.js Removed old flow diagram dist asset (rebuilt/renamed output).
appland-webview/dist/flowDiagram-v2-4f6560a1-R6D7EA5S.js.map Adds rebuilt flow diagram sourcemap.
appland-webview/dist/flowDiagram-v2-4f6560a1-R6D7EA5S.js Adds rebuilt flow diagram dist asset.
appland-webview/dist/findings.js.map Rebuilt webview dist sourcemap (dependency bump).
appland-webview/dist/findings.js Rebuilt webview dist bundle (dependency bump).
appland-webview/dist/classDiagram-v2-2358418a-M6GJQI5I.js Rebuilt webview dist asset (dependency bump).
appland-webview/dist/classDiagram-beda092f-5UIOK7GK.js Rebuilt webview dist asset (dependency bump).
appland-webview/dist/chunk-TAOBJH36.js Rebuilt webview dist chunk (dependency bump).
appland-webview/dist/chunk-LUO6KAAB.js.map Rebuilt webview dist sourcemap (dependency bump).
appland-webview/dist/chunk-LUO6KAAB.js Rebuilt webview dist chunk (dependency bump).
appland-webview/dist/chunk-2HYH4KIK.js.map Rebuilt webview dist sourcemap (dependency bump).
appland-webview/dist/chunk-2HYH4KIK.js Rebuilt webview dist chunk (dependency bump).
appland-webview/dist/appmap.js.map Rebuilt webview dist sourcemap (dependency bump).
appland-webview/dist/appmap.js Rebuilt webview dist bundle (dependency bump).
.editorconfig Keeps blank lines in .properties files.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread plugin-core/src/main/java/appland/enterpriseConfig/EnterpriseConfigService.java Outdated
Comment thread plugin-core/src/main/java/appland/enterpriseConfig/EnterpriseConfigService.java Outdated

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 120 changed files in this pull request and generated 3 comments.

Comment thread plugin-core/src/main/java/appland/utils/AppMapFileChoosers.java
Comment thread plugin-core/src/main/java/appland/utils/AppMapFileChoosers.java
@dividedmind dividedmind force-pushed the feat/remote-config branch 2 times, most recently from 7ee8608 to 38c6d44 Compare June 22, 2026 14:33
…file

Add EnterpriseConfigService that fetches organization deployment settings
from a configured URL (or APPMAP_CONFIG_URL env var) or a local file,
caches the last successful result, and merges enterprise settings over
bundled defaults. Wires the "Apply Organization Configuration" action,
sign-in view affordances, and settings panel fields.

Assisted-by: Claude:claude-opus-4-8
Preserves the blank-line separation before the enterprise-config strings.

Assisted-by: Claude:claude-opus-4-8
Adds the signin.js counterpart for the organization-configuration
sign-in affordances (enableOrgConfig / apply-org-config) and includes
the regenerated webview dist bundle.

Assisted-by: Claude:claude-opus-4-8
The restart notification fired unconditionally whenever org config was
applied, even when the config had no telemetry section. Rebuild the
telemetry reporter from the current deployment settings when the org
configuration changes, so telemetry routing updates without an IDE restart.

Assisted-by: Claude:claude-opus-4-8
When the user changes the configuration URL to one that is unreachable or
returns invalid content, the previously applied organization config used to
silently linger. Treat an interactive apply failure (and empty/invalid JSON)
as a clear-and-notify: drop the in-memory settings and cache and warn the user.

Startup/lazy fetches stay non-interactive and keep the cached settings as an
offline fallback instead of wiping a known-good config on a transient failure.

Assisted-by: Claude:claude-opus-4-8
Applying an organization config previously had no visible effect when the
user had already set the same setting, because user overrides always win at
runtime. On apply, clear the user override for each setting the org config
specifies so it actually takes effect (the user can override again afterward).

A new URL or local-file apply supersedes every specified setting; a repeat
fetch from the same URL only supersedes settings whose org value changed
since the last fetch. Each superseded override is logged.

Assisted-by: Claude:claude-opus-4-8
Reading deployment settings used to lazily trigger and block on the org-config
fetch, which logged a SEVERE "wait requested on EDT" error whenever the UI read
settings (e.g. on Apply). Fetch eagerly at startup instead, and make the read
path only restore the persisted cache — cheap, thread-safe, and EDT-safe.

Because the fetch is now asynchronous, AutoDownloadSettingsListener also reacts
to enterpriseDeploymentSettingsChanged so a freshly-applied org config still
re-evaluates downloads without a restart.

Assisted-by: Claude:claude-opus-4-8
The settings page only loaded values when first opened, so applying a config
URL (whose fetch completes asynchronously) left stale values shown until the
page was reopened. Subscribe to settings changes while the page is open and
reload it when the organization deployment settings or auto-update flag change.

Assisted-by: Claude:claude-opus-4-8
Add a settings-page row that appears when an organization configuration is
active, showing its source (URL / local file / environment) and when it was
applied, with a Clear button that removes the URL, cache, applied timestamp
and in-memory settings. Plugin status now also reports whether an org config
is active and where it came from.

Assisted-by: Claude:claude-opus-4-8
Plugin status is a debugging aid, so include the full raw JSON contents of the
currently-applied organization configuration alongside its source.

Assisted-by: Claude:claude-opus-4-8
- Reconfigure telemetry (reload reporter + restart the JSON-RPC server, which
  receives telemetry settings via its environment) only when the effective
  telemetry settings actually change, via a new telemetrySettingsChanged signal.
  Restarting is relatively expensive, so it must not fire on unrelated changes.
- Switching config source (local file, or Clear) now clears the previous URL
  non-notifying, avoiding a redundant second apply pipeline that produced the
  duplicate "reloaded telemetry reporter" log lines.
- Show an informational notification when an org config is applied interactively
  (not on startup refresh).
- Tests for the URL-clearing, clear-all-state, and telemetry-change-only-on-change
  behavior; rebuild the shared telemetry reporter in teardown to avoid leaking an
  always-enabled Splunk reporter into other tests.

Assisted-by: Claude:claude-opus-4-8
The settings page no longer has a raw Organization Config URL text field. Instead
it shows an "Apply Organization Configuration…" button when no config is applied,
and the active-config indicator with "Change…" and "Clear" buttons when one is.
All three buttons drive the existing apply/clear flows, keeping URL/local-file
handling in one place (the action) rather than split across the settings form.

Assisted-by: Claude:claude-opus-4-8
…loyers

Documents the runtime organization configuration: the JSON schema, the three
ways to apply it (APPMAP_CONFIG_URL env var, in-IDE URL, local file), the
bundled-vs-organization-vs-user precedence and override-on-apply behavior,
offline/failure handling, clearing, verification via Plugin Status Report, and
a snippet deployers can adapt to guide their users. Linked from the README.

Assisted-by: Claude:claude-opus-4-8
Addresses findings from a review of the feature branch:

Security:
- Redact the Splunk HEC token from getAppliedConfigJson(), so it can't leak via
  the Plugin Status Report (which users paste into bug reports). The public CA
  cert and other fields are kept for debuggability.
- Move the cached org-config JSON (which can contain the token) out of the
  roaming application settings into a new non-roaming EnterpriseConfigCacheService
  (RoamingType.DISABLED), so the token is no longer synced to JetBrains cloud /
  other machines. It is still persisted locally as an offline fallback.

Concurrency:
- Replace the tangled fetch-future machinery (AtomicReference<CompletableFuture>,
  getOrStartFetchFuture, IS_FETCHING) — which could return null (NPE), abandon
  futures, spawn duplicate fetches, and log a spurious EDT "logic error" — with a
  generation counter plus a lock. A slow in-flight fetch now checks it hasn't been
  superseded before mutating, and all apply/clear critical sections are serialized,
  so a fetch can no longer clobber a newer Clear or local-file apply.
- awaitInitialFetchIfConfigured now fetches synchronously on its background thread
  and only warns (not errors) if mis-called on the EDT.

Also fixes the stale "never cleared" doc on orgConfigAppliedAt. Adds tests for
token redaction, same-URL-only-changed-keys supersession, and interactive-failure
clearing.

Assisted-by: Claude:claude-opus-4-8
If createComponent() is called again before disposeUIResources(), disconnect the
previous message-bus connection first so the old listener isn't leaked.

Assisted-by: Claude:claude-opus-4-8
FileChooser.chooseFile/chooseFiles resolve a path via VFS on the EDT (their
"restore last selection" logic), which trips the platform's slow-operations
assertion. Add appland.utils.AppMapFileChoosers: it resolves a sensible initial
directory off the EDT (last-opened -> project root -> user home) and opens the
chooser with it pre-selected, delivering the result via callback. Use it for the
organization-config local-file picker and the Navie pinned-files picker.

Public API only (no deprecated allowSlowOperations, no @internal knownIssue). The
assertion is a dev/internal-mode diagnostic not shown to released users, but this
keeps dev logs clean and restores the "reopen the last folder" behavior.

Assisted-by: Claude:claude-opus-4-8
The apply-org-config handler read isApplied() immediately after showPicker() and
posted it back, but applying is asynchronous (URL fetch, and the local-file chooser
now opens via invokeLater), so the read happened before the apply landed — the
local-file case never updated the webview. The IDE side now subscribes to
enterpriseDeploymentSettingsChanged and pushes the applied state when it actually
changes; the webview (signin.js) registers the apply-org-config handler with `on`
(not `once`) at init so it reacts to those pushes regardless of where the config
was applied. (isApplied() still treats a configured URL as applied, by design.)

Assisted-by: Claude:claude-opus-4-8
Review follow-ups in EnterpriseConfigService:
- Convert file:// org-config URLs to a path via proper URI parsing with a raw-path
  fallback: handles file:/// , escaped characters, and UNC, while still accepting the
  "file://" + raw OS path form (incl. Windows file://C:\...). An unparseable file URL
  is treated as a fetch failure.
- Mask URL credentials (userinfo) in logs, the status report, and notifications so
  they can't leak.
- getConfigSourceDescription reports "local file" when a local-file snapshot is the
  active config even if APPMAP_CONFIG_URL is set (the env URL only re-applies on the
  next startup, so the session's effective source is the local file).

Assisted-by: Claude:claude-opus-4-8
This allows all the test suites to run to completion so there are
fewer feedback cycles (hopefully).
The previously used version is no longer publicly available in the
Maven repository. This change updates it to the newest available
version.
@dividedmind dividedmind merged commit c0fdf5f into main Jun 29, 2026
5 checks passed
@dividedmind dividedmind deleted the feat/remote-config branch June 29, 2026 21:06
@appmap-releasebot

Copy link
Copy Markdown

🎉 This PR is included in version 0.85.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants