Build a LaTeX document by opening an issue (or pull request)
Tags: actionshackathon21, github, latex
Published: 2021-12-07 00:47:28

I heard about [Tectonic](https://tectonic-typesetting.github.io/en-US/) for the first time during the [PackagingCon 2021](https://packaging-con.org/)  and immediately I knew that's the kind of stuff I want to deploy on GitHub Actions.

LaTeX is a high-quality typesetting system, and it's the de facto standard for the communication and publication of scientific documents. Installing a full LaTeX system takes up a considerable amount time and space on your hard drive.

Also, the reproducibility of these documents is not trivial. That's where Tectonic comes in:

> _Tectonic is a modernized, complete, self-contained TeX/LaTeX engine, powered by XeTeX and TeXLive. (...) Tectonic **automatically downloads support files** so you don’t have to install a full LaTeX system in order to start using it. If you start using a new LaTeX package, Tectonic just pulls down the files it needs and continues processing. The underyling “bundle” technology allows for **completely reproducible document compiles**._

But... Why not just use Overleaf? Personally, I think the Git version control system fits perfectly when it comes to collaborative writing.

## The workflow

This time, I wrote two different workflows: one to build from **pull requests** and other to build from **issues**.

I'm going to focus on the second one, because the other is very similar and easier to follow.

First, I created an [issue template](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository) to submit LaTeX code:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/89l19gn41puxdyhw289w.png)

Then, the workflow runs as you can see in the trigger section. Notice the job requires the `[Build]` label on the issue title.

```yaml
name: issues

on:
  issues:
      types: [opened, edited]

env:
  REPO_URL: https://github.com/${{ github.repository_owner }}/${{ github.event.repository.name }}

jobs:
  build:
    if: startsWith(github.event.issue.title, '[Build]')
    runs-on: ubuntu-latest
```

The latest version of Tectonic is installed. `dos2unix` is required because the text from issue the body uses [CRLF](https://www.datafix.com.au/BASHing/2019-03-17.html).

```yaml    
    steps:
      - uses: actions/checkout@v2

      - name: Install dependencies
        run: |
          sudo apt-get install dos2unix

          LAST_TAG=$(curl -s https://api.github.com/repos/tectonic-typesetting/tectonic/releases/latest | jq -r .tag_name)
          VERSION="${LAST_TAG##tectonic@}"

          wget -q https://github.com/tectonic-typesetting/tectonic/releases/latest/download/tectonic-$VERSION-x86_64-unknown-linux-gnu.tar.gz
          tar -zxf tectonic-$VERSION-x86_64-unknown-linux-gnu.tar.gz -C /usr/local/bin

```

The code from the body is extracted with a `sed` command and built with Tectonic. The result is uploaded as an artifact.

```yaml
      - name: Extract code
        run: |
          echo $ISSUE_BODY | dos2unix -c mac > issue_body.txt
          sed -n '/%%build/, /%%dliub/{ /%%build/! { /%%dliub/! p } }' issue_body.txt > build/main.tex
        env:
          ISSUE_BODY: ${{ github.event.issue.body }}

      - name: Build PDF
        run: tectonic -X compile build/main.tex

      - uses: actions/upload-artifact@v2
        with:
          name: main
          path: build/main.pdf
```

Finally, the **github-actions** bot post the result of the workflow run as a comment in the corresponding issue.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tswl7dmpavnuj2vlj3dj.png)

Successive runs will update this comment, not creating new ones.

```yaml
      - name: Find comment
        uses: peter-evans/find-comment@v1
        id: fc
        with:
          issue-number: ${{ github.event.issue.number }}
          comment-author: 'github-actions[bot]'
        if: always()

      - name: Post comment (success)
        uses: peter-evans/create-or-update-comment@v1
        with:
          issue-number: ${{ github.event.issue.number }}
          comment-id: ${{ steps.fc.outputs.comment-id }}
          edit-mode: replace
          body: |
            Hi, @${{ github.event.sender.login }}! :wave: <br>
            The workflow run has **succeeded**. [**Click here**](${{ env.LOG_URL }}) to download your results.
        env:
          LOG_URL: ${{ env.REPO_URL }}/actions/runs/${{ github.run_id }}?check_suite_focus=true

      - name: Post comment (failure)
        uses: peter-evans/create-or-update-comment@v1
        with:
          issue-number: ${{ github.event.issue.number }}
          comment-id: ${{ steps.fc.outputs.comment-id }}
          edit-mode: replace
          body: |
            Sorry, @${{ github.event.sender.login }}! :disappointed: <br>
            The workflow run has **failed**. [**Click here**](${{ env.LOG_URL }}) to see the build log.
        env:
          LOG_URL: ${{ env.REPO_URL }}/actions/runs/${{ github.run_id }}?check_suite_focus=true
        if: failure()
```

## Future improvements

Currently, you need to download them and extract the ZIP file in order to see your results. This happens because workflows does not have access to the artifact ID while the job is running, and GitHub does not allow uploading unzipped archives.

An alternative is pushing the PDF to a specific branch or folder, but populating the repository with binary files is not a good idea.

## Get the code

{% github epassaro/texbuilder %}