UNPKG

5.16 kBJavaScriptView Raw
1import R from "ramda";
2import Promise from "bluebird";
3import yaml from "js-yaml";
4import github from "file-system-github";
5import gateway from "./gateway";
6
7
8/**
9 * Retrieves a manifest file from a remote repo.
10 * @param {string} repoPath: The path to the `repo/file.yml:branch` to retrieve.
11 * @return {Promise}
12 */
13export const getManifest = (repo, repoPath, branch) => {
14 return new Promise((resolve, reject) => {
15 Promise.coroutine(function*() {
16 // Ensure a YAML file was specified.
17 if (!repoPath.endsWith(".yml")) {
18 return reject(new Error("A path to a YAML file must be specified (.yml)"));
19 }
20
21 // Pull file from the repo.
22 const download = yield repo.get(repoPath, { branch }).catch(err => reject(err));
23 const files = download && download.files;
24
25 // Parse and return the manifest.
26 if (files && files.length > 0) {
27 const manifest = files[0].toString();
28 try {
29 resolve(yaml.safeLoad(manifest));
30 } catch (e) {
31 reject(new Error(`Failed while parsing YAML: ${ e.message }`));
32 }
33 }
34 })();
35 });
36};
37
38
39const toRepoObject = (repoPath) => {
40 let parts;
41 parts = repoPath.split(":");
42 repoPath = parts[0].trim();
43 const branch = (parts[1] || "master").trim();
44 parts = repoPath.split("/");
45 return {
46 name: R.take(2, parts).join("/"),
47 path: R.takeLast(parts.length - 2, parts).join("/"),
48 branch,
49 fullPath: repoPath
50 };
51 };
52
53
54
55/**
56 * Manages the manifest of applications.
57 */
58export default (userAgent, token, repoPath, main) => {
59 // Create the repo proxy.
60 const repoObject = toRepoObject(repoPath);
61 const repo = github.repo(userAgent, repoObject.name, { token });
62 const getApp = (id) => R.find(item => item.id === id, main.apps);
63
64 const api = {
65 repo: repoObject,
66
67
68 /**
69 * Retrieves the latest version of the manifest, and stores is as `current`.
70 * @return {Promise}
71 */
72 get() {
73 return new Promise((resolve, reject) => {
74 Promise.coroutine(function*() {
75 this.current = yield getManifest(repo, this.repo.path, this.repo.branch).catch(err => reject(err));
76 resolve(this.current);
77 }).call(this);
78 });
79 },
80
81
82 /**
83 * Connects to the remote manifest and syncs the local state with the
84 * defined applications.
85 * @return {Promise}
86 */
87 update() {
88 return new Promise((resolve, reject) => {
89 Promise.coroutine(function*() {
90 const current = current;
91 let restart = false;
92
93
94 // Retrieve the manifest from the repo.
95 const manifest = yield this.get().catch(err => reject(err));
96 if (manifest) {
97
98 // Check for global changes with the previous manifest.
99 if (current) {
100 if (!R.equals(current.api, manifest.api)) { restart = true; }
101 }
102
103 const isAppChanged = (manifestApp, app) => {
104 if (manifestApp.repo !== app.repo.fullPath) { return true; }
105 if (manifestApp.route !== app.route.toString()) { return true; }
106 if (manifestApp.branch !== app.branch) { return true; }
107 return false;
108 };
109
110 const addApp = (id, manifestApp) => {
111 main.add(id, manifestApp.repo, manifestApp.route, { branch: manifestApp.branch });
112 };
113
114 // Remove apps that are no longer specified in the manifest.
115 const manifestKeys = Object.keys(manifest.apps);
116 const isWithinManifest = (app) => R.any(key => key === app.id, manifestKeys);
117 for (let app of main.apps) {
118 if (!isWithinManifest(app)) {
119 yield main.remove(app.id);
120 restart = true;
121 }
122 }
123
124 // Add or update each app.
125 for (let id of manifestKeys) {
126 const manifestApp = manifest.apps[id];
127 if (!R.is(String, manifestApp.repo)) { throw new Error(`The app '${ id } does not have a repo, eg: user/repo/path'`); }
128 if (!R.is(String, manifestApp.route)) { throw new Error(`The app '${ id } does not have a route, eg: www.domain.com/path'`); }
129 manifestApp.branch = manifestApp.branch || "master";
130 const app = getApp(id);
131 if (app) {
132 if (isAppChanged(manifestApp, app)) {
133 // The app has changed. Replace it with the new definition.
134 yield main.remove(id);
135 addApp(id, manifestApp);
136 restart = true;
137 }
138 } else {
139 // The app has not yet been added. Add it now.
140 addApp(id, manifestApp);
141 restart = true;
142 }
143 }
144
145 // Restart the gateway if a change occured (and it's already running)
146 if (gateway.isRunning() && restart) {
147 yield main.stop();
148 yield main.start();
149 }
150 }
151 resolve({ manifest });
152 }).call(this);
153 });
154 }
155 };
156
157 // Finish up.
158 return api;
159};