Skip to content

Instantly share code, notes, and snippets.

@battis
Last active February 7, 2024 15:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save battis/6e32031196316acd1b5e5700b328aef6 to your computer and use it in GitHub Desktop.
Save battis/6e32031196316acd1b5e5700b328aef6 to your computer and use it in GitHub Desktop.
Publish a Google Workspace add-on written in TypeScript using @google/clasp

Publish a Google Workspace Add-on

Based on Google documentation

I have attached my draft version of a setup script for these projects (including my frequent need to connect to the Blackbaud SKY API), but not being able to script a number of key publishing steps makes it not really worthwhile. This assumes that you already have node installed on your computer to build the project.

In the browser

  1. Create a new Google Cloud project in the console.
  2. Under APIs & Services, configure OAuth Consent. I usually publish with Internal users (no need for Google review). Add the OAuth Scopes listed in the projects appscripts.json file to the OAuth Consent.
  3. Enable the Google Workspace Markplace SDK API.
  4. Enable any other specific APIs required by the add-on (e.g. the Google Drive API).
  5. Enable billing on the Google Cloud account for this project (not always strictly necessary, but usually a good idea).
  6. On the project dashboard, make a note of the project number.

In the terminal

  1. Clone the project's GitHub repo.
git clone git@github.com:groton-school/<project name>.git
  1. Install dependencies for the project
cd <project-name>
npm install
  1. Create a Google Apps Script standalone project. (It creates a new, default appscript.json that we don't want.)
npx clasp create --title "<project name>" --type standalone
git checkout appscript.json
  1. Build the project
npm run build
  1. Push the built code to the project (overwriting the default appscript.json file).
npx clasp push --force

In the browser

  1. Open the new project (in your MyDrive, named <project name> -- it can be safely moved anywhere, including Shared Drives, since the connection will be by file ID).
  2. In Settings, change the GCP project from the default to the previously noted project number.
  3. From the Deploy menu, create a New Deployment.
  4. Make a note of the deployment ID and version number.
  5. In the Google Cloud console for the project, go APIs & Services > Enabled APIs & Services > Google Workspace Marketplace SDK > App Configuration
  6. Configure your app to be Private, Individual and Admin install, a. Under App Integration, choose the appropriate option and enter the previously noted deployment ID (and version number, if required). b. Enter the OAuth Scopes listed in appscript.json
  7. Configure the Store Listing. I usually store the various media files in the marketplace directory of the project.
  8. Copy the App URL for distribution.
export default {
name: {
description: 'Google Cloud project name',
},
projectId: {
description: 'Google Cloud project ID'
},
billing: {
description: 'Google Cloud billing account ID for this project'
},
supportEmail: {
description: 'Support email address for app OAuth consent screen'
},
accessKey: {
description: 'Blackbaud SKY API subscription access key',
url: 'https://developer.blackbaud.com/subscriptions'
},
clientId: {
description: 'Blackbaud SKY API app OAuth client ID',
url: 'https://developer.blackbaud.com/apps'
},
clientSecret: {
description: 'Blackbaud SKY API app OAuth client secret'
}
};
{
"scripts": {
"build": "webpack",
"deploy": "run-s deploy:*",
"deploy:build": "npm run build",
"deploy:push": "clasp push --force"
},
"dependencies": {
"@battis/gas-lighter": "^0.2.1"
},
"devDependencies": {
"@battis/partly-gcloudy": "^0.3.0",
"@battis/qui-cli": "^0.3.6",
"npm-run-all": "^4.1.5",
"open": "^9.1.0",
}
}
import gcloud from '@battis/partly-gcloudy';
import cli from '@battis/qui-cli';
import fs from 'fs';
import open from 'open';
import options from './options.mjs';
(async () => {
const args = gcloud.init({ args: { options } });
const project = await gcloud.projects.create({
name: args.name,
projectId: args.projectId
});
gcloud.projects.active.set(project.projectId, project);
await gcloud.services.enable({ service: 'drive.googleapis.com' });
await gcloud.services.enable({
service: 'appsmarket-component.googleapis.com'
});
const appscript = JSON.parse(fs.readFileSync('appscript.json').toString());
cli.shell.echo(
`Configure the OAuth consent for ${cli.colors.value(
project.name
)} using the following OAuth scopes:`
);
cli.shell.echo(appscript.oauthScopes.join('\n'));
await open(
`https://console.cloud.google.com/apis/credentials/consent?project=${project.projectId}`
);
await cli.prompts.confirm({ message: 'OAuth consent ocnfigured' });
cli.shell.mv('appscript.json', 'appscript.bak.json');
cli.shell.exec(
`npx clasp create --title "${project.name}" --type standalone`
);
cli.shell.rm('appscript.json');
cli.shell.mv('appscript.bak.json', 'appscript.json');
const clasp = JSON.parse(fs.readFileSync('.clasp.json').toString());
await open(
`https://script.google.com/home/projects/${clasp.scriptId}/settings`
);
cli.prompts.confirm(
`Configure the apps script project to use GCP project ${cli.colors.value(
project.projectNumber
)}`
);
await open('https://developer.blackbaud.com/subscriptions/');
await cli.prompts.confirm({ message: 'Copy your Blackbaud SKY access key' });
await cli.prompts.confirm({
message: `Create a secret in the Google Apps Script project in named ${cli.colors.value(
'SKY_ACCESS_KEY'
)} that holds the value of your Blackbaud SKY access key`
});
await open('https://developer.blackbaud.com/apps/');
await cli.prompts.confirm({
message: 'Create a new Blackbaud SKY application'
});
await cli.prompts.confirm({
message: `Add ${cli.colors.value(
`https://script.google.com/macros/d/${clasp.scriptId}/usercallback`
)} as the redirect URL for the app`
});
await cli.prompts.confirm({ message: "Copy the app's OAuth Client ID" });
await cli.prompts.confirm({
message: `Create a secret in the Google Apps Script project in named ${cli.colors.value(
'SKY_CLIENT_ID'
)} that holds the value of the app's OAuth client ID`
});
await cli.prompts.confirm({ message: "Copy the app's OAuth client secret" });
await cli.prompts.confirm({
message: `Create a secret in the Google Apps Script project in named ${cli.colors.value(
'SKY_CLIENT_SECRET'
)} that holds the value of the app's OAuth client secret`
});
await cli.prompts.confirm({
message: 'Create a new deployment in the Google Apps script project'
});
await cli.prompts.confirm({ message: 'Copy the deployment ID' });
await open(`https://console.cloud.google.com/apis/api/appsmarket-component.googleapis.com/googleapps_sdk?project=${project.projectId}`);
await cli.prompts.confirm({ message: 'Create a private app as a Sheets Add-on');
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment