Skip to content

Commit

Permalink
Initial version.
Browse files Browse the repository at this point in the history
  • Loading branch information
lgarron committed May 5, 2024
0 parents commit b3a1efe
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/node_modules
/package-lock.json
11 changes: 11 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.PHONY: lint
lint:
npx @biomejs/biome check ./src

.PHONY: format
format:
npx @biomejs/biome check ./src

.PHONY: publish
publish:
npm publish
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# @cubing/deploy

Dreamhost-compatible deploys using [`bun`](https://bun.sh/) and `rsync`.

1. Install:

```shell
bun add --development @cubing/deploy
# or
npm install --save-dev @cubing/deploy
```

2. Add URLs to `package.json`:

```json
{
"@cubing/deploy": {
"https://experiments.cubing.net/test/deploy": {}
}
}
```

3. Run:

```shell
bun x @cubing/deploy
```
13 changes: 13 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"formatter": {
"indentStyle": "space",
"indentWidth": 2
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
}

Binary file added bun.lockb
Binary file not shown.
25 changes: 25 additions & 0 deletions dist/web/experiments.cubing.net/test/deploy/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
<title>Example</title>
<!-- From: https://github.com/lgarron/minimal-html-style (v1.0.0) -->
<meta name="viewport" content="width=device-width, initial-scale=0.75">
<style>
html {
font-family: -apple-system, Roboto, Ubuntu, Tahoma, sans-serif;
font-size: 1.25rem; padding: 2em;
display: grid; justify-content: center;
}
body { width: 100%; max-width: 40em; margin: 0; }
@media (prefers-color-scheme: dark) {
html { background: #000D; color: #EEE; }
a { color: #669df5; }
a:visited { color: #af73d5; }
}
</style>
</head>
<body>
Example!
</body>
</html>
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@cubing/deploy",
"version": "0.1.0",
"module": "src/main.ts",
"type": "module",
"devDependencies": {
"@biomejs/biome": "^1.7.2",
"@types/bun": "latest",
"typescript": "^5.4.5"
},
"@cubing/deploy": {
"https://experiments.cubing.net/test/deploy": {}
}
}
117 changes: 117 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import assert from "node:assert";
import { exit } from "node:process";
import { parseArgs } from "node:util";
import { argv } from "bun";

function printHelpAndExit(): void {
console.log(
`Usage: bun x @cubing/deploy
Options:
--dry-run
--create-folder-on-server
Requires \`bun\` and \`rsync\` to be installed. Reads paths from a field in \`package.json\` in the current folder:
{
"@cubing/deploy": {
"https://experiments.cubing.net/test/deploy": {}
}
}
`,
);
exit(1);
}

const { values } = parseArgs({
args: argv.slice(2),
options: {
"dry-run": {
type: "boolean",
},
"create-folder-on-server": {
type: "boolean",
},
},
strict: true,
});

function barebonesShellEscape(s: string): string {
return s.replaceAll('"', '\\"');
}

function printCommand(c: string[]): void {
console.log(c.map((s) => `"${barebonesShellEscape(s)}"`).join(" "));
}

// TODO: reuse connections based on domain or host IP.
async function deployURL(urlString: string): Promise<void> {
if (urlString.at(-1) !== "/") {
// biome-ignore lint/style/noParameterAssign: Safety check
urlString = `${urlString}/`; // Only sync folder contents.
}
const url = new URL(urlString); // TODO: avoid URL encoding special chars

const localDistPath = `./dist/web/${url.hostname}${url.pathname}`;

const rsyncCommand = ["rsync", "-avz"];
// rsyncCommand.push("--mkpath"); // TODO: requires `rsync` v3.2.3 (https://stackoverflow.com/a/65435579) but Dreamhost is stuck on 3.1.3. 😖
rsyncCommand.push("--exclude", ".DS_Store");
rsyncCommand.push("--exclude", ".git"); // TODO: we probably don't need this?
rsyncCommand.push(localDistPath);

let login_host = url.hostname;
if (url.username) {
login_host = `${url.username}@${url.hostname}`;
}

const serverFolder = url.hostname + url.pathname;

const rsyncTarget = `${login_host}:~/${serverFolder}`;
rsyncCommand.push(rsyncTarget);

const sshCommand = [
"ssh",
login_host,
`mkdir -p "${barebonesShellEscape(serverFolder)}"`,
];

console.log("--------");
console.log(`Deploying from: ${localDistPath}`);
console.log(`Deploying to: ${rsyncTarget}`);
if (values["dry-run"]) {
if (values["create-folder-on-server"]) {
console.write("[--dry-run] ");
printCommand(sshCommand);
}
console.write("[--dry-run] ");
printCommand(rsyncCommand);
} else {
if (values["create-folder-on-server"]) {
assert((await Bun.spawn(sshCommand).exited) === 0);
}
assert((await Bun.spawn(rsyncCommand).exited) === 0);
console.log(`
Successfully deployed:
${url}
`);
}
}

const packageJSON = await Bun.file("package.json").json();
const cubingDeployArgs = packageJSON["@cubing/deploy"];
if (!cubingDeployArgs) {
console.log("No `@cubing/deploy` entry was found in `package.json`");
printHelpAndExit();
}
const urlStrings = Object.keys(cubingDeployArgs);

if (urlStrings.length === 0) {
printHelpAndExit();
}

for (const urlString of urlStrings) {
await deployURL(urlString);
}
27 changes: 27 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compilerOptions": {
// Enable latest features
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,

// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,

// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,

// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}

0 comments on commit b3a1efe

Please sign in to comment.