Deploy projects by pushing files to Dokku server via git.

This commit is contained in:
James Murdza 2024-07-23 22:17:26 -04:00
parent 870783940d
commit 7ed19188d4
4 changed files with 185 additions and 2 deletions

View File

@ -15,6 +15,7 @@
"e2b": "^0.16.1",
"express": "^4.19.2",
"rate-limiter-flexible": "^5.0.3",
"simple-git": "^3.25.0",
"socket.io": "^4.7.5",
"ssh2": "^1.15.0",
"zod": "^3.22.4"
@ -77,6 +78,40 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@kwsites/file-exists": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
"dependencies": {
"debug": "^4.1.1"
}
},
"node_modules/@kwsites/file-exists/node_modules/debug": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/@kwsites/file-exists/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/@kwsites/promise-deferred": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz",
@ -1695,6 +1730,41 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/simple-git": {
"version": "3.25.0",
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.25.0.tgz",
"integrity": "sha512-KIY5sBnzc4yEcJXW7Tdv4viEz8KyG+nU0hay+DWZasvdFOYKeUZ6Xc25LUHHjw0tinPT7O1eY6pzX7pRT1K8rw==",
"dependencies": {
"@kwsites/file-exists": "^1.1.1",
"@kwsites/promise-deferred": "^1.1.1",
"debug": "^4.3.5"
},
"funding": {
"type": "github",
"url": "https://github.com/steveukx/git-js?sponsor=1"
}
},
"node_modules/simple-git/node_modules/debug": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/simple-git/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/simple-update-notifier": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",

View File

@ -17,6 +17,7 @@
"e2b": "^0.16.1",
"express": "^4.19.2",
"rate-limiter-flexible": "^5.0.3",
"simple-git": "^3.25.0",
"socket.io": "^4.7.5",
"ssh2": "^1.15.0",
"zod": "^3.22.4"

View File

@ -0,0 +1,80 @@
import simpleGit, { SimpleGit } from "simple-git";
import path from "path";
import fs from "fs";
import os from "os";
export type FileData = {
id: string;
data: string;
};
export class SecureGitClient {
private gitUrl: string;
private sshKeyPath: string;
constructor(gitUrl: string, sshKeyPath: string) {
this.gitUrl = gitUrl;
this.sshKeyPath = sshKeyPath;
}
async pushFiles(fileData: FileData[], repository: string): Promise<void> {
let tempDir: string | undefined;
try {
// Create a temporary directory
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-push-'));
console.log(`Temporary directory created: ${tempDir}`);
// Write files to the temporary directory
for (const { id, data } of fileData) {
const filePath = path.join(tempDir, id);
const dirPath = path.dirname(filePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
console.log("Writing ", filePath, data);
fs.writeFileSync(filePath, data);
}
// Initialize the simple-git instance with the temporary directory and custom SSH command
const git: SimpleGit = simpleGit(tempDir, {
config: [
'core.sshCommand=ssh -i ' + this.sshKeyPath + ' -o IdentitiesOnly=yes'
]
});
// Initialize a new Git repository
await git.init();
// Add remote repository
await git.addRemote("origin", `${this.gitUrl}:${repository}`);
// Add files to the repository
for (const {id, data} of fileData) {
await git.add(id);
}
// Commit the changes
await git.commit("Add files.");
// Push the changes to the remote repository
await git.push("origin", "master");
console.log("Files successfully pushed to the repository");
if (tempDir) {
fs.rmSync(tempDir, { recursive: true, force: true });
console.log(`Temporary directory removed: ${tempDir}`);
}
} catch (error) {
if (tempDir) {
fs.rmSync(tempDir, { recursive: true, force: true });
console.log(`Temporary directory removed: ${tempDir}`);
}
console.error("Error pushing files to the repository:", error);
throw error;
}
}
}

View File

@ -1,11 +1,11 @@
import os from "os";
import path from "path";
import cors from "cors";
import express, { Express } from "express";
import dotenv from "dotenv";
import { createServer } from "http";
import { Server } from "socket.io";
import { DokkuClient, SSHConfig } from "./DokkuClient";
import { DokkuClient } from "./DokkuClient";
import { SecureGitClient, FileData } from "./SecureGitClient";
import fs from "fs";
import { z } from "zod";
@ -126,6 +126,11 @@ const client = new DokkuClient({
client.connect();
const git = new SecureGitClient(
"dokku@gitwit.app",
process.env.DOKKU_KEY
)
io.on("connection", async (socket) => {
try {
if (inactivityTimeout) clearTimeout(inactivityTimeout);
@ -292,6 +297,33 @@ io.on("connection", async (socket) => {
}
);
socket.on(
"deploy",
async (callback: (response: CallbackResponse) => void) => {
try {
// Push the project files to the Dokku server
console.log("Deploying project ${data.sandboxId}...");
// Remove the /project/[id]/ component of each file path:
const fixedFilePaths = sandboxFiles.fileData.map((file) => {
return {
...file,
id: file.id.split("/").slice(2).join("/"),
};
});
// Push all files to Dokku.
await git.pushFiles(fixedFilePaths, data.sandboxId);
callback({
success: true,
});
} catch (error) {
callback({
success: false,
message: "Failed to deploy project: " + error,
});
}
}
);
socket.on("createFile", async (name: string, callback) => {
try {
const size: number = await getProjectSize(data.sandboxId);