How to render a Google Docs file in LaTeX with GitHub Actions directly from GUI of that file
Introduction
During my job search, I had to update the resume that I used to keep as a simple Google Docs file, which perfectly satisfied almost all my needs until that point… it couldn’t fit any more text while being nice-looking. Before getting rid of irrelevant experience points for particular companies or positions, I was advised to try out one solution which renders your CV as one page, you just need to choose a layout of your liking and fill in certain fields. And the advised solution worked!
However, Google Docs has some features like spellchecking, easiness of proofreading and editing for trusted 3rd parties, so I still couldn’t afford to get rid of it, which made the pipeline of updating my CV look like this:
- Change it in Google Docs
- Wait until the independent 3rd party (btw, thank you guys) makes sure that it’s okay or suggests what should be changed
- When it’s okay, then Copy and Paste the changed part to the proposed solution
- Render and Download!
For me, it became annoying after going through all these steps a couple of times. Besides, this tool has some minor lack of flexibility for my “writings” and “skills” sections. So, I migrated it to LaTeX on overleaf platform.
After making a nice-looking LaTeX layout for myself based on Awesome-CV I realized that it would be great in general if Google Docs could have this custom button “render” which would send all the content to something like Overleaf platform and then download a rendered PDF file to my Google Drive and optionally to my computer.
Well, this post is dedicated to the detailed instruction that I carefully documented for people who want to have something like that, where I completely replaced the Overleaf platform with a free GitHub Actions tool.
Nevertheless, the solution that I have mentioned earlier was creddle.io, which is a really great tool, and if you do not experience any problems with English and are confident enough to share your CV without having it proofread, there is no need to read further unless you want to know how to connect Google Docs/Sheets and GitHub Actions.
Before diving in, let me list the stack of technologies that were used to solve the problem:
- Google Cloud Platform
- Google Docs
- Google Drive
- Google App script
- HTML/CSS
- Google API
- Python
- XeLaTeX
- Docker
- GitHub Actions
Instruction
Step 1: Use my repository as a template
Follow here my repository and push “Use this template”, then “Create a new repository”. You can give a fancy name to a new repository (for example, “CV”). The next step is creating a personal access token.
Step 2: Create a personal access token
To create a personal access token, you should follow the GitHub instruction. You need to choose the “workflow” checkbox (automatically it will check all necessary options). Then you should select the Expiration duration of your liking and create it. Please don’t lose that token. We will need it later.
Step 3: Create a Google Docs File from a template
You can find the Template. Then choose “File” and “Make a copy”.
Step 4: Create a service account on the Google Cloud Platform
To create a service account on the Google Cloud Platform, you should follow the Google instruction. It has 4 steps, I can add from my side that you should do it in the following way:
- Follow the 1st step as is
- In the 2nd step of the instruction, you should enable only the following APIs: Google Docs API and Google Drive API
- You can completely skip the 3rd step!
- Follow the 4th step also as is
So in the result, you should have a service account Email something like “cv-service-account@cv-test-123456.iam.gserviceaccount.com”. Share your Google Docs file created in the previous step with your service account Email. Please also don’t lose the credentials JSON file!
Optionally, you can keep the service email as “Viewer”.
Step 5: Add secrets
To enable the correct work of your GitHub Action workflow, we need to create repository secrets!
Copy the entire content of your credentials JSON file from the previous step as is and insert it into the “Secret” field. Please don’t forget to name it “CREDENTIALS”!
Then add the “DOCUMENT_ID” secret with the content, like this:
{
"id": "1xX111JjJJtJdp2Toy44unJ38SVw4DBLntXD7xHquNjA"
}
You can get the id of your document out of its URL. For example, if the URL of the document you want to render looks like this:
https://docs.google.com/document/d/1xX111JjJJtJdp2Toy44unJ38SVw4DBLntXD7xHquNjA/edit
Then you should take 1xX111JjJJtJdp2Toy44unJ38SVw4DBLntXD7xHquNjA and put as value as in the example above.
Optionally, you can add your phone number as the “MOBILE” secret with the following example content:
\mobile{+01 234 5678 9876}
Please don’t forget to replace the dummy phone number with yours.
Step 6: Update your personal data in cv.tex file
I believe the name and contact details are the type of data that does not change often, so you should change those in the “cv.tex” file, for which the file path is the following:
<your-repository-name>/my-awesome-cv/cv/cv.tex
(or if you named repository “CV”, then it has the name CV/my-awesome-cv/cv/cv.tex
) lines 57–79.
And you can do it right in GitHub!
As the changes are pushed into the main branch the workflow will be automatically launched (you can check the details in the “Actions” tab of your repository) and besides being green it should render your CV! You will find it as an “Artifact” under the zipped_cv
name.
Since I was using an empty template, my “cv.pdf” file looks like this.
Step 7: Create a Simple Apps Script
Let’s create a simple Apps Script in the Google Docs document created in Step 3. You should click “Extensions”, then “Apps Script”:
Then insert the following code in the code space and save it:
function onOpen() {
const ui = DocumentApp.getUi();
ui.createMenu('Custom')
.addItem('Show FooBar', 'foobar')
.addToUi();
}
function foobar() {
const ui = DocumentApp.getUi();
ui.alert('FooBar');
}
Optionally, you can rename the project to “CV” and should look like in the picture below.
Now, let’s get back to the document tab in your browser and reload the document page.
The rightmost tab now should become “Custom” as in the picture below. If you choose “Custom” and “Show FooBar” the script that we created earlier should be launched.
You might see something like that:
If you have something similar, then congrats! We are almost there!
Step 8: Create a Google Drive Folder
Before triggering GitHub Actions from Apps Script, you should create or choose a folder where you would like to keep your rendered PDF files. You will need a folder id in the next step.
For example, the opened Google Drive folder in my browser has the URL:
https://drive.google.com/drive/folders/1gt00mVv3_YNw0XjKdu0oKv964c6Un-uy
which has id 1gt00mVv3_YNw0XjKdu0oKv964c6Un-uy (similar to step 5). This id will be needed in the next step, so please don’t lose it!
Step 9: Copy and Paste code.gs file
Go back to your freshly created repository (or my repository) and find the content of code.gs
file in the Apps_Script
folder and copy-paste the content into your Apps Script coding space. But remember that it’s important to reference your repository to keep it isolated from my repository.
After copy-pasting, please draw your attention on the beginning of the “code.gs”, there are 6 important variables:
GH_REPO_URL = "https://api.github.com/repos/<your-nickname>/<your-CV-repo>";
GH_TOKEN = "ghp_aASFOaKQ4xI65zEZu00xdLZeAn2D691QB8s9";
GD_FOLDER = "1JoGzZTUSbjzX5fRHgSYshGRzUOItPpnb";
GD_FILENAME = "ILIA_OZHMEGOV_CV.pdf"; // don't forget to rename
GD_DOC_ID = DocumentApp.getActiveDocument().getId();
GH_PURGE = true;
In GH_REPO_URL
you should replace <your-nickname>
with your GitHub nickname, as well as <your-CV-repo>
unless you named also “CV”. GD_FOLDER
is an id from the previous step. Further, you may want to download the resulting PDF file GD_FILENAME
with a different name than “ILIA_OZHMEGOV_CV.pdf” so also don’t hesitate to rename it!
GH_PURGE
is responsible here for removing the resulting GitHub Actions artifact named zipped_cv
. If its value is “true” it will remove the artifact, making it inaccessible to 3rd parties.
Optionally, you can hide your GitHub personal token. You can do that by going to “Project Settings” (Gear Icon) then “Add script property” (at the bottom of the page). Name property “github_token” with the value of your token from the 1st step. Like in the picture below:
Then, you can access the token with following line:
GH_TOKEN = PropertiesService.getScriptProperties().getProperty("github_token");
Step 10: Launch the final script!
Go back to your Google Docs file and reload it, now under “Custom” you should see “Trigger Rendering”. Smash it! It will require “Authorization” just follow the navigation with “Continue”. There you should click “Advance” in the left bottom part of the message and then “Go to Untitled project (unsafe)”. In the picture below instead it says — “Go to ILIA_OZHMEGOV_CV (unsafe)” instead, please don’t worry I took this picture too early to replace now, but it still does the job. Then allow the required access (relax, you give permission to yourself instead of some shady 3rd party). After, “Trigger Rendering” button actually starts to render your CV! You can even go to the “Actions” tab and watch the details of that building.
After you should see the button “Download” in green rectangular, besides, the same file appears in the Google Drive folder you referenced in the 7th step.
Congratulations now you have a relatively “simple” (hehe) way of rendering your Google Docs file in XeLaTeX for free! GitHub Actions provide every user 2000 minutes per month for free and since rendering takes approximately a minute, you can render about 2000 CVs per month or one CV 2000 times.
Recycling
Now, let us share an interesting feature. Since many companies and positions might expect you to highlight different points of your career to help a recruiter to understand that you are the perfect fit and that the company urgently needs you, we can safely say that the very same pipeline can be applied to all versions of your CV, based on the one we already created.
You would need to change/update only these things:
- Do “File” and then “make a copy” and check the “share with the same people” option (it is essential because of the service account)
- If our “Custom” tab doesn’t appear, go “Extensions” and “Apps Script” as soon as you see the familiar code, that tab also shows up where it used to be, then go back to the copy of your document and change it (for example according to the job requirement)
- Click already familiar “Custom” and “Trigger Rendering” and complete “Authorisation” in the same way as in the 10th Step
- if you followed the option of hiding the GitHub token in step 9 then again, add a GitHub token in the project properties
Additionally, you can keep different versions of your CV for different companies in the same folder (GD_FOLDER) or in other (sub)folder, it’s up to you!
Google Docs Layout
The Google Docs template has four sections: “EXPERIENCE”, “EDUCATION”, “WRITINGS”, “SKILLS”. You could already notice that the internal section structure repeats. For all section names except the section named “SKILLS” it doesn’t matter how you name your section, it will just generate a PDF file purely relying on the table right after the section name. Unfortunately, the “SKILLS” section should be highlighted by containing the word “skill” in its section name (case-insensitive) since it has a relatively unique structure. So, it means there is still some freedom, and you can name it “skill”, “SKILL”, “skillS”, “sKILLs” etc.
That is because every section table except the “SKILLS” table consists of main units (see the table below “Main unit”). In which the first row usually contains the first cell company name or University or Journal, while the second cell has the location (Berlin or New York). In the second row, the first cell contains some general information like Position/Role (Software Engineer) or Degree (B.S. in Computer Science), and the second cell contains only the range of dates, for example, “Jan 2020 — Mar 2022”. The optional third row is completely dedicated to the description, for example, experience bullet points or some short description (see Note II below).
So in my template, the section named “WRITING” has two main units. “EDUCATION” section has also 2 main units with short descriptions each, while “EXPERIENCE” section consists of 2 main units with full bullet point lists.
Note I: it does parse the links and preserves them by placing them into a PDF file in the same way as it was provided in the Google Docs document. Just compare the template and the resulting PDF file.
Note II: in the case of a short description, it keeps the description and information from the first cell of the second row (Position/Role/Degree) of the main unit completely in the second row, which saves up some space. Only under the condition that the amount of explicit symbols is less than or equal to 138, which is dictated by my LaTeX layout.
Some Backend details
If you have any interest in some general explanation of the backend part. Then let us redirect you to the [repository](https://github.com/IliaOzhmegov/CV). Currently, there are 4 main parts:
- download-and-convert-gdocs — in general it downloads, parses, and converts the Google Docs document to TeX format, and this part consists of another three classes:
- getter.py unit downloads JSON representation of the Google Docs document
- parser.py unit parsers JSON file into a python dictionary (from JSON to JSON, hehe)
- converter.py takes the parsed python dictionary and converts it into the “section.tex” file, saving it in the corresponding folder “my-awesome-cv/cv/sections.tex” - my-awesome-cv originates from legendary Awesome-CV LaTeX template with a few minor amendments. It does exactly what the original template does, except using some more lightweight docker image
- Apps_Script contains the Apps Script code.gs and HTML files that are fetched directly from the repository. The “code.gs” script triggers GitHub Actions, downloads the resulting zipped file (also known as Artifact in GitHub Platform), and then saves it to the Google Drive folder of your choice
- .github/workflows can be triggered by external API call as well as by “push” into the main branch. After being triggered, it runs the 1st part (download-and-convert-gdocs) and the 2nd part (my-awesome-cv) of the current list, generating the resulting zip file that was mentioned in the previous point
Why GitHub artifact? My attempts to upload the rendered PDF file directly to a shared Google Drive Folder right from GitHub Actions workflow failed since the Google API just doesn’t allow such functionality for service accounts without using OAuth2 authentication (although I found a few bits of information that it’s possible if folder belongs a corporate account on Stack Overflow platform).
Conclusion
You could already notice that it’s an overcomplicated approach of rendering your CV since every small amendment might make you wait additional 60 seconds of rendering before applying for a job, so we might conclude that the current post might not bear much value and be created based on the pure desire to give some meaningful functionality to a custom tab in the Google Docs as well as to entertain and prevent the reader from the shown path.
Although it’s an overcomplicated waste of time, we still believe that some points from this post still might find their use in helping people to realize that there is a simpler and better way. For example, reducing the number of bullet points so that your CV still looks nice in the original Google Docs Document.