From c43426a4a4b14490514cbe42745ef4ed93bfdead Mon Sep 17 00:00:00 2001 From: vincent guyader Date: Wed, 26 Nov 2025 21:54:04 +0100 Subject: [PATCH 1/3] Fix punctuation typos across chapters --- 02-planning-ahead.Rmd | 8 ++++---- 10-step-by-step-build.Rmd | 2 +- 17-javascript.Rmd | 7 +++---- 19-appendix.Rmd | 2 +- chapter-abstracts.Rmd | 30 +++++++++++++++--------------- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/02-planning-ahead.Rmd b/02-planning-ahead.Rmd index ec979e17..c55eeafd 100644 --- a/02-planning-ahead.Rmd +++ b/02-planning-ahead.Rmd @@ -13,7 +13,7 @@ Whatever your ambitions for your `{shiny}` application, you should take time tod A common thing you will hear about `{shiny}` is that it is a good prototyping tool. This cannot be denied. Building a Proof of Concept (PoC) for an app is relatively straightforward if you compare to what is needed to build applications in other languages. -With `{shiny}`, you can build an "it works on my machine" web application in a couple of hours, and show it to your team, your boss, your investors. +With `{shiny}`, you can build an "it works on my machine" web application in a couple of hours, and show it to your team, your boss, or your investors. Thanks to the way `{shiny}` was designed, you do not have to care about websockets, ports, HTML (HyperText Markup Language), JavaScript libraries, and all the things that are elegantly bundled into `{shiny}`. Hence, you can have a quick, hacky application that will work on your machine, and very rapidly. @@ -31,7 +31,7 @@ The larger the codebase, the harder it is to untangle everything and make it wor In this book, we will present a framework called `{golem}`, which is a toolbox for building production-grade `{shiny}` applications. Even if `{golem}` is focused on production, there is no reason not to use it for your proof of concepts: starting a new `{golem}` project is relatively straightforward, and even if you do not use the advanced features, you can use it for very small apps. The benefit of starting straight inside a `{golem}` application really outweighs the cost. -We hear a lot the question "When should I switch to `{golem}`?" The answer is simple: do not switch to `{golem}`, start with it. +We often hear the question "When should I switch to `{golem}`?" The answer is simple: do not switch to `{golem}`; start with it. That way, you are getting ready for complexity, and if, one day, you need to turn this small app into a production app, the foundations are there. ### Develop with the KISS principle @@ -85,10 +85,10 @@ The previous chapter introduced the notion of complexity in size, where the app Splitting a `{shiny}` project is made possible by following two methods: -- **Extract your core "non-reactive" functions, which we will also call the "business logic", and include them in external files**, so that you can work on these outside of the app. +- **Extract your core "non-reactive" functions, which we will also call the "business logic", and include them in external files**, so that you can work on them outside of the app. Working on independent functions to implement features will prevent you from relaunching the whole application every time you need to add something new. -- **Split your app into `{shiny}` modules**, so that your app can be though of as a tree, making it possible for every developer to concentrate on one node, and only one, instead of having to think about the global infrastructure when implementing features. +- **Split your app into `{shiny}` modules**, so that your app can be thought of as a tree, making it possible for every developer to concentrate on one node, and only one, instead of having to think about the global infrastructure when implementing features. Figure \@ref(fig:02-planning-ahead-1) is, for example, a representation of a `{shiny}` application with modules and sub-modules. You will not be able to decipher the text inside the node, but the idea is to give you a sense of how a `{shiny}` application with modules can be organized and split into smaller pieces that are all related to each other in a tree form. diff --git a/10-step-by-step-build.Rmd b/10-step-by-step-build.Rmd index ee89c5ac..26ddda96 100644 --- a/10-step-by-step-build.Rmd +++ b/10-step-by-step-build.Rmd @@ -243,7 +243,7 @@ Continuous integration, on the other hand, is ensuring the software is still wor The idea is to add to the centralized version control system (for example, Git)[^step-by-step-build-6] a service like Travis CI, GitHub Action (if you are on GitHub), or GitLab CI (for GitLab) that runs a series of commands whenever something is integrated in the repository, i.e. every time a change to the codebase is made. In other words, every time a new piece of code is sent to the central repository, a service runs regression tests that check that the software is still in a valid, working state. -[^step-by-step-build-6]: We will get back to version control in the Chapter \@ref(version-control), "Version Control".. +[^step-by-step-build-6]: We will get back to version control in the Chapter \@ref(version-control), "Version Control". You can set up various continuous integration services automatically by using functions from the `{usethis}` package: diff --git a/17-javascript.Rmd b/17-javascript.Rmd index 2222e923..d62dc08d 100644 --- a/17-javascript.Rmd +++ b/17-javascript.Rmd @@ -54,8 +54,7 @@ It's important to note here that the **communication happens in both directions* In fact, when we write a piece of code like `sliderInput("first_input", "Select a number", 1, 10, 5)`, what we are doing is creating a binding between JavaScript and R, where the JavaScript runtime (in the browser) listens to any event happening on the slider with the id `"plop"`, and whenever it detects that something happens to this element, something (most of the time its value) is sent back to R, and R does computation based on that value. With `output$bla <- renderPlot({})`, what we are doing is making the two communicate the other way around: we are telling JavaScript to listen to any incoming data from R for the `id` `"bla"`, and whenever JavaScript sees incoming data from R, it puts it into the proper HTML tag (here, JavaScript inserts the image received from R in the `` tags with the id `bla`). -Even if everything is written in R, we **are** writing a web application, i.e.. -HTML, CSS and JavaScript elements. +Even if everything is written in R, we **are** writing a web application—i.e., one built from HTML, CSS, and JavaScript elements. Once you have realized that, the possibilities are endless: in fact almost anything doable in a "classic" web app can be done in `{shiny}` with a little bit of tweaking. What this also implies is that getting (even a little bit) better at writing HTML, CSS, and especially JavaScript will make your app better, smaller, and more user-friendly, as JavaScript is a language that has been designed to interact with a web page: change element appearances, hide and show things, click somewhere, show alerts and prompts, etc. **Knowing just enough JavaScript can improve the quality of your app**: especially when you have been using R to render some complex UIs: think conditional panels, simulating a button click from the server, hide and show elements, etc. @@ -64,9 +63,9 @@ All these things are good examples of where you should be using JavaScript inste Moreover, the number of JavaScript libraries available on the web is tremendous; and the good news is that `{shiny}` has everything it needs to bundle external JavaScript libraries inside your application.[^javascript-2] [^javascript-2]: This can also be done by wrapping a JS libraries inside a package, which will later be used inside an application. - See for example `{glouton}` [@R-glouton], which is a wrapper around the [`js-cookie` >https://github.com/js-cookie/js-cookie> JavaScript library. + See for example `{glouton}` [@R-glouton], which is a wrapper around the [`js-cookie`](https://github.com/js-cookie/js-cookie) JavaScript library. -This is what this section of the book aims at: giving you just enough JavaScript knowledge to lighten your `{shiny}` app, in order to improve the global user and developer experience. +This is what this section of the book aims to do: give you just enough JavaScript knowledge to lighten your `{shiny}` app, improving the overall user and developer experience. In this chapter, we will first review some JavaScript basics which can be used "client-side" only, i.e. only in your browser. Then, we will talk about making R and JS communicate with each other, and explore some common patterns for JavaScript in `{shiny}`. Finally, we will quickly present some of the functions available in `{golem}` [@R-golem] that can be used to launch JavaScript. diff --git a/19-appendix.Rmd b/19-appendix.Rmd index aa771438..f8a224ba 100644 --- a/19-appendix.Rmd +++ b/19-appendix.Rmd @@ -477,7 +477,7 @@ Once this is done, we will create/update the `.dockerignore` file at the root of Removing the installation of `libgit2-dev` solved the issue. Inside our `Dockerfile`, we will also change the default repo to use , which proposes precompiled packages for our system, making the installation faster. -We will also add an installation of NodeJS, which is needed by our application.. +We will also add an installation of NodeJS, which is needed by our application. Then, we can go to our terminal and compile the image! diff --git a/chapter-abstracts.Rmd b/chapter-abstracts.Rmd index 7c9bb49c..cc66c1f5 100644 --- a/chapter-abstracts.Rmd +++ b/chapter-abstracts.Rmd @@ -26,7 +26,7 @@ How can we measure both these aspects when it comes to R and `{shiny}`? This second chapter of the book will cover a crucial concept when it comes to leading a successful software engineering project: planning. -In this chapter, the reader will be presented project management and planning in the context of a `{shiny}` project: why it's important to plan ahead, how to leverage the KISS principle, and how to practically organize a team of `{shiny}` developers, both from the tooling and management point of views. +In this chapter, the reader will be presented project management and planning in the context of a `{shiny}` project: why it's important to plan ahead, how to leverage the KISS principle, and how to practically organize a team of `{shiny}` developers, both from the tooling and management points of view. ## Book Title -- Engineering Production-Grade Shiny Apps @@ -36,7 +36,7 @@ In this chapter, the reader will be presented project management and planning in Chapter 3 will cover the technical aspects of structuring your `{shiny}` projects for production. -In this chapter, we will cover the importance of building a `{shiny}` application as a package and all the benefits that will come with this infrastructure choice: metadata, documentation, native testing, and the ability to leverage all the toolkit available to the R developers. Then, we will move to another key concept that will power any large-scale `{shiny}` application: modules. Finally, we will introduce why convention matters when it comes to working as a team, and how to put this into practice when building applications using the `{golem}` framework. +In this chapter, we will cover the importance of building a `{shiny}` application as a package and all the benefits that will come with this infrastructure choice: metadata, documentation, native testing, and the ability to leverage all the toolkit available to R developers. Then, we will move to another key concept that will power any large-scale `{shiny}` application: modules. Finally, we will introduce why convention matters when it comes to working as a team, and how to put this into practice when building applications using the `{golem}` framework. ## Book Title – Engineering Production-Grade Shiny Apps @@ -44,9 +44,9 @@ In this chapter, we will cover the importance of building a `{shiny}` applicatio ## Chapter Number & Title – Chapter 4, Introduction to {golem} -Chapter will introduce `{golem}`, a framework for building production-grade `{shiny}` applications. +This chapter will introduce `{golem}`, a framework for building production-grade `{shiny}` applications. -In this chapter, we will give an introduction to the general philosophy behind `{golem}`, and will describe in details the structure of a `{golem}` project: what makes it similar to a standard R package (`DESCRIPTION`, `NAMESPACE`, `man/`, ...), and what makes it unique. Notably, we will spend some time reviewing the functions contained in the default `{golem}` project, the very same functions that will be the starting point of your future `{shiny}` applications. +In this chapter, we will give an introduction to the general philosophy behind `{golem}`, and will describe in detail the structure of a `{golem}` project: what makes it similar to a standard R package (`DESCRIPTION`, `NAMESPACE`, `man/`, ...), and what makes it unique. Notably, we will spend some time reviewing the functions contained in the default `{golem}` project, the very same functions that will be the starting point of your future `{shiny}` applications. ## Book Title -- Engineering Production-Grade Shiny Apps @@ -62,23 +62,23 @@ This fifth chapter succinctly presents the steps of the workflow later developed ## Chapter Number & Title – Chapter 6, UX Matters -Chapter 6 covers the basics of what make a web application user-friendly. +Chapter 6 covers the basics of what makes a web application user-friendly. First, we cover the importance of simplicity when it comes to designing an interface, keeping in mind the "Don't make me think" mantra. When reading the web, users tend to scan, instead of cautiously make logical decisions about how to behave. In other word, they do not really read but tend to scan the content, making it crucial for the page to be as simple as possible, so that the visitors easily find their way through the interface. -Then, we cover the importance of lowering complexity of a software by restraining from implementing way too many features: when building an application with `{shiny}`, it's crucial to think about the necessity of the features we're implementing, so that we don't end with too much reactivity, and/or an application that is way to slow to be used. +Then, we cover the importance of lowering complexity of a software by restraining from implementing way too many features: when building an application with `{shiny}`, it's crucial to think about the necessity of the features we're implementing, so that we don't end with too much reactivity, and/or an application that is way too slow to be used. -Finally, we cover one of the most important topic when it comes to making a successful application: accessibility. In other words, how do we make an application usable by the widest audience possible? How do we work on making our application usable by people with visual, mobility, or cognitive disabilities. This chapter will introduce the notion of semantic HTML in the context of `{shiny}`, structure, and, something important in a context where we build applications with data visualization: choice of colors. +Finally, we cover one of the most important topic when it comes to making a successful application: accessibility. In other words, how do we make an application usable by the widest audience possible? How do we work on making our application usable by people with visual, mobility, or cognitive disabilities? This chapter will introduce the notion of semantic HTML in the context of `{shiny}`, structure, and, something important in a context where we build applications with data visualization: choice of colors. ## Book Title -- Engineering Production-Grade Shiny Apps ## Chapter Author -- Colin Fay -## Chapter Number & Title -- Chapter 7, +## Chapter Number & Title -- Chapter 7, Step by Step Design -This seventh chapter covers the need for getting yourself prepared upfront when it comes to engineering a `{shiny}`. +This seventh chapter covers the need for getting yourself prepared upfront when it comes to engineering a `{shiny}` application. -Here, we will cover the importance of starting with planning, thinking, and evaluating existing solution before rushing into coding. We will also introduce concept maps, and give a series of tools to evaluate the project before even writing a single line of code. In other words, we'll see how to get started with user interview, how to create personas, and why it's important to evaluate pre-existing codebase before starting the project. +Here, we will cover the importance of starting with planning, thinking, and evaluating existing solutions before rushing into coding. We will also introduce concept maps, and give a series of tools to evaluate the project before even writing a single line of code. In other words, we'll see how to get started with user interviews, how to create personas, and why it's important to evaluate pre-existing codebases before starting the project. ## Book Title – Engineering Production-Grade Shiny Apps @@ -156,7 +156,7 @@ This chapter starts with a checklist of things to do before sending an applicati Chapter 14 starts with the reflection around the necessity to optimize, and around managing the optimization process when building your application. -When building a `{shiny}` application that will be send to production, schedule matters, and focusing on optimizing too soon, or too much, might endanger the whole success of the project. On the other hand, choices made when optimizing the application might have a big impact on the longevity of the project. +When building a `{shiny}` application that will be sent to production, schedule matters, and focusing on optimizing too soon, or too much, might endanger the whole success of the project. On the other hand, choices made when optimizing the application might have a big impact on the longevity of the project. If you decide to go along the optimization road, you better start by benchmarking what and where you need to optimize, so that you're sure you're not working on optimizing parts of the application that do not need to be optimized. The second part of the chapter presents tools you can use to perform this code profiling. @@ -166,7 +166,7 @@ If you decide to go along the optimization road, you better start by benchmarkin ## Chapter Number & Title -- Chapter 15, Common Application Caveats -Some code bottlenecks (parts of the codebase that slow the application) might not be caught by simple profiling tools: sometime a slow application can be explained by caveats in the way the application is designed. +Some code bottlenecks (parts of the codebase that slow the application) might not be caught by simple profiling tools: sometimes a slow application can be explained by caveats in the way the application is designed. This chapter will present three main sources of design patterns that might be slowing your application: uncontrolled reactivity, where too much happens (also known as "reactivity hell"), making R perform too much computation, and data source management. @@ -177,9 +177,9 @@ This chapter will present three main sources of design patterns that might be sl ## Chapter Number & Title – Chapter 16, Optimizing {shiny} Code -There are several methods you can choose to optimize your application so that it works faster, and handles memory in a more efficient way. This chapter presents three of them. +There are several methods you can choose to optimize your application so that it works faster and handles memory in a more efficient way. This chapter presents three of them. -First, focusing on the R code itself. Then, caching elements. Caching is the process of storing computation results from a function so that it can be reused, instead of recomputing the function every time. In this chapter, we will see how you can implement caching with R code, and how to use the `{shiny}`-specific functions to do that. Finally, we will present a way to build asynchronousity inside a `{shiny}` application using `{promises}` and `{future}`. +First, focusing on the R code itself. Then, caching elements. Caching is the process of storing computation results from a function so that it can be reused, instead of recomputing the function every time. In this chapter, we will see how you can implement caching with R code, and how to use the `{shiny}`-specific functions to do that. Finally, we will present a way to build asynchronicity inside a `{shiny}` application using `{promises}` and `{future}`. ## Book Title – Engineering Production-Grade Shiny Apps @@ -187,7 +187,7 @@ First, focusing on the R code itself. Then, caching elements. Caching is the pro ## Chapter Number & Title – Chapter 17, Using JavaScript -One of the best way to enhance your application, in the long run, is to get comfortable with JavaScript, a scripting language that runs in your web browser. +One of the best ways to enhance your application, in the long run, is to get comfortable with JavaScript, a scripting language that runs in your web browser. With JavaScript, you'll be able to enhance the user experience by leveraging in-browser events, and lower the computation performed by R as you can perform them inside the browser, leaving more space to the server for computation. And in the long run, when you're comfortable with JavaScript, a whole world of `{shiny}` extensions opens. From 48192d1f39f4ffe2418a0bbd68b91c73bbdfdf08 Mon Sep 17 00:00:00 2001 From: vincent guyader Date: Wed, 26 Nov 2025 22:07:59 +0100 Subject: [PATCH 2/3] Update GitHub Actions cache and artifact versions --- .github/workflows/build_bookdown_pr.yml | 6 +++--- .github/workflows/deploy_bookdown.yml | 8 ++++---- .github/workflows/deploy_bookdown_wip.yml | 20 ++++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build_bookdown_pr.yml b/.github/workflows/build_bookdown_pr.yml index 86c87585..5169d0b9 100644 --- a/.github/workflows/build_bookdown_pr.yml +++ b/.github/workflows/build_bookdown_pr.yml @@ -16,14 +16,14 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: r-lib/actions/setup-r@v2 with: crayon.enabled: 'FALSE' r-version: '3.6.1' - - uses: actions/cache@v2 + - uses: actions/cache@v4 with: path: ~/.local/share/renv key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} @@ -52,7 +52,7 @@ jobs: - name: Render Book run: Rscript -e 'bookdown::render_book("index.Rmd")' - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: _site path: _site/ diff --git a/.github/workflows/deploy_bookdown.yml b/.github/workflows/deploy_bookdown.yml index b0108da8..0e9c014c 100644 --- a/.github/workflows/deploy_bookdown.yml +++ b/.github/workflows/deploy_bookdown.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/cache@v1 + - uses: actions/cache@v4 with: path: ~/.local/share/renv key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} @@ -65,7 +65,7 @@ jobs: - name: Build redirect run: Rscript redirect.R - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 with: name: _site path: _site/ @@ -79,9 +79,9 @@ jobs: needs: bookdown steps: - name: Checkout - uses: actions/checkout@master + uses: actions/checkout@v4 - name: Download artifact - uses: actions/download-artifact@v1.0.0 + uses: actions/download-artifact@v4 with: # Artifact name name: _site # optional diff --git a/.github/workflows/deploy_bookdown_wip.yml b/.github/workflows/deploy_bookdown_wip.yml index e6a5414c..b38f81c9 100644 --- a/.github/workflows/deploy_bookdown_wip.yml +++ b/.github/workflows/deploy_bookdown_wip.yml @@ -13,23 +13,23 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - - uses: r-lib/actions/setup-r@v1 + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-r@v2 with: crayon.enabled: 'FALSE' - - - uses: actions/cache@v1 + + - uses: actions/cache@v4 with: path: ~/.local/share/renv key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} restore-keys: | ${{ runner.os }}-renv- - - - uses: r-lib/actions/setup-pandoc@v1 - + + - uses: r-lib/actions/setup-pandoc@v2 + - name: Install tinytex - uses: r-lib/actions/setup-tinytex@master + uses: r-lib/actions/setup-tinytex@v2 - name: Install sysreq run: sudo apt update && sudo apt install -y gdal-bin git-core libcairo2-dev libgdal-dev libgeos-dev libgeos++-dev libgit2-dev libpng-dev libssh2-1-dev libssl-dev libudunits2-dev libxml2-dev make pandoc pandoc-citeproc zlib1g-dev libmagick++-dev libssl-dev libsasl2-dev @@ -54,7 +54,7 @@ jobs: - name: Render Book run: Rscript -e 'bookdown::render_book("index.Rmd", output_dir = "_book/wip")' - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v4 with: name: _book path: _book/ From f13110490e9033b43e8896f6b1678a62c54e5cf6 Mon Sep 17 00:00:00 2001 From: vincent guyader Date: Wed, 26 Nov 2025 22:12:39 +0100 Subject: [PATCH 3/3] Remove pandoc-citeproc from workflow dependencies --- .github/workflows/build_bookdown_pr.yml | 2 +- .github/workflows/deploy_bookdown.yml | 2 +- .github/workflows/deploy_bookdown_wip.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_bookdown_pr.yml b/.github/workflows/build_bookdown_pr.yml index 5169d0b9..41da8958 100644 --- a/.github/workflows/build_bookdown_pr.yml +++ b/.github/workflows/build_bookdown_pr.yml @@ -36,7 +36,7 @@ jobs: uses: r-lib/actions/setup-tinytex@v2 - name: Install sysreq - run: sudo apt update && sudo apt install -y gdal-bin git-core libcairo2-dev libgdal-dev libgeos-dev libgeos++-dev libgit2-dev libpng-dev libssh2-1-dev libssl-dev libudunits2-dev libxml2-dev make pandoc pandoc-citeproc zlib1g-dev libmagick++-dev libssl-dev libsasl2-dev + run: sudo apt update && sudo apt install -y gdal-bin git-core libcairo2-dev libgdal-dev libgeos-dev libgeos++-dev libgit2-dev libpng-dev libssh2-1-dev libssl-dev libudunits2-dev libxml2-dev make pandoc zlib1g-dev libmagick++-dev libssl-dev libsasl2-dev - name: Pulling hexmake run: docker pull colinfay/hexmake diff --git a/.github/workflows/deploy_bookdown.yml b/.github/workflows/deploy_bookdown.yml index 0e9c014c..5ecc8bf3 100644 --- a/.github/workflows/deploy_bookdown.yml +++ b/.github/workflows/deploy_bookdown.yml @@ -40,7 +40,7 @@ jobs: uses: r-lib/actions/setup-tinytex@v2 - name: Install sysreq - run: sudo apt update && sudo apt install -y gdal-bin git-core libcairo2-dev libgdal-dev libgeos-dev libgeos++-dev libgit2-dev libpng-dev libssh2-1-dev libssl-dev libudunits2-dev libxml2-dev make pandoc pandoc-citeproc zlib1g-dev libmagick++-dev libssl-dev libsasl2-dev + run: sudo apt update && sudo apt install -y gdal-bin git-core libcairo2-dev libgdal-dev libgeos-dev libgeos++-dev libgit2-dev libpng-dev libssh2-1-dev libssl-dev libudunits2-dev libxml2-dev make pandoc zlib1g-dev libmagick++-dev libssl-dev libsasl2-dev - name: Pulling hexmake run: docker pull colinfay/hexmake diff --git a/.github/workflows/deploy_bookdown_wip.yml b/.github/workflows/deploy_bookdown_wip.yml index b38f81c9..d139dc66 100644 --- a/.github/workflows/deploy_bookdown_wip.yml +++ b/.github/workflows/deploy_bookdown_wip.yml @@ -32,7 +32,7 @@ jobs: uses: r-lib/actions/setup-tinytex@v2 - name: Install sysreq - run: sudo apt update && sudo apt install -y gdal-bin git-core libcairo2-dev libgdal-dev libgeos-dev libgeos++-dev libgit2-dev libpng-dev libssh2-1-dev libssl-dev libudunits2-dev libxml2-dev make pandoc pandoc-citeproc zlib1g-dev libmagick++-dev libssl-dev libsasl2-dev + run: sudo apt update && sudo apt install -y gdal-bin git-core libcairo2-dev libgdal-dev libgeos-dev libgeos++-dev libgit2-dev libpng-dev libssh2-1-dev libssl-dev libudunits2-dev libxml2-dev make pandoc zlib1g-dev libmagick++-dev libssl-dev libsasl2-dev - name: Pulling hexmake run: docker pull colinfay/hexmake