From f07bc2ff11e3e2064932037fceaac8c6666b9e5d Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Thu, 19 Sep 2024 23:03:48 +0500 Subject: [PATCH 1/7] Add Cursor IDE module with Terraform support This commit introduces the Cursor IDE module, allowing users to add a one-click button for launching Cursor IDE into their workspaces. The module is designed to work with Terraform, making it straightforward to manage and configure through infrastructure as code. - Adds a comprehensive README for setting up the module. - Includes example configurations for typical usage scenarios. - Provides automated tests to ensure module reliability. - Defines Terraform resources and variables to handle essential configurations like agent ID, folder locations, and order in the UI. --- cursor/README.md | 35 ++++++++++++++++++ cursor/main.test.ts | 89 +++++++++++++++++++++++++++++++++++++++++++++ cursor/main.tf | 62 +++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 cursor/README.md create mode 100644 cursor/main.test.ts create mode 100644 cursor/main.tf diff --git a/cursor/README.md b/cursor/README.md new file mode 100644 index 00000000..6a1a0f05 --- /dev/null +++ b/cursor/README.md @@ -0,0 +1,35 @@ +--- +display_name: Cursor IDE +description: Add a one-click button to launch Cursor IDE +icon: ../.icons/cursor.svg +maintainer_github: coder +verified: true +tags: [ide, cursor, helper] +--- + +# Cursor IDE + +Add a button to open any workspace with a single click in Cursor IDE. + +Uses the [Coder Remote VS Code Extension](https://github.com/coder/cursor-coder). + +```tf +module "cursor" { + source = "registry.coder.com/modules/cursor/coder" + version = "1.0.15" + agent_id = coder_agent.example.id +} +``` + +## Examples + +### Open in a specific directory + +```tf +module "cursor" { + source = "registry.coder.com/modules/cursor/coder" + version = "1.0.15" + agent_id = coder_agent.example.id + folder = "/home/coder/project" +} +``` diff --git a/cursor/main.test.ts b/cursor/main.test.ts new file mode 100644 index 00000000..edce3b05 --- /dev/null +++ b/cursor/main.test.ts @@ -0,0 +1,89 @@ +import { describe, expect, it } from "bun:test"; +import { + executeScriptInContainer, + runTerraformApply, + runTerraformInit, + testRequiredVariables, +} from "../test"; + +describe("cursor", async () => { + await runTerraformInit(import.meta.dir); + + testRequiredVariables(import.meta.dir, { + agent_id: "foo", + }); + + it("default output", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + }); + expect(state.outputs.cursor_url.value).toBe( + "cursor://coder.coder-remote/open?owner=default&workspace=default&url=https://mydeployment.coder.com&token=$SESSION_TOKEN", + ); + + const coder_app = state.resources.find( + (res) => res.type == "coder_app" && res.name == "cursor", + ); + + expect(coder_app).not.toBeNull(); + expect(coder_app?.instances.length).toBe(1); + expect(coder_app?.instances[0].attributes.order).toBeNull(); + }); + + it("adds folder", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + folder: "/foo/bar", + }); + expect(state.outputs.cursor_url.value).toBe( + "cursor://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&url=https://mydeployment.coder.com&token=$SESSION_TOKEN", + ); + }); + + it("adds folder and open_recent", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + folder: "/foo/bar", + open_recent: "true", + }); + expect(state.outputs.cursor_url.value).toBe( + "cursor://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&openRecent&url=https://mydeployment.coder.com&token=$SESSION_TOKEN", + ); + }); + + it("adds folder but not open_recent", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + folder: "/foo/bar", + openRecent: "false", + }); + expect(state.outputs.cursor_url.value).toBe( + "cursor://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&url=https://mydeployment.coder.com&token=$SESSION_TOKEN", + ); + }); + + it("adds open_recent", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + open_recent: "true", + }); + expect(state.outputs.cursor_url.value).toBe( + "cursor://coder.coder-remote/open?owner=default&workspace=default&openRecent&url=https://mydeployment.coder.com&token=$SESSION_TOKEN", + ); + }); + + it("expect order to be set", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + order: "22", + }); + + const coder_app = state.resources.find( + (res) => res.type == "coder_app" && res.name == "cursor", + ); + + expect(coder_app).not.toBeNull(); + expect(coder_app?.instances.length).toBe(1); + expect(coder_app?.instances[0].attributes.order).toBe(22); + }); +}); diff --git a/cursor/main.tf b/cursor/main.tf new file mode 100644 index 00000000..1ea9c39e --- /dev/null +++ b/cursor/main.tf @@ -0,0 +1,62 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 0.23" + } + } +} + +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +variable "folder" { + type = string + description = "The folder to open in Cursor IDE." + default = "" +} + +variable "open_recent" { + type = bool + description = "Open the most recent workspace or folder. Falls back to the folder if there is no recent workspace or folder to open." + default = false +} + +variable "order" { + type = number + description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)." + default = null +} + +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +resource "coder_app" "vscode" { + agent_id = var.agent_id + external = true + icon = "/icon/code.svg" + slug = "vscode" + display_name = "VS Code Desktop" + order = var.order + url = join("", [ + "vscode://coder.coder-remote/open", + "?owner=", + data.coder_workspace_owner.me.name, + "&workspace=", + data.coder_workspace.me.name, + var.folder != "" ? join("", ["&folder=", var.folder]) : "", + var.open_recent ? "&openRecent" : "", + "&url=", + data.coder_workspace.me.access_url, + "&token=$SESSION_TOKEN", + ]) +} + +output "vscode_url" { + value = coder_app.vscode.url + description = "VS Code Desktop URL." +} From 4a5c908ea7e8823c5242d5191682a1a2a52d9bda Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Thu, 19 Sep 2024 23:09:07 +0500 Subject: [PATCH 2/7] add biome lint suggestions --- cursor/main.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cursor/main.test.ts b/cursor/main.test.ts index edce3b05..cdf70e6b 100644 --- a/cursor/main.test.ts +++ b/cursor/main.test.ts @@ -22,7 +22,7 @@ describe("cursor", async () => { ); const coder_app = state.resources.find( - (res) => res.type == "coder_app" && res.name == "cursor", + (res) => res.type === "coder_app" && res.name === "cursor", ); expect(coder_app).not.toBeNull(); @@ -79,7 +79,7 @@ describe("cursor", async () => { }); const coder_app = state.resources.find( - (res) => res.type == "coder_app" && res.name == "cursor", + (res) => res.type === "coder_app" && res.name === "cursor", ); expect(coder_app).not.toBeNull(); From 48d074e633e3ce2d97706e062ad6254039ac315e Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Thu, 19 Sep 2024 23:28:02 +0500 Subject: [PATCH 3/7] Add cursor SVG icon file --- .icons/cursor.svg | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .icons/cursor.svg diff --git a/.icons/cursor.svg b/.icons/cursor.svg new file mode 100644 index 00000000..abcfe54e --- /dev/null +++ b/.icons/cursor.svg @@ -0,0 +1,25 @@ + + + + + + + + + + From a1260f52216a94d53fc05d122e6aa3471b546e86 Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Thu, 19 Sep 2024 23:53:59 +0500 Subject: [PATCH 4/7] update logo --- .icons/cursor.svg | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/.icons/cursor.svg b/.icons/cursor.svg index abcfe54e..c074bf27 100644 --- a/.icons/cursor.svg +++ b/.icons/cursor.svg @@ -1,25 +1 @@ - - - - - - - - - - + \ No newline at end of file From 5ba919a3c173b841f737274a53ff99a62d15af41 Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Thu, 19 Sep 2024 23:56:17 +0500 Subject: [PATCH 5/7] Rename resources and outputs for Cursor IDE module Add explicit naming for the `coder_app` resource and output to reflect the switch from "VS Code Desktop" to "Cursor IDE." --- cursor/main.tf | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cursor/main.tf b/cursor/main.tf index 1ea9c39e..3207ae8f 100644 --- a/cursor/main.tf +++ b/cursor/main.tf @@ -35,15 +35,15 @@ variable "order" { data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} -resource "coder_app" "vscode" { +resource "coder_app" "cursor" { agent_id = var.agent_id external = true icon = "/icon/code.svg" - slug = "vscode" - display_name = "VS Code Desktop" + slug = "cursor" + display_name = "Cursor IDE" order = var.order url = join("", [ - "vscode://coder.coder-remote/open", + "cursor://coder.coder-remote/open", "?owner=", data.coder_workspace_owner.me.name, "&workspace=", @@ -56,7 +56,7 @@ resource "coder_app" "vscode" { ]) } -output "vscode_url" { - value = coder_app.vscode.url - description = "VS Code Desktop URL." +output "cursor_url" { + value = coder_app.cursor.url + description = "Cursor IDE Desktop URL." } From 7dea2a09a3e4b75f50dab3b5cc237061081d1faf Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Fri, 20 Sep 2024 00:02:46 +0500 Subject: [PATCH 6/7] Bump Cursor module version in README to 1.0.18 --- cursor/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cursor/README.md b/cursor/README.md index 6a1a0f05..a62743be 100644 --- a/cursor/README.md +++ b/cursor/README.md @@ -16,7 +16,7 @@ Uses the [Coder Remote VS Code Extension](https://github.com/coder/cursor-coder) ```tf module "cursor" { source = "registry.coder.com/modules/cursor/coder" - version = "1.0.15" + version = "1.0.18" agent_id = coder_agent.example.id } ``` @@ -28,7 +28,7 @@ module "cursor" { ```tf module "cursor" { source = "registry.coder.com/modules/cursor/coder" - version = "1.0.15" + version = "1.0.18" agent_id = coder_agent.example.id folder = "/home/coder/project" } From 7b32ac3f1047a9976f43a18952701f1763a706fb Mon Sep 17 00:00:00 2001 From: Muhammad Atif Ali Date: Fri, 20 Sep 2024 00:19:33 +0500 Subject: [PATCH 7/7] Update icon for Cursor IDE module --- cursor/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cursor/main.tf b/cursor/main.tf index 3207ae8f..4d481919 100644 --- a/cursor/main.tf +++ b/cursor/main.tf @@ -38,7 +38,7 @@ data "coder_workspace_owner" "me" {} resource "coder_app" "cursor" { agent_id = var.agent_id external = true - icon = "/icon/code.svg" + icon = "/icon/cursor.svg" slug = "cursor" display_name = "Cursor IDE" order = var.order