1 | import R from "ramda";
|
2 | import Promise from "bluebird";
|
3 | import shell from "shelljs";
|
4 | import fsPath from "path";
|
5 | import github from "file-system-github";
|
6 | import Route from "./route";
|
7 | import { isEmpty } from "./util";
|
8 | import { DEFAULT_APP_PORT, DEFAULT_TARGET_FOLDER } from "./const";
|
9 |
|
10 | import appInstall from "./app-install";
|
11 | import appVersion from "./app-version";
|
12 | import appDownload from "./app-download";
|
13 | import appUpdate from "./app-update";
|
14 | import { getLocalPackage, getRemotePackage } from "./app-package";
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | export default (settings = {}) => {
|
36 |
|
37 | let { userAgent, token, targetFolder, id, repo, port, branch, route, publishEvent } = settings;
|
38 | if (isEmpty(id)) { throw new Error(`'id' for the app is required`); }
|
39 | if (isEmpty(repo)) { throw new Error(`'repo' name required, eg. 'username/my-repo'`); }
|
40 | if (isEmpty(userAgent)) { throw new Error(`The github API user-agent must be specified. See: https://developer.github.com/v3/#user-agent-required`); }
|
41 | if (isEmpty(route)) { throw new Error(`A 'route' must be specified for the '${ id }' app.`); }
|
42 | route = Route.parse(route);
|
43 | branch = branch || "master";
|
44 | targetFolder = targetFolder || DEFAULT_TARGET_FOLDER;
|
45 | port = port || DEFAULT_APP_PORT;
|
46 | const WORKING_DIRECTORY = process.cwd();
|
47 |
|
48 |
|
49 | const fullPath = repo;
|
50 | let parts = repo.split("/");
|
51 | if (parts.length < 2) { throw new Error(`A repo must have a 'username' and 'repo-name', eg 'username/repo'.`); }
|
52 | const repoUser = parts[0];
|
53 | const repoName = parts[1];
|
54 | repo = github.repo(userAgent, `${ repoUser }/${ repoName }`, { token });
|
55 | parts = R.takeLast(parts.length - 2, parts);
|
56 | const repoSubFolder = parts.join("/");
|
57 | const localFolder = fsPath.resolve(fsPath.join(targetFolder, id));
|
58 | repo.path = repoSubFolder;
|
59 | repo.fullPath = fullPath;
|
60 |
|
61 |
|
62 | const app = {
|
63 | id,
|
64 | repo,
|
65 | route,
|
66 | port,
|
67 | branch,
|
68 | localFolder,
|
69 |
|
70 |
|
71 | |
72 |
|
73 |
|
74 |
|
75 | localPackage() { return getLocalPackage(id, this.localFolder); },
|
76 |
|
77 |
|
78 | |
79 |
|
80 |
|
81 |
|
82 | remotePackage() { return getRemotePackage(id, repo, repoSubFolder, branch); },
|
83 |
|
84 |
|
85 | |
86 |
|
87 |
|
88 |
|
89 | version() { return appVersion(id, this.localPackage(), this.remotePackage()); },
|
90 |
|
91 |
|
92 | |
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 | download(options = {}) {
|
103 |
|
104 | if (this.downloading) { return this.downloading; }
|
105 |
|
106 |
|
107 | this.downloading = appDownload(id, localFolder, repo, repoSubFolder, branch, options)
|
108 | .then(result => {
|
109 | this.isDownloading = false;
|
110 | delete this.downloading;
|
111 | return result;
|
112 | });
|
113 | return this.downloading;
|
114 | },
|
115 |
|
116 |
|
117 | |
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 | update(options = {}) {
|
125 |
|
126 | const updating = appUpdate(
|
127 | id,
|
128 | localFolder,
|
129 | () => this.version(),
|
130 | (args) => this.download(args),
|
131 | (args) => this.start(args),
|
132 | options
|
133 | );
|
134 |
|
135 |
|
136 | updating.then(result => {
|
137 | if (publishEvent && result.updated) {
|
138 | publishEvent("app:updated", {
|
139 | id: this.id,
|
140 | version: result.version
|
141 | });
|
142 | }
|
143 | });
|
144 |
|
145 |
|
146 | return updating;
|
147 | },
|
148 |
|
149 |
|
150 | |
151 |
|
152 |
|
153 |
|
154 | install() { return appInstall(localFolder); },
|
155 |
|
156 |
|
157 |
|
158 | |
159 |
|
160 |
|
161 |
|
162 | start() {
|
163 | return new Promise((resolve, reject) => {
|
164 | Promise.coroutine(function*() {
|
165 |
|
166 | const status = yield this.update({ start: false }).catch(err => reject(err));
|
167 | yield this.stop().catch(err => reject(err));
|
168 |
|
169 |
|
170 | shell.cd(localFolder);
|
171 | shell.exec(`pm2 start . --name '${ id }:${ port }' --node-args '. --port ${ port }'`);
|
172 | shell.cd(WORKING_DIRECTORY);
|
173 |
|
174 |
|
175 | resolve({ id, started: true, port: this.port, route: this.route, version: status.version });
|
176 | }).call(this);
|
177 | });
|
178 | },
|
179 |
|
180 |
|
181 | |
182 |
|
183 |
|
184 |
|
185 | stop() {
|
186 | return new Promise((resolve) => {
|
187 | shell.exec(`pm2 stop '${ id }:${ port }'`);
|
188 | resolve({ id });
|
189 | });
|
190 | }
|
191 | };
|
192 |
|
193 |
|
194 |
|
195 | return app;
|
196 | };
|