<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://ulysseszh.github.io/feed/guide.xml" rel="self" type="application/atom+xml" /><link href="https://ulysseszh.github.io/" rel="alternate" type="text/html" hreflang="en-US" /><updated>2026-04-30T17:49:58-07:00</updated><id>https://ulysseszh.github.io/feed/guide.xml</id><title type="html"><![CDATA[Ulysses’ trip | Guide]]></title><subtitle>Here we are at the awesome (awful) blog written by UlyssesZhan!</subtitle><author><name>UlyssesZhan</name><email>ulysseszhan@gmail.com</email></author><entry><title type="html"><![CDATA[How to use JavaScript in GitHub Actions without committing <code>node_modules</code>]]></title><link href="https://ulysseszh.github.io/guide/2024/11/15/gh-action-modules.html" rel="alternate" type="text/html" title="How to use JavaScript in GitHub Actions without committing node_modules" /><published>2024-11-15T16:34:55-08:00</published><updated>2024-11-15T16:34:55-08:00</updated><id>https://ulysseszh.github.io/guide/2024/11/15/gh-action-modules</id><content type="html" xml:base="https://ulysseszh.github.io/guide/2024/11/15/gh-action-modules.html"><![CDATA[<p>I was creating my first GitHub Action (it is now working, which you can check out <a href="https://github.com/UlyssesZh/grs-action" target="_blank" rel="external">here</a>). I am familiar with JavaScript, so I chose to create a JavaScript action. According to the <a href="https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-javascript-action#commit-tag-and-push-your-action-to-github" target="_blank" rel="external">official documentation</a>, I need to do one of the following to make my JavaScript code correctly import 3rd-party modules:</p>
<ul>
<li>commit the <code>node_modules</code> to the repo, or</li>
<li>use <a href="https://github.com/vercel/ncc" target="_blank" rel="external">@vercel/ncc</a> to bundle everything into a single script.</li>
</ul>
<p class="no-indent">
I think both of these methods are not good practices. Especially, for my case, the way with @vercel/ncc is not feasible because some package uses <code>require</code>.
</p>
<p>The best way to handle this is to make GitHub cache and install all the dependencies for the user every time the action is run. This can be made possible by <a href="https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-composite-action" target="_blank" rel="external">creating a composite action</a>. Here is a snippet in <code>action.yml</code>:</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-yaml">
        <pre>
          <code>
            <span class="line line-1"><span class="na">runs</span><span class="pi">:</span>
</span>
            <span class="line line-2">  <span class="na">using</span><span class="pi">:</span> <span class="s">composite</span>
</span>
            <span class="line line-3">  <span class="na">steps</span><span class="pi">:</span>
</span>
            <span class="line line-4">    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Node.js</span>
</span>
            <span class="line line-5">      <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-node@v4</span>
</span>
            <span class="line line-6">      <span class="na">with</span><span class="pi">:</span>
</span>
            <span class="line line-7">        <span class="na">node-version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">20'</span>
</span>
            <span class="line line-8">
</span>
            <span class="line line-9">    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Get cache key and path</span>
</span>
            <span class="line line-10">      <span class="na">id</span><span class="pi">:</span> <span class="s">lock</span>
</span>
            <span class="line line-11">      <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
</span>
            <span class="line line-12">        <span class="s">cd $GITHUB_ACTION_PATH</span>
</span>
            <span class="line line-13">        <span class="s">node &lt;&lt;'JAVASCRIPT'</span>
</span>
            <span class="line line-14">          <span class="s">const fs = require('fs');</span>
</span>
            <span class="line line-15">          <span class="s">const os = require('os');</span>
</span>
            <span class="line line-16">          <span class="s">const sha256 = require('crypto').createHash('sha256');</span>
</span>
            <span class="line line-17">          <span class="s">const hash = sha256.update(fs.readFileSync('package-lock.json')).digest('hex');</span>
</span>
            <span class="line line-18">          <span class="s">fs.appendFileSync(process.env.GITHUB_OUTPUT, `key=${os.platform()}-${os.arch()}-grs-modules-${hash}\n`);</span>
</span>
            <span class="line line-19">        <span class="s">JAVASCRIPT</span>
</span>
            <span class="line line-20">        <span class="s">modules=$(pwd)/node_modules</span>
</span>
            <span class="line line-21">        <span class="s">command -v cygpath &amp;&gt; /dev/null &amp;&amp; modules=$(cygpath -w $modules)</span>
</span>
            <span class="line line-22">        <span class="s">echo "modules=$modules" &gt;&gt; $GITHUB_OUTPUT</span>
</span>
            <span class="line line-23">        <span class="s">cat $GITHUB_OUTPUT</span>
</span>
            <span class="line line-24">        <span class="s">cd $GITHUB_WORKSPACE</span>
</span>
            <span class="line line-25">      <span class="na">shell</span><span class="pi">:</span> <span class="s">bash</span>
</span>
            <span class="line line-26">
</span>
            <span class="line line-27">    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Restore cache</span>
</span>
            <span class="line line-28">      <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache/restore@v4</span>
</span>
            <span class="line line-29">      <span class="na">id</span><span class="pi">:</span> <span class="s">cache-restore</span>
</span>
            <span class="line line-30">      <span class="na">with</span><span class="pi">:</span>
</span>
            <span class="line line-31">        <span class="na">path</span><span class="pi">:</span> <span class="s">${{ steps.lock.outputs.modules }}</span>
</span>
            <span class="line line-32">        <span class="na">key</span><span class="pi">:</span> <span class="s">${{ steps.lock.outputs.key }}</span>
</span>
            <span class="line line-33">
</span>
            <span class="line line-34">    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">CI</span>
</span>
            <span class="line line-35">      <span class="na">if</span><span class="pi">:</span> <span class="s">steps.cache-restore.outputs.cache-hit != 'true'</span>
</span>
            <span class="line line-36">      <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
</span>
            <span class="line line-37">        <span class="s">cd $GITHUB_ACTION_PATH</span>
</span>
            <span class="line line-38">        <span class="s">npm ci</span>
</span>
            <span class="line line-39">        <span class="s">cd $GITHUB_WORKSPACE</span>
</span>
            <span class="line line-40">      <span class="na">shell</span><span class="pi">:</span> <span class="s">bash</span>
</span>
            <span class="line line-41">
</span>
            <span class="line line-42">    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Save cache</span>
</span>
            <span class="line line-43">      <span class="na">if</span><span class="pi">:</span> <span class="s">steps.cache-restore.outputs.cache-hit != 'true'</span>
</span>
            <span class="line line-44">      <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/cache/save@v4</span>
</span>
            <span class="line line-45">      <span class="na">with</span><span class="pi">:</span>
</span>
            <span class="line line-46">        <span class="na">path</span><span class="pi">:</span> <span class="s">${{ steps.lock.outputs.modules }}</span>
</span>
            <span class="line line-47">        <span class="na">key</span><span class="pi">:</span> <span class="s">${{ steps.lock.outputs.key }}</span>
</span>
            <span class="line line-48">
</span>
            <span class="line line-49">    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Run</span>
</span>
            <span class="line line-50">      <span class="na">run</span><span class="pi">:</span> <span class="s">node $GITHUB_ACTION_PATH/index.js</span>
</span>
            <span class="line line-51">      <span class="na">shell</span><span class="pi">:</span> <span class="s">bash</span>
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>I manually use <code>actions/cache</code> to cache the <code>node_modules</code> directory although <code>actions/setup-node</code> has caching functionality. This is because it <a href="https://github.com/actions/setup-node/issues/819" target="_blank" rel="external">is not good yet</a>. Also, because <code>github.action_path</code> ends with <code>/./</code>, it can cause some problem due to the relative pathing. My workflow here works around those problems.</p>
<p>The hash in the cache key is calculated using JavaScript because there is no <code>sha256sum</code> command on macOS and Ubuntu runners.</p>]]></content><author><name>UlyssesZhan</name><email>ulysseszhan@gmail.com</email></author><category term="guide" /><category term="javascript" /><category term="github" /><summary type="html"><![CDATA[I was creating my first GitHub Action. According to the official documentation, I should commit the <code>node_modules</code> to the repo or generate a script that bundles the whole <code>node_modules</code> if I want to create a JavaScript action. I think this is not good practice, so I try to find another way: manually managing the caches for the dependencies in <code>node_modules</code>.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ulysseszh.github.io/assets/images/covers/2024-11-15-gh-action-modules.png" /><media:content medium="image" url="https://ulysseszh.github.io/assets/images/covers/2024-11-15-gh-action-modules.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html"><![CDATA[Letting people know when you are asleep]]></title><link href="https://ulysseszh.github.io/guide/2024/02/21/sleep-status.html" rel="alternate" type="text/html" title="Letting people know when you are asleep" /><published>2024-02-21T02:26:45-08:00</published><updated>2024-02-21T02:26:45-08:00</updated><id>https://ulysseszh.github.io/guide/2024/02/21/sleep-status</id><content type="html" xml:base="https://ulysseszh.github.io/guide/2024/02/21/sleep-status.html"><![CDATA[<p>While I have been trying to respond whenever people reach out to me, it is impossible to be available 24/7 due to the simple fact that <a href="https://www.sleepfoundation.org/how-sleep-works/why-do-we-need-sleep" target="_blank" rel="external">a human has to sleep to be alive</a>. While this is annoying, it is also a fact that I cannot change.</p>
<p>Therefore, a natural idea that comes to my mind is to simply let people know when I am asleep so that they do not expect me to respond immediately.</p>
<p>Discord and GitHub have been among my most used platforms for some time, and they both have a feature to let users set a custom status (with a custom text and an emoji). This opens up a possibility of using a program to automatically set the status to indicate that I am asleep.</p>
<p>For Discord, this is as simple as invoking a REST API (<a href="https://support.discord.com/hc/en-us/articles/115002192352-Automated-user-accounts-self-bots" target="_blank" rel="external"><strong>Notice that this is against Discord’s ToS</strong></a>):</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-shell">
        <pre>
          <code>
            <span class="line line-1"><span class="c"># Set sleeping status</span>
</span>
            <span class="line line-2">curl <span class="nt">-X</span> PATCH <span class="se">\</span>
</span>
            <span class="line line-3">	<span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
</span>
            <span class="line line-4">	<span class="nt">-H</span> <span class="s2">"Authorization: YoUr.DiScOrD.ToKeN"</span> <span class="se">\</span>
</span>
            <span class="line line-5">	<span class="nt">-d</span> <span class="s1">'{"settings":"WhwKBQoDZG5kEhMKC1NsZWVwaW5nLi4uGgTwn5i0"}'</span> <span class="se">\</span>
</span>
            <span class="line line-6">	https://discord.com/api/v9/users/@me/settings-proto/1
</span>
            <span class="line line-7">
</span>
            <span class="line line-8">
</span>
            <span class="line line-9"><span class="c"># Clear sleeping status</span>
</span>
            <span class="line line-10">curl <span class="nt">-X</span> PATCH <span class="se">\</span>
</span>
            <span class="line line-11">	<span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
</span>
            <span class="line line-12">	<span class="nt">-H</span> <span class="s2">"Authorization: YoUr.DiScOrD.ToKeN"</span> <span class="se">\</span>
</span>
            <span class="line line-13">	<span class="nt">-d</span> <span class="s1">'{"settings":"WgoKCAoGb25saW5l"}'</span> <span class="se">\</span>
</span>
            <span class="line line-14">	https://discord.com/api/v9/users/@me/settings-proto/1
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>If you wonder what those base64-encoded strings mean, they are actually the binary contents of protobuf-encoded messages. See <a href="https://github.com/discord-userdoccers/discord-protos/blob/master/discord_protos/discord_users/v1/PreloadedUserSettings.proto" target="_blank" rel="external">this <code>.proto</code> file</a> for the definition of the message structure.</p>
<details>
<summary>
API v8
</summary>
<p>The commands written above was added when editing this article on July 20, 2025, which uses API v9. The latest API version as of originally writing this article was v8, but now <strong>using API v8 will get your account disabled</strong>, so do not use it!</p>
<table class="rouge-table"><tbody><tr><td class="highlight language-shell"><pre><code><span class="line line-1"><span class="c"># Set sleeping status</span>
</span><span class="line line-2">curl <span class="nt">-X</span> PATCH <span class="se">\</span>
</span><span class="line line-3">	<span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
</span><span class="line line-4">	<span class="nt">-H</span> <span class="s2">"Authorization: YoUr.DiScOrD.ToKeN"</span> <span class="se">\</span>
</span><span class="line line-5">	<span class="nt">-d</span> <span class="s1">'{"custom_status":{"text":"Sleeping...","emoji_id":null,"emoji_name":"😴","expires_at":null},"status":"dnd"}'</span> <span class="se">\</span>
</span><span class="line line-6">	https://discordapp.com/api/v8/users/@me/settings
</span><span class="line line-7">
</span><span class="line line-8"><span class="c"># Clear sleeping status</span>
</span><span class="line line-9">curl <span class="nt">-X</span> PATCH <span class="se">\</span>
</span><span class="line line-10">	<span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
</span><span class="line line-11">	<span class="nt">-H</span> <span class="s2">"Authorization: YoUr.DiScOrD.ToKeN"</span> <span class="se">\</span>
</span><span class="line line-12">	<span class="nt">-d</span> <span class="s1">'{"custom_status":null,"status":"online"}'</span> <span class="se">\</span>
</span><span class="line line-13">	https://discordapp.com/api/v8/users/@me/settings
</span></code></pre></td></tr></tbody></table>
</details>
<p>For GitHub, <a href="https://github.com/orgs/community/discussions/108473" target="_blank" rel="external">there is not a REST API for that</a>, but you can install the <a href="https://github.com/vilmibm/gh-user-status" target="_blank" rel="external">user-status plugin</a> for <a href="https://cli.github.com/" target="_blank" rel="external">GitHub CLI</a>:</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-shell">
        <pre>
          <code>
            <span class="line line-1">gh extension <span class="nb">install </span>vilmibm/gh-user-status
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p class="no-indent">
Then, you can set the status with:
</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-shell">
        <pre>
          <code>
            <span class="line line-1"><span class="c"># Set sleeping status</span>
</span>
            <span class="line line-2">gh user-status <span class="nb">set</span> <span class="s1">'Sleeping...'</span> <span class="nt">--emoji</span><span class="o">=</span><span class="s1">'sleeping'</span> <span class="nt">--limited</span>
</span>
            <span class="line line-3">
</span>
            <span class="line line-4"><span class="c"># Clear sleeping status</span>
</span>
            <span class="line line-5">gh user-status <span class="nb">set</span> <span class="s1">'null'</span> <span class="nt">--expiry</span><span class="o">=</span>1s
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>Now, the next step is to run these commands automatically when I fall asleep and wake up. This can be done with <a href="https://www.macrodroid.com/" target="_blank" rel="external">MacroDroid</a>, which can trigger actions based on various triggers. To run arbitrary commands, you can use <a href="https://f-droid.org/en/packages/com.termux.tasker/" target="_blank" rel="external">the Tasker plugin for Termux</a>. To have it working, one also needs to uncomment <code>allow-external-apps = true</code> in <code>~/.termux/termux.properties</code>, and grant MacroDroid the permission to run Termux commands by</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-shell">
        <pre>
          <code>
            <span class="line line-1">adb shell pm grant com.arlosoft.macrodroid com.termux.permission.RUN_COMMAND
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>MacroDroid supports using the return value of <a href="https://developers.google.com/location-context/sleep" target="_blank" rel="external">the sleep API</a> to trigger an action, but this tends to be quite unreliable on my device. Therefore, I use it in conjunction with a quick setting tile that I can toggle manually. The macro has two triggers:</p>
<ul>
<li>Fell Asleep / Woke Up (Android sleep API),</li>
<li>Quick Tile On/Off,</li>
</ul>
<p class="no-indent">
and it has these actions:
</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-plain">
        <pre>
          <code>
            <span class="line line-1">If Trigger Fired: Woke Up, or Quick Tile Off
</span>
            <span class="line line-2">	If Sleeping = True
</span>
            <span class="line line-3">		Clear sleeping status on Discord and GitHub
</span>
            <span class="line line-4">		# Include other waking up logic here, such as turning off DND mode
</span>
            <span class="line line-5">	End If
</span>
            <span class="line line-6">	Sleeping = False
</span>
            <span class="line line-7">Else If Trigger Fired: Fell Asleep, or Quick Tile On
</span>
            <span class="line line-8">	If Sleeping = False
</span>
            <span class="line line-9">		Set GitHub and Discord user status to sleeping
</span>
            <span class="line line-10">		# Include other falling asleep logic here, such as turning on DND mode
</span>
            <span class="line line-11">	End If
</span>
            <span class="line line-12">	Sleeping = True
</span>
            <span class="line line-13">End If
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>By the way, I have a bunch of topics that I want to write blog articles about, but I have been quite busy recently, so I may have to pause updating this blog for a while. I hope I can get back to writing soon!</p>]]></content><author><name>UlyssesZhan</name><email>ulysseszhan@gmail.com</email></author><category term="guide" /><category term="android" /><category term="github" /><summary type="html"><![CDATA[While I am asleep, the sleeping state is indicated on my Discord and GitHub user status. I achieve this by using MacroDroid.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ulysseszh.github.io/assets/images/covers/2024-02-21-sleep-status.png" /><media:content medium="image" url="https://ulysseszh.github.io/assets/images/covers/2024-02-21-sleep-status.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html"><![CDATA[Using nmcli to connect to eduroam in UCSB]]></title><link href="https://ulysseszh.github.io/guide/2024/01/08/nmcli-eduroam.html" rel="alternate" type="text/html" title="Using nmcli to connect to eduroam in UCSB" /><published>2024-01-08T01:03:36-08:00</published><updated>2024-01-08T01:03:36-08:00</updated><id>https://ulysseszh.github.io/guide/2024/01/08/nmcli-eduroam</id><content type="html" xml:base="https://ulysseszh.github.io/guide/2024/01/08/nmcli-eduroam.html"><![CDATA[<p>Save this certificate to some file, say <code>/YOUR/PATH/TO/ca.pem</code>:</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-plain">
        <pre>
          <code>
            <span class="line line-1">-----BEGIN CERTIFICATE-----
</span>
            <span class="line line-2">MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
</span>
            <span class="line line-3">MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
</span>
            <span class="line line-4">GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
</span>
            <span class="line line-5">YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
</span>
            <span class="line line-6">MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
</span>
            <span class="line line-7">BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
</span>
            <span class="line line-8">GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
</span>
            <span class="line line-9">ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
</span>
            <span class="line line-10">BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
</span>
            <span class="line line-11">3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
</span>
            <span class="line line-12">YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
</span>
            <span class="line line-13">rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
</span>
            <span class="line line-14">ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
</span>
            <span class="line line-15">oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
</span>
            <span class="line line-16">MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
</span>
            <span class="line line-17">QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
</span>
            <span class="line line-18">b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
</span>
            <span class="line line-19">AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
</span>
            <span class="line line-20">GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
</span>
            <span class="line line-21">Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
</span>
            <span class="line line-22">G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
</span>
            <span class="line line-23">l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
</span>
            <span class="line line-24">smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
</span>
            <span class="line line-25">-----END CERTIFICATE-----
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>Then, run</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-shell">
        <pre>
          <code>
            <span class="line line-1">nmcli con mod eduroam 802-1x.eap peap
</span>
            <span class="line line-2">nmcli con mod eduroam 802-11-wireless-security.key-mgmt wpa-eap
</span>
            <span class="line line-3">nmcli con mod eduroam 802-11-wireless-security.proto rsn
</span>
            <span class="line line-4">nmcli con mod eduroam 802-11-wireless-security.pairwise ccmp
</span>
            <span class="line line-5">nmcli con mod eduroam 802-11-wireless-security.group ccmp,tkip
</span>
            <span class="line line-6">nmcli con mod eduroam 802-1x.ca-cert /YOUR/PATH/TO/ca.pem
</span>
            <span class="line line-7">nmcli con mod eduroam 802-1x.phase2-autheap mschapv2
</span>
            <span class="line line-8">nmcli con mod eduroam 802-1x.anonymous-identity anonymous@ucsb.edu
</span>
            <span class="line line-9">nmcli con mod eduroam 802-1x.identity YOUR_EDU_EMAIL_ADDRESS
</span>
            <span class="line line-10">nmcli con mod eduroam 802-1x.password YOUR_PASSWORD
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>Finally, you can connect to eduroam now!</p>
<p>This may also apply to eduroam in other campuses, but I haven’t tested it yet.</p>]]></content><author><name>UlyssesZhan</name><email>ulysseszhan@gmail.com</email></author><category term="guide" /><category term="linux" /><category term="school" /><summary type="html"><![CDATA[Use nmcli to connect to eduroam in UCSB without using the configuration assistant tool provided by eduroam.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ulysseszh.github.io/assets/images/covers/2024-01-08-nmcli-eduroam.png" /><media:content medium="image" url="https://ulysseszh.github.io/assets/images/covers/2024-01-08-nmcli-eduroam.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html"><![CDATA[How to self-host Overleaf (ShareLaTeX)]]></title><link href="https://ulysseszh.github.io/guide/2023/09/29/self-host-overleaf.html" rel="alternate" type="text/html" title="How to self-host Overleaf (ShareLaTeX)" /><published>2023-09-29T11:11:38-07:00</published><updated>2023-09-29T11:11:38-07:00</updated><id>https://ulysseszh.github.io/guide/2023/09/29/self-host-overleaf</id><content type="html" xml:base="https://ulysseszh.github.io/guide/2023/09/29/self-host-overleaf.html"><![CDATA[<p>Install Docker and NGINX before you go. Replace every <code>overleaf.your-domain.com</code> with your own domain name.</p>
<h2 data-label="0.1" id="set-up-nginx">Set up NGINX</h2>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-nginx">
        <pre>
          <code>
            <span class="line line-1"><span class="k">server</span> <span class="p">{</span>
</span>
            <span class="line line-2">	<span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span>
</span>
            <span class="line line-3">	<span class="kn">listen</span> <span class="s">[::]:80</span><span class="p">;</span>
</span>
            <span class="line line-4">	<span class="kn">server_name</span> <span class="s">overleaf.your-domain.com</span><span class="p">;</span>
</span>
            <span class="line line-5">	<span class="kn">return</span> <span class="mi">301</span> <span class="s">https://</span><span class="nv">$host$request_uri</span><span class="p">;</span>
</span>
            <span class="line line-6"><span class="p">}</span>
</span>
            <span class="line line-7"><span class="k">server</span> <span class="p">{</span>
</span>
            <span class="line line-8">	<span class="kn">listen</span> <span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
</span>
            <span class="line line-9">	<span class="kn">listen</span> <span class="s">[::]:443</span> <span class="s">ssl</span><span class="p">;</span>
</span>
            <span class="line line-10">	<span class="kn">server_name</span> <span class="s">overleaf.your-domain.com</span><span class="p">;</span>
</span>
            <span class="line line-11">	<span class="kn">ssl_certificate</span> <span class="n">/path/to/your/cert/fullchain.pem</span><span class="p">;</span>
</span>
            <span class="line line-12">	<span class="kn">ssl_certificate_key</span> <span class="n">/path/to/your/cert/privkey.pem</span><span class="p">;</span>
</span>
            <span class="line line-13">	<span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
</span>
            <span class="line line-14">		<span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$host</span><span class="p">;</span>
</span>
            <span class="line line-15">		<span class="kn">proxy_pass</span> <span class="s">http://localhost:8444</span><span class="p">;</span> <span class="c1"># use a port you like</span>
</span>
            <span class="line line-16">		<span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
</span>
            <span class="line line-17">
</span>
            <span class="line line-18">		<span class="kn">proxy_http_version</span> <span class="mf">1.1</span><span class="p">;</span>
</span>
            <span class="line line-19">		<span class="kn">proxy_set_header</span> <span class="s">Upgrade</span> <span class="nv">$http_upgrade</span><span class="p">;</span>
</span>
            <span class="line line-20">		<span class="kn">proxy_set_header</span> <span class="s">Connection</span> <span class="nv">$connection_upgrade</span><span class="p">;</span>
</span>
            <span class="line line-21">		<span class="kn">proxy_set_header</span> <span class="s">X-Scheme</span> <span class="nv">$scheme</span><span class="p">;</span>
</span>
            <span class="line line-22">
</span>
            <span class="line line-23">		<span class="kn">proxy_buffering</span> <span class="no">off</span><span class="p">;</span>
</span>
            <span class="line line-24">	<span class="p">}</span>
</span>
            <span class="line line-25">	<span class="kn">location</span> <span class="p">~</span> <span class="sr">/.well-known</span> <span class="p">{</span>
</span>
            <span class="line line-26">		<span class="kn">allow</span> <span class="s">all</span><span class="p">;</span>
</span>
            <span class="line line-27">	<span class="p">}</span>
</span>
            <span class="line line-28"><span class="p">}</span>
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<h2 data-label="0.2" id="set-up-sharelatex">Set up ShareLaTeX</h2>
<p><em>Refer to</em>: <a href="https://github.com/overleaf/toolkit/blob/master/doc/quick-start-guide.md" target="_blank" rel="external">quick start guide</a>.</p>
<p>Run the following:</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-shell">
        <pre>
          <code>
            <span class="line line-1"><span class="nv">OVERLEAF_HOME</span><span class="o">=</span>./overleaf <span class="c"># set to whatever you want; sudo in following if not having write access</span>
</span>
            <span class="line line-2">git clone https://github.com/overleaf/toolkit.git <span class="nv">$OVERLEAF_HOME</span>
</span>
            <span class="line line-3"><span class="nb">cd</span> <span class="nv">$OVERLEAF_HOME</span>
</span>
            <span class="line line-4">bin/init
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p><em>Notice</em>: You may look at <code>git log -n 1 --pretty=format:"%H"</code>. Mine was <code>cc4d01bb46d4e0d7c08124372ff69a4578e7333d</code>. I can guarantee that the following steps work with this version of Overleaf toolkit, but may fail in future versions.</p>
<p>Edit <code>config/variables.env</code>, add the following:</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-ini">
        <pre>
          <code>
            <span class="line line-1"><span class="c"># See https://github.com/overleaf/overleaf/issues/1044#issuecomment-1741289459</span>
</span>
            <span class="line line-2"><span class="py">PATH</span><span class="p">=</span><span class="s">/usr/local/texlive/2023/bin/x86_64-linux:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin</span>
</span>
            <span class="line line-3">
</span>
            <span class="line line-4"><span class="py">SHARELATEX_BEHIND_PROXY</span><span class="p">=</span><span class="s">true</span>
</span>
            <span class="line line-5"><span class="c"># Do NOT set SHARELATEX_SECURE_COOKIE to true, see https://github.com/overleaf/overleaf/issues/388#issuecomment-1741162658</span>
</span>
            <span class="line line-6">
</span>
            <span class="line line-7"><span class="py">SHARELATEX_SITE_URL</span><span class="p">=</span><span class="s">https://overleaf.your-domain.com</span>
</span>
            <span class="line line-8"><span class="c"># If you want, set SHARELATEX_APP_NAME, SHARELATEX_NAV_TITLE, SHARELATEX_HEADER_IMAGE_URL, SHARELATEX_ADMIN_EMAIL.</span>
</span>
            <span class="line line-9">
</span>
            <span class="line line-10"><span class="c"># Email settings, see https://github.com/overleaf/overleaf/issues/816#issuecomment-864665071</span>
</span>
            <span class="line line-11"><span class="py">SHARELATEX_EMAIL_FROM_ADDRESS</span><span class="p">=</span><span class="s">Overleaf &lt;your.email@domain.com&gt;</span>
</span>
            <span class="line line-12"><span class="c"># SHARELATEX_EMAIL_REPLY_TO does not seem to work.</span>
</span>
            <span class="line line-13"><span class="py">SHARELATEX_EMAIL_SMTP_HOST</span><span class="p">=</span><span class="s">smtp.domain.com</span>
</span>
            <span class="line line-14"><span class="py">SHARELATEX_EMAIL_SMTP_PORT</span><span class="p">=</span><span class="s">465</span>
</span>
            <span class="line line-15"><span class="py">SHARELATEX_EMAIL_SMTP_SECURE</span><span class="p">=</span><span class="s">true</span>
</span>
            <span class="line line-16"><span class="py">SHARELATEX_EMAIL_SMTP_USER</span><span class="p">=</span><span class="s">your.email@domain.com</span>
</span>
            <span class="line line-17"><span class="py">SHARELATEX_EMAIL_SMTP_PASS</span><span class="p">=</span><span class="s">yourpassword</span>
</span>
            <span class="line line-18"><span class="py">SHARELATEX_EMAIL_SMTP_TLS_REJECT_UNAUTH</span><span class="p">=</span><span class="s">false</span>
</span>
            <span class="line line-19"><span class="py">SHARELATEX_EMAIL_SMTP_IGNORE_TLS</span><span class="p">=</span><span class="s">false</span>
</span>
            <span class="line line-20">
</span>
            <span class="line line-21"><span class="c"># Uncomment this when you want to debug:</span>
</span>
            <span class="line line-22"><span class="c">#LOG_LEVEL=debug</span>
</span>
            <span class="line line-23">
</span>
            <span class="line line-24"><span class="c"># Search for process.env in github.com/overleaf/overleaf to see more options (shame for not documenting them):</span>
</span>
            <span class="line line-25"><span class="c"># https://github.com/search?q=repo%3Aoverleaf%2Foverleaf+process.env&amp;type=code</span>
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>Edit these entries in <code>config/overleaf.rc</code>:</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-ini">
        <pre>
          <code>
            <span class="line line-1"><span class="c"># Match the port in NGINX conf</span>
</span>
            <span class="line line-2"><span class="py">SHARELATEX_PORT</span><span class="p">=</span><span class="s">8444</span>
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>Create a file <code>config/docker-compose.override.yml</code> and write:</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-yaml">
        <pre>
          <code>
            <span class="line line-1"><span class="nn">---</span>
</span>
            <span class="line line-2"><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">2.2'</span>
</span>
            <span class="line line-3"><span class="na">services</span><span class="pi">:</span>
</span>
            <span class="line line-4">  <span class="na">mongo</span><span class="pi">:</span>
</span>
            <span class="line line-5">    <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
</span>
            <span class="line line-6">    <span class="na">container_name</span><span class="pi">:</span> <span class="s">overleaf-mongo</span>
</span>
            <span class="line line-7">
</span>
            <span class="line line-8">  <span class="na">redis</span><span class="pi">:</span>
</span>
            <span class="line line-9">    <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
</span>
            <span class="line line-10">    <span class="na">container_name</span><span class="pi">:</span> <span class="s">overleaf-redis</span>
</span>
            <span class="line line-11">
</span>
            <span class="line line-12">  <span class="na">sharelatex</span><span class="pi">:</span>
</span>
            <span class="line line-13">    <span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
</span>
            <span class="line line-14">    <span class="c1">#image: sharelatex/sharelatex:with-texlive-full # will be uncommented later</span>
</span>
            <span class="line line-15">    <span class="na">container_name</span><span class="pi">:</span> <span class="s">overleaf-sharelatex</span>
</span>
            <span class="line line-16">    <span class="na">stop_grace_period</span><span class="pi">:</span> <span class="s">10s</span> <span class="c1"># see https://github.com/overleaf/overleaf/issues/1156</span>
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>Run <code>bin/up</code>. Wait for the containers to be up. Then, go to <code>https://overleaf.your-domain.com/launchpad</code> and set up the admin account. Use <code>bin/logs</code> in another shell to check the logs if there are any issues.</p>
<h2 data-label="0.3" id="set-up-texlive">Set up TeXLive</h2>
<p><em>Refer to</em>: <a href="https://github.com/overleaf/toolkit/blob/master/doc/ce-upgrading-texlive.md" target="_blank" rel="external">upgrading TeXLive</a>.</p>
<p>While the containers are up, run <code>bin/shell</code> and run <code>tlmgr install scheme-full</code>. You need to wait for a long time. If you run into the <a href="https://github.com/overleaf/overleaf/issues/1009" target="_blank" rel="external">problem</a> of “Local TeX Live is older than remote repository,” you need to run <code>update-tlmgr-latest.sh</code> in the container (after fetching it from CTAN first).</p>
<p>After that, run <code>docker commit overleaf-sharelatex sharelatex/sharelatex:with-texlive-full</code>. Then, edit <code>config/docker-compose.override.yml</code> and uncomment the line <code>image: sharelatex/sharelatex:with-texlive-full</code>. Then, run</p>
<table class="rouge-table">
  <tbody>
    <tr>
      <td class="highlight language-shell">
        <pre>
          <code>
            <span class="line line-1">bin/stop
</span>
            <span class="line line-2">bin/docker-compose <span class="nb">rm</span> <span class="nt">-f</span> sharelatex
</span>
            <span class="line line-3">bin/up
</span>
          </code>
        </pre>
      </td>
    </tr>
  </tbody>
</table>
<p>When you upgrade later, you need to re-comment the line in <code>config/docker-compose.override.yml</code>, delete the container, and do the above steps again.</p>]]></content><author><name>UlyssesZhan</name><email>ulysseszhan@gmail.com</email></author><category term="guide" /><category term="linux" /><category term="selfhosting" /><category term="tex" /><summary type="html"><![CDATA[It has been a pain setting up Overleaf on my own server. I have finally figured it out and I am sharing my notes here.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://ulysseszh.github.io/assets/images/covers/2023-09-29-self-host-overleaf.png" /><media:content medium="image" url="https://ulysseszh.github.io/assets/images/covers/2023-09-29-self-host-overleaf.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>