Skip to content

feat: add DigitalOcean integration with droplet trigger and action#2983

Open
aldoabellto235 wants to merge 10 commits intosuperplanehq:mainfrom
aldoabellto235:feat/digital-ocean-integration
Open

feat: add DigitalOcean integration with droplet trigger and action#2983
aldoabellto235 wants to merge 10 commits intosuperplanehq:mainfrom
aldoabellto235:feat/digital-ocean-integration

Conversation

@aldoabellto235
Copy link

@aldoabellto235 aldoabellto235 commented Feb 9, 2026

Summary

Adds a new DigitalOcean integration to SuperPlane, connecting to the
DigitalOcean API via Personal Access Token authentication.

  • Trigger: On Droplet Event — polls the DigitalOcean Actions API every
    60s for droplet lifecycle events (create, destroy, power_on, power_off,
    shutdown, reboot, snapshot, rebuild, resize, rename)
  • Action: Create Droplet — provisions a new droplet with selectable
    region, size, and image from the user's account; returns droplet ID, IP
    address, and status
  • Includes hostname validation on the droplet name field to prevent invalid
    characters before hitting the API
  • Frontend mappers for trigger/component rendering, DigitalOcean icon, and
    display name registration

Changes

Backend (pkg/integrations/digitalocean/)

File Purpose
digitalocean.go Base integration: auth, Sync via GET /v2/account,
ListResources for region/size/image
client.go HTTP client (Bearer token) with methods for account,
regions, sizes, images, droplets, actions
on_droplet_event.go Polling trigger using ScheduleActionCall
filters completed actions by type and time
create_droplet.go Component: POST /v2/droplets with hostname
validation
example.go + JSON files Embedded example payloads for trigger and
component
*_test.go Unit tests for Sync, ListResources, Setup, Execute,
HandleAction

Frontend

File Purpose
mappers/digitalocean/ index, trigger renderer, component mapper
integrationIcons.tsx DigitalOcean logo registration
integrationDisplayName.ts "DigitalOcean" display name
mappers/index.ts Registration in app-wide registries

Modified

  • pkg/server/server.go — import for init() registration

Test plan

  • Unit tests pass for Sync (valid/invalid/empty token)
  • Unit tests pass for ListResources (region, size, image, unknown type)
  • Unit tests pass for CreateDroplet Setup (missing fields, invalid
    hostname, valid config)
  • Unit tests pass for CreateDroplet Execute (success, API error)
  • Unit tests pass for OnDropletEvent Setup (valid, empty events, already
    set up)
  • Unit tests pass for OnDropletEvent HandleAction (matching events
    emitted, no events re-schedules)
  • Manual: set up integration with a real DO token → status becomes ready
  • Manual: Create Droplet with region/size/image dropdowns populated from
    account
  • Manual: invalid hostname rejected at setup time with clear error
  • Video demo attached showing setup and working components

Closes #2398

Video:
https://drive.google.com/file/d/1frbOy9LM_DvesaIi_c0Zo4shlk2ebPhA/view?usp=sharing

@AleksandarCole AleksandarCole added the pr:stage-1/3 Needs to pass basic review. label Feb 9, 2026
@AleksandarCole
Copy link
Collaborator

Thanks @aldoabellto235 for the submission.

Please review these instructions: https://github.com/superplanehq/superplane/blob/main/docs/contributing/integration-prs.md

Can you attach the video of the working integration and components?

@aldoabellto235
Copy link
Author

Thanks @aldoabellto235 for the submission.

Please review these instructions: https://github.com/superplanehq/superplane/blob/main/docs/contributing/integration-prs.md

Can you attach the video of the working integration and components?

i have attached link to google drive vedeo

@aldoabellto235 aldoabellto235 force-pushed the feat/digital-ocean-integration branch from e960565 to 1789d18 Compare February 10, 2026 03:27
@aldoabellto235 aldoabellto235 force-pushed the feat/digital-ocean-integration branch from 0d0bc1c to 5d1cd59 Compare February 10, 2026 07:40
@AleksandarCole AleksandarCole self-requested a review February 10, 2026 09:21
@AleksandarCole AleksandarCole added pr:stage-2/3 Needs to pass functional review and removed pr:stage-1/3 Needs to pass basic review. labels Feb 10, 2026
@AleksandarCole
Copy link
Collaborator

AleksandarCole commented Feb 10, 2026

