lol

devpod-desktop: refactor

- combine the frontent derivation into the main derivation
- use cargo-tauri.hook
- patch out sidecar file logic
- patch out schema registering logic
- add glib-networking for image loading
- add darwin support
- add myself as a maintainer

TomaSajt 110c476b efcf17a6

+148 -210
-12
pkgs/development/tools/devpod/add-tauri-updater-feature.patch
··· 1 - diff --git a/Cargo.toml b/Cargo.toml 2 - index 03f64e53..9e2ddcb6 100644 3 - --- a/Cargo.toml 4 - +++ b/Cargo.toml 5 - @@ -15,6 +15,7 @@ serde_json = "1.0" 6 - serde = { version = "1.0", features = ["derive"] } 7 - # Tauri 8 - tauri = { version = "1.2.4", features = [ 9 - + "updater", 10 - "dialog-save", 11 - "process-relaunch", 12 - "window-close",
+110 -132
pkgs/development/tools/devpod/default.nix
··· 1 1 { 2 2 lib, 3 + stdenv, 3 4 buildGoModule, 4 - copyDesktopItems, 5 - darwin, 6 - desktopToDarwinBundle, 5 + rustPlatform, 7 6 fetchFromGitHub, 8 7 fetchYarnDeps, 9 - gtk3, 8 + 9 + cargo-tauri_1, 10 + desktop-file-utils, 10 11 installShellFiles, 11 - jq, 12 + makeBinaryWrapper, 13 + nodejs, 14 + pkg-config, 15 + yarnConfigHook, 16 + wrapGAppsHook3, 17 + 18 + glib-networking, 12 19 libayatana-appindicator, 13 20 libsoup_2_4, 14 - makeDesktopItem, 15 - mkYarnPackage, 16 21 openssl, 17 - pkg-config, 18 - rust, 19 - rustPlatform, 20 - stdenv, 22 + webkitgtk_4_0, 23 + 21 24 testers, 22 - webkitgtk_4_0, 23 25 }: 24 26 25 27 let 26 - pname = "devpod"; 27 28 version = "0.5.20"; 28 29 29 30 src = fetchFromGitHub { 30 31 owner = "loft-sh"; 31 - repo = pname; 32 - rev = "v${version}"; 33 - sha256 = "sha256-8LbqrOKC1als3Xm6ZuU2AySwT0UWjLN2xh+/CvioYew="; 32 + repo = "devpod"; 33 + tag = "v${version}"; 34 + hash = "sha256-8LbqrOKC1als3Xm6ZuU2AySwT0UWjLN2xh+/CvioYew="; 34 35 }; 35 36 36 - meta = with lib; { 37 + meta = { 37 38 description = "Codespaces but open-source, client-only and unopinionated: Works with any IDE and lets you use any cloud, kubernetes or just localhost docker"; 38 39 mainProgram = "devpod"; 39 40 homepage = "https://devpod.sh"; 40 - license = licenses.mpl20; 41 - maintainers = with maintainers; [ maxbrunet ]; 41 + license = lib.licenses.mpl20; 42 + maintainers = with lib.maintainers; [ 43 + maxbrunet 44 + tomasajt 45 + ]; 42 46 }; 43 - in 44 - rec { 45 - devpod = buildGoModule { 46 - inherit 47 - version 48 - src 49 - pname 50 - meta 51 - ; 47 + 48 + devpod = buildGoModule (finalAttrs: { 49 + pname = "devpod"; 50 + inherit version src meta; 52 51 53 52 vendorHash = null; 54 53 ··· 70 69 ''; 71 70 72 71 passthru.tests.version = testers.testVersion { 73 - package = devpod; 72 + package = finalAttrs.finalPackage; 74 73 command = "devpod version"; 75 74 version = "v${version}"; 76 75 }; 77 - }; 76 + }); 78 77 79 - devpod-desktop = 80 - let 81 - frontend-build = mkYarnPackage { 82 - inherit version; 83 - pname = "devpod-frontend"; 84 - 85 - src = "${src}/desktop"; 86 - 87 - offlineCache = fetchYarnDeps { 88 - yarnLock = "${src}/desktop/yarn.lock"; 89 - hash = "sha256-vUV4yX+UvEKrP0vHxjGwtW2WyONGqHVmFor+WqWbkCc="; 90 - }; 91 - 92 - packageJSON = ./package.json; 93 - 94 - buildPhase = '' 95 - export HOME=$(mktemp -d) 96 - yarn --offline run build 78 + devpod-desktop = rustPlatform.buildRustPackage { 79 + pname = "devpod-desktop"; 80 + inherit version src; 97 81 98 - cp -r deps/devpod/dist $out 99 - ''; 82 + sourceRoot = "${src.name}/desktop"; 100 83 101 - doDist = false; 102 - dontInstall = true; 103 - }; 84 + offlineCache = fetchYarnDeps { 85 + yarnLock = "${src}/desktop/yarn.lock"; 86 + hash = "sha256-vUV4yX+UvEKrP0vHxjGwtW2WyONGqHVmFor+WqWbkCc="; 87 + }; 104 88 105 - rustTargetPlatformSpec = stdenv.hostPlatform.rust.rustcTarget; 106 - in 107 - rustPlatform.buildRustPackage { 108 - inherit version src; 109 - pname = "devpod-desktop"; 89 + cargoRoot = "src-tauri"; 90 + buildAndTestSubdir = "src-tauri"; 110 91 111 - sourceRoot = "${src.name}/desktop/src-tauri"; 92 + useFetchCargoVendor = true; 93 + cargoHash = "sha256-HD9b7OWilltL5Ymj28zoZwv5TJV3HT3LyCdagMqLH6E="; 112 94 113 - useFetchCargoVendor = true; 114 - cargoHash = "sha256-HD9b7OWilltL5Ymj28zoZwv5TJV3HT3LyCdagMqLH6E="; 95 + patches = [ 96 + # don't create a .desktop file automatically registered to open the devpod:// URI scheme 97 + # we edit the in-store .desktop file in postInstall to support opening the scheme, 98 + # but users will have to configure the default handler manually 99 + ./dont-auto-register-scheme.patch 115 100 116 - # Workaround: 117 - # The `tauri` dependency features on the `Cargo.toml` file does not match the allowlist defined under `tauri.conf.json`. 118 - # Please run `tauri dev` or `tauri build` or add the `updater` feature. 119 - # Upstream is not interested in fixing that: https://github.com/loft-sh/devpod/pull/648 120 - patches = [ ./add-tauri-updater-feature.patch ]; 101 + # disable the button that symlinks the `devpod-cli` binary to ~/.local/bin/devpod 102 + # and don't show popup where it prompts you to press the above mentioned button 103 + # we'll symlink it manually to $out/bin/devpod in postInstall 104 + ./dont-copy-sidecar-out-of-store.patch 105 + ]; 121 106 122 - postPatch = 123 - '' 124 - ln -s ${devpod}/bin/devpod bin/devpod-cli-${rustTargetPlatformSpec} 125 - cp -r ${frontend-build} frontend-build 107 + postPatch = 108 + '' 109 + ln -s ${lib.getExe devpod} src-tauri/bin/devpod-cli-${stdenv.hostPlatform.rust.rustcTarget} 110 + '' 111 + + lib.optionalString stdenv.hostPlatform.isLinux '' 112 + substituteInPlace $cargoDepsCopy/libappindicator-sys-*/src/lib.rs \ 113 + --replace-fail "libayatana-appindicator3.so.1" "${libayatana-appindicator}/lib/libayatana-appindicator3.so.1" 114 + ''; 126 115 127 - substituteInPlace tauri.conf.json --replace '"distDir": "../dist",' '"distDir": "frontend-build",' 128 - '' 129 - + lib.optionalString stdenv.hostPlatform.isLinux '' 130 - substituteInPlace $cargoDepsCopy/libappindicator-sys-*/src/lib.rs \ 131 - --replace "libayatana-appindicator3.so.1" "${libayatana-appindicator}/lib/libayatana-appindicator3.so.1" 116 + nativeBuildInputs = 117 + [ 118 + cargo-tauri_1.hook 119 + nodejs 120 + yarnConfigHook 121 + ] 122 + ++ lib.optionals stdenv.hostPlatform.isLinux [ 123 + desktop-file-utils 124 + pkg-config 125 + wrapGAppsHook3 126 + ] 127 + ++ lib.optionals stdenv.hostPlatform.isDarwin [ 128 + makeBinaryWrapper 129 + ]; 132 130 133 - # Since `cargo build` is used instead of `tauri build`, configs are merged manually. 134 - jq --slurp '.[0] * .[1]' tauri.conf.json tauri-linux.conf.json >tauri.conf.json.merged 135 - mv tauri.conf.json.merged tauri.conf.json 136 - ''; 131 + buildInputs = lib.optionals stdenv.hostPlatform.isLinux [ 132 + glib-networking 133 + libayatana-appindicator 134 + libsoup_2_4 135 + openssl 136 + webkitgtk_4_0 137 + ]; 137 138 138 - nativeBuildInputs = 139 - [ 140 - copyDesktopItems 141 - pkg-config 142 - ] 143 - ++ lib.optionals stdenv.hostPlatform.isLinux [ 144 - jq 145 - ] 146 - ++ lib.optionals stdenv.hostPlatform.isDarwin [ 147 - desktopToDarwinBundle 148 - ]; 139 + postInstall = 140 + lib.optionalString stdenv.hostPlatform.isDarwin '' 141 + # replace sidecar binary with symlink 142 + ln -sf ${lib.getExe devpod} "$out/Applications/DevPod.app/Contents/MacOS/devpod-cli" 149 143 150 - buildInputs = 151 - [ 152 - libsoup_2_4 153 - openssl 154 - ] 155 - ++ lib.optionals stdenv.hostPlatform.isLinux [ 156 - gtk3 157 - libayatana-appindicator 158 - webkitgtk_4_0 159 - ] 160 - ++ lib.optionals stdenv.hostPlatform.isDarwin [ 161 - darwin.apple_sdk.frameworks.Carbon 162 - darwin.apple_sdk.frameworks.Cocoa 163 - darwin.apple_sdk.frameworks.WebKit 164 - ]; 144 + makeWrapper "$out/Applications/DevPod.app/Contents/MacOS/DevPod" "$out/bin/dev-pod" 145 + '' 146 + + lib.optionalString stdenv.hostPlatform.isLinux '' 147 + # replace sidecar binary with symlink 148 + ln -sf ${lib.getExe devpod} "$out/bin/devpod-cli" 165 149 166 - desktopItems = [ 167 - (makeDesktopItem { 168 - name = "DevPod"; 169 - categories = [ "Development" ]; 170 - comment = "Spin up dev environments in any infra"; 171 - desktopName = "DevPod"; 172 - exec = "DevPod %U"; 173 - icon = "DevPod"; 174 - terminal = false; 175 - type = "Application"; 176 - mimeTypes = [ "x-scheme-handler/devpod" ]; 177 - }) 178 - ]; 150 + # set up scheme handling 151 + desktop-file-edit "$out/share/applications/dev-pod.desktop" \ 152 + --set-key="Exec" --set-value="dev-pod %u" \ 153 + --set-key="MimeType" --set-value="x-scheme-handler/devpod" 154 + '' 155 + + '' 156 + # propagate the `devpod` command 157 + ln -s ${lib.getExe devpod} "$out/bin/devpod" 158 + ''; 179 159 180 - postInstall = '' 181 - ln -sf ${devpod}/bin/devpod $out/bin/devpod-cli 182 - mv $out/bin/devpod-desktop $out/bin/DevPod 160 + # we only want to wrap the main binary 161 + dontWrapGApps = true; 183 162 184 - mkdir -p $out/share/icons/hicolor/{256x256@2,128x128,32x32}/apps 185 - cp icons/128x128@2x.png $out/share/icons/hicolor/256x256@2/apps/DevPod.png 186 - cp icons/128x128.png $out/share/icons/hicolor/128x128/apps/DevPod.png 187 - cp icons/32x32.png $out/share/icons/hicolor/32x32/apps/DevPod.png 188 - ''; 163 + postFixup = lib.optionalString stdenv.hostPlatform.isLinux '' 164 + wrapGApp "$out/bin/dev-pod" 165 + ''; 189 166 190 - meta = meta // { 191 - mainProgram = "DevPod"; 192 - # darwin does not build 193 - # https://github.com/h4llow3En/mac-notification-sys/issues/28 194 - platforms = lib.platforms.linux; 195 - }; 167 + meta = meta // { 168 + mainProgram = "dev-pod"; 169 + platforms = lib.platforms.linux ++ lib.platforms.darwin; 196 170 }; 171 + }; 172 + in 173 + { 174 + inherit devpod devpod-desktop; 197 175 }
+13
pkgs/development/tools/devpod/dont-auto-register-scheme.patch
··· 1 + diff --git a/src-tauri/src/custom_protocol.rs b/src-tauri/src/custom_protocol.rs 2 + index b6ed6e7..5434337 100644 3 + --- a/src-tauri/src/custom_protocol.rs 4 + +++ b/src-tauri/src/custom_protocol.rs 5 + @@ -162,7 +162,7 @@ impl CustomProtocol { 6 + pub fn setup(&self, app: AppHandle) { 7 + let app_handle = app.clone(); 8 + 9 + - let result = tauri_plugin_deep_link::register(APP_URL_SCHEME, move |url_scheme| { 10 + + let result = tauri_plugin_deep_link::listen(move |url_scheme| { 11 + tauri::async_runtime::block_on(async { 12 + info!("App opened with URL: {:?}", url_scheme.to_string()); 13 +
+25
pkgs/development/tools/devpod/dont-copy-sidecar-out-of-store.patch
··· 1 + diff --git a/src/client/client.ts b/src/client/client.ts 2 + index 3e1747e..a842534 100644 3 + --- a/src/client/client.ts 4 + +++ b/src/client/client.ts 5 + @@ -250,6 +250,7 @@ class Client { 6 + } 7 + 8 + public async isCLIInstalled(): Promise<Result<boolean>> { 9 + + return Return.Value(true); 10 + try { 11 + // we're in a flatpak, we need to check in other paths. 12 + if (import.meta.env.TAURI_IS_FLATPAK === "true") { 13 + diff --git a/src/components/useInstallCLI.tsx b/src/components/useInstallCLI.tsx 14 + index ba3be79..ad25f3f 100644 15 + --- a/src/components/useInstallCLI.tsx 16 + +++ b/src/components/useInstallCLI.tsx 17 + @@ -77,7 +77,7 @@ export function useInstallCLI() { 18 + variant="outline" 19 + isLoading={isLoading} 20 + onClick={() => addBinaryToPath({})} 21 + - isDisabled={status === "success"}> 22 + + isDisabled={true}> 23 + Add CLI to Path 24 + </Button> 25 + <AlertDialog isOpen={isOpen} onClose={onClose} leastDestructiveRef={cancelRef}>
-66
pkgs/development/tools/devpod/package.json
··· 1 - { 2 - "name": "devpod", 3 - "private": true, 4 - "version": "0.0.0", 5 - "type": "module", 6 - "scripts": { 7 - "dev": "vite", 8 - "build": "tsc && vite build", 9 - "preview": "vite preview", 10 - "tauri": "tauri", 11 - "desktop:dev": "tauri dev --config src-tauri/tauri-dev.conf.json", 12 - "desktop:dev:debug": "export DEBUG=true; yarn desktop:dev", 13 - "desktop:build:dev": "DEBUG=true tauri build --config src-tauri/tauri-dev.conf.json", 14 - "desktop:build:debug": "tauri build --debug", 15 - "desktop:build": "tauri build --features enable-updater", 16 - "format:check": "prettier --check .", 17 - "format:fix": "prettier --write .", 18 - "types:check": "tsc -p ./tsconfig.json --noEmit" 19 - }, 20 - "dependencies": { 21 - "@chakra-ui/icons": "2.1.1", 22 - "@chakra-ui/react": "2.8.1", 23 - "@emotion/react": "11.11.1", 24 - "@emotion/styled": "11.11.0", 25 - "@headlessui/react": "1.7.17", 26 - "@tanstack/react-query": "4.36.1", 27 - "@tanstack/react-query-devtools": "4.36.1", 28 - "@tanstack/react-table": "8.10.7", 29 - "@tauri-apps/api": "1.5.3", 30 - "dayjs": "1.11.10", 31 - "framer-motion": "10.16.9", 32 - "markdown-to-jsx": "7.3.2", 33 - "react": "18.2.0", 34 - "react-dom": "18.2.0", 35 - "react-hook-form": "7.48.2", 36 - "react-icons": "4.12.0", 37 - "react-router": "6.20.0", 38 - "react-router-dom": "6.20.0", 39 - "tauri-plugin-store-api": "https://github.com/tauri-apps/tauri-plugin-store#v1", 40 - "uuid": "9.0.1", 41 - "xterm": "5.3.0", 42 - "xterm-addon-fit": "0.7.0" 43 - }, 44 - "devDependencies": { 45 - "@tanstack/eslint-plugin-query": "4.36.1", 46 - "@tauri-apps/cli": "1.5.11", 47 - "@types/node": "18.15.3", 48 - "@types/react": "18.2.28", 49 - "@types/react-dom": "18.2.13", 50 - "@types/uuid": "9.0.5", 51 - "@typescript-eslint/eslint-plugin": "5.59.11", 52 - "@typescript-eslint/parser": "5.59.11", 53 - "@vitejs/plugin-react": "4.1.0", 54 - "eslint": "8.44.0", 55 - "eslint-config-prettier": "8.8.0", 56 - "eslint-config-react-app": "7.0.1", 57 - "eslint-plugin-react": "7.34.1", 58 - "eslint-plugin-react-hooks": "4.6.0", 59 - "prettier": "3.0.3", 60 - "typescript": "5.0.4", 61 - "vite": "4.4.9" 62 - }, 63 - "resolutions": { 64 - "lodash": "4.17.21" 65 - } 66 - }