Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
/deploy-code-serverPublic template

Add deploy cli [WIP]#59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
BrunoQuaresma merged 14 commits intomainfromadd-cli
Aug 24, 2021
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
14 commits
Select commitHold shift + click to select a range
7a2afa4
Add deploy cli
BrunoQuaresmaAug 18, 2021
61e94eb
add development instructions
bpmctAug 23, 2021
f680570
Fix async runner and add extra token info
BrunoQuaresmaAug 23, 2021
d312c61
Merge branch 'add-cli' of github.com:cdr/deploy-code-server into add-cli
BrunoQuaresmaAug 23, 2021
9bfcebd
Add prettier
BrunoQuaresmaAug 23, 2021
82417e5
Move prettier config
BrunoQuaresmaAug 23, 2021
266a683
Add Prettier and pre-commit linting
BrunoQuaresmaAug 23, 2021
c21d48a
Remove package.json
BrunoQuaresmaAug 23, 2021
3c67a8d
Move Prettier config to cli
BrunoQuaresmaAug 23, 2021
933ddcb
Pull version from package.json
BrunoQuaresmaAug 24, 2021
7c8acea
Get description from package.json
BrunoQuaresmaAug 24, 2021
fe99f9f
Update package info
BrunoQuaresmaAug 24, 2021
5dd0b05
Update package name
BrunoQuaresmaAug 24, 2021
3031ea2
Add cli to coder org
BrunoQuaresmaAug 24, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions.gitignore
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
node_modules
bin
1 change: 1 addition & 0 deletions.husky/.gitignore
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
_
4 changes: 4 additions & 0 deletions.husky/pre-commit
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
#!/bin/sh
."$(dirname"$0")/_/husky.sh"

npx lint-staged
3 changes: 3 additions & 0 deletionscli/.prettierignore
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
node_modules
bin
yarn.lock
4 changes: 4 additions & 0 deletionscli/.prettierrc
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
{
"tabWidth": 2,
"useTabs": false
}
14 changes: 14 additions & 0 deletionscli/README.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
# dcs-cli
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

nit: not super obvious whatdcs stands for. maybe we should spell it out below "Deploy code-server (dcs)"


Provision a code-server instance from your terminal.

## Development

```console
git clone git@github.com:cdr/deploy-code-server.git
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

nit: Is it standard to use the SSH URL instead of HTTPS? I feel like HTTPS is more common, at least from what I see in OSS.

cd deploy-code-server/cli
npm install && npm run build:watch

# in another session:
node bin/index.js
```
33 changes: 33 additions & 0 deletionscli/package.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
{
"name": "@coder/deploy-code-server",
"version": "0.1.0",
"repository": "cdr/deploy-code-server",
"homepage": "https://github.com/cdr/deploy-code-server",
"description": "CLI to deploy code-server",
"main": "bin/index.js",
"bin": "bin/index.js",
"scripts": {
"build": "tsc",
"build:watch": "tsc -w",
"prepare": "yarn build"
},
"keywords": ["code-server", "coder"],
"author": "coder",
"publishConfig": {
"access": "public"
},
"license": "ISC",
"devDependencies": {
"@types/inquirer": "^7.3.3",
"@types/node": "^14.14.20",
"typescript": "^4.1.3"
},
"dependencies": {
"async-wait-until": "^2.0.7",
"chalk": "^4.1.2",
"commander": "^8.1.0",
"got": "^11.8.2",
"inquirer": "^8.1.2",
"ora": "^5.4.1"
}
}
138 changes: 138 additions & 0 deletionscli/src/deploys/deployDigitalOcean.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
import inquirer from "inquirer";
import got from "got";
import ora from "ora";
import chalk from "chalk";
import {
createDroplet,
Droplet,
DropletV4Network,
getDroplet,
} from "../lib/digitalOcean";
import waitUntil from "async-wait-until";

const getUserDataScript = async () =>
got(
"https://raw.githubusercontent.com/cdr/deploy-code-server/main/deploy-vm/launch-code-server.sh"
).text();

const isPermissionError = (error: unknown) => {
return error instanceof got.HTTPError && error.response.statusCode === 401;
};

const getPublicIp = (droplet: Droplet) => {
const network = droplet.networks.v4.find(
(network) => network.type === "public"
);
return network?.ip_address;
};

const isCodeServerLive = async (droplet: Droplet) => {
try {
const response = await got(`http://${getPublicIp(droplet)}`, { retry: 0 });
return response.statusCode === 200;
} catch {
return false;
}
};

