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

Commit0cedc14

Browse files
Add deploy cli [WIP] (#59)
* Add deploy cli* add development instructions* Fix async runner and add extra token info* Add prettier* Move prettier config* Add Prettier and pre-commit linting* Remove package.json* Move Prettier config to cli* Pull version from package.json* Get description from package.json* Update package info* Update package name* Add cli to coder orgCo-authored-by: Ben Potter <me@bpmct.net>
1 parent8fcc596 commit0cedc14

File tree

14 files changed

+1518
-0
lines changed

14 files changed

+1518
-0
lines changed

‎.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
bin

‎.husky/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
_

‎.husky/pre-commit

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/sh
2+
."$(dirname"$0")/_/husky.sh"
3+
4+
npx lint-staged

‎cli/.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
bin
3+
yarn.lock

‎cli/.prettierrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"tabWidth": 2,
3+
"useTabs": false
4+
}

‎cli/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#dcs-cli
2+
3+
Provision a code-server instance from your terminal.
4+
5+
##Development
6+
7+
```console
8+
git clone git@github.com:cdr/deploy-code-server.git
9+
cd deploy-code-server/cli
10+
npm install && npm run build:watch
11+
12+
#in another session:
13+
node bin/index.js
14+
```

‎cli/package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name":"@coder/deploy-code-server",
3+
"version":"0.1.0",
4+
"repository":"cdr/deploy-code-server",
5+
"homepage":"https://github.com/cdr/deploy-code-server",
6+
"description":"CLI to deploy code-server",
7+
"main":"bin/index.js",
8+
"bin":"bin/index.js",
9+
"scripts": {
10+
"build":"tsc",
11+
"build:watch":"tsc -w",
12+
"prepare":"yarn build"
13+
},
14+
"keywords": ["code-server","coder"],
15+
"author":"coder",
16+
"publishConfig": {
17+
"access":"public"
18+
},
19+
"license":"ISC",
20+
"devDependencies": {
21+
"@types/inquirer":"^7.3.3",
22+
"@types/node":"^14.14.20",
23+
"typescript":"^4.1.3"
24+
},
25+
"dependencies": {
26+
"async-wait-until":"^2.0.7",
27+
"chalk":"^4.1.2",
28+
"commander":"^8.1.0",
29+
"got":"^11.8.2",
30+
"inquirer":"^8.1.2",
31+
"ora":"^5.4.1"
32+
}
33+
}

‎cli/src/deploys/deployDigitalOcean.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
importinquirerfrom"inquirer";
2+
importgotfrom"got";
3+
importorafrom"ora";
4+
importchalkfrom"chalk";
5+
import{
6+
createDroplet,
7+
Droplet,
8+
DropletV4Network,
9+
getDroplet,
10+
}from"../lib/digitalOcean";
11+
importwaitUntilfrom"async-wait-until";
12+
13+
constgetUserDataScript=async()=>
14+
got(
15+
"https://raw.githubusercontent.com/cdr/deploy-code-server/main/deploy-vm/launch-code-server.sh"
16+
).text();
17+
18+
constisPermissionError=(error:unknown)=>{
19+
returnerrorinstanceofgot.HTTPError&&error.response.statusCode===401;
20+
};
21+
22+
constgetPublicIp=(droplet:Droplet)=>{
23+
constnetwork=droplet.networks.v4.find(
24+
(network)=>network.type==="public"
25+
);
26+
returnnetwork?.ip_address;
27+
};
28+
29+
constisCodeServerLive=async(droplet:Droplet)=>{
30+
try{
31+
constresponse=awaitgot(`http://${getPublicIp(droplet)}`,{retry:0});
32+
returnresponse.statusCode===200;
33+
}catch{
34+
returnfalse;
35+
}
36+
};
37+
38+
consthandleErrorLog=(error:unknown)=>{
39+
if(isPermissionError(error)){
40+
console.log(
41+
chalk.red(
42+
chalk.bold("Invalid token."),
43+
"Please, verify your token and try again."
44+
)
45+
);
46+
}else{
47+
console.log(chalk.red.bold("Something wrong happened"));
48+
console.log(
49+
chalk.red(
50+
"You may have to delete the droplet manually on your Digital Ocean dashboard."
51+
)
52+
);
53+
}
54+
};
55+
56+
constoneMinute=1000*60;
57+
constfiveMinutes=oneMinute*5;
58+
59+
constwaitUntilBeActive=(droplet:Droplet,token:string)=>{
60+
returnwaitUntil(
61+
async()=>{
62+
constdropletInfo=awaitgetDroplet({ token,id:droplet.id});
63+
returndropletInfo.status==="active";
64+
},
65+
{timeout:fiveMinutes,intervalBetweenAttempts:oneMinute/2}
66+
);
67+
};
68+
69+
constwaitUntilHasPublicIp=(droplet:Droplet,token:string)=>{
70+
returnwaitUntil(
71+
async()=>{
72+
constdropletInfo=awaitgetDroplet({ token,id:droplet.id});
73+
constip=getPublicIp(dropletInfo);
74+
returnip!==undefined;
75+
},
76+
{timeout:fiveMinutes,intervalBetweenAttempts:oneMinute/2}
77+
);
78+
};
79+
80+
constwaitUntilCodeServerIsLive=(droplet:Droplet,token:string)=>{
81+
returnwaitUntil(
82+
async()=>{
83+
constdropletInfo=awaitgetDroplet({ token,id:droplet.id});
84+
returnisCodeServerLive(dropletInfo);
85+
},
86+
{timeout:fiveMinutes*2,intervalBetweenAttempts:oneMinute/2}
87+
);
88+
};
89+
90+
exportconstdeployDigitalOcean=async()=>{
91+
letspinner:ora.Ora;
92+
93+
console.log(
94+
chalk.blue(
95+
"You can create a token on",
96+
chalk.bold("https://cloud.digitalocean.com/account/api/tokens")
97+
)
98+
);
99+
const{ token}=awaitinquirer.prompt([
100+
{name:"token",message:"Your Digital Ocean token:",type:"password"},
101+
]);
102+
103+
try{
104+
letspinner=ora("Creating droplet and installing code-server").start();
105+
letdroplet=awaitcreateDroplet({
106+
userData:awaitgetUserDataScript(),
107+
token,
108+
});
109+
spinner.stop();
110+
console.log(chalk.green("✅ Droplet created"));
111+
112+
spinner=ora("Waiting droplet to be active").start();
113+
awaitwaitUntilBeActive(droplet,token);
114+
spinner.stop();
115+
console.log(chalk.green("✅ Droplet active"));
116+
117+
spinner=ora("Waiting droplet to have a public IP").start();
118+
awaitwaitUntilHasPublicIp(droplet,token);
119+
spinner.stop();
120+
console.log(chalk.green("✅ Public IP is available"));
121+
122+
spinner=ora(
123+
"Waiting code-server to be live. It can take up to 5 minutes."
124+
).start();
125+
awaitwaitUntilCodeServerIsLive(droplet,token);
126+
droplet=awaitgetDroplet({ token,id:droplet.id});
127+
spinner.stop();
128+
console.log(
129+
chalk.green(
130+
`🚀 Your code-server is live. You can access it on`,
131+
chalk.bold(`http://${getPublicIp(droplet)}`)
132+
)
133+
);
134+
}catch(error){
135+
spinner.stop();
136+
handleErrorLog(error);
137+
}
138+
};

