如何开发具有不同阶段/不同 GSuite 帐户的 Google Apps Script Web App?
How to develop a Google Apps Script Web App with different stages / different GSuite accounts?
我想知道在开发 Google Apps Script webapp 时如何有不同的阶段 (dev.prod)。 Afaik 没有开箱即用的东西,尤其是当您想要一个 G Suite 帐户用于开发而另一个用于生产时。
我想出的唯一解决方法是使用 Clasp 并使用 clasp logout / login
在两个 GSuite 帐户之间切换
我的问题是:
- 这行得通吗,或者有没有提到一个 GSuite 帐户在推送到另一个帐户时中断?
- 有没有更好的方法?
我觉得还是举个例子来解释比较好:
我有一个 Google App 脚本项目设置,在域 test.mydomain.com 上有 clasp,用户为 user@test.mydomain.com
现在我需要用 clasp 将它推送到不同域 prod.mydomain.com 上的另一个项目(同名)。此项目与我拥有凭据的另一个用户共享。
问题是,当我现在用 clasp 中的用户登录时,我需要先创建一个项目,对吗?但是该项目已经存在。那我该如何处理呢?
这个有用吗?
是的,只要确保该帐户具有编辑权限即可推送(但我相信您知道这一点)。
有没有更好的方法?
引用 clasp 项目的 issue #42 GitHub:
New flag:
clasp login --local
Saves .clasprc.json locally, but would use global clasp credentials.
clasp login # global for all projects
clasp login --creds creds.json # your own creds
clasp login --local # local project. Useful for multiple Google accounts.
clasp login --creds creds.json --local # own creds, local
所以从技术上讲,您可以使用多个帐户,但最终归结为您的 login
/ logout
技术。
重新讨论关于使用 CLASP 在应用程序脚本项目之间切换的讨论,我最终编写了一个基本的实用程序脚本,用于在要推送到的应用程序脚本项目之间切换(没有依赖性,但如果你想以样式管理标志,请查看热门 Yargs 套餐):
const fs = require('fs').promises;
const { promisify } = require("util");
const { spawn } = require("child_process");
const rl = require("readline");
const promiseAns = () => {
const dummy = rl.createInterface({
input: process.stdin
});
dummy.question[promisify.custom] = function (query) {
return new Promise((resolve) => this.question( query, resolve));
};
return promisify(dummy.question);
};
/**
* @summary asks to confirm and exits if ok
* @param {import("readline").Interface} cons
* @param {string} init
*/
const checkExit = async (cons, init) =>{
if ( !/exit/.test(init) ) {
return;
}
const question = promiseAns();
const ans = await question.bind(cons)(`Are you sure (Y|n)?\n`);
if (/^Y(?:es)?/i.test(ans)) {
process.exit();
}
}
/**
* @summary repeat question until matches
* @param {import("readline").Interface} cons
* @param {string} query
* @param {(ans: string) => boolean} condition
* @param {(ans: string) => any} onSuccess
*/
const askUntil = (cons, query, condition, onSuccess) => cons.question(query, async (ans) => {
await checkExit(cons, ans);
if (!condition(ans)) {
return askUntil(cons, query, condition, onSuccess);
}
onSuccess(ans);
});
/**
* @summary makes readline interface
*/
const makeRL = () => {
const cons = rl.createInterface({
input: process.stdin,
output: process.stdout,
});
cons.on("line", (ln) => console.log(ln));
return cons;
};
process.on("unhandledRejection", (err) => {
console.log(err);
process.exit();
});
const APP_CONFIG = {
paths: {
dotclasp: "./.clasp.json",
ids: "./ids.txt"
}
};
(async (configuration) => {
const cons = makeRL();
const { paths: { dotclasp, ids } } = configuration;
const text = await fs.readFile(ids, { encoding: "utf-8" });
const lines = text.split(/\r?\n/);
const relevant = lines.filter((line) => /^(\w+)\s+\w+/.test(line));
const lookup = {};
relevant.forEach((lbl) => {
const [label, id] = lbl.split(/\s+/);
lookup[label] = id;
});
const config = require(dotclasp);
const [label] = Object.entries(lookup).find(([, id]) => config.scriptId === id);
cons.emit("line", `Currently selected: ${label}`);
const { argv } = process;
const push = argv.includes("--push");
askUntil(cons, `Select project (${Object.keys(lookup).join(" | ")})\n`, (ans) => lookup[ans], async (ans) => {
config.scriptId = lookup[ans];
try {
await fs.writeFile(dotclasp, JSON.stringify(config), { encoding: "utf-8" });
cons.emit("line", `switched to ${ans}`);
}
catch {
cons.emit("line", `failed to switch to ${ans}`);
}
if (!push) {
process.exit();
}
const cp = spawn(`clasp push --force`, {
stdio: "inherit",
shell: true
});
cp.on("error", ({ message }) => {
cons.write(message);
process.exit();
});
cp.on("exit", () => {
cons.emit("line", `pushed to ${ans}`);
process.exit();
});
});
})(APP_CONFIG);
我想知道在开发 Google Apps Script webapp 时如何有不同的阶段 (dev.prod)。 Afaik 没有开箱即用的东西,尤其是当您想要一个 G Suite 帐户用于开发而另一个用于生产时。
我想出的唯一解决方法是使用 Clasp 并使用 clasp logout / login
我的问题是:
- 这行得通吗,或者有没有提到一个 GSuite 帐户在推送到另一个帐户时中断?
- 有没有更好的方法?
我觉得还是举个例子来解释比较好:
我有一个 Google App 脚本项目设置,在域 test.mydomain.com 上有 clasp,用户为 user@test.mydomain.com
现在我需要用 clasp 将它推送到不同域 prod.mydomain.com 上的另一个项目(同名)。此项目与我拥有凭据的另一个用户共享。
问题是,当我现在用 clasp 中的用户登录时,我需要先创建一个项目,对吗?但是该项目已经存在。那我该如何处理呢?
这个有用吗?
是的,只要确保该帐户具有编辑权限即可推送(但我相信您知道这一点)。
有没有更好的方法?
引用 clasp 项目的 issue #42 GitHub:
New flag:
clasp login --local Saves .clasprc.json locally, but would use global clasp credentials.
clasp login # global for all projects
clasp login --creds creds.json # your own creds
clasp login --local # local project. Useful for multiple Google accounts.
clasp login --creds creds.json --local # own creds, local
所以从技术上讲,您可以使用多个帐户,但最终归结为您的 login
/ logout
技术。
重新讨论关于使用 CLASP 在应用程序脚本项目之间切换的讨论,我最终编写了一个基本的实用程序脚本,用于在要推送到的应用程序脚本项目之间切换(没有依赖性,但如果你想以样式管理标志,请查看热门 Yargs 套餐):
const fs = require('fs').promises;
const { promisify } = require("util");
const { spawn } = require("child_process");
const rl = require("readline");
const promiseAns = () => {
const dummy = rl.createInterface({
input: process.stdin
});
dummy.question[promisify.custom] = function (query) {
return new Promise((resolve) => this.question( query, resolve));
};
return promisify(dummy.question);
};
/**
* @summary asks to confirm and exits if ok
* @param {import("readline").Interface} cons
* @param {string} init
*/
const checkExit = async (cons, init) =>{
if ( !/exit/.test(init) ) {
return;
}
const question = promiseAns();
const ans = await question.bind(cons)(`Are you sure (Y|n)?\n`);
if (/^Y(?:es)?/i.test(ans)) {
process.exit();
}
}
/**
* @summary repeat question until matches
* @param {import("readline").Interface} cons
* @param {string} query
* @param {(ans: string) => boolean} condition
* @param {(ans: string) => any} onSuccess
*/
const askUntil = (cons, query, condition, onSuccess) => cons.question(query, async (ans) => {
await checkExit(cons, ans);
if (!condition(ans)) {
return askUntil(cons, query, condition, onSuccess);
}
onSuccess(ans);
});
/**
* @summary makes readline interface
*/
const makeRL = () => {
const cons = rl.createInterface({
input: process.stdin,
output: process.stdout,
});
cons.on("line", (ln) => console.log(ln));
return cons;
};
process.on("unhandledRejection", (err) => {
console.log(err);
process.exit();
});
const APP_CONFIG = {
paths: {
dotclasp: "./.clasp.json",
ids: "./ids.txt"
}
};
(async (configuration) => {
const cons = makeRL();
const { paths: { dotclasp, ids } } = configuration;
const text = await fs.readFile(ids, { encoding: "utf-8" });
const lines = text.split(/\r?\n/);
const relevant = lines.filter((line) => /^(\w+)\s+\w+/.test(line));
const lookup = {};
relevant.forEach((lbl) => {
const [label, id] = lbl.split(/\s+/);
lookup[label] = id;
});
const config = require(dotclasp);
const [label] = Object.entries(lookup).find(([, id]) => config.scriptId === id);
cons.emit("line", `Currently selected: ${label}`);
const { argv } = process;
const push = argv.includes("--push");
askUntil(cons, `Select project (${Object.keys(lookup).join(" | ")})\n`, (ans) => lookup[ans], async (ans) => {
config.scriptId = lookup[ans];
try {
await fs.writeFile(dotclasp, JSON.stringify(config), { encoding: "utf-8" });
cons.emit("line", `switched to ${ans}`);
}
catch {
cons.emit("line", `failed to switch to ${ans}`);
}
if (!push) {
process.exit();
}
const cp = spawn(`clasp push --force`, {
stdio: "inherit",
shell: true
});
cp.on("error", ({ message }) => {
cons.write(message);
process.exit();
});
cp.on("exit", () => {
cons.emit("line", `pushed to ${ans}`);
process.exit();
});
});
})(APP_CONFIG);