@aldoabellto235 amazing work! Tested it and it runs good with no issues.
Can we address these small changes before I push it to the code review:

  • Details tab (onEvent): can we move timestamps at the top, can we also include more info about the droplet (like what we have in other component) - currently only region is displayed, need info on size and os. Let's use Dropplet ID instead of ResourceID. Overall we want to have similar details for these two components.
  • Details tab (createDroplet) - Can we add timestamps at the top? Ip address appears empty. Status doesn't make sense to have here. Let's also include OS info
  • createDropplet configuration - when I enable tag and add some string - it will result in 500 when I try to save it

@aldoabellto235
Copy link
Author

@aldoabellto235 amazing work! Tested it and it runs good with no issues. Can we address these small changes before I push it to the code review:

  • Details tab (onEvent): can we move timestamps at the top, can we also include more info about the droplet (like what we have in other component) - currently only region is displayed, need info on size and os. Let's use Dropplet ID instead of ResourceID. Overall we want to have similar details for these two components.
  • Details tab (createDroplet) - Can we add timestamps at the top? Ip address appears empty. Status doesn't make sense to have here. Let's also include OS info
  • createDropplet configuration - when I enable tag and add some string - it will result in 500 when I try to save it

Done can you check again

@aldoabellto235 aldoabellto235 force-pushed the feat/digital-ocean-integration branch from d89aa9b to db1be15 Compare February 10, 2026 14:37
@AleksandarCole
Copy link
Collaborator

@aldoabellto235 works well, thanks for prompt update! I will move it on to code review. Not sure about using 60s pooling for the trigger component - but I will let the engineering team check that.

In the meantime - I see that there is a conflict - please resolve that one while code review is in progress.

@AleksandarCole AleksandarCole added pr:stage-3/3 Ready for full, in-depth, review and removed pr:stage-2/3 Needs to pass functional review labels Feb 10, 2026
@shiroyasha
Copy link
Collaborator

Hey @aldoabellto235. Congrats on your first PR.

I see that you have quite a lot of unrelated changes, e.g. changes to GitLab, PagerDuty, etc. Please rebase with main if you haven't already, and remove changes from the Core system and from non-Digital Ocean components.

Returning to stage-2/3. The diff is quite big, and it will require a new functional review after the cleanup and rebase with main is done.

@shiroyasha shiroyasha added pr:stage-2/3 Needs to pass functional review and removed pr:stage-3/3 Ready for full, in-depth, review labels Feb 10, 2026
@aldoabellto235 aldoabellto235 force-pushed the feat/digital-ocean-integration branch 3 times, most recently from c51b964 to a72339f Compare February 11, 2026 01:33
@aldoabellto235 aldoabellto235 force-pushed the feat/digital-ocean-integration branch from 56debde to 47ef679 Compare February 11, 2026 04:38
@aldoabellto235
Copy link
Author

Hey @aldoabellto235. Congrats on your first PR.

I see that you have quite a lot of unrelated changes, e.g. changes to GitLab, PagerDuty, etc. Please rebase with main if you haven't already, and remove changes from the Core system and from non-Digital Ocean components.

Returning to stage-2/3. The diff is quite big, and it will require a new functional review after the cleanup and rebase with main is done.

Done updated

@AleksandarCole AleksandarCole added pr:stage-3/3 Ready for full, in-depth, review and removed pr:stage-2/3 Needs to pass functional review labels Feb 11, 2026
@shiroyasha shiroyasha self-assigned this Feb 11, 2026
@shiroyasha
Copy link
Collaborator

Hey @aldoabellto235. The code looks great! I'm doing the final round of tests and trying to set up this flow:

1/ Create a dropplet
2/ SSH into it, and run echo "hello world"

CleanShot 2026-02-13 at 11 10 03@2x

It seems that the IP address is not returned in the payload.
This would be a critical info to have in the downstream nodes.

Can we get it from DO's API?

@shiroyasha shiroyasha added pr:stage-2/3 Needs to pass functional review and removed pr:stage-3/3 Ready for full, in-depth, review labels Feb 13, 2026
@aldoabellto235
Copy link
Author

Hey @aldoabellto235. The code looks great! I'm doing the final round of tests and trying to set up this flow:

1/ Create a dropplet 2/ SSH into it, and run echo "hello world"

CleanShot 2026-02-13 at 11 10 03@2x It seems that the IP address is not returned in the payload. This would be a critical info to have in the downstream nodes.

Can we get it from DO's API?

Okey let me add that

@aldoabellto235 aldoabellto235 force-pushed the feat/digital-ocean-integration branch 2 times, most recently from 7cdea17 to 43e2e11 Compare February 14, 2026 16:27
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