const handleErrorLog = (error: unknown) => {
if (isPermissionError(error)) {
console.log(
chalk.red(
chalk.bold("Invalid token."),
"Please, verify your token and try again."
)
);
} else {
console.log(chalk.red.bold("Something wrong happened"));
console.log(
chalk.red(
"You may have to delete the droplet manually on your Digital Ocean dashboard."
)
);
}
};

const oneMinute = 1000 * 60;
const fiveMinutes = oneMinute * 5;

const waitUntilBeActive = (droplet: Droplet, token: string) => {
return waitUntil(
async () => {
const dropletInfo = await getDroplet({ token, id: droplet.id });
return dropletInfo.status === "active";
},
{ timeout: fiveMinutes, intervalBetweenAttempts: oneMinute / 2 }
);
};

const waitUntilHasPublicIp = (droplet: Droplet, token: string) => {
return waitUntil(
async () => {
const dropletInfo = await getDroplet({ token, id: droplet.id });
const ip = getPublicIp(dropletInfo);
return ip !== undefined;
},
{ timeout: fiveMinutes, intervalBetweenAttempts: oneMinute / 2 }
);
};

const waitUntilCodeServerIsLive = (droplet: Droplet, token: string) => {
return waitUntil(
async () => {
const dropletInfo = await getDroplet({ token, id: droplet.id });
return isCodeServerLive(dropletInfo);
},
{ timeout: fiveMinutes * 2, intervalBetweenAttempts: oneMinute / 2 }
);
};

export const deployDigitalOcean = async () => {
let spinner: ora.Ora;

console.log(
chalk.blue(
"You can create a token on",
chalk.bold("https://cloud.digitalocean.com/account/api/tokens")
)
);
const { token } = await inquirer.prompt([
{ name: "token", message: "Your Digital Ocean token:", type: "password" },
]);

try {
let spinner = ora("Creating droplet and installing code-server").start();
let droplet = await createDroplet({
userData: await getUserDataScript(),
token,
});
spinner.stop();
console.log(chalk.green("✅ Droplet created"));

spinner = ora("Waiting droplet to be active").start();
await waitUntilBeActive(droplet, token);
spinner.stop();
console.log(chalk.green("✅ Droplet active"));

spinner = ora("Waiting droplet to have a public IP").start();
await waitUntilHasPublicIp(droplet, token);
spinner.stop();
console.log(chalk.green("✅ Public IP is available"));

spinner = ora(
"Waiting code-server to be live. It can take up to 5 minutes."
).start();
await waitUntilCodeServerIsLive(droplet, token);
droplet = await getDroplet({ token, id: droplet.id });
spinner.stop();
console.log(
chalk.green(
`🚀 Your code-server is live. You can access it on`,
chalk.bold(`http://${getPublicIp(droplet)}`)
)
);
} catch (error) {
spinner.stop();
handleErrorLog(error);
}
};
14 changes: 14 additions & 0 deletionscli/src/index.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env node

import { program } from "commander";
import { deployDigitalOcean } from "./deploys/deployDigitalOcean";
import packageJson from "../package.json";

const main = async () => {
program.version(packageJson.version).description(packageJson.description);
program.parse();
await deployDigitalOcean();
process.exit(0);
};

main();
55 changes: 55 additions & 0 deletionscli/src/lib/digitalOcean.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
import got from "got";

const DIGITALOCEAN_API_URL = "https://api.digitalocean.com/v2";

export type DropletV4Network = {
ip_address: string;
type: "private" | "public";
};
export type Droplet = {
id: string;
name: string;
networks: { v4: DropletV4Network[] };
status: "new" | "active";
};

type CreateDropletOptions = {
userData: string;
token: string;
};

export const createDroplet = async ({
token,
userData,
}: CreateDropletOptions) => {
return got
.post(`${DIGITALOCEAN_API_URL}/droplets`, {
json: {
name: "code-server",
region: "nyc3",
size: "s-1vcpu-1gb",
image: "ubuntu-20-10-x64",
user_data: userData,
},
headers: {
Authorization: `Bearer ${token}`,
},
})
.json<{ droplet: Droplet }>()
.then((data) => data.droplet);
};

type GetDropletOptions = {
id: string;
token: string;
};

export const getDroplet = async ({ token, id }: GetDropletOptions) => {
return got(`${DIGITALOCEAN_API_URL}/droplets/${id}`, {
headers: {
Authorization: `Bearer ${token}`,
},
})
.json<{ droplet: Droplet }>()
.then((data) => data.droplet);
};
17 changes: 17 additions & 0 deletionscli/tsconfig.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"lib": ["es2015"],
"moduleResolution": "node",
"sourceMap": true,
"outDir": "bin",
"baseUrl": ".",
"paths": {
"*": ["node_modules/*", "src/types/*"]
},
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["src/**/*"]
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp