<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Julia Community 🟣: Wen Wei Tseng</title>
    <description>The latest articles on Julia Community 🟣 by Wen Wei Tseng (@sosiristseng).</description>
    <link>https://forem.julialang.org/sosiristseng</link>
    <image>
      <url>https://forem.julialang.org/images/Ps2_Bbd1M5CWCOIiDyv3JhKJWnH3ZYi4gJOnZB3Y6UE/rs:fill:90:90/g:sm/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L3VzZXIvcHJvZmls/ZV9pbWFnZS8xNjEv/YTc3M2Q3YjMtNWY2/OS00ZTgzLWIxMGQt/NTUzODUxNjI2N2E2/LmpwZWc</url>
      <title>Julia Community 🟣: Wen Wei Tseng</title>
      <link>https://forem.julialang.org/sosiristseng</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.julialang.org/feed/sosiristseng"/>
    <language>en</language>
    <item>
      <title>Avoiding precompilation in Julia projects in GitHub actions</title>
      <dc:creator>Wen Wei Tseng</dc:creator>
      <pubDate>Wed, 29 Mar 2023 16:32:35 +0000</pubDate>
      <link>https://forem.julialang.org/sosiristseng/avoiding-precompilation-in-julia-projects-in-github-actions-3ihg</link>
      <guid>https://forem.julialang.org/sosiristseng/avoiding-precompilation-in-julia-projects-in-github-actions-3ihg</guid>
      <description>&lt;p&gt;&lt;strong&gt;Edit on 2023-05-11&lt;/strong&gt;&lt;br&gt;
This trick might be no longer needed. It seems like Julia 1.9 has resolved this invalidation issue in GitHub actions. Kudos to the Julia development team 🎉.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sosiristseng/julia-precompile-test/actions/runs/4945502605"&gt;First run (uncached)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sosiristseng/julia-precompile-test/actions/runs/4945706971"&gt;Second run (cached)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sosiristseng/julia-precompile-test/actions/runs/4945733713"&gt;Third run (cached)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Notes on 2023-06-27&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After trial and error, the most reliable way to avoid precompile cache rejection is to build a docker image with Julia dependencies and set &lt;code&gt;JULIA_CPU_TARGET=generic&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;TL;DR. Running Julia &lt;em&gt;in a container&lt;/em&gt; may avoid precompiling for the same dependencies. Example &lt;a href="https://github.com/sosiristseng/julia-precompile-test/"&gt;here&lt;/a&gt; and the difference can be found in the &lt;code&gt;Actions&lt;/code&gt; tab.&lt;/p&gt;



&lt;p&gt;I accidentally found this "hack" when I was preparing the code examples for the &lt;a href="https://github.com/ntumitolab/mmsb-bebi-5009"&gt;class&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Setting up Julia with the regular way &lt;code&gt;julia-actions/setup-julia@v1&lt;/code&gt; and caching the full Julia depot &lt;code&gt;~/.julia&lt;/code&gt; (or using Julia's &lt;a href="https://github.com/julia-actions/cache"&gt;cache action&lt;/a&gt; ) still needs some precompilation desipite the vary same dependencies. This might be related to &lt;a href="https://github.com/julia-actions/setup-julia/pull/100#issuecomment-1198675969"&gt;this issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forem.julialang.org/images/N634ijuN8Kp5v6sxfQlEJdD7JS0wGT9MgwLVVAjyYkE/w:800/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L2FydGljbGVzLzM0/d2hiN3JpZm56amNy/aHl5dXN2LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://forem.julialang.org/images/N634ijuN8Kp5v6sxfQlEJdD7JS0wGT9MgwLVVAjyYkE/w:800/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L2FydGljbGVzLzM0/d2hiN3JpZm56amNy/aHl5dXN2LnBuZw" alt="regular" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, Julia &lt;a href="https://docs.github.com/en/actions/using-jobs/running-jobs-in-a-container"&gt;in the docker container&lt;/a&gt; seemed to fully reuse the precompiled code and would not precompile for the same dependencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forem.julialang.org/images/m7pALB92sswT_j91MIy_l9ynTo4kZEXJ4mQDRb75UFQ/w:800/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L2FydGljbGVzL3kx/eml0cHY0dWZrcWs4/a2VzeHAzLnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://forem.julialang.org/images/m7pALB92sswT_j91MIy_l9ynTo4kZEXJ4mQDRb75UFQ/w:800/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L2FydGljbGVzL3kx/eml0cHY0dWZrcWs4/a2VzeHAzLnBuZw" alt="docker" width="800" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For comparison, this is the &lt;a href="https://github.com/sosiristseng/julia-precompile-test/actions/runs/4552357806"&gt;first run&lt;/a&gt;. Caches were not populated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forem.julialang.org/images/HOCVSdH-2Ko7N-xY--NX11xaTqnEB58RhjAQF7QFon8/w:800/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L2FydGljbGVzL2p3/NzRoZnoyOTQ1ZGR5/bTM5c3d3LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://forem.julialang.org/images/HOCVSdH-2Ko7N-xY--NX11xaTqnEB58RhjAQF7QFon8/w:800/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L2FydGljbGVzL2p3/NzRoZnoyOTQ1ZGR5/bTM5c3d3LnBuZw" alt="first run" width="301" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is the &lt;a href="https://github.com/sosiristseng/julia-precompile-test/actions/runs/4552496154"&gt;second run&lt;/a&gt; after caches were populated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forem.julialang.org/images/fkC68EGDYYRjvuAIa6v1ymWWf3AdR1mvq4RtKXtIwME/w:800/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L2FydGljbGVzL2N5/dHIybGs5MTNuN2dp/c3prNmNvLnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://forem.julialang.org/images/fkC68EGDYYRjvuAIa6v1ymWWf3AdR1mvq4RtKXtIwME/w:800/mb:500000/ar:1/aHR0cHM6Ly9mb3Jl/bS5qdWxpYWxhbmcu/b3JnL3JlbW90ZWlt/YWdlcy91cGxvYWRz/L2FydGljbGVzL2N5/dHIybGs5MTNuN2dp/c3prNmNvLnBuZw" alt="second run" width="300" height="209"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;The GitHub actions &lt;a href="https://github.com/sosiristseng/julia-precompile-test/blob/main/.github/workflows/ci.yml"&gt;CI workflow&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workflow }}-${{ github.ref }}&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;JULIA_CACHE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;
  &lt;span class="na"&gt;JULIA_NUM_THREADS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auto'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;julia:1.8.5&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Julia dependencies&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.JULIA_CACHE != '0'}}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/.julia&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-juliacontainer-${{ env.JULIA_CACHE }}-${{ hashFiles('**/Manifest.toml') }}&lt;/span&gt;
          &lt;span class="c1"&gt;# restore-keys: |&lt;/span&gt;
          &lt;span class="c1"&gt;#   ${{ runner.os }}-juliacontainer-${{ env.JULIA_CACHE }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Julia dependencies&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;JULIA_PROJECT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workspace }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;julia --color=yes -e 'import Pkg; Pkg.instantiate(); Pkg.resolve(); Pkg.precompile()'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Julia execution&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;JULIA_PROJECT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workspace }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;julia --color=yes -e 'using DifferentialEquations; println("Hello")'&lt;/span&gt;
  &lt;span class="na"&gt;regular&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Julia&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;julia-actions/setup-julia@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.8.5'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Julia dependencies&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.JULIA_CACHE != '0'}}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/.julia&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-julia-${{ env.JULIA_CACHE }}-${{ hashFiles('**/Manifest.toml') }}&lt;/span&gt;
          &lt;span class="c1"&gt;# restore-keys: |&lt;/span&gt;
          &lt;span class="c1"&gt;#   ${{ runner.os }}-julia-${{ env.JULIA_CACHE }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Julia dependencies&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;JULIA_PROJECT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workspace }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;julia --color=yes -e 'import Pkg; Pkg.instantiate(); Pkg.resolve(); Pkg.precompile()'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Julia execution&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;JULIA_PROJECT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workspace }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;julia --color=yes -e 'using DifferentialEquations; println("Hello")'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right now, I'm using this trick to test run and publish my &lt;a href="https://github.com/ntumitolab/mmsb-bebi-5009/"&gt;class material&lt;/a&gt;. Click &lt;a href="https://github.com/ntumitolab/mmsb-bebi-5009/actions/runs/4554302240/jobs/8031975809"&gt;here&lt;/a&gt; to see it in action.&lt;/p&gt;

&lt;p&gt;As a side note, the repository uses &lt;code&gt;Literate.jl&lt;/code&gt; to execute code and generate Jupyter notebooks from Julia scripts, with 2 worker processes to fully utilize both cores of the GitHub runner. This new workflow is more efficient than &lt;a href="https://forem.julialang.org/sosiristseng/publishing-julia-notebooks-with-docker-and-github-actions-114h"&gt;the previous one&lt;/a&gt; in terms of CPU time. The previous workflow built a docker image for the runtime environment. Then a runner was assigned for each notebook to load the environment and execute the code. Finally, a runner gathered and rendered the executed notebooks into a website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Manifest.toml&lt;/code&gt; is required here to fully capture Julia dependencies; therefore, this hack may not work for Julia packages, but may work for projects with a &lt;code&gt;Manifest.toml&lt;/code&gt; to lock their dependencies.&lt;/li&gt;
&lt;li&gt;I only test it on the Linux runner (ubuntu-latest). Other OSes (Mac and Windows) may or may not work.&lt;/li&gt;
&lt;li&gt;Caching the precompiled code might not be a good idea. See &lt;a href="https://github.com/julia-actions/cache/issues/11"&gt;this issue&lt;/a&gt;.
Also, GitHub has a &lt;a href="https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows"&gt;10 GB size limit&lt;/a&gt; for the total size of all caches. Older ones will be deleted when the limit is exceeded.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>julia</category>
      <category>cicd</category>
      <category>gha</category>
    </item>
    <item>
      <title>Publishing Julia Notebooks with Docker and GitHub Actions (II: GitHub actions)</title>
      <dc:creator>Wen Wei Tseng</dc:creator>
      <pubDate>Sun, 12 Feb 2023 16:10:57 +0000</pubDate>
      <link>https://forem.julialang.org/sosiristseng/publishing-julia-notebooks-with-docker-and-github-actions-ii-github-actions-3ajm</link>
      <guid>https://forem.julialang.org/sosiristseng/publishing-julia-notebooks-with-docker-and-github-actions-ii-github-actions-3ajm</guid>
      <description>&lt;p&gt;This series will&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Docker to build a Julia runtime environment for continuous integration (CI).&lt;/li&gt;
&lt;li&gt;Use GitHub actions and the Docker container to execute notebooks in parallel.&lt;/li&gt;
&lt;li&gt;Use GitHub actions and jupyter-book to publish notebooks automatically upon git push.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Main workflow file: &lt;a href="https://github.com/sosiristseng/template-juliabook/blob/main/.github/workflows/ci-matrix.yml"&gt;https://github.com/sosiristseng/template-juliabook/blob/main/.github/workflows/ci-matrix.yml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workflow includes 4 stages&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;setup&lt;/code&gt;: builds and caches the runtime docker container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute&lt;/code&gt;: executes notebooks in parallel&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jupyter-book&lt;/code&gt;: renders executed notebooks&lt;/li&gt;
&lt;li&gt;Deployment: pushes rendered webpages to GitHub pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Build runtime Docker container
&lt;/h2&gt;

&lt;p&gt;The Docker container for the runtime is built by &lt;a href="https://github.com/docker/setup-buildx-action"&gt;&lt;code&gt;setup-buildx-action&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/docker/build-push-action"&gt;&lt;code&gt;build-push-action&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.set-matrix.outputs.matrix }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and cache Docker container&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.DFILE }}&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.IMAGE_NAME }}&lt;/span&gt;
          &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=cacheonly&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;
          &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;List notebooks as a JSON array&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;set-matrix&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "matrix=$(python -c 'import glob, json; print(json.dumps(glob.glob("docs/*.ipynb")))')" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;setup-buildx-action&lt;/code&gt; uses &lt;code&gt;buildx&lt;/code&gt; for more docker builder abilities. (e.g. image layer caching)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;build-push-action&lt;/code&gt; builds the docker image from our &lt;code&gt;.github/Dockerfile&lt;/code&gt;, with &lt;a href="https://docs.docker.com/build/cache/backends/gha/"&gt;GitHub actions caching&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We also list all the jupyter notebooks (&lt;code&gt;*.ipynb&lt;/code&gt;) in the &lt;code&gt;docs&lt;/code&gt; folder as a JSON array for the next step.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Execute notebooks
&lt;/h2&gt;

&lt;p&gt;To decrease build time, I use a &lt;a href="https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs"&gt;job matrix&lt;/a&gt; to execute notebooks in parallel. This stage uses the Docker container from the previous stage and execute notebooks from the output of the previous step. Finished notebooks are then uploaded as artifacts. &lt;a href="https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration"&gt;The concurrency limit&lt;/a&gt; is 20 for GitHub free personal and organization accounts. That is, you can run up to 20 notebooks simultaneously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setup&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;max-parallel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;notebook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ fromJSON(needs.setup.outputs.matrix) }}&lt;/span&gt; &lt;span class="c1"&gt;# Notebooks need to be executed&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore Docker container&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.DFILE }}&lt;/span&gt;
          &lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.IMAGE_NAME }}&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Execute Notebook&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="s"&gt;docker run --rm&lt;/span&gt;
          &lt;span class="s"&gt;--workdir=/tmp -v ${{ github.workspace }}:/tmp&lt;/span&gt;
          &lt;span class="s"&gt;${{ env.IMAGE_NAME }}&lt;/span&gt;
          &lt;span class="s"&gt;jupyter nbconvert --to notebook --execute --inplace&lt;/span&gt;
          &lt;span class="s"&gt;--ExecutePreprocessor.timeout=${{ env.TIMEOUT }}&lt;/span&gt;
          &lt;span class="s"&gt;--ExecutePreprocessor.kernel_name=julia-$(julia -e 'print(VERSION.major,".",VERSION.minor)')&lt;/span&gt;
          &lt;span class="s"&gt;${{ matrix.notebook }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload Notebook&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;notebook&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.notebook }}&lt;/span&gt;
          &lt;span class="na"&gt;retention-days&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the parallel matrix in action : &lt;a href="https://github.com/ww-jl/dataframes/actions/runs/4131921732"&gt;https://github.com/ww-jl/dataframes/actions/runs/4131921732&lt;/a&gt;&lt;br&gt;
(The repo is my modification on &lt;code&gt;DataFrames.jl&lt;/code&gt; examples by Bogumił Kamiński)&lt;/p&gt;
&lt;h2&gt;
  
  
  Render the website using jupyter-book
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://jupyterbook.org/"&gt;jupyter-book&lt;/a&gt; is a static site generator (SSG) and builds publication-quality books and websites from Markdown documents(&lt;code&gt;*.md&lt;/code&gt;) and Jupyter notebooks (&lt;code&gt;*.ipynb&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Here, we collect executed notebooks from previous jobs using &lt;code&gt;actions/download-artifact&lt;/code&gt;, use &lt;code&gt;jupyter-book&lt;/code&gt; to render them into a website, and upload them as a website artifact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;jupyter-book&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;execute&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="c1"&gt;# store success output flag for the ci job&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.setoutput.outputs.success }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v4&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.x'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Jupyter Book&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip install jupyter-book&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Download notebooks&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/download-artifact@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;notebook&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;out/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Display structure of downloaded files&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ls -R&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;out&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Move notebooks&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;find out/ -type f -iname '*.ipynb' -exec mv -t docs/ {} +&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build website&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jupyter-book build docs/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload pages artifact&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.ref == 'refs/heads/main' }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-pages-artifact@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs/_build/html&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set output flag&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setoutput&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "success=true" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy to GH pages
&lt;/h3&gt;

&lt;p&gt;Finally, we deploy the rendered files to GitHub pages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# Deployment job&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jupyter-book&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;if{{ github.ref == 'refs/heads/main' }}&lt;/span&gt;
    &lt;span class="c1"&gt;# Grant GITHUB_TOKEN the permissions required to make a Pages deployment&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# to deploy to Pages&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# to verify the deployment originates from an appropriate source&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pages&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.deployment.outputs.page_url }}&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Caveat: CI status check
&lt;/h2&gt;

&lt;p&gt;GitHub status check treats skipped workflows as passed. Thus, even if any of the notebooks went wrong, the &lt;code&gt;jupyter-book&lt;/code&gt; step will be skipped and the overall status check will still pass, which is not ideal for CI depending on status checks. The &lt;a href="https://brunoscheufler.com/blog/2022-04-09-the-required-github-status-check-that-wasnt"&gt;blog post by Bruno Scheufler&lt;/a&gt; provides a workaround for this issue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# GitHub status check&lt;/span&gt;
  &lt;span class="c1"&gt;# https://brunoscheufler.com/blog/2022-04-09-the-required-github-status-check-that-wasnt&lt;/span&gt;
  &lt;span class="na"&gt;ci&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jupyter-book&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt; &lt;span class="c1"&gt;# always run, so we never skip the check&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# pass step only when output of previous jupyter-book job is set&lt;/span&gt;
      &lt;span class="c1"&gt;# in case at least one of the execution fails, jupyter-book is skipped&lt;/span&gt;
      &lt;span class="c1"&gt;# and the output will not be set, which will then cause the ci job to fail&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;passed="${{ needs.jupyter-book.outputs.success }}"&lt;/span&gt;
          &lt;span class="s"&gt;if [[ $passed == "true" ]]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "Tests passed"&lt;/span&gt;
            &lt;span class="s"&gt;exit 0&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
            &lt;span class="s"&gt;echo "Tests failed"&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>julia</category>
      <category>gha</category>
      <category>jupyter</category>
      <category>docker</category>
    </item>
    <item>
      <title>Publishing Julia Notebooks with Docker and GitHub Actions (I: Dockerfile)</title>
      <dc:creator>Wen Wei Tseng</dc:creator>
      <pubDate>Sat, 10 Sep 2022 09:11:29 +0000</pubDate>
      <link>https://forem.julialang.org/sosiristseng/publishing-julia-notebooks-with-docker-and-github-actions-114h</link>
      <guid>https://forem.julialang.org/sosiristseng/publishing-julia-notebooks-with-docker-and-github-actions-114h</guid>
      <description>&lt;p&gt;This series will&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Docker to build a Julia runtime environment for continuous integration (CI).&lt;/li&gt;
&lt;li&gt;Use GitHub actions and the Docker container to execute notebooks in parallel.&lt;/li&gt;
&lt;li&gt;Use GitHub actions and &lt;code&gt;jupyter-book&lt;/code&gt; to publish notebooks automatically upon &lt;code&gt;git push&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The one-click-to-copy repository is on: &lt;a href="https://github.com/sosiristseng/template-juliabook-docker"&gt;https://github.com/sosiristseng/template-juliabook-docker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will try to explain major steps in my Dockerfile to build a runtime environment for Julia code running in Jupyter notebooks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11.2-slim&lt;/span&gt;

&lt;span class="c"&gt;# Julia&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JULIA_CI true&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JULIA_NUM_THREADS "auto"&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JULIA_PATH /usr/local/julia/&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JULIA_DEPOT_PATH /srv/juliapkg/&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH ${JULIA_PATH}/bin:${PATH}&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=julia:1.8.5 ${JULIA_PATH} ${JULIA_PATH}&lt;/span&gt;

&lt;span class="c"&gt;# Python dependencies. e.g. matplotlib&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; nbconvert

&lt;span class="c"&gt;# Julia environment&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Project.toml Manifest.toml ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/ src&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;julia &lt;span class="nt"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'import Pkg; Pkg.add("IJulia"); using IJulia; installkernel("Julia", "--project=@.")'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    julia &lt;span class="nt"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@. &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'import Pkg; Pkg.instantiate(); Pkg.resolve(); Pkg.precompile()'&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["julia"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Base docker image
&lt;/h2&gt;

&lt;p&gt;Usually, Julia projects uses &lt;a href="https://hub.docker.com/_/julia"&gt;&lt;code&gt;julia&lt;/code&gt;&lt;/a&gt; as the base image; however, we need a python package &lt;code&gt;nbconvert&lt;/code&gt; to render Jupyter notebooks. Thus, we use &lt;a href="https://hub.docker.com/_/python"&gt;&lt;code&gt;python&lt;/code&gt;&lt;/a&gt; as our base image, which includes &lt;code&gt;pip&lt;/code&gt; to install required Python packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11.2-slim&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can copy the julia executable from the official julia docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JULIA_CI true&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JULIA_NUM_THREADS "auto"&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JULIA_PATH /usr/local/julia/&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JULIA_DEPOT_PATH /srv/juliapkg/&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH ${JULIA_PATH}/bin:${PATH}&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=julia:1.8.5 ${JULIA_PATH} ${JULIA_PATH}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  System packages (optional)
&lt;/h2&gt;

&lt;p&gt;One might need some system dependencies, installed by &lt;code&gt;apt-get&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &amp;lt;pkgs&amp;gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gnuplot&lt;/code&gt; is required by &lt;code&gt;Gnuplot.jl&lt;/code&gt; or &lt;code&gt;Gaston.jl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parallel&lt;/code&gt; can run multiple notebooks in parallel in multi-core machines (e.g. cirrus CI provides 8-core CI machines &lt;em&gt;for free&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;clang&lt;/code&gt; or &lt;code&gt;gcc&lt;/code&gt; if you want to use &lt;code&gt;PacakgeCompiler.jl&lt;/code&gt; to compile a sysimage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Python dependencies
&lt;/h2&gt;

&lt;p&gt;Install Python dependencies e.g. &lt;code&gt;matplotlib&lt;/code&gt; for &lt;code&gt;PyPlot.jl&lt;/code&gt; via requirements.txt so that it is easier to maintain and update. You can leave requirements.txt blank if you have no such dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; nbconvert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;requirements.txt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;matplotlib==3.5.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Julia dependencies
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;IJulia.jl&lt;/code&gt; is install globally for the Jupyter kernel. Julia dependencies are defined in &lt;code&gt;Project.toml&lt;/code&gt; and &lt;code&gt;Manifest.toml&lt;/code&gt; into the container's working directory and &lt;code&gt;Pkg.instantiate()&lt;/code&gt; installs the dependencies. Please make sure &lt;code&gt;Manifest.toml&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; in &lt;code&gt;.gitignore&lt;/code&gt; to ensure &lt;code&gt;Manifest.toml&lt;/code&gt; is tracked and the runtime docker image is reproducible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Julia environment&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Project.toml Manifest.toml ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/ src&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;julia &lt;span class="nt"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'import Pkg; Pkg.add("IJulia"); using IJulia; installkernel("Julia", "--project=@.")'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    julia &lt;span class="nt"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@. &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'import Pkg; Pkg.instantiate(); Pkg.resolve(); Pkg.precompile()'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  (Optiona) Building a sysimage
&lt;/h3&gt;

&lt;p&gt;You can also build a sysimage to reduce package load time. I recommend &lt;a href="https://github.com/terasakisatoshi/sysimage_creator/"&gt;Satoshi Terasaki's sysimage_creator&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;You'll need to add a compiler to the docker container by adding this line to &lt;code&gt;Dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; gcc &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and in the Julia packages part in the Dockerfile, put julia command into script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Project.toml Manifest.toml build-kernel.jl ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/ src&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;julia &lt;span class="nt"&gt;--color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes &lt;/span&gt;build-kernel.jl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Content of build-kernel.jl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="c"&gt;# Adapted from https://github.com/terasakisatoshi/sysimage_creator/&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pkg&lt;/span&gt;

&lt;span class="n"&gt;Pkg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="x"&gt;([&lt;/span&gt;&lt;span class="s"&gt;"PackageCompiler"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"IJulia"&lt;/span&gt;&lt;span class="x"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;PackageCompiler&lt;/span&gt;

&lt;span class="n"&gt;sysimage_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;joinpath&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@__DIR__&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"sysimage.so"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@info&lt;/span&gt; &lt;span class="s"&gt;"SysImage path: "&lt;/span&gt; &lt;span class="n"&gt;sysimage_path&lt;/span&gt;

&lt;span class="n"&gt;PackageCompiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_sysimage&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;
    &lt;span class="x"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Plots"&lt;/span&gt;&lt;span class="x"&gt;];&lt;/span&gt; &lt;span class="c"&gt;# Packages of your choice&lt;/span&gt;
    &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"."&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sysimage_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sysimage_path&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cpu_target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PackageCompiler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default_app_cpu_target&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;IJulia&lt;/span&gt;
&lt;span class="n"&gt;IJulia&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;installkernel&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Julia-sys"&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"--project=@."&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"--sysimage=&lt;/span&gt;&lt;span class="si"&gt;$(sysimage_path)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Pkg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PackageCompiler"&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Pkg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="x"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>docker</category>
      <category>julia</category>
      <category>jupyter</category>
      <category>gha</category>
    </item>
  </channel>
</rss>
