diff --git a/CHANGELOG.md b/CHANGELOG.md
index 47efc3d..8f4dd16 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@
## Fixes and improvements
-
+# 3.2.0:
+## Added features
+- Add `hosting` command to setup BitBucket hosting with service user
+
# 3.1.0
## Added features
- Ability to `replay` service runs from `queue`
diff --git a/dist/windows.zip b/dist/windows.zip
index 8170ef7..9f4e476 100644
Binary files a/dist/windows.zip and b/dist/windows.zip differ
diff --git a/executors.js b/executors.js
index 8c65e15..c93b4a8 100644
--- a/executors.js
+++ b/executors.js
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
-exports.do_delete_org = exports.do_delete_group = exports.do_delete_service = exports.do_event = exports.do_spending = exports.do_remove_auto_approve = exports.do_auto_approve = exports.do_attach_role = exports.do_join = exports.do_post = exports.do_help = exports.do_queue_time = exports.printTableHeader = exports.alignLeft = exports.alignRight = exports.do_cron = exports.do_envvar = exports.do_key = exports.do_replay = exports.do_build = exports.do_redeploy = exports.do_deploy = exports.generateNewKey = exports.useExistingKey = exports.do_register = exports.addKnownHost = exports.do_duplicate = exports.fetch_template = exports.createService = exports.createServiceGroup = exports.createOrganization = exports.do_clone = exports.do_fetch = void 0;
+exports.do_bitbucket = exports.do_create_deployment_agent = exports.do_delete_org = exports.do_delete_group = exports.do_delete_service = exports.do_event = exports.do_spending = exports.do_remove_auto_approve = exports.do_auto_approve = exports.do_attach_role = exports.do_join = exports.do_post = exports.do_help = exports.do_queue_time = exports.printTableHeader = exports.alignLeft = exports.alignRight = exports.do_cron = exports.do_envvar = exports.do_key = exports.do_replay = exports.do_build = exports.do_redeploy = exports.do_deploy = exports.generateNewKey = exports.useExistingKey = exports.do_register = exports.addKnownHost = exports.do_duplicate = exports.fetch_template = exports.createService = exports.createServiceGroup = exports.createOrganization = exports.do_clone = exports.do_fetch = exports.SPECIAL_FOLDERS = void 0;
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const utils_1 = require("./utils");
@@ -24,6 +24,7 @@ const path_1 = __importDefault(require("path"));
const args_1 = require("./args");
const process_1 = require("process");
const secret_lib_1 = require("@merrymake/secret-lib");
+exports.SPECIAL_FOLDERS = ["event-catalogue", "public"];
function clone(struct, name) {
return __awaiter(this, void 0, void 0, function* () {
try {
@@ -37,21 +38,21 @@ function clone(struct, name) {
yield (0, utils_1.execPromise)(`git init --initial-branch=main`, dir);
yield (0, utils_1.execPromise)(`git remote add origin "${config_1.GIT_HOST}/${name}/public"`, dir);
// await execPromise(`git fetch`, dir);
- fetch(`./${name}`, name, struct);
+ fetch(`./${name}/`, struct, (path, team, service) => createServiceFolder(path, name, team, service));
}
catch (e) {
throw e;
}
});
}
-function fetch(prefix, org, struct) {
+function fetch(prefix, struct, func) {
return __awaiter(this, void 0, void 0, function* () {
try {
let keys = Object.keys(struct);
for (let i = 0; i < keys.length; i++) {
let group = keys[i];
- fs_1.default.mkdirSync(`${prefix}/${group}`, { recursive: true });
- yield createFolderStructure(struct[group], `${prefix}/${group}`, org, group);
+ fs_1.default.mkdirSync(`${prefix}${group}`, { recursive: true });
+ yield createFolderStructure(struct[group], `${prefix}${group}`, group, func);
}
}
catch (e) {
@@ -70,7 +71,7 @@ function do_fetch() {
}
(0, utils_1.output2)(`Fetching...`);
let structure = JSON.parse(reply);
- yield fetch(org.pathToRoot, org.org.name, structure);
+ yield fetch(org.pathToRoot, structure, (path, team, service) => createServiceFolder(path, org.org.name, team, service));
}
catch (e) {
throw e;
@@ -78,43 +79,47 @@ function do_fetch() {
});
}
exports.do_fetch = do_fetch;
-function createFolderStructure(struct, prefix, org, team) {
+function createServiceFolder(path, org, team, service) {
return __awaiter(this, void 0, void 0, function* () {
+ let repo = `"${config_1.GIT_HOST}/${org}/${team}/${service}"`;
+ let dir = `${path}/${service}`;
try {
- let keys = Object.keys(struct);
- for (let i = 0; i < keys.length; i++) {
- let k = keys[i];
- if (struct[k] instanceof Object)
- yield createFolderStructure(struct[k], prefix + "/" + k, org, team);
- else {
- // output(`git clone "${HOST}/${org}/${team}/${k}" "${prefix}/${k}"`);
- let repo = `"${config_1.GIT_HOST}/${org}/${team}/${k}"`;
- let dir = `${prefix}/${k}`;
- try {
- if (!fs_1.default.existsSync(dir)) {
- fs_1.default.mkdirSync(dir, { recursive: true });
- }
- if (!fs_1.default.existsSync(dir + "/.git")) {
- yield (0, utils_1.execPromise)(`git init --initial-branch=main`, dir);
- yield (0, utils_1.execPromise)(`git remote add origin ${repo}`, dir);
- fs_1.default.writeFileSync(dir + "/fetch.bat", `@echo off
+ if (!fs_1.default.existsSync(dir)) {
+ fs_1.default.mkdirSync(dir, { recursive: true });
+ }
+ if (!fs_1.default.existsSync(dir + "/.git")) {
+ yield (0, utils_1.execPromise)(`git init --initial-branch=main`, dir);
+ yield (0, utils_1.execPromise)(`git remote add origin ${repo}`, dir);
+ fs_1.default.writeFileSync(dir + "/fetch.bat", `@echo off
git fetch
git reset --hard origin/main
del fetch.sh
(goto) 2>nul & del fetch.bat`);
- fs_1.default.writeFileSync(dir + "/fetch.sh", `#!/bin/sh
+ fs_1.default.writeFileSync(dir + "/fetch.sh", `#!/bin/sh
git fetch
git reset --hard origin/main
rm fetch.bat fetch.sh`, {});
- fs_1.default.chmodSync(dir + "/fetch.sh", "755");
- }
- else {
- yield (0, utils_1.execPromise)(`git remote set-url origin ${repo}`, dir);
- }
- }
- catch (e) {
- console.log(e);
- }
+ fs_1.default.chmodSync(dir + "/fetch.sh", "755");
+ }
+ else {
+ yield (0, utils_1.execPromise)(`git remote set-url origin ${repo}`, dir);
+ }
+ }
+ catch (e) {
+ console.log(e);
+ }
+ });
+}
+function createFolderStructure(struct, prefix, team, func) {
+ return __awaiter(this, void 0, void 0, function* () {
+ try {
+ let keys = Object.keys(struct);
+ for (let i = 0; i < keys.length; i++) {
+ let k = keys[i];
+ if (struct[k] instanceof Object)
+ yield createFolderStructure(struct[k], prefix + "/" + k, team, func);
+ else {
+ func(prefix, team, k);
}
}
}
@@ -165,7 +170,7 @@ function createServiceGroup(pth, name) {
console.log("Creating service group...");
let { org } = (0, utils_1.fetchOrg)();
fs_1.default.mkdirSync(name);
- console.log(yield (0, utils_1.sshReq)(`team`, name, `--org`, org.name));
+ yield (0, utils_1.sshReq)(`team`, name, `--org`, org.name);
process.chdir(before);
}
catch (e) {
@@ -180,23 +185,33 @@ function createService(pth, group, name) {
let before = process.cwd();
process.chdir(pth.toString());
console.log("Creating service...");
- let { org } = (0, utils_1.fetchOrg)();
+ let { org, pathToRoot } = (0, utils_1.fetchOrg)();
yield (0, utils_1.sshReq)(`service`, name, `--team`, group, `--org`, org.name);
- let repoBase = `${config_1.GIT_HOST}/${org.name}/${group}`;
- try {
- yield (0, utils_1.execPromise)(`git clone -q "${repoBase}/${name}" ${name}`);
+ if (fs_1.default.existsSync(pathToRoot + BITBUCKET_FILE)) {
+ fs_1.default.mkdirSync(name, { recursive: true });
+ fs_1.default.appendFileSync(pathToRoot + BITBUCKET_FILE, "\n" + bitbucketStep(group + "/" + name));
+ (0, utils_1.addExitMessage)(`Use '${prompt_1.GREEN}cd ${pth
+ .with(name)
+ .toString()
+ .replace(/\\/g, "\\\\")}${prompt_1.NORMAL_COLOR}' to go to the new service. \nAutomatic deployment added to BitBucket pipeline.`);
}
- catch (e) {
- if (("" + e).startsWith("warning: You appear to have cloned an empty repository.")) {
+ else {
+ let repoBase = `${config_1.GIT_HOST}/${org.name}/${group}`;
+ try {
+ yield (0, utils_1.execPromise)(`git clone -q "${repoBase}/${name}" ${name}`);
+ }
+ catch (e) {
+ if (("" + e).startsWith("warning: You appear to have cloned an empty repository.")) {
+ }
+ else
+ throw e;
}
- else
- throw e;
+ yield (0, utils_1.execPromise)(`git symbolic-ref HEAD refs/heads/main`, name);
+ (0, utils_1.addExitMessage)(`Use '${prompt_1.GREEN}cd ${pth
+ .with(name)
+ .toString()
+ .replace(/\\/g, "\\\\")}${prompt_1.NORMAL_COLOR}' to go to the new service. \nThen use '${prompt_1.GREEN}${process.env["COMMAND"]} deploy${prompt_1.NORMAL_COLOR}' to deploy it.`);
}
- yield (0, utils_1.execPromise)(`git symbolic-ref HEAD refs/heads/main`, name);
- (0, utils_1.addExitMessage)(`Use '${prompt_1.GREEN}cd ${pth
- .with(name)
- .toString()
- .replace(/\\/g, "\\\\")}${prompt_1.NORMAL_COLOR}' to go to the new service. \nThen use '${prompt_1.GREEN}${process.env["COMMAND"]} deploy${prompt_1.NORMAL_COLOR}' to deploy it.`);
process.chdir(before);
}
catch (e) {
@@ -210,7 +225,12 @@ function do_pull(pth, repo) {
try {
let before = process.cwd();
process.chdir(pth.toString());
- yield (0, utils_1.execPromise)(`git pull -q "${repo}"`);
+ if (fs_1.default.existsSync(".git"))
+ yield (0, utils_1.execPromise)(`git pull -q "${repo}"`);
+ else {
+ yield (0, utils_1.execPromise)(`git clone -q "${repo}" .`);
+ fs_1.default.rmSync(".git", { recursive: true, force: true });
+ }
process.chdir(before);
}
catch (e) {
@@ -423,7 +443,7 @@ exports.do_build = do_build;
function do_replay(org, id, river) {
return __awaiter(this, void 0, void 0, function* () {
try {
- yield (0, utils_1.sshReq)(`replay`, id, `--river`, river, `--org`, `${org}`);
+ yield (0, utils_1.sshReq)(`replay`, id, `--river`, river, `--org`, org);
(0, utils_1.output2)("Replayed event.");
}
catch (e) {
@@ -804,3 +824,81 @@ function do_delete_org(org) {
});
}
exports.do_delete_org = do_delete_org;
+function do_create_deployment_agent(org, name, file) {
+ return __awaiter(this, void 0, void 0, function* () {
+ try {
+ (0, utils_1.output2)("Creating service user...");
+ let cmd = [`service-user`, org];
+ if (name !== "")
+ cmd.push(`--name`, name);
+ let key = yield (0, utils_1.sshReq)(...cmd);
+ fs_1.default.writeFileSync(file, key);
+ }
+ catch (e) {
+ throw e;
+ }
+ });
+}
+exports.do_create_deployment_agent = do_create_deployment_agent;
+const BITBUCKET_FILE = "bitbucket-pipelines.yml";
+function bitbucketStep(pth) {
+ return ` - step:
+ name: ${pth}
+ script:
+ - ./.merrymake/deploy.sh ${pth}`;
+}
+function do_bitbucket(org, host, key) {
+ return __awaiter(this, void 0, void 0, function* () {
+ try {
+ let struct = (0, utils_1.fetchOrg)();
+ fs_1.default.writeFileSync(struct.pathToRoot + path_1.default.join(".merrymake", "deploy.sh"), `set -o errexit
+chmod 600 ${key}
+eval \`ssh-agent\`
+ssh-add ${key}
+echo "api.merrymake.io ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOW2dgo+0nuahOzHD7XVnSdrCwhkK9wMnAZyr6XOKotO" >> ~/.ssh/known_hosts
+cd $1
+git init
+git remote add merrymake ssh://mist@api.merrymake.io/${org}/$1
+git fetch merrymake
+git reset merrymake/main || echo "No previous deployment"
+git config --global user.email "support@merrymake.eu"
+git config --global user.name "Merrymake"
+git add -A && (git diff-index --quiet HEAD || git commit -m 'Deploy from BitBucket')
+export RES=$(git push merrymake HEAD:main --force 2>&1); echo "\${RES}"
+case $RES in "Everything up-to-date"*) exit 0 ;; *"if/when the smoke test succeeds"*) exit 0 ;; *) echo "Deployment failed"; exit -1 ;; esac`);
+ let reply = yield (0, utils_1.sshReq)(`clone`, struct.org.name);
+ if (!reply.startsWith("{")) {
+ (0, utils_1.output2)(reply);
+ return;
+ }
+ let structure = JSON.parse(reply);
+ let pipelineFile = [
+ `pipelines:
+ branches:
+ master:
+ - parallel:`,
+ ];
+ let folders = [...exports.SPECIAL_FOLDERS];
+ fetch("", structure, (path, team, service) => folders.push(path + "/" + service));
+ for (let i = 0; i < folders.length; i++) {
+ let folder = folders[i];
+ let toService = struct.pathToRoot + folder;
+ try {
+ yield (0, utils_1.execPromise)(`git fetch`, toService);
+ yield (0, utils_1.execPromise)(`git reset origin/main`, toService);
+ }
+ catch (e) { }
+ fs_1.default.rmSync(`${toService}/.git`, { recursive: true, force: true });
+ pipelineFile.push(bitbucketStep(folder));
+ }
+ fs_1.default.writeFileSync(struct.pathToRoot + BITBUCKET_FILE, pipelineFile.join("\n"));
+ yield (0, utils_1.execPromise)(`git init`, struct.pathToRoot);
+ yield (0, utils_1.execPromise)(`git remote add origin ${host}`, struct.pathToRoot);
+ yield (0, utils_1.execPromise)(`git update-index --add --chmod=+x .merrymake/deploy.sh`, struct.pathToRoot);
+ }
+ catch (e) {
+ throw e;
+ }
+ });
+}
+exports.do_bitbucket = do_bitbucket;
diff --git a/executors.ts b/executors.ts
index 9b7834e..c06bfd3 100644
--- a/executors.ts
+++ b/executors.ts
@@ -26,6 +26,8 @@ import { getArgs } from "./args";
import { stdout } from "process";
import { MerrymakeCrypto } from "@merrymake/secret-lib";
+export const SPECIAL_FOLDERS = ["event-catalogue", "public"];
+
async function clone(struct: any, name: string) {
try {
output2(`Cloning ${name}...`);
@@ -44,23 +46,29 @@ async function clone(struct: any, name: string) {
dir
);
// await execPromise(`git fetch`, dir);
- fetch(`./${name}`, name, struct);
+ fetch(`./${name}/`, struct, (path, team, service) =>
+ createServiceFolder(path, name, team, service)
+ );
} catch (e) {
throw e;
}
}
-async function fetch(prefix: string, org: string, struct: any) {
+async function fetch(
+ prefix: string,
+ struct: any,
+ func: (path: string, team: string, service: string) => void
+) {
try {
let keys = Object.keys(struct);
for (let i = 0; i < keys.length; i++) {
let group = keys[i];
- fs.mkdirSync(`${prefix}/${group}`, { recursive: true });
+ fs.mkdirSync(`${prefix}${group}`, { recursive: true });
await createFolderStructure(
struct[group],
- `${prefix}/${group}`,
- org,
- group
+ `${prefix}${group}`,
+ group,
+ func
);
}
} catch (e) {
@@ -78,58 +86,68 @@ export async function do_fetch() {
}
output2(`Fetching...`);
let structure = JSON.parse(reply);
- await fetch(org.pathToRoot, org.org.name, structure);
+ await fetch(org.pathToRoot, structure, (path, team, service) =>
+ createServiceFolder(path, org.org.name, team, service)
+ );
} catch (e) {
throw e;
}
}
-async function createFolderStructure(
- struct: any,
- prefix: string,
+async function createServiceFolder(
+ path: string,
org: string,
- team: string
+ team: string,
+ service: string
) {
+ let repo = `"${GIT_HOST}/${org}/${team}/${service}"`;
+ let dir = `${path}/${service}`;
try {
- let keys = Object.keys(struct);
- for (let i = 0; i < keys.length; i++) {
- let k = keys[i];
- if (struct[k] instanceof Object)
- await createFolderStructure(struct[k], prefix + "/" + k, org, team);
- else {
- // output(`git clone "${HOST}/${org}/${team}/${k}" "${prefix}/${k}"`);
- let repo = `"${GIT_HOST}/${org}/${team}/${k}"`;
- let dir = `${prefix}/${k}`;
- try {
- if (!fs.existsSync(dir)) {
- fs.mkdirSync(dir, { recursive: true });
- }
- if (!fs.existsSync(dir + "/.git")) {
- await execPromise(`git init --initial-branch=main`, dir);
- await execPromise(`git remote add origin ${repo}`, dir);
- fs.writeFileSync(
- dir + "/fetch.bat",
- `@echo off
+ if (!fs.existsSync(dir)) {
+ fs.mkdirSync(dir, { recursive: true });
+ }
+ if (!fs.existsSync(dir + "/.git")) {
+ await execPromise(`git init --initial-branch=main`, dir);
+ await execPromise(`git remote add origin ${repo}`, dir);
+ fs.writeFileSync(
+ dir + "/fetch.bat",
+ `@echo off
git fetch
git reset --hard origin/main
del fetch.sh
(goto) 2>nul & del fetch.bat`
- );
- fs.writeFileSync(
- dir + "/fetch.sh",
- `#!/bin/sh
+ );
+ fs.writeFileSync(
+ dir + "/fetch.sh",
+ `#!/bin/sh
git fetch
git reset --hard origin/main
rm fetch.bat fetch.sh`,
- {}
- );
- fs.chmodSync(dir + "/fetch.sh", "755");
- } else {
- await execPromise(`git remote set-url origin ${repo}`, dir);
- }
- } catch (e) {
- console.log(e);
- }
+ {}
+ );
+ fs.chmodSync(dir + "/fetch.sh", "755");
+ } else {
+ await execPromise(`git remote set-url origin ${repo}`, dir);
+ }
+ } catch (e) {
+ console.log(e);
+ }
+}
+
+async function createFolderStructure(
+ struct: any,
+ prefix: string,
+ team: string,
+ func: (path: string, team: string, service: string) => void
+) {
+ try {
+ let keys = Object.keys(struct);
+ for (let i = 0; i < keys.length; i++) {
+ let k = keys[i];
+ if (struct[k] instanceof Object)
+ await createFolderStructure(struct[k], prefix + "/" + k, team, func);
+ else {
+ func(prefix, team, k);
}
}
} catch (e) {
@@ -172,7 +190,7 @@ export async function createServiceGroup(pth: Path, name: string) {
console.log("Creating service group...");
let { org } = fetchOrg();
fs.mkdirSync(name);
- console.log(await sshReq(`team`, name, `--org`, org.name));
+ await sshReq(`team`, name, `--org`, org.name);
process.chdir(before);
} catch (e) {
throw e;
@@ -184,31 +202,48 @@ export async function createService(pth: Path, group: string, name: string) {
let before = process.cwd();
process.chdir(pth.toString());
console.log("Creating service...");
- let { org } = fetchOrg();
+ let { org, pathToRoot } = fetchOrg();
await sshReq(`service`, name, `--team`, group, `--org`, org.name);
- let repoBase = `${GIT_HOST}/${org.name}/${group}`;
- try {
- await execPromise(`git clone -q "${repoBase}/${name}" ${name}`);
- } catch (e) {
- if (
- ("" + e).startsWith(
- "warning: You appear to have cloned an empty repository."
- )
- ) {
- } else throw e;
+ if (fs.existsSync(pathToRoot + BITBUCKET_FILE)) {
+ fs.mkdirSync(name, { recursive: true });
+ fs.appendFileSync(
+ pathToRoot + BITBUCKET_FILE,
+ "\n" + bitbucketStep(group + "/" + name)
+ );
+ addExitMessage(
+ `Use '${GREEN}cd ${pth
+ .with(name)
+ .toString()
+ .replace(
+ /\\/g,
+ "\\\\"
+ )}${NORMAL_COLOR}' to go to the new service. \nAutomatic deployment added to BitBucket pipeline.`
+ );
+ } else {
+ let repoBase = `${GIT_HOST}/${org.name}/${group}`;
+ try {
+ await execPromise(`git clone -q "${repoBase}/${name}" ${name}`);
+ } catch (e) {
+ if (
+ ("" + e).startsWith(
+ "warning: You appear to have cloned an empty repository."
+ )
+ ) {
+ } else throw e;
+ }
+ await execPromise(`git symbolic-ref HEAD refs/heads/main`, name);
+ addExitMessage(
+ `Use '${GREEN}cd ${pth
+ .with(name)
+ .toString()
+ .replace(
+ /\\/g,
+ "\\\\"
+ )}${NORMAL_COLOR}' to go to the new service. \nThen use '${GREEN}${
+ process.env["COMMAND"]
+ } deploy${NORMAL_COLOR}' to deploy it.`
+ );
}
- await execPromise(`git symbolic-ref HEAD refs/heads/main`, name);
- addExitMessage(
- `Use '${GREEN}cd ${pth
- .with(name)
- .toString()
- .replace(
- /\\/g,
- "\\\\"
- )}${NORMAL_COLOR}' to go to the new service. \nThen use '${GREEN}${
- process.env["COMMAND"]
- } deploy${NORMAL_COLOR}' to deploy it.`
- );
process.chdir(before);
} catch (e) {
throw e;
@@ -219,7 +254,11 @@ async function do_pull(pth: Path, repo: string) {
try {
let before = process.cwd();
process.chdir(pth.toString());
- await execPromise(`git pull -q "${repo}"`);
+ if (fs.existsSync(".git")) await execPromise(`git pull -q "${repo}"`);
+ else {
+ await execPromise(`git clone -q "${repo}" .`);
+ fs.rmSync(".git", { recursive: true, force: true });
+ }
process.chdir(before);
} catch (e) {
throw e;
@@ -445,7 +484,7 @@ export async function do_build() {
export async function do_replay(org: string, id: string, river: string) {
try {
- await sshReq(`replay`, id, `--river`, river, `--org`, `${org}`);
+ await sshReq(`replay`, id, `--river`, river, `--org`, org);
output2("Replayed event.");
} catch (e) {
throw e;
@@ -901,3 +940,89 @@ export async function do_delete_org(org: string) {
throw e;
}
}
+
+export async function do_create_deployment_agent(
+ org: string,
+ name: string,
+ file: string
+) {
+ try {
+ output2("Creating service user...");
+ let cmd = [`service-user`, org];
+ if (name !== "") cmd.push(`--name`, name);
+ let key = await sshReq(...cmd);
+ fs.writeFileSync(file, key);
+ } catch (e) {
+ throw e;
+ }
+}
+
+const BITBUCKET_FILE = "bitbucket-pipelines.yml";
+function bitbucketStep(pth: string) {
+ return ` - step:
+ name: ${pth}
+ script:
+ - ./.merrymake/deploy.sh ${pth}`;
+}
+
+export async function do_bitbucket(org: string, host: string, key: string) {
+ try {
+ let struct = fetchOrg();
+ fs.writeFileSync(
+ struct.pathToRoot + path.join(".merrymake", "deploy.sh"),
+ `set -o errexit
+chmod 600 ${key}
+eval \`ssh-agent\`
+ssh-add ${key}
+echo "api.merrymake.io ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOW2dgo+0nuahOzHD7XVnSdrCwhkK9wMnAZyr6XOKotO" >> ~/.ssh/known_hosts
+cd $1
+git init
+git remote add merrymake ssh://mist@api.merrymake.io/${org}/$1
+git fetch merrymake
+git reset merrymake/main || echo "No previous deployment"
+git config --global user.email "support@merrymake.eu"
+git config --global user.name "Merrymake"
+git add -A && (git diff-index --quiet HEAD || git commit -m 'Deploy from BitBucket')
+export RES=$(git push merrymake HEAD:main --force 2>&1); echo "\${RES}"
+case $RES in "Everything up-to-date"*) exit 0 ;; *"if/when the smoke test succeeds"*) exit 0 ;; *) echo "Deployment failed"; exit -1 ;; esac`
+ );
+ let reply = await sshReq(`clone`, struct.org.name);
+ if (!reply.startsWith("{")) {
+ output2(reply);
+ return;
+ }
+ let structure = JSON.parse(reply);
+ let pipelineFile = [
+ `pipelines:
+ branches:
+ master:
+ - parallel:`,
+ ];
+ let folders: string[] = [...SPECIAL_FOLDERS];
+ fetch("", structure, (path, team, service) =>
+ folders.push(path + "/" + service)
+ );
+ for (let i = 0; i < folders.length; i++) {
+ let folder = folders[i];
+ let toService = struct.pathToRoot + folder;
+ try {
+ await execPromise(`git fetch`, toService);
+ await execPromise(`git reset origin/main`, toService);
+ } catch (e) {}
+ fs.rmSync(`${toService}/.git`, { recursive: true, force: true });
+ pipelineFile.push(bitbucketStep(folder));
+ }
+ fs.writeFileSync(
+ struct.pathToRoot + BITBUCKET_FILE,
+ pipelineFile.join("\n")
+ );
+ await execPromise(`git init`, struct.pathToRoot);
+ await execPromise(`git remote add origin ${host}`, struct.pathToRoot);
+ await execPromise(
+ `git update-index --add --chmod=+x .merrymake/deploy.sh`,
+ struct.pathToRoot
+ );
+ } catch (e) {
+ throw e;
+ }
+}
diff --git a/package.json b/package.json
index a3e57d7..2142ec4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@merrymake/cli",
- "version": "3.1.0",
+ "version": "3.2.0",
"description": "",
"main": "index.js",
"scripts": {
diff --git a/questions.js b/questions.js
index 0167e85..f888363 100644
--- a/questions.js
+++ b/questions.js
@@ -85,6 +85,10 @@ function delete_org_name(org) {
(0, utils_1.addToExecuteQueue)(() => (0, executors_1.do_delete_org)(org));
return (0, utils_1.finish)();
}
+function hosting_bitbucket_key_host(org, host, key) {
+ (0, utils_1.addToExecuteQueue)(() => (0, executors_1.do_bitbucket)(org, host, key));
+ return (0, utils_1.finish)();
+}
function service_template(pathToService, template) {
return __awaiter(this, void 0, void 0, function* () {
try {
@@ -249,26 +253,26 @@ function register_manual() {
function register() {
return __awaiter(this, void 0, void 0, function* () {
try {
- let keyfiles = (0, utils_1.getFiles)(new utils_2.Path(`${os_1.default.homedir()}/.ssh`), "").filter((x) => x.endsWith(".pub"));
+ let keyfiles = (0, utils_1.getFiles)(new utils_2.Path(`${os_1.default.homedir()}/.ssh`)).filter((x) => x.endsWith(".pub"));
let keys = keyfiles.map((x) => {
let f = x.substring(0, x.length - ".pub".length);
return {
long: f,
- text: `Use key ${f}`,
+ text: `use key ${f}`,
action: () => register_key(() => (0, executors_1.useExistingKey)(f)),
};
});
keys.push({
long: "add",
short: "a",
- text: "Manually add key",
+ text: "manually add key",
action: () => register_manual(),
});
if (!keyfiles.includes("merrymake")) {
keys.push({
long: "new",
short: "n",
- text: "Setup new key specifically for Merrymake",
+ text: "setup new key specifically for Merrymake",
action: () => register_key(executors_1.generateNewKey),
});
}
@@ -793,6 +797,85 @@ function post(org) {
}
});
}
+function hosting_bitbucket_key(org, file) {
+ return __awaiter(this, void 0, void 0, function* () {
+ try {
+ let host = yield (0, prompt_1.shortText)("Bitbucket repo", "The URL to the bitbucket mono-repository.", `https://...`).then((x) => x);
+ return hosting_bitbucket_key_host(org, host, file);
+ }
+ catch (e) {
+ throw e;
+ }
+ });
+}
+function hosting_bitbucket_create(pathToRoot, org) {
+ return __awaiter(this, void 0, void 0, function* () {
+ try {
+ let name = yield (0, prompt_1.shortText)("Name", "Display name for the service user", `Service User`).then((x) => x);
+ let file = ".merrymake/" + name.toLowerCase().replace(" ", "-") + ".key";
+ (0, utils_1.addToExecuteQueue)(() => (0, executors_1.do_create_deployment_agent)(org, name, file));
+ return hosting_bitbucket_key(org, file);
+ }
+ catch (e) {
+ throw e;
+ }
+ });
+}
+function hosting_bitbucket(pathToRoot, org) {
+ return __awaiter(this, void 0, void 0, function* () {
+ try {
+ let keyfiles = (0, utils_1.getFiles)(new utils_2.Path(`${pathToRoot}/.merrymake`)).filter((x) => x.endsWith(".key"));
+ let options = keyfiles.map((x) => {
+ let f = x.substring(0, x.length - ".key".length);
+ return {
+ long: f,
+ text: `use service user ${f}`,
+ action: () => hosting_bitbucket_key(org, f),
+ };
+ });
+ options.push({
+ long: `create`,
+ short: `c`,
+ text: `create service user`,
+ action: () => hosting_bitbucket_create(pathToRoot, org),
+ });
+ return yield (0, prompt_1.choice)("Which service user would you like to use?", options, {
+ invertedQuiet: { cmd: false, select: true },
+ }).then((x) => x);
+ }
+ catch (e) {
+ throw e;
+ }
+ });
+}
+function hosting(pathToRoot, org) {
+ return (0, prompt_1.choice)("Which host would you like to use?", [
+ {
+ long: "bitbucket",
+ short: "b",
+ text: "bitbucket",
+ action: () => hosting_bitbucket(pathToRoot, org),
+ },
+ // {
+ // long: "github",
+ // short: "h",
+ // text: "github",
+ // action: () => hosting_github(),
+ // },
+ // {
+ // long: "gitlab",
+ // short: "h",
+ // text: "gitlab",
+ // action: () => hosting_gitlab(),
+ // },
+ // {
+ // long: "azure devops",
+ // short: "h",
+ // text: "azure devops",
+ // action: () => hosting_azure_devops(),
+ // },
+ ]);
+}
function event_key_events(key, events) {
(0, utils_1.addToExecuteQueue)(() => (0, executors_1.do_event)(key, events));
return (0, utils_1.finish)();
@@ -1009,7 +1092,6 @@ function sim() {
(0, utils_1.addToExecuteQueue)(() => new simulator_1.Run(3000).execute());
return (0, utils_1.finish)();
}
-const SPECIAL_FOLDERS = ["event-catalogue", "public"];
function start() {
return __awaiter(this, void 0, void 0, function* () {
try {
@@ -1022,7 +1104,7 @@ function start() {
let struct = {
org: rawStruct.org,
serviceGroup: rawStruct.serviceGroup !== null &&
- !SPECIAL_FOLDERS.includes(rawStruct.serviceGroup)
+ !executors_1.SPECIAL_FOLDERS.includes(rawStruct.serviceGroup)
? rawStruct.serviceGroup
: null,
inEventCatalogue: rawStruct.serviceGroup === "event-catalogue",
@@ -1178,6 +1260,11 @@ function start() {
text: "register an additional sshkey or email to account",
action: () => register(),
});
+ options.push({
+ long: "hosting",
+ text: "configure git hosting with bitbucket", // TODO add github, gitlab, and azure devops
+ action: () => hosting(struct.pathToRoot, orgName),
+ });
options.push({
long: "help",
text: "help diagnose potential issues",
diff --git a/questions.ts b/questions.ts
index 1aa5054..ecfd16c 100644
--- a/questions.ts
+++ b/questions.ts
@@ -21,6 +21,7 @@ import {
finish,
getCache,
getFiles,
+ getFilesFilter,
output2,
sshReq,
} from "./utils";
@@ -60,6 +61,9 @@ import {
do_delete_group,
do_delete_org,
do_replay,
+ do_create_deployment_agent,
+ do_bitbucket,
+ SPECIAL_FOLDERS,
} from "./executors";
import { VERSION_CMD, type ProjectType } from "@merrymake/detect-project-type";
import { execSync } from "child_process";
@@ -148,6 +152,11 @@ function delete_org_name(org: string) {
return finish();
}
+function hosting_bitbucket_key_host(org: string, host: string, key: string) {
+ addToExecuteQueue(() => do_bitbucket(org, host, key));
+ return finish();
+}
+
async function service_template(pathToService: Path, template: string) {
try {
let langs = await Promise.all(
@@ -347,28 +356,28 @@ async function register_manual() {
async function register() {
try {
- let keyfiles = getFiles(new Path(`${os.homedir()}/.ssh`), "").filter((x) =>
+ let keyfiles = getFiles(new Path(`${os.homedir()}/.ssh`)).filter((x) =>
x.endsWith(".pub")
);
let keys = keyfiles.map