@aldoabellto235 aldoabellto235 force-pushed the feat/digital-ocean-integration branch from 0cea1d6 to c10218b Compare February 14, 2026 17:06
aldo235 and others added 10 commits February 15, 2026 00:12
Add DigitalOcean integration with:
- Create Droplet component (with SSH keys, tags, user data support)
- On Droplet Event trigger (polls for lifecycle events)
- Client with account, regions, sizes, images, actions, and droplet APIs
- Frontend mappers with execution details and event rendering
- Icon registration in sidebar and component header

Signed-off-by: Aldo <aldo.abellto14@gmail.com>
The validateList function accessed field.TypeOptions.List without
first checking if TypeOptions was nil, unlike every other validator
(validateNumber, validateString, validateText, validateSelect, etc).

This could cause a nil pointer dereference when validating a
FieldTypeList field that has no TypeOptions configured.

Signed-off-by: Aldo <aldo.abellto14@gmail.com>
Avoid N+1 API calls when multiple actions reference the same droplet
by caching GetDroplet results per ResourceID during each poll cycle.

Signed-off-by: Aldo <aldo.abellto14@gmail.com>
Closes superplanehq#2618

This PR introduces the **Cursor** integration to SuperPlane, allowing
users to build workflows utilizing Cursor's AI-powered capabilities.

It includes the base integration setup and two starter components:

1. **Launch Cloud Agent (Action):** Triggers a Cursor Cloud Agent on a
specific repository/branch and tracks the execution state to completion.
It links to the Cloud Agent and PR in the output. This agent has no
limits(except credit limit).
2. **Get Daily Usage Data (Action):** Fetches daily team usage metrics
from the Cursor Admin API for reporting and cost tracking.

- **Authentication:** Connects via Cursor BasicAuth (Admin API and Cloud
Agents API).
- **Code Logic:** The `Launch Cloud Agent` implementation handles
significant logic to track the agent's lifecycle (polling status,
handling completion, etc.). The code is structured to robustly handle
this weight to ensure reliable execution tracking.

[Watch the Loom
Video](https://www.loom.com/share/2f6f3f98ab6b47ce88444a15f93afe45)

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation (`make
gen.components.docs`)
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] I have signed off my commits (`git commit -s`)

---------

Signed-off-by: Harsh <harxhist@gmail.com>
Signed-off-by: Igor Šarčević <igor@operately.com>
Co-authored-by: Igor Šarčević <igisar@gmail.com>
Co-authored-by: Igor Šarčević <igor@operately.com>
Signed-off-by: Aldo <aldo.abellto14@gmail.com>
Add networks/v4 data to the droplet enrichment payload so the
trigger detail tab can display the droplet's public IP address.

Signed-off-by: Aldo <aldo.abellto14@gmail.com>
Signed-off-by: Aldo <aldo.abellto14@gmail.com>
Add DigitalOcean integration with:
- Create Droplet component (with SSH keys, tags, user data support)
- On Droplet Event trigger (polls for lifecycle events)
- Client with account, regions, sizes, images, actions, and droplet APIs
- Frontend mappers with execution details and event rendering
- Icon registration in sidebar and component header

Signed-off-by: Aldo <aldo.abellto14@gmail.com>
The rebase conflict resolution accidentally kept older versions of
Cursor integration files, missing ConversationMessage and
GetAgentConversation that get_last_message.go depends on.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Aldo <aldo.abellto14@gmail.com>
Instead of emitting immediately after creation (when droplet status is
"new" and has no IP), store the droplet ID in metadata and schedule a
poll action every 10s. Once the droplet reaches "active" status with
an assigned IP address, emit the full droplet data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Aldo <aldo.abellto14@gmail.com>
- createDroplet: handle terminal droplet states (off, archive) instead
  of polling indefinitely. Only continue polling for "new" status.
- onDropletEvent: log emit failures instead of returning error, so the
  trigger keeps polling and doesn't stop permanently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Aldo <aldo.abellto14@gmail.com>
@aldoabellto235 aldoabellto235 force-pushed the feat/digital-ocean-integration branch from e8ed841 to 831df54 Compare February 14, 2026 17:12
@aldoabellto235
Copy link
Author

@shiroyasha i add ip address, you can try it
Screenshot 2026-02-15 at 00 11 53

@AleksandarCole
Copy link
Collaborator

Thanks @aldoabellto235!

@shiroyasha I've tested this, it waits for droplet to spin up (Running state), and provides IP at the end.

If code looks good I would like to merge this.

@AleksandarCole AleksandarCole added pr:stage-3/3 Ready for full, in-depth, review and removed pr:stage-2/3 Needs to pass functional review labels Feb 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:stage-3/3 Ready for full, in-depth, review wfh

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DigitalOcean] Base

5 participants