@androbinco/robin-deployment-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +43 -0
- package/src/index.js +179 -0
- package/src/templates/Dockerfile +36 -0
- package/src/templates/cloudbuild.yml +22 -0
package/package.json
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
{
|
2
|
+
"name": "@androbinco/robin-deployment-cli",
|
3
|
+
"version": "0.1.0",
|
4
|
+
"description": "CLI tool to fetch deployment template files (Dockerfile and cloudbuild.yml) into your project",
|
5
|
+
"type": "module",
|
6
|
+
"main": "./src/index.js",
|
7
|
+
"bin": {
|
8
|
+
"robin-deployment": "./src/index.js"
|
9
|
+
},
|
10
|
+
"files": [
|
11
|
+
"src/",
|
12
|
+
"README.md"
|
13
|
+
],
|
14
|
+
"keywords": [
|
15
|
+
"cli",
|
16
|
+
"robin",
|
17
|
+
"deployment",
|
18
|
+
"docker",
|
19
|
+
"gcp",
|
20
|
+
"templates"
|
21
|
+
],
|
22
|
+
"author": "Hernán Mendez / DevOps",
|
23
|
+
"license": "MIT",
|
24
|
+
"dependencies": {
|
25
|
+
"commander": "^11.0.0",
|
26
|
+
"chalk": "^5.3.0",
|
27
|
+
"ora": "^7.0.1",
|
28
|
+
"fs-extra": "^11.1.1"
|
29
|
+
},
|
30
|
+
"devDependencies": {
|
31
|
+
"nodemon": "^3.0.0"
|
32
|
+
},
|
33
|
+
"publishConfig": {
|
34
|
+
"access": "public"
|
35
|
+
},
|
36
|
+
"scripts": {
|
37
|
+
"start": "node ./src/index.js",
|
38
|
+
"dev": "node ./src/index.js",
|
39
|
+
"test": "echo \"Error: no test specified for robin-deployment-cli\" && exit 1",
|
40
|
+
"build": "echo \"robin-deployment-cli: No build step required.\"",
|
41
|
+
"lint": "echo \"robin-deployment-cli: Lint not configured.\""
|
42
|
+
}
|
43
|
+
}
|
package/src/index.js
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import { program } from 'commander';
|
4
|
+
import chalk from 'chalk';
|
5
|
+
import ora from 'ora';
|
6
|
+
import fs from 'fs-extra';
|
7
|
+
import path from 'path';
|
8
|
+
import { fileURLToPath } from 'url';
|
9
|
+
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
11
|
+
const TEMPLATES_DIR = path.join(__dirname, 'templates');
|
12
|
+
|
13
|
+
|
14
|
+
program
|
15
|
+
.name('robin-deployment')
|
16
|
+
.description('CLI tool to fetch deployment files (Dockerfile and cloudbuild.yml) into your project')
|
17
|
+
.version('0.1.0');
|
18
|
+
|
19
|
+
|
20
|
+
program
|
21
|
+
.command('fetch')
|
22
|
+
.description('Fetch deployment files into current directory')
|
23
|
+
.option('-d, --docker-only', 'Fetch only Dockerfile')
|
24
|
+
.option('-g, --gcp-only', 'Fetch only GCP configuration file')
|
25
|
+
.option('-p, --path <path>', 'Target directory (default: current directory)', '.')
|
26
|
+
.option('-f, --force', 'Overwrite existing files without prompting')
|
27
|
+
.action(async (options) => {
|
28
|
+
const spinner = ora('Fetching deployment files...').start();
|
29
|
+
|
30
|
+
try {
|
31
|
+
const targetDir = path.resolve(options.path);
|
32
|
+
|
33
|
+
|
34
|
+
await fs.ensureDir(targetDir);
|
35
|
+
|
36
|
+
|
37
|
+
let filesToCopy = [];
|
38
|
+
if (options.dockerOnly) {
|
39
|
+
filesToCopy = ['Dockerfile'];
|
40
|
+
} else if (options.gcpOnly) {
|
41
|
+
filesToCopy = ['cloudbuild.yml'];
|
42
|
+
} else {
|
43
|
+
filesToCopy = ['Dockerfile', 'cloudbuild.yml'];
|
44
|
+
}
|
45
|
+
|
46
|
+
|
47
|
+
const existingFiles = [];
|
48
|
+
for (const file of filesToCopy) {
|
49
|
+
const targetPath = path.join(targetDir, file);
|
50
|
+
if (await fs.pathExists(targetPath)) {
|
51
|
+
existingFiles.push(file);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
|
56
|
+
if (existingFiles.length > 0 && !options.force) {
|
57
|
+
spinner.warn(chalk.yellow(`Found existing files: ${existingFiles.join(', ')}`));
|
58
|
+
console.log(chalk.yellow('\nUse --force to overwrite or remove the files manually.'));
|
59
|
+
process.exit(1);
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
let copiedFiles = [];
|
64
|
+
for (const file of filesToCopy) {
|
65
|
+
const sourcePath = path.join(TEMPLATES_DIR, file);
|
66
|
+
const targetPath = path.join(targetDir, file);
|
67
|
+
|
68
|
+
|
69
|
+
if (!await fs.pathExists(sourcePath)) {
|
70
|
+
spinner.warn(chalk.yellow(`Template not found: ${file}`));
|
71
|
+
continue;
|
72
|
+
}
|
73
|
+
|
74
|
+
await fs.copy(sourcePath, targetPath, { overwrite: options.force });
|
75
|
+
copiedFiles.push(file);
|
76
|
+
}
|
77
|
+
|
78
|
+
if (copiedFiles.length === 0) {
|
79
|
+
spinner.fail(chalk.red('No files were copied'));
|
80
|
+
process.exit(1);
|
81
|
+
}
|
82
|
+
|
83
|
+
spinner.succeed(chalk.green('Deployment files fetched successfully'));
|
84
|
+
|
85
|
+
|
86
|
+
console.log(chalk.blue('\n📁 Files created:'));
|
87
|
+
copiedFiles.forEach(file => {
|
88
|
+
console.log(chalk.gray(` ✓ ${path.join(targetDir, file)}`));
|
89
|
+
});
|
90
|
+
|
91
|
+
|
92
|
+
console.log(chalk.yellow('\n💡 Next steps:'));
|
93
|
+
if (copiedFiles.includes('Dockerfile')) {
|
94
|
+
console.log(chalk.gray(' • Review and customize the Dockerfile for your project'));
|
95
|
+
console.log(chalk.gray(' • Build: docker build -t your-image-name .'));
|
96
|
+
}
|
97
|
+
if (copiedFiles.includes('cloudbuild.yml')) {
|
98
|
+
console.log(chalk.gray(' • Update the variables in cloudbuild.yml'));
|
99
|
+
console.log(chalk.gray(' • Deploy: gcloud builds submit'));
|
100
|
+
}
|
101
|
+
|
102
|
+
} catch (error) {
|
103
|
+
spinner.fail(chalk.red('Error fetching deployment files'));
|
104
|
+
console.error(chalk.red(`\nDetails: ${error.message}`));
|
105
|
+
process.exit(1);
|
106
|
+
}
|
107
|
+
});
|
108
|
+
|
109
|
+
|
110
|
+
program
|
111
|
+
.command('list')
|
112
|
+
.description('List all available deployment templates')
|
113
|
+
.action(async () => {
|
114
|
+
try {
|
115
|
+
if (!await fs.pathExists(TEMPLATES_DIR)) {
|
116
|
+
console.error(chalk.red('\n❌ Templates directory not found'));
|
117
|
+
console.log(chalk.yellow('Make sure template files are placed in src/templates/'));
|
118
|
+
process.exit(1);
|
119
|
+
}
|
120
|
+
|
121
|
+
const files = await fs.readdir(TEMPLATES_DIR);
|
122
|
+
|
123
|
+
if (files.length === 0) {
|
124
|
+
console.log(chalk.yellow('\n⚠️ No templates found'));
|
125
|
+
console.log(chalk.gray('Add your deployment files to src/templates/'));
|
126
|
+
return;
|
127
|
+
}
|
128
|
+
|
129
|
+
console.log(chalk.blue('\n📋 Available deployment templates:\n'));
|
130
|
+
|
131
|
+
for (const file of files) {
|
132
|
+
const filePath = path.join(TEMPLATES_DIR, file);
|
133
|
+
const stats = await fs.stat(filePath);
|
134
|
+
const size = (stats.size / 1024).toFixed(2);
|
135
|
+
|
136
|
+
console.log(` ${chalk.white('•')} ${chalk.cyan(file)} ${chalk.gray(`(${size} KB)`)}`);
|
137
|
+
}
|
138
|
+
|
139
|
+
console.log(chalk.gray('\nUse "robin-deployment show <template>" to view contents'));
|
140
|
+
|
141
|
+
} catch (error) {
|
142
|
+
console.error(chalk.red('Error listing templates:', error.message));
|
143
|
+
process.exit(1);
|
144
|
+
}
|
145
|
+
});
|
146
|
+
|
147
|
+
program
|
148
|
+
.command('show <template>')
|
149
|
+
.description('Show the contents of a specific template')
|
150
|
+
.action(async (template) => {
|
151
|
+
try {
|
152
|
+
const filePath = path.join(TEMPLATES_DIR, template);
|
153
|
+
|
154
|
+
if (!await fs.pathExists(filePath)) {
|
155
|
+
console.error(chalk.red(`\n❌ Template not found: ${template}`));
|
156
|
+
console.log(chalk.gray('Use "robin-deployment list" to see available templates'));
|
157
|
+
process.exit(1);
|
158
|
+
}
|
159
|
+
|
160
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
161
|
+
|
162
|
+
console.log(chalk.blue(`\n📄 ${template}:`));
|
163
|
+
console.log(chalk.gray('─'.repeat(60)));
|
164
|
+
console.log(content);
|
165
|
+
console.log(chalk.gray('─'.repeat(60)));
|
166
|
+
|
167
|
+
} catch (error) {
|
168
|
+
console.error(chalk.red('Error showing template:', error.message));
|
169
|
+
process.exit(1);
|
170
|
+
}
|
171
|
+
});
|
172
|
+
|
173
|
+
|
174
|
+
program.parse(process.argv);
|
175
|
+
|
176
|
+
|
177
|
+
if (!process.argv.slice(2).length) {
|
178
|
+
program.outputHelp();
|
179
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
FROM node:18-alpine AS builder
|
2
|
+
|
3
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
4
|
+
|
5
|
+
WORKDIR /app
|
6
|
+
|
7
|
+
COPY package.json pnpm-lock.yaml ./
|
8
|
+
|
9
|
+
RUN pnpm install --frozen-lockfile
|
10
|
+
|
11
|
+
COPY . .
|
12
|
+
|
13
|
+
RUN mkdir -p src/common/utils src/common/ui
|
14
|
+
|
15
|
+
RUN pnpm build
|
16
|
+
|
17
|
+
FROM node:18-alpine AS runner
|
18
|
+
|
19
|
+
WORKDIR /app
|
20
|
+
|
21
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
22
|
+
|
23
|
+
COPY --from=builder /app/package.json /app/pnpm-lock.yaml ./
|
24
|
+
COPY --from=builder /app/.next ./.next
|
25
|
+
COPY --from=builder /app/public ./public
|
26
|
+
COPY --from=builder /app/next.config.ts ./
|
27
|
+
COPY --from=builder /app/src ./src
|
28
|
+
|
29
|
+
RUN pnpm install --prod --frozen-lockfile
|
30
|
+
|
31
|
+
ENV NODE_ENV=production
|
32
|
+
ENV PORT=3000
|
33
|
+
|
34
|
+
EXPOSE 3000
|
35
|
+
|
36
|
+
CMD ["pnpm", "start"]
|
@@ -0,0 +1,22 @@
|
|
1
|
+
steps:
|
2
|
+
- name: 'gcr.io/cloud-builders/docker'
|
3
|
+
args: ['build', '-t', 'gcr.io/$PROJECT_ID/YOUR_SERVICE_NAME', '.']
|
4
|
+
|
5
|
+
- name: 'gcr.io/cloud-builders/docker'
|
6
|
+
args: ['push', 'gcr.io/$PROJECT_ID/YOUR_SERVICE_NAME']
|
7
|
+
|
8
|
+
- name: 'gcr.io/cloud-builders/gcloud'
|
9
|
+
args:
|
10
|
+
- 'run'
|
11
|
+
- 'deploy'
|
12
|
+
- 'YOUR_SERVICE_NAME'
|
13
|
+
- '--image=gcr.io/$PROJECT_ID/YOUR_SERVICE_NAME'
|
14
|
+
- '--platform=managed'
|
15
|
+
- '--region=us-east1'
|
16
|
+
- '--allow-unauthenticated'
|
17
|
+
- '--memory=2Gi'
|
18
|
+
- '--cpu=4'
|
19
|
+
- '--min-instances=1'
|
20
|
+
- '--max-instances=3'
|
21
|
+
options:
|
22
|
+
logging: CLOUD_LOGGING_ONLY
|