‎cli/src/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env node
2+
3+
import{program}from"commander";
4+
import{deployDigitalOcean}from"./deploys/deployDigitalOcean";
5+
importpackageJsonfrom"../package.json";
6+
7+
constmain=async()=>{
8+
program.version(packageJson.version).description(packageJson.description);
9+
program.parse();
10+
awaitdeployDigitalOcean();
11+
process.exit(0);
12+
};
13+
14+
main();

‎cli/src/lib/digitalOcean.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
importgotfrom"got";
2+
3+
constDIGITALOCEAN_API_URL="https://api.digitalocean.com/v2";
4+
5+
exporttypeDropletV4Network={
6+
ip_address:string;
7+
type:"private"|"public";
8+
};
9+
exporttypeDroplet={
10+
id:string;
11+
name:string;
12+
networks:{v4:DropletV4Network[]};
13+
status:"new"|"active";
14+
};
15+
16+
typeCreateDropletOptions={
17+
userData:string;
18+
token:string;
19+
};
20+
21+
exportconstcreateDroplet=async({
22+
token,
23+
userData,
24+
}:CreateDropletOptions)=>{
25+
returngot
26+
.post(`${DIGITALOCEAN_API_URL}/droplets`,{
27+
json:{
28+
name:"code-server",
29+
region:"nyc3",
30+
size:"s-1vcpu-1gb",
31+
image:"ubuntu-20-10-x64",
32+
user_data:userData,
33+
},
34+
headers:{
35+
Authorization:`Bearer${token}`,
36+
},
37+
})
38+
.json<{droplet:Droplet}>()
39+
.then((data)=>data.droplet);
40+
};
41+
42+
typeGetDropletOptions={
43+
id:string;
44+
token:string;
45+
};
46+
47+
exportconstgetDroplet=async({ token, id}:GetDropletOptions)=>{
48+
returngot(`${DIGITALOCEAN_API_URL}/droplets/${id}`,{
49+
headers:{
50+
Authorization:`Bearer${token}`,
51+
},
52+
})
53+
.json<{droplet:Droplet}>()
54+
.then((data)=>data.droplet);
55+
};

‎cli/tsconfig.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"module":"commonjs",
4+
"target":"es2017",
5+
"lib": ["es2015"],
6+
"moduleResolution":"node",
7+
"sourceMap":true,
8+
"outDir":"bin",
9+
"baseUrl":".",
10+
"paths": {
11+
"*": ["node_modules/*","src/types/*"]
12+
},
13+
"esModuleInterop":true,
14+
"resolveJsonModule":true
15+
},
16+
"include": ["src/**/*"]
17+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp