Skip to content

Add "from-Toolbox" connection flow; auto port-forward; multi-conection#4

Merged
azatsarynnyy merged 9 commits into
mainfrom
from-tbx-flow
May 19, 2026
Merged

Add "from-Toolbox" connection flow; auto port-forward; multi-conection#4
azatsarynnyy merged 9 commits into
mainfrom
from-tbx-flow

Conversation

@azatsarynnyy
Copy link
Copy Markdown
Member

@azatsarynnyy azatsarynnyy commented Apr 30, 2026

https://redhat.atlassian.net/browse/CRW-10454 ("from Toolbox" connection flow; connecting to existing CDEs w/o the Dashboard usage)
https://redhat.atlassian.net/browse/CRW-10451 (manage port forwarding automatically, w/o the user's involvement)
https://redhat.atlassian.net/browse/CRW-10452 (multi-connection support)

Changes:

  • Before this patch, there was a single way to connect - from the Dashboard. This patch introduces the "from Toolbox" flow:

    • The Toolbox-compatible workspaces are listed in the Toolbox App. It's possible to connect w/o using the Dashboard. The Dev Spaces instance is detected based on the current-context in the local kubeconfig
    • The workspaces list is auto-synced with the remote every 5 secs.
    • Workspace status (Stopped/Running) is displayed in the Environment tile
  • Connect command does the following steps:

    • starts a workspace if it is stopped
    • forwards the remote SSH port to a random local one
    • fetches the SSH credentials
    • establishes the SSH connection to CDE
image
  • Added support for multiple simultaneous connections:
image
  • Port-forwarding is handled automatically, transparently for the user. No need to execute the oc port-forward ... command manually anymore.

  • UI/UX enhancements:

    • removed the Toolbox-provided Empty page section at the top of the Toolbox window
    • removed the Toolbox-provided Delete action from the Environment actions menu, as we don't need it for now:

Short demo of current state:

Screen.Recording.2026-04-30.at.19.09.35.mov

Signed-off-by: Artem Zatsarynnyi <azatsary@redhat.com>
Signed-off-by: Artem Zatsarynnyi <azatsary@redhat.com>
@azatsarynnyy azatsarynnyy changed the title [WIP] From tbx flow [WIP] Add from-Toolbox connection flow + auto-port-forward Apr 30, 2026
@azatsarynnyy azatsarynnyy changed the title [WIP] Add from-Toolbox connection flow + auto-port-forward [WIP] Add "from-Toolbox" connection flow + auto-port-forward Apr 30, 2026
Signed-off-by: Artem Zatsarynnyi <azatsary@redhat.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

Warning

Rate limit exceeded

@azatsarynnyy has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 46 minutes and 43 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: b751c147-318b-48bb-876a-dd2fa77b62f8

📥 Commits

Reviewing files that changed from the base of the PR and between 6351a39 and 1e7fec4.

📒 Files selected for processing (1)
  • plugin/src/main/kotlin/com/redhat/devtools/toolbox/DevSpacesRemoteProvider.kt
📝 Walkthrough

Walkthrough

Integrates OpenShift client access and CRD models, discovers DevWorkspace environments, adds connection lifecycle hooks to start workspaces and establish SSH port-forwards, provides dynamic port/credential callbacks to environment views, and updates plugin/provider wiring and build artifacts.

Changes

OpenShift Dev Spaces Integration

Layer / File(s) Summary
Build configuration and documentation
plugin/build.gradle.kts, build-logic/src/main/kotlin/toolbox/buildlogic/InstallToolboxPlugin.kt, CLAUDE.md
Bumped version to 0.0.2; added io.fabric8:openshift-client:7.6.1 with exclusions; runtimeClasspath excludes Kotlin/Kotlinx; install task copies runtime dependencies; added CLAUDE.md documentation.
OpenShift client and CRD models
plugin/src/main/kotlin/.../OpenShiftClientFactory.kt, Projects.kt, openshift/DevWorkspace.kt, openshift/DevWorkspaceTemplate.kt, openshift/DevWorkspaces.kt
Adds OpenShiftClientFactory, Projects wrapper, DevWorkspace and DevWorkspaceTemplate data models, and DevWorkspaces helper with list/get/start/startAndWait/waitUntilRunning.
Environment discovery and repository polling
plugin/src/main/kotlin/.../datasource/DevWorkspacesDataSource.kt, datasource/EnvironmentDataSource.kt, plugin/src/main/kotlin/.../EnvironmentRepository.kt
DevWorkspacesDataSource queries OpenShift via clientFactory and maps results to EnvironmentConfig; EnvironmentDataSource removes external request API; EnvironmentRepository polls every 10s and passes clientFactory into remote environments.
Plugin wiring and provider updates
plugin/src/main/kotlin/com/redhat/devtools/toolbox/DevSpacesPlugin.kt, DevSpacesRemoteProvider.kt
DevSpacesPlugin creates and injects OpenShiftClientFactory; DevSpacesRemoteProvider API updated for LocalizableStringFactory, iconResource, localized messages, new UI page, and simplified handleUri that schedules repository.updateConnectionRequest.
Connection lifecycle and environment implementation
plugin/src/main/kotlin/.../environment/DevSpacesRemoteEnvironment.kt, EnvironmentConfig.toRemoteEnvironment
DevSpacesRemoteEnvironment implements BeforeConnectionHook/AfterDisconnectHook, manages OpenShift client and LocalPortForward, starts/workspace wait, establishes port-forward, reads SSH credentials from pod, caches/releases resources, and closePortForward; toRemoteEnvironment now accepts clientFactory.
Dynamic port & credential providers
plugin/src/main/kotlin/.../environment/EnvironmentContentsViewFactory.kt, SshEnvironmentContentsViewFactory.kt
EnvironmentContentsViewFactory.create now accepts portProvider and credentialsProvider; SshEnvironmentContentsViewFactory and SimpleEnvironmentContentsView obtain port/credentials dynamically; WorkspaceSshConnectionInfo derives values from providers and equality semantics changed.
Port and credential stores
plugin/src/main/kotlin/.../environment/PortAllocator.kt, SshCredentialsStore.kt
Adds PortAllocator singleton to persist per-environment local ports and SshCredentialsStore to cache SSH credentials across connection lifecycle.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.89% 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 describes the main changes: introducing a 'from-Toolbox' connection flow, adding automatic port-forwarding, and enabling multi-connection support.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing the connection flow changes, port-forwarding automation, multi-connection support, and UI/UX enhancements with references to Jira issues.
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.

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


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.

@azatsarynnyy azatsarynnyy changed the title [WIP] Add "from-Toolbox" connection flow + auto-port-forward [WIP] Add "from-Toolbox" connection flow + auto-port-forward + multi-conection May 11, 2026
…mprovments; simplify from-Dashboard flow

Signed-off-by: Artem Zatsarynnyi <azatsary@redhat.com>
Signed-off-by: Artem Zatsarynnyi <azatsary@redhat.com>
@azatsarynnyy azatsarynnyy changed the title [WIP] Add "from-Toolbox" connection flow + auto-port-forward + multi-conection Add "from-Toolbox" connection flow; auto port-forward; multi-conection May 19, 2026
Signed-off-by: Artem Zatsarynnyi <azatsary@redhat.com>
@azatsarynnyy azatsarynnyy marked this pull request as ready for review May 19, 2026 09:49
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (1)
plugin/src/main/kotlin/com/redhat/devtools/toolbox/EnvironmentRepository.kt (1)

58-68: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard startPolling() against duplicate poller jobs.

Each call currently starts a new infinite polling loop. Re-entry can duplicate refresh traffic and race state updates.

Proposed fix
 class EnvironmentRepository(
@@
 ) {
+    private var pollingJob: Job? = null
@@
     fun startPolling() {
-        coroutineScope.launch(CoroutineName("EnvironmentRepository-Polling")) {
+        if (pollingJob?.isActive == true) return
+        pollingJob = coroutineScope.launch(CoroutineName("EnvironmentRepository-Polling")) {
             // Initial fetch
             refreshEnvironments()
@@
         }
     }
🤖 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 `@plugin/src/main/kotlin/com/redhat/devtools/toolbox/EnvironmentRepository.kt`
around lines 58 - 68, startPolling() currently launches a new infinite coroutine
each call; add a nullable Job property (e.g., pollerJob) to
EnvironmentRepository and guard startPolling() so it does not start a second
poller if pollerJob?.isActive is true: if active, return; otherwise assign
pollerJob =
coroutineScope.launch(CoroutineName("EnvironmentRepository-Polling")) { ... }
and ensure pollerJob is cleared when the coroutine completes or is cancelled.
Use the existing coroutineScope, refreshEnvironments(), refreshInterval and
isActive checks; optionally cancel any previous pollerJob before launching if
you prefer restart behavior.
🧹 Nitpick comments (4)
CLAUDE.md (1)

26-28: 💤 Low value

Add language specifier to fenced code block.

The architecture diagram code block should specify text as the language to satisfy the Markdown linter and improve rendering consistency.

📝 Proposed fix
-```
+```text
 DataSource → EnvironmentConfig → Repository → RemoteEnvironment → Provider → Toolbox UI
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In @CLAUDE.md around lines 26 - 28, The fenced code block containing the
architecture line "DataSource → EnvironmentConfig → Repository →
RemoteEnvironment → Provider → Toolbox UI" lacks a language specifier; update
that code fence to include "text" (i.e., use ```text) so the Markdown linter
passes and rendering is consistent for the architecture diagram snippet.


</details>

</blockquote></details>
<details>
<summary>plugin/build.gradle.kts (1)</summary><blockquote>

`3-3`: _💤 Low value_

**Remove or explain the commented-out code.**

The commented `kotlin-dsl` plugin application has no explanation. If it's not needed, remove it; if it's temporarily disabled, add a comment explaining why.

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In `@plugin/build.gradle.kts` at line 3, Remove the stray commented plugin marker
"//  `kotlin-dsl`" or replace it with a short explanatory comment; locate the
top of plugin/build.gradle.kts where the kotlin-dsl mention appears and either
delete that commented line if unused, or add a one-line reason (e.g.,
"temporarily disabled due to X" or "kept for reference - not applied because Y")
so future readers understand why `kotlin-dsl` is present but not applied.
```

</details>

</blockquote></details>
<details>
<summary>plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/DevSpacesRemoteEnvironment.kt (1)</summary><blockquote>

`237-240`: _💤 Low value_

**Consider logging exec failures for debuggability.**

`onFailure` silently counts down without logging the error, making it hard to diagnose issues when reading files from containers.

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In
`@plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/DevSpacesRemoteEnvironment.kt`
around lines 237 - 240, In DevSpacesRemoteEnvironment.kt update the ExecListener
passed to .usingListener so its override fun onFailure(t: Throwable?, response:
io.fabric8.kubernetes.client.dsl.ExecListener.Response?) logs the error and
response details before calling latch.countDown(); specifically modify the
anonymous io.fabric8.kubernetes.client.dsl.ExecListener implementation used in
usingListener to call your class logger/processLogger with a descriptive message
plus t and response (or their useful fields) so failures reading files from
containers are recorded for debugging, then still call latch.countDown().
```

</details>

</blockquote></details>
<details>
<summary>plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/SshEnvironmentContentsViewFactory.kt (1)</summary><blockquote>

`91-101`: _💤 Low value_

**`equals()` always returning `false` violates the contract and risks unexpected behavior.**

While the comment explains this forces Toolbox to refresh cached SSH keys, this breaks the `equals`/`hashCode` contract. If `WorkspaceSshConnectionInfo` is ever used in hash-based collections (HashMap, HashSet), behavior will be incorrect.

Consider overriding `hashCode()` to maintain the contract, even if `equals()` has this workaround. Alternatively, document this class must never be used in collections.


<details>
<summary>📝 Suggested documentation and hashCode override</summary>

```diff
+/**
+ * Note: This class intentionally returns false from equals() to force Toolbox
+ * to refresh cached SSH keys. Do not use in hash-based collections.
+ */
 private class WorkspaceSshConnectionInfo(
     private val portProvider: () -> Int,
     private val credentialsProvider: () -> SshCredentialsStore.Credentials?
 ) : SshConnectionInfo {
     // ... existing code ...
 
     override fun equals(other: Any?): Boolean {
         // ... existing comment and return false ...
     }
+
+    override fun hashCode(): Int = System.identityHashCode(this)
 }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

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

In
`@plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/SshEnvironmentContentsViewFactory.kt`
around lines 91 - 101, The equals() implementation currently always returns
false which breaks the equals/hashCode contract; fix by implementing a proper
equals and matching hashCode for the class (WorkspaceSshConnectionInfo or the
class that defines equals()): compare the concrete identity fields used for SSH
identity (e.g., privateKeys path, host, port, user, and any other
connection-identifying properties) inside equals(other: Any?) and compute
hashCode() from the same fields (use Kotlin's Objects.hash or Kotlin data class
style formula) so instances with the same connection identity are equal and have
the same hash; if the "always refresh key" behavior is required, instead add
clear KDoc on the class that it must not be placed into hash-based collections
and choose a consistent alternative (e.g., return System.identityHashCode(this)
in hashCode) while keeping equals implemented appropriately.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

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

Inline comments:
In @CLAUDE.md:

  • Line 42: The doc line about SshEnvironmentContentsViewFactory is out of date:
    update the sentence under SshEnvironmentContentsViewFactory (environment/) to
    remove "supports only one active connection" and instead state that multiple
    simultaneous SSH connections are supported (and mention whether the hardcoded
    local port 2022 restriction remains or has been removed). Edit the CLAUDE.md
    entry for SshEnvironmentContentsViewFactory to reflect the current behavior
    (support for multiple connections) and clarify the port behavior accordingly.

In
@plugin/src/main/kotlin/com/redhat/devtools/toolbox/DevSpacesRemoteProvider.kt:

  • Around line 69-72: The code reads dwID from queryParams and unconditionally
    calls repository.updateConnectionRequest(dwID, true) which can receive an empty
    string; validate that dwID is non-empty (and optionally well-formed) before
    calling updateConnectionRequest on the repository, e.g., check the dwID variable
    and if it's blank skip the call and log or return an error response; update the
    logic around the dwID variable and the repository.updateConnectionRequest(...)
    invocation to only proceed when dwID is present.

In
@plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/DevSpacesRemoteEnvironment.kt:

  • Around line 55-57: The fields activeClient and activePortForward are accessed
    from multiple threads (written in beforeConnection() and read/written in
    closePortForward()), causing a race; mark both properties with @Volatile or
    guard all accesses with a synchronized block to ensure visibility and atomicity.
    Locate the declarations of activeClient and activePortForward in
    DevSpacesRemoteEnvironment and either add the Kotlin @Volatile annotation to
    both properties or wrap reads/writes inside a single private lock object (e.g.,
    synchronized(lock) { ... }) used consistently in beforeConnection(),
    closePortForward(), and any other accessors to prevent races.
  • Around line 230-244: The code currently calls latch.await(10,
    TimeUnit.SECONDS) and blindly returns output, risking silent failures; update
    the method (in DevSpacesRemoteEnvironment) to capture the boolean result of
    latch.await(10, java.util.concurrent.TimeUnit.SECONDS), and if it returns false
    throw an appropriate exception (e.g., IOException or IllegalStateException) that
    includes contextual details (podName, namespace, filePath) so callers know the
    read timed out; also ensure the ExecListener onFailure surfaces the Throwable
    (for example by storing it and including it in the thrown exception) so timeouts
    and execution errors are not swallowed.

In
@plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/PortAllocator.kt:

  • Around line 26-36: The PortAllocator singleton uses a non-thread-safe
    mutableMapOf in the allocatedPorts field, which can corrupt port mappings under
    concurrent lifecycle events; change allocatedPorts to a thread-safe map (e.g.,
    java.util.concurrent.ConcurrentHashMap or Collections.synchronizedMap) and
    ensure the accessor methods get(environmentId), save(environmentId, port) and
    release(environmentId) operate on that concurrent map (or wrap their bodies with
    appropriate synchronization) so reads/writes are atomic and safe under
    concurrent access.

In
@plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/SshCredentialsStore.kt:

  • Around line 31-41: The cache field in SshCredentialsStore is a plain
    mutableMapOf (cache) and its accessors get, save, and release are called from
    connection hooks on different threads; replace the non-thread-safe mutableMapOf
    with a thread-safe concurrent map (e.g.,
    java.util.concurrent.ConcurrentHashMap<String, Credentials> or
    Collections.synchronizedMap) and update the cache declaration accordingly,
    keeping the existing get(environmentId: String), save(environmentId: String,
    credentials: Credentials) and release(environmentId: String) methods unchanged;
    add the necessary import for ConcurrentHashMap if used.

In
@plugin/src/main/kotlin/com/redhat/devtools/toolbox/openshift/DevWorkspace.kt:

  • Around line 42-47: The id field assignment in DevWorkspace.kt must not default
    to an empty string; change the fallback so workspace identity is stable: use
    status["devworkspaceId"] as? String ?: metadata.uid ?:
    "${metadata.namespace}/${metadata.name}" (or similar namespace+name composite)
    instead of ""; update the id assignment where id = status["devworkspaceId"] as?
    String ?: "" to prefer metadata.uid then a namespace/name composite to avoid
    collapsing distinct workspaces.

In
@plugin/src/main/kotlin/com/redhat/devtools/toolbox/openshift/DevWorkspaces.kt:

  • Around line 148-154: The polling loop in DevWorkspaces.kt is catching
    Exception around the call to get(namespace, name), which also swallows
    kotlinx.coroutines.CancellationException; update the try/catch so cancellation
    is not swallowed by either (a) add a specific catch for CancellationException
    that rethrows it, or (b) narrow the caught exception type to a non-cancellation
    exception (e.g., IOException/RuntimeException) and keep the existing
    delay/continue behavior for those; reference the get(namespace, name) call and
    the catch block in DevWorkspaces.kt and ensure CancellationException is rethrown
    immediately.
  • Around line 118-124: The current start(namespace: String, name: String) uses a
    JSON Patch with op "replace" for "/spec/started" which fails if spec.started is
    absent; change the patch payload to use op "add" (e.g.
    [{"op":"add","path":"/spec/started","value":true}]) so the field is created when
    missing, keep using
    client.genericKubernetesResources(devWorkspaceContext).inNamespace(namespace).withName(name).patch(PatchContext.of(PatchType.JSON),
    ...) and update the patch string accordingly before the logger.info call.

Outside diff comments:
In @plugin/src/main/kotlin/com/redhat/devtools/toolbox/EnvironmentRepository.kt:

  • Around line 58-68: startPolling() currently launches a new infinite coroutine
    each call; add a nullable Job property (e.g., pollerJob) to
    EnvironmentRepository and guard startPolling() so it does not start a second
    poller if pollerJob?.isActive is true: if active, return; otherwise assign
    pollerJob =
    coroutineScope.launch(CoroutineName("EnvironmentRepository-Polling")) { ... }
    and ensure pollerJob is cleared when the coroutine completes or is cancelled.
    Use the existing coroutineScope, refreshEnvironments(), refreshInterval and
    isActive checks; optionally cancel any previous pollerJob before launching if
    you prefer restart behavior.

Nitpick comments:
In @CLAUDE.md:

  • Around line 26-28: The fenced code block containing the architecture line
    "DataSource → EnvironmentConfig → Repository → RemoteEnvironment → Provider →
    Toolbox UI" lacks a language specifier; update that code fence to include "text"
    (i.e., use ```text) so the Markdown linter passes and rendering is consistent
    for the architecture diagram snippet.

In @plugin/build.gradle.kts:

  • Line 3: Remove the stray commented plugin marker "// kotlin-dsl" or replace
    it with a short explanatory comment; locate the top of plugin/build.gradle.kts
    where the kotlin-dsl mention appears and either delete that commented line if
    unused, or add a one-line reason (e.g., "temporarily disabled due to X" or "kept
    for reference - not applied because Y") so future readers understand why
    kotlin-dsl is present but not applied.

In
@plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/DevSpacesRemoteEnvironment.kt:

  • Around line 237-240: In DevSpacesRemoteEnvironment.kt update the ExecListener
    passed to .usingListener so its override fun onFailure(t: Throwable?, response:
    io.fabric8.kubernetes.client.dsl.ExecListener.Response?) logs the error and
    response details before calling latch.countDown(); specifically modify the
    anonymous io.fabric8.kubernetes.client.dsl.ExecListener implementation used in
    usingListener to call your class logger/processLogger with a descriptive message
    plus t and response (or their useful fields) so failures reading files from
    containers are recorded for debugging, then still call latch.countDown().

In
@plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/SshEnvironmentContentsViewFactory.kt:

  • Around line 91-101: The equals() implementation currently always returns false
    which breaks the equals/hashCode contract; fix by implementing a proper equals
    and matching hashCode for the class (WorkspaceSshConnectionInfo or the class
    that defines equals()): compare the concrete identity fields used for SSH
    identity (e.g., privateKeys path, host, port, user, and any other
    connection-identifying properties) inside equals(other: Any?) and compute
    hashCode() from the same fields (use Kotlin's Objects.hash or Kotlin data class
    style formula) so instances with the same connection identity are equal and have
    the same hash; if the "always refresh key" behavior is required, instead add
    clear KDoc on the class that it must not be placed into hash-based collections
    and choose a consistent alternative (e.g., return System.identityHashCode(this)
    in hashCode) while keeping equals implemented appropriately.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: Organization UI

**Review profile**: CHILL

**Plan**: Enterprise

**Run ID**: `be74ff6a-c118-41ac-a07b-f470d43f9558`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between e65b946554887de304f39b6c7bc30739fa1b2155 and 8576f559a9ae27d27d83424aca183c98402ba9a2.

</details>

<details>
<summary>📒 Files selected for processing (18)</summary>

* `CLAUDE.md`
* `build-logic/src/main/kotlin/toolbox/buildlogic/InstallToolboxPlugin.kt`
* `plugin/build.gradle.kts`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/DevSpacesPlugin.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/DevSpacesRemoteProvider.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/EnvironmentRepository.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/datasource/DevWorkspacesDataSource.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/datasource/EnvironmentDataSource.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/DevSpacesRemoteEnvironment.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/EnvironmentContentsViewFactory.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/PortAllocator.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/SshCredentialsStore.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/environment/SshEnvironmentContentsViewFactory.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/openshift/DevWorkspace.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/openshift/DevWorkspaceTemplate.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/openshift/DevWorkspaces.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/openshift/OpenShiftClientFactory.kt`
* `plugin/src/main/kotlin/com/redhat/devtools/toolbox/openshift/Projects.kt`

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* plugin/src/main/kotlin/com/redhat/devtools/toolbox/datasource/EnvironmentDataSource.kt

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread CLAUDE.md
azatsarynnyy and others added 2 commits May 19, 2026 18:56
…evWorkspaces.kt

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Artem Zatsarynnyi <azatsary@redhat.com>
@azatsarynnyy azatsarynnyy merged commit 3446341 into main May 19, 2026
3 checks passed
@azatsarynnyy azatsarynnyy deleted the from-tbx-flow branch May 19, 2026 17:13
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