2

我想知道在开发 Google Apps Script webapp 时如何有不同的阶段(开发产品)。Afaik 没有什么是开箱即用的,尤其是当您想拥有一个 G Suite 帐户用于开发而另一个用于生产时。

我想出的唯一解决方案是使用Clasp并在两个 GSuite 帐户之间切换clasp logout / login

我的问题是:

  • 这是否有效,或者是否有提到一个 GSuite 帐户在推送到另一个帐户时会中断?
  • 有更好的方法吗?

我认为最好用一个例子来解释它:

我在域 test.mydomain.com 上有一个 Google App Script 项目设置,用户 user@test.mydomain.com

现在我需要用 clasp 将它推送到另一个域 prod.mydomain.com 上的另一个项目(同名)。此项目与我拥有凭据的另一个用户共享。

问题是当我现在使用 clasp 中的用户登录时,我需要先创建一个项目,对吗?但该项目已经存在。那么我该如何处理呢?

4

1 回答 1

3

这行得通吗?

是的,只需确保该帐户具有能够推送的编辑权限(但我相信您知道这一点)。

有更好的方法吗?

有一种是(除非你是隐含的意思),引用GitHub 的 clasp 项目#42 的问题:

新标志:

clasp login --local 在本地保存 .clasprc.json,但将使用全局 clasp 凭据。

clasp login # 所有项目的全局
clasp login --creds creds.json # 你自己的 creds
clasp login --local # 本地项目。适用于多个 Google 帐户。
clasp login --creds creds.json --local # 自己的 creds,本地

所以从技术上讲,您可以使用多个帐户,但最终归结为您的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);

样品运行

于 2020-06-29T10:10:38.597 回答