Compiling a “Hello World” Project locally written on Google Apps Script with Clasp
Rather a long tittle, for a long complex endeavour that gets tangled Programming Languages, Cloud Services, Text Editors and Command Line Applications.
The objective is simple, Coding locally for Google Apps Script (from now on GAS) executing that code in the terminal.
If you are reading this you should be looking at developing something in GAS, basically a programming environment based in Javascript which can be runned with a Google Account and that is able to call and automate processes on services such as: - Google Sheets - Google Docs - Google Calendar - Google Mail (Gmail) - Google Forms etc..
It has it’s own Object Model , with tons of Objects and methods in order to perform actions on this services such as writting in a certain cell of a spreadsheet upon filling in a web form while also creating a new appointment on the User Calendar and sending and email to a client.
Nobody wants to marry with a payment service, after all Google is a private company and everything you will run on their servers is subject to be charged if surpassed cetain quotas. But been able to do all these actions in such a unified way, in a platform that you can access from almost every browser in this World, securely, worth getting in the Enterprise, plus “Who knows?” maybe you end up developing something that you can monetize $$$.
The Official toolset
To start developing on this you just need to get into script.google.com , while reading the documentation. If you click on New Project you will start a script, and their IDE will help you with features such as Autocomplete and so on…
If you like this, then go for it, but the next chapters will be explaining a comprehensive way of efficiently using your keystrokes while holding your focus in what you need to do.
Our toolset
We will be using Linux , with vim
, and clasp
which is a tool that allow us to have a little vesion control capabilities, while pushing the projects to your Google Account and run them on your terminal (we won’t say locally as remember you are using a Cloud Service Provider, we wont be downloading the whole Google Engine to create our own instance).
-----------------------------------------------
| ------------ _ |
----------- ------------------ ||Google Script __ _ ___ ___ __ _| | ___ |
| Code |->|Version Manager |-> ||script. | / _` |/ _ \ / _ \ / _` | |/ _ \ |
| | |clasp | || google.com| | (_| | (_) | (_) | (_| | | __/ |
| ./Code.gs | |./appscript,json | ||---------- \__, |\___/ \___/ \__, |_|\___| |
| (...) | |./.claspignore | | |___/ |___/ |
| | |~/.clasprc.json | | |
| | |./clasp.json | | |
| | | | | |
| | | | | ---------------- |
---------- ------------------ | --------------------- |Google Drive | |
| |Google Cloud Platform| |drive.google.com| |
| |GCP | ---------------- |
| |console.google.com | ---------------- |
| -------------------- |Google Calendar | |
| |calendar.google.com
| ---------------- |
| ---------------- |
| |more services...| |
| ---------------- |
-----------------------------------------------
Process overview
1. Setting up vim.
2. Preparing our cheatsheet.
3. Installing Clasp
4. Creating our first project.
5. Running functions from Clasp.
6. Seeing the execution log from the terminal.
If you don’t use vim
you can skip 1 and 2.
Step 1. Setting up vim.
GAS
uses .gs
file extension, but the syntax used is a subset of javascript
, in order to make vim
recognise its syntax, append the following snippet on your vim ~/.vimrc
"read .gs as .js
augroup MyAutoCmds
autocmd!
autocmd BufRead,BufNewFile *.gs set filetype=javascript
augroup END
Step 2. Preparing our cheatsheet.
If you want to be able to handle such a amounts of Objects, properties and methods, you rather use the terminal to traverse through the documentation, for that I would highly recommend installing w3m
to render the documentation in plaintext , and also the following snippet will make things easier for our cheatsheet.
sudo apt install w3m
vim ~/.vimrc
"Open a whole-line link in a new tab by pressing <F6>
function! Scrape()
let url = getline('.')
tabnew +set\ buftype=nofile Scraped
call append(0, systemlist('w3m -dump ' . url))
normal! gg
endfunction
nnoremap <f6> :call Scrape()<CR>
Our documentation can now look something like
vim gs-cheatsheet.gs
https://developers.google.com/apps-script/reference/drive
https://developers.google.com/apps-script/reference/drive/drive-app
https://developers.google.com/apps-script/reference/drive/file
https://developers.google.com/apps-script/reference/drive/file-iterator
https://developers.google.com/apps-script/reference/drive/folder
https://developers.google.com/apps-script/reference/drive/folder-iterator
https://developers.google.com/apps-script/reference/drive/user
Now by locating your cursor on any of the lines that have a link , just press <F6>
and a new tab will be loaded with all the text from that determined page.
This is in my opinion a fairly nice technique, as not expecting the object model to change drasticaly, minor changes will be updated on the pages that by doing <F6>
will be re-rendered.
How I personaly like it is to Copy and paste all of this objects in the same document , then I will append a line such as -------(X)
on the signature of the objects that I have already used and understood ending up with something like.
SpreadsheetApp.getActiveRange() Range sheet, or null if -------(X)
there is no active
range.
Returns the list of
active ranges in the
SpreadsheetApp.getActiveRangeList() RangeList active sheet or null
if there are no
ranges selected.
Gets the active
SpreadsheetApp.getActiveSheet() Sheet sheet in a -------(X)
This way we can scroll down the document and recall easily the Object Model. The issue is that we end up with a huge document of more than 100.000 lines, that will take us scrolling down forever to get a big picture of what we know, We could do something like
grep "(X)" gs-cheatsheet.gs
But we will have to update this along with the changes we introduce so the best way is to use tmux open a new-window
then.
watch 'grep "(X)" gs-cheatsheet.gs'
The problem with this is that once highlight more lines than the number you have available on the height of your screen, then you will be missing the lastones, you could tail this command, but you will be missing the firstones.
For that and with the help of uwharrie
from irc.libera.com
we have developed the following script.
$ vim ~/bin/cat-alternate.sh
#! /usr/bin/bash
# created by uwharrie from #linux irc.libera
# this script displays a refreshed multipage output of grep
# so you can see the output without having to enter user input
# example of use:
# cat-alternate.sh '(X)' /home/fakuve/baul-documents/gs-learning.js
# it takes two arguments
# - first argument is the string to grep
# - second argument is the path of the input file
pat="$1"
file="$2"
while true;do
res="$(grep "$pat" "$file")"
res_lines="$(echo "$res"|wc -l)"
tty_lines="$(($(stty size|cut -d' ' -f1)-2))"
iters="$((res_lines/tty_lines+1))"
for n in $(seq "$iters");do
beg="$(((n-1)*tty_lines+1))"
end="$((beg+tty_lines))"
clear
echo "$res"|sed -n "$beg,${end}p"
sleep 5
done
done
Now you can do cat-alternate.sh gs-cheatsheet.gs
and will have as many pages alternating on your terminal without you having to do any keystroke.
If you want to stop on a page use tmux
copy-mode
to freeze the STDOUT
of your terminal (default keybinding Ctrl + b and [
)
Step 3. Installing Clasp
clasp
is a CLI to interface with script.google.com
.
It will let you create
a project, push
it , pull
, clone
and run
functions from the terminal.
clasp
is an npm
dependency so install it globally
sudo npm install -g @google/clasp
You can add the following lines to your cheatsheet gs-cheatsheet.gs
you will probably need to perform some actions that may be documented on their repo.
https://github.com/google/clasp
https://github.com/google/clasp/blob/master/docs/run.md
Also you can check the help page
clasp help
clasp <command> help
Step 4. Creating our first project.
You just need to log in your CLI to your Google Account with
sudo clasp login
It will open up an instance of your browser so you can authenticate to it. In my case as I code from my VPS which has no X-server running I use the following command and paste the link in my localHost Browser
sudo clasp login --no-localhost
Then we want to create a project
mkdir -p gas-scripts/myproject001
cd gas-scripts/myproject001
clasp create --title "project001"
## Select Standalone
Now you can see new files have been generated locally with information for clasp
to do its thing.
├── appsscript.json
├── .clasp.json
But also on the server side Google
a script file has been generated (you can see that in drive.google.com
) , which is also a project in script.google.com
You can list all your projects from the CLI with
clasp list
Ok so lets write some Code. Create a Code.gs
file and put there a function to be runned. Note that everything has to be encapsulated by a function otherwise it wont be runned. Also you dont need to call the function within the script as their compiler will call a function directly.
$ vim ./Code.gs
function main () {
Logger.log("Hello World");
}
Note that GAS
I said is based in Javascript
but is not exactly it, so you need to see how to do certain things the GAS
way , in this case Logger.log("String");
is the equivalent to console.log("String");
.
Now you just need to push this changes
clasp push
Now you can go to the browser and put on the link of your script, something like ` https://script.google.com/d/longAlphaNumStringAkaScriptId/edit`
Press Run
in the tool bar. It will show you the Execution Log
popping up , Congratulatione amici !!.
But hold on, this is quite not the 100% terminal based experience you sold us before. Is it? Ok we will learn how to run functions from clasp
without having to use the browser.
Step 5. Running functions from Clasp.
This is quite a tricky step that can consume an unecessary amount of time of your life for just a little tweak. The process is a bit tricky and involves setting up a project on Google Cloud Platform
GCP console.cloud.googe.com
. The step by step tutorial is writen in the following link1.
This is the abstract of what I have done, as I got stuck on some parts.
## Create a GCP Project
https://console.cloud.google.com/cloud-resource-manager
>> Create Project >> Project name "gas-viewer"
Location "No organisation"
>> Create
>> "gas-viewer" hamburger menu >> Settings >>
(Write down Project ID "gas-viewer"
Project number "422101l28129"
Now go back to your terminal
$ cd ~/gas-scripts/project001
$ clasp setting projectId gas-viewer
Now in your browser
https://console.developers.google.com/apis/credentials/consent?project=gas-viewer
>> Applicaton name "clasp project"
>> Save
Now go to your script.google.com
project ` https://script.google.com/d/longAlphaNumStringAkaScriptId/edit on the top right corner
Use Classic Editor`
Resources >> Cloud Platform project >>
Enter Project Number Here ""
>> Set Project "422101l28129"
Now again in console.cloud.google.com
Go to your settings project (“gas-viewer”) and You need to set up the App Publishing Status
to Testing
(it should come like this) . And add User
on test Users
and input your email@gmail.com
Then authenticate
$ clasp open --creds
>> Create Credentials > OAuth cient ID
>> Application type : Desktop App
Create > OK
Download the .json file and place it somewhere as for instance ~/creds.json
clasp login --creds ~/creds.json
You need to enable Apps Script API for that project such as in this page shows.
Follow this link clicking >> Next >> Next
Also you need to modify your project vim ~/gas.scripts/projet001/appscript.json
Enter the following object in the JSON body
"executionApi": {
"access": "ANYONE"
}
And finally!! we can run our script. But note that we can get the output return of a certain function runned on the terminal. For that you need to re-write your vim ./Code.gs
to.
function main () {
return "Hello World";
}
And now you can do
clasp push
clasp run main
Sweating tears and blood we have tamed Google’s beast.
Step 6. Seeing the execution log from the terminal.
clasp
allows us also to check the excution logs from the commandline, this are been refreshed every 6 seconds. So we can use the following command to perform debugging of our Scripts from the terminal.
Note that this takes 30 seconds to execute, and also instead of Logger.log()
use console.log()
method.
clasp push ; clasp run main ; sleep 4; clasp logs
Setting up Clasp to run a function diagram
Just to recap I have made a diagram setup clasp to run a function gist
// diagram-setup-clasp-to-run-functions.txt
// by freddieventura
// https://github.com/freddieventura
Follow from Step0 to Step 9 the path to be able to execute
clasp run functionName
--------------------------------------------------
| ____ _ |
| / ___| | __ _ ___ _ __ |
| | | | |/ _` / __| '_ \ |
| | |___| | (_| \__ \ |_) | |
| \____|_|\__,_|___/ .__/ |
| |_| |
| |
| ------------------------------------------ |
| |config files ||
| | ----------------------------------------||
| | |system wide |||
| | | stored in ~/ |||
| | | --------------------------------- |||
| | | |~/.clasprc.json | |||
| | | |clasp login | ||| Step0
| | | | Objects | |||
| | | | ------------------ | |||
| | | | |token | |||
| | | | |oauth2ClientSettings |||
| | | | | (....) | |||
| | | --------------------------------- |||
| | ----------------------------------------||
| | |scipt specific |||
| | | stored in ./path/to/project/ |||
| | | --------------------------------- |||
| | | |~/script/appscript.json | |||
| | | |clasp create --title "script1" | |||Step1
| | | | Objects | |||
| | | | ------------------ | |||
| | | | |timeZone | |||
| | | | |dependencies | |||
| | | | |exceoptionLogging | |||
| | | | |runtimeVersion | |||
| | | | |executionApi ----->
| | | | | access: ANYONE -----> Do it manually Step7
| | | | | | |||
| | | | | | |||
| | | --------------------------------- |||
| | | --------------------------------- |||
| | | |~/script/.clasp.json | |||
| | | |clasp create --title "script1" | |||
| | | | Objects | |||
| | | | ------------------ | |||
| | | | |scriptId | |||
| | | | |rootDir | |||
| | | | |projectId -> clasp setting projectId <projectId> <--\ Step6
| | | | | | ||| |
| | | --------------------------------- ||| |
| | | --------------------------------- ||| |
| | | |~/script/.clasp-localhost-cred.json ||| |
| | | |created on console.cloud.google.com <----------------------------------------------\
| | | | Objects | ||| | |
| | | | ------------------ | ||| | |
| | | | |installed | ||| | |
| | | | | client_id | ||| | |
| | | | | project_id | ||| | |
| | | | | client_secret | ||| | |
| | | | | (.....) | ||| | |
| | | --------------------------------- ||| | |
| | | --------------------------------- ||| | |
| | | |~/script/.clasprc.json | ||| | |
| | | |clasp login --creds .clasp-localhost-cred.json ------> Step9 | |
| | | | Objects | ||| | |
| | | | ------------------ | ||| | |
| | | | |token | ||| | |
| | | | | (....) | ||| | |
| | | --------------------------------- ||| | |
| | -------------------------------------- ||| | |
| ||| | |
-------------------------------------------------- | |
------------------------------------------------------------------------- | |
| ____ _ _ ____ _ _ __ | | |
| / ___| | ___ _ _ __| | | _ \| | __ _| |_ / _| ___ _ __ _ __ ___ | | |
|| | | |/ _ \| | | |/ _` | | |_) | |/ _` | __| |_ / _ \| '__| '_ ` _ \ | | |
|| |___| | (_) | |_| | (_| | | __/| | (_| | |_| _| (_) | | | | | | | | | | |
| \____|_|\___/ \__,_|\__,_| |_| |_|\__,_|\__|_| \___/|_| |_| |_| |_| | | |
| | | |
| console.cloud.google.com /----------------------------------------------/ |
| Variables: projectId , projectNumber (given), appName | |
| | |
| | |
|Step2>> New GCP Project | |
| >> Project Name "projectId" >> Location "No organisation" |
| ---------> it will give as a projectNumber
|Step3>> https://console.cloud.google.com/apis/credentials/consent?project=<projectId> |
| >> External | | |
| >> Create | | |
| | | |
| >> App name "clasp project" | | |
| >> User Support email | | |
| >> Developer Contact Email | | |
| >> Save and Continue | | |
| >> Save and Continue | | |
| >> (Test Users) >> Add Users "youremail@gmail.com" | | |
| >> Save and Continue | | |
| >> Back to dashboard | | |
|Step4>> Enable Apps Script API | | |
| >> https://console.cloud.google.com/apis/enableflow?apiid=script&project=<projectId> | |
| >> Next >> Enable | | |
| | | |
|Step8>> Create Oauth Credentials | | |
| >> https://console.cloud.google.com/apis/credentials?project=<projectId> | |
| >> Create Credentials >> OAuth client ID | | |
| >> Application type "Desktop App" | | |
| >> Name "hostNameAA" -----------------------------------> ( | |
| >> Create | | |
| >> Download JSON ~/script/.clasp-localhost-cred.json --------/ |
| | |
| | |
------------------------------------------------------------------------- |
|
|
-------------------------------------------------- |
| | |
| ___ ____ _____ | |
| |_ _| _ \| ____| | |
| | || | | | _| | |
| | || |_| | |___ | |
| |___|____/|_____| | |
| | |
| script.google.com | |
| | |
|Step5 >> Use classic Editor | |
| > Resources > Cloud Platform Projects | |
| > Enter Project Number | <--------------------------------------------------/
| > Set project |
| |
--------------------------------------------------