Skip to content

Commit

Permalink
📦 Improve kastro auto deploy
Browse files Browse the repository at this point in the history
  • Loading branch information
KimlikDAO-bot committed Jan 10, 2025
1 parent 133abea commit e2873a7
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 95 deletions.
1 change: 1 addition & 0 deletions kastro/cloudflare/api.d.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import cloudflare from "./cloudflare.d";
/**
* @typedef {{
* accountId: string,
* zoneId: string,
* token: string
* }}
*/
Expand Down
1 change: 1 addition & 0 deletions kastro/cloudflare/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "./api.d";
* @struct
* @typedef {{
* account: string,
* zone: string,
* apiToken: string
* }}
*/
Expand Down
27 changes: 27 additions & 0 deletions kastro/cloudflare/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { chunk } from "../../util/arrays";
import { ApiV4 } from "./api";

const purge = async (crateName, secrets, namedAssets) => {
const { HostUrl } = await import(crateName);
const { zoneId, token } = secrets.CloudflareAuth;
namedAssets.push("");
/** @const {!Array<!Array<string>>} */
const batches = chunk(namedAssets, 30);
for (let i = 0; i < batches.length; ++i) {
console.log(`Purging batch ${i + 1} of ${batches.length}:`, batches[i]);
/** @const {string} */
const body = JSON.stringify({ files: batches[i].map((asset) => `${HostUrl}/${asset}`) });
const res = await fetch(`${ApiV4}/zones/${zoneId}/purge_cache`, {
method: "POST",
headers: {
"content-type": "application/json",
"authorization": `Bearer ${token}`,
},
body
});
const { success } = await res.json();
console.log(success ? "Success" : "Failed");
}
}

export default { purge };
76 changes: 0 additions & 76 deletions kastro/cloudflare/cftrimmer.py

This file was deleted.

3 changes: 2 additions & 1 deletion kastro/cloudflare/crate.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ const uploadAssets = async (auth, namespaceId, namedAssets) => {
}


const deployCrate = (crateName) => import(crateName)
const deploy = (crateName) => import(crateName)
.then((crate) => {

})

export {
deploy,
Auth,
};
2 changes: 2 additions & 0 deletions kastro/cloudflare/test/workers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const getAuth = () => {
/** @const {Auth} */
const auth = {
account: process.env["CF_TESTING_ACCOUNT_ID"],
zone: "",
apiToken: process.env["CF_TESTING_API_TOKEN"],
};

Expand All @@ -20,6 +21,7 @@ const getAuth = () => {
: import(secrets)
.then((mod) => /** @type {Auth} */({
account: mod["CloudflareAuth"].accountId,
zone: "",
apiToken: mod["CloudflareAuth"].token,
}))
.catch(() => /** @type {Auth} */({ account: "", apiToken: "" }));
Expand Down
4 changes: 2 additions & 2 deletions kastro/kastro.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ const buildCrate = (crateName, buildMode) => import(crateName)
const deployCrate = (crateName, target) => Promise.all([
buildCrate(crateName, compiler.BuildMode.Compiled),
import(`${process.cwd()}/.secrets.js`),
import(`${target}/crate.js`)
import(`./${target}/crate.js`)
])
.then(([_, secrets, crate]) => crate.deploy(crateName, secrets, compiler.getNamedAssets()));

Expand All @@ -201,4 +201,4 @@ if (args.command == "serve")
else if (args.command == "build")
buildCrate(crateName, compiler.BuildMode.Compiled);
else if (args.command == "deploy")
deployCrate(crateName, args["target"] || "cloudflare");
deployCrate(crateName, args["target"] || "vps");
29 changes: 14 additions & 15 deletions kastro/vps/crate.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
import { spawn } from "bun";
import cache from "../cloudflare/cache";
import { restart } from "./service";

const deploy = async (crateName, secrets, namedAssets) => {
const {
host = 'localhost',
username = 'root',
remotePath = '/var/www/crate',
host,
username,
remotePath,
sshKey,
} = secrets.VpsConfig;

const target = `${username}@${host}:${remotePath}`;
/** @const {string} */
const target = `[${host}]:${remotePath}/crate`;

try {
// Deploy files
const rsync = spawn([
"rsync",
"-azP",
"--delete",
"-e", `ssh -p 22`,
"build/crate/",
"-rP",
"-e", `ssh -6 -i ${sshKey} -l ${username}`,
"./build/crate/",
target
]);

const deployOutput = await new Response(rsync.stdout).text();
console.log('Deploy successful:', deployOutput);

return { success: true };
console.log("Deploy successful:", deployOutput);
} catch (error) {
console.error('Deploy failed:', error);
console.error("Deploy failed:", error);
throw error;
}
return restart(secrets).then(() => cache.purge(crateName, secrets, namedAssets));
}

export { deploy };
89 changes: 89 additions & 0 deletions kastro/vps/service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { spawn } from "bun";

const setup = async (secrets) => {
const {
host = "localhost",
username = "root",
sshKey,
remotePath = "/var/www/crate",
} = secrets.VpsConfig;

// Create systemd service file content
const serviceConfig = `[Unit]
Description=Kastro service
After=network.target
[Service]
Type=simple
User=${username}
WorkingDirectory=${remotePath}
ExecStart=/snap/bin/bun run ./crateServer.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target`;

try {
// Write service file remotely
const sshCommand = `echo '${serviceConfig}' | sudo tee /etc/systemd/system/kastro.service`;
const setupCommands = [
sshCommand,
"sudo chmod 644 /etc/systemd/system/kastro.service",
"sudo systemctl daemon-reload",
"sudo systemctl enable kastro",
"sudo systemctl restart kastro"
].join(" && ");

const ssh = spawn([
"ssh",
"-6",
"-i", sshKey,
"-l", username,
host,
setupCommands
]);

const output = await new Response(ssh.stdout).text();
console.log("Service setup successful:", output);

// Check service status
const status = spawn([
"ssh",
"-6",
"-i", sshKey,
"-l", username,
host,
"sudo systemctl status kastro"
]);

const statusOutput = await new Response(status.stdout).text();
console.log("Service status:", statusOutput);
} catch (error) {
console.error("Service setup failed:", error);
throw error;
}
};

const restart = async (secrets) => {
const {
host = "localhost",
username = "root",
sshKey,
} = secrets.VpsConfig;

const ssh = spawn([
"ssh",
"-6",
"-i", sshKey,
"-l", username,
host,
"sudo systemctl restart kastro"
]);

const output = await new Response(ssh.stdout).text();
console.log("Service restart successful:", output);
}

export { setup, restart };
2 changes: 2 additions & 0 deletions kdjs/externs/bun/test.d.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Matcher.prototype.toBeTrue = function () { }

Matcher.prototype.toBeTruthy = function () { }

Matcher.prototype.toThrow = function () { }

/** @param {string=} message */
Matcher.prototype.fail = function (message) { }

Expand Down
19 changes: 18 additions & 1 deletion util/arrays.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,21 @@ const shuffle = (arr) => {
return arr;
}

export { shuffle };
/**
* Splits an array into chunks of size n.
*
* @template T
* @param {!Array<T>} arr
* @param {number} n
* @return {!Array<!Array<T>>}
*/
const chunk = (arr, n) => {
if (n <= 0) throw 0;
/** @const {!Array<!Array<T>>} */
const result = [];
for (let i = 0; i < arr.length; i += n)
result.push(arr.slice(i, i + n));
return result;
};

export { chunk, shuffle };
25 changes: 25 additions & 0 deletions util/test/arrays.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { expect, test } from "bun:test";
import { chunk } from "../arrays";

/** @const {!Array<!Array>} */
const TestVectors = [
// [input array, chunk size, expected result]
[[], 2, []],
[[1], 2, [[1]]],
[[1, 2, 3, 4], 2, [[1, 2], [3, 4]]],
[[1, 2, 3, 4, 5], 2, [[1, 2], [3, 4], [5]]],
[[1, 2, 3], 1, [[1], [2], [3]]],
[['a', 'b', 'c', 'd'], 3, [['a', 'b', 'c'], ['d']]],
[[1, 2, 3, 4, 5, 6], 3, [[1, 2, 3], [4, 5, 6]]],
];

test("chunk arrays on sample points", () => {
for (const [input, size, expected] of TestVectors) {
expect(chunk(input, size)).toEqual(expected);
}
});

test("throws on invalid chunk size", () => {
expect(() => chunk([1, 2, 3], 0)).toThrow();
expect(() => chunk([1, 2, 3], -1)).toThrow();
});

0 comments on commit e2873a7

Please sign in to comment.