如何将 Google Application Script 项目迁移到不同的帐户?

Posted

技术标签:

【中文标题】如何将 Google Application Script 项目迁移到不同的帐户?【英文标题】:How to migrate Google Application Script project to different a account? 【发布时间】:2021-01-27 09:42:25 【问题描述】:

是否有任何(理想情况下简单快捷的)方法可以将 Google 应用程序脚本项目迁移到另一个帐户?

假设我为客户处理一个项目,一旦准备好我想迁移它。但该脚本连接到 Google 表格,并且还有一个时间触发器。该项目还充当 Web 应用程序。

我认为,如果客户创建一个项目并与我共享该项目并授权脚本通过自己运行脚本来访问 Google 表格,它将解决“迁移”问题。但它看起来不像。当我运行脚本时,它也会要求我授权访问表格。而且我不知道它如何与触发器和 Web 应用程序一起工作。

谁能解释一下?如何确保项目最终以最终客户权限运行、使用他们的配额等?

【问题讨论】:

您客户的帐户需要授权 Apps 脚本项目才能从他们的帐户运行。他们需要做一些“设置”。要为他们获取副本,您需要做的就是共享文件,然后他们可以制作共享文件的副本,该副本将作为所有者放入他们的云端硬盘中。他们将需要重命名文件以删除“复制”一词并授权脚本运行。如果 Apps 脚本项目使用任何高级服务或 API,那么他们还需要在其 Google Cloud Platform 中启用 API。 哦,我忘了 API。另一件事。但是客户当然不知道该怎么做。有没有简单的方法来处理它?当然,如果需要修改编码器而不是客户需要这样做。我确信一定有编码人员交付项目,他们过去必须处理这个问题。你的方法是什么? 我发现转移脚本所有权的最简单方法是通过共享驱动器。至于设置,如果您的客户不太了解脚本,那么您最好设置它,这意味着您需要访问他们的帐户。 访问他们的帐户意味着用户名和密码?这是我为朋友做的第一个项目,所以我想这还可以,但总的来说,我认为询问用户名和密码可能会有问题。我想如果项目在他们的帐户下设置客户并与我分享,我会解决它。但后来我遇到了从脚本访问表格的麻烦。我不敢相信没有简单的方法。 【参考方案1】:

有几点要谈:

    当我在共享项目中设置触发器时,会发生什么? 当我在共享项目中部署时,正在使用谁的配额? 如何更轻松地进行设置?

The documentation has a nice table 很好地概述了何时执行哪些权限。

1。当我在共享项目中设置触发器时,会发生什么?

引用the documentation:

当您在项目上进行协作时,您创建的任何可安装触发器都不会与有权访问您项目的人共享。 […]

因此,基本上,如果您制作了一个可安装的触发器,该触发器会在您执行时执行并且它会使用您的配额。

2。当我在共享项目中部署时,正在使用谁的配额?

The documentation states:

您可以使用共享云端硬盘协作开发网络应用。部署共享中的 Web 应用时,选择“以您的身份执行”会导致该 Web 应用在部署它的用户的权限下执行(因为没有脚本所有者)。

3。如何设置更方便?

您可以使用 ScriptApp 设置触发器并使用 Apps Scripts API 进行部署。这将重复配置 GCP 并手动设置项目清单 (see manifest’s structure reference)。以下是我编写的代码,在一切设置正确后即可使用:

//
// This file is for easy setup of the project.
//
 
 
/**
* Remove any installed trigger and makes the ones on the list
*/
function remakeTriggers() 
 // Remove all triggers for this project for this user
 ScriptApp.getProjectTriggers().forEach(trigger => ScriptApp.deleteTrigger(trigger))
 
  // Remake triggers
  // Change as desired to set the triggers you want
 ScriptApp.newTrigger('myTimer')
   .timeBased()
   .atHour(2)
   .everyDays(1)
   .create()
  ScriptApp.newTrigger('myTimer')
   .forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
   .onEdit()
   .create()

 
const DEPLOYMENT_ID_KEY = 'deploymentId'
 
/**
* Deploys this script as a WebApp.
*/
function deploy() 
 const properties = PropertiesService.getScriptProperties()
 const version = newVersion_()
  const deploymentId = properties.getProperty(DEPLOYMENT_ID_KEY)
 if (!deploymentId) 
   console.log(`Creating new deployment to version $version.versionNumber`)
   const deployment = newDeploy_(version.versionNumber)
   properties.setProperty(DEPLOYMENT_ID_KEY, deployment.deploymentId)
  else 
   console.log(`Updating deployment to version $version.versionNumber`)
   updateDeploy_(deploymentId, version.versionNumber)
 

 
/**
* Makes a new version.
*/
function newVersion_() 
 const response = UrlFetchApp.fetch(
   `https://script.googleapis.com/v1/projects/$ScriptApp.getScriptId()/versions`,
   
     method: 'POST',
     muteHttpExceptions: true,
     headers: 
       'Authorization': `Bearer $ScriptApp.getOAuthToken()`
     ,
   ,
 )
 return parseResponse_(response)

 
/**
* Makes a new deployment
*/
function newDeploy_(versionNumber) 
 const response = UrlFetchApp.fetch(
   `https://script.googleapis.com/v1/projects/$ScriptApp.getScriptId()/deployments`,
   
     method: 'POST',
     payload: JSON.stringify( versionNumber ),
     contentType: 'application/json; charset=utf-8',
     headers: 
       'Authorization': `Bearer $ScriptApp.getOAuthToken()`
     ,
     muteHttpExceptions: true,
   ,
 )
 return parseResponse_(response)

 
/**
* Redeploys
*/
function updateDeploy_(deploymentId, versionNumber) 
 const response = UrlFetchApp.fetch(
   `https://script.googleapis.com/v1/projects/$ScriptApp.getScriptId()/deployments/$deploymentId`,
   
     method: 'PUT',
     payload: JSON.stringify(
       deploymentConfig: 
         versionNumber
       
     ),
     contentType: 'application/json; charset=utf-8',
     headers: 
       'Authorization': `Bearer $ScriptApp.getOAuthToken()`
     ,
     muteHttpExceptions: true,
   ,
 )
 return parseResponse_(response)

 
/**
* Helper function to parse a fetch's JSON response. Handles errors.
*
* @param UrlFetchApp.HTTPResponse response to parse
* @returns Object Parsed JSON response
*/
function parseResponse_(response) 
 const result = response.getContentText()
 const code = response.getResponseCode()
 if (code < 200 || code >= 400) 
   throw result
 
 return JSON.parse(result)

记得更改remakeTriggers,以便它创建您想要的触发器。

我的清单如下所示:


 […]
 "oauthScopes": [
   "https://www.googleapis.com/auth/spreadsheets.currentonly",
   "https://www.googleapis.com/auth/script.external_request",
   "https://www.googleapis.com/auth/script.projects",
   "https://www.googleapis.com/auth/script.deployments"
 ],
 "webapp": 
   "access": "ANYONE",
   "executeAs": "USER_DEPLOYING"
 

请注意,我们在此处配置 webapp(无法通过 API 完成)并且需要手动定义 OAuth2 范围。一切设置好后,最终用户只需要转到脚本文件并根据需要执行函数(remakeTriggers 重新制作触发器,deploy 这样做)。

您还可以将这两个函数设为私有(在其名称末尾添加_)并创建一个调用它们的函数,因此用户只需单击Run 即可转到Deploy.gs 文件.

参考文献

Collaborating with Other Developers (Google Apps Script Guides) Authorization for Google Services - Permissions and types of scripts (Google Apps Script Guides) Web Apps - Permissions (Google Apps Script Guides) ScriptApp.getProjectTriggers() (Google Apps Script Reference) ScriptApp.deleteTrigger(trigger) (Google Apps Script Reference) ScriptApp.newTrigger(functionName) (Google Apps Script Reference) External APIs (Google Apps Script Guides) UrlFetchApp.fetch(url, params) (Google Apps Script Reference) ScriptApp.getOAuthToken() (Google Apps Script Reference) Method: projects.versions.create (Apps Script API v1 Reference) Method: projects.deployments.create (Apps Script API v1 Reference) Method: projects.deployments.update (Apps Script API v1 Reference) Properties Service (Google Apps Script Guides) Class PropertiesService (Google Apps Script Reference) Manifests (Google Apps Script Guides) Manifest structure (Google Apps Script Reference)

【讨论】:

如果有人需要了解这些功能,请告诉我:)

以上是关于如何将 Google Application Script 项目迁移到不同的帐户?的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 项目中设置 GOOGLE_APPLICATION_CREDENTIALS 以使用 Google API

Firestore:如何设置 GOOGLE_APPLICATION_CREDENTIALS?

如何传递 GOOGLE_APPLICATION_CREDENTIALS 中的内容而不是文件路径?

在测试中运行应用程序时如何解决 GOOGLE_APPLICATION_CREDENTIALS,Spring Boot?

如何在Google搜索“application / ld + json”脚本中指定产品ID,而不会显示“缺少产品ID(可选)”

如何部署 GOOGLE_APPLICATION_CREDENTIALS 并使用 Elastic Beanstalk 从 Django 应用程序部署